From 2572fae6ecc815f1db646b088478e46bf9f5dffa Mon Sep 17 00:00:00 2001 From: HarshitZom Date: Fri, 17 Oct 2025 21:15:48 +0530 Subject: [PATCH 01/13] created aws-specific mode --- core/index.d.ts | 2 +- core/llm/defaultSystemMessages.ts | 49 +++ extensions/vscode/package-lock.json | 4 +- gui/src/components/ModeSelect/ModeIcon.tsx | 3 + gui/src/components/ModeSelect/ModeSelect.tsx | 29 +- gui/src/redux/util/getBaseSystemMessage.ts | 5 +- manual-testing-sandbox/dynamodb/README.md | 301 ++++++++++++++++ manual-testing-sandbox/dynamodb/go.mod | 9 + manual-testing-sandbox/dynamodb/main.go | 352 +++++++++++++++++++ 9 files changed, 749 insertions(+), 5 deletions(-) create mode 100644 manual-testing-sandbox/dynamodb/README.md create mode 100644 manual-testing-sandbox/dynamodb/go.mod create mode 100644 manual-testing-sandbox/dynamodb/main.go diff --git a/core/index.d.ts b/core/index.d.ts index 685b8ade1bd..ea4db0944a2 100644 --- a/core/index.d.ts +++ b/core/index.d.ts @@ -462,7 +462,7 @@ export interface PromptLog { completion: string; } -export type MessageModes = "chat" | "agent" | "plan"; +export type MessageModes = "chat" | "agent" | "plan" | "aws-sdk-expert"; export type ToolStatus = | "generating" // Tool call arguments are being streamed from the LLM diff --git a/core/llm/defaultSystemMessages.ts b/core/llm/defaultSystemMessages.ts index 43ab7990ec7..e700ff3e97c 100644 --- a/core/llm/defaultSystemMessages.ts +++ b/core/llm/defaultSystemMessages.ts @@ -89,3 +89,52 @@ However, only output codeblocks for suggestion and planning purposes. When ready In plan mode, only write code when directly suggesting changes. Prioritize understanding and developing a plan. `; + +export const DEFAULT_AWS_SDK_EXPERT_SYSTEM_MESSAGE = `\ + + You are in AWS SDK Expert mode - specialized for AWS SDK development across all languages (JavaScript/TypeScript, Python, Java, Go, .NET, Ruby, PHP, etc.). + + ## Core Behavior + - Interpret ALL queries as AWS SDK related unless explicitly stated otherwise + - Examples: + - "upload file" → "upload file to S3 using AWS SDK" + - "send message" → "send message via SQS/SNS using AWS SDK" + - "query database" → "query DynamoDB using AWS SDK" + - "invoke function" → "invoke Lambda function using AWS SDK" + - "store data" → "store data in S3/DynamoDB/RDS using AWS SDK" + + ## Context7 MCP Usage + - You have access to Context7 MCP tools for fetching latest AWS SDK documentation + - ALWAYS use Context7 tools to get up-to-date SDK documentation before answering + - If Context7 is not available, use your knowledge but mention it may not be the latest version + - Query Context7 for specific AWS services mentioned in the user's question + + ## Tool Access + - All agent tools are available (read, write, execute files) + - Can call multiple read-only tools simultaneously + - Use file operations to examine existing AWS SDK usage in the codebase + + ## Response Requirements + - Provide working code examples with latest SDK syntax + - Include proper error handling (try-catch, SDK-specific error types) + - Include necessary imports/requires at the start + - Mention SDK versions when relevant (e.g., AWS SDK v3 for JavaScript) + - Consider AWS best practices: + - Use IAM roles instead of hardcoded credentials + - Enable encryption (at rest and in transit) + - Implement retry logic with exponential backoff + - Use environment variables for configuration + - Handle pagination for list operations + - Close/cleanup resources properly + +${CODEBLOCK_FORMATTING_INSTRUCTIONS} + +${BRIEF_LAZY_INSTRUCTIONS} + + However, only output codeblocks for suggestion and demonstration purposes. For implementing changes, use the edit tools. + + ## When User Asks Non-AWS Questions + - If the query is clearly unrelated to AWS SDKs, politely mention this mode is specialized for AWS SDK development + - Suggest switching to Agent or Chat mode for general programming questions + +`; diff --git a/extensions/vscode/package-lock.json b/extensions/vscode/package-lock.json index 5ca2cc7be12..567e6fc0727 100644 --- a/extensions/vscode/package-lock.json +++ b/extensions/vscode/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "1.3.16", + "version": "1.3.18", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "continue", - "version": "1.3.16", + "version": "1.3.18", "license": "Apache-2.0", "dependencies": { "@continuedev/config-types": "file:../../packages/config-types", diff --git a/gui/src/components/ModeSelect/ModeIcon.tsx b/gui/src/components/ModeSelect/ModeIcon.tsx index 92dfc1723f5..2ffff199a64 100644 --- a/gui/src/components/ModeSelect/ModeIcon.tsx +++ b/gui/src/components/ModeSelect/ModeIcon.tsx @@ -1,5 +1,6 @@ import { ChatBubbleLeftIcon, + CloudIcon, SparklesIcon, SwatchIcon, } from "@heroicons/react/24/outline"; @@ -21,5 +22,7 @@ export function ModeIcon({ return ; case "chat": return ; + case "aws-sdk-expert": + return ; } } diff --git a/gui/src/components/ModeSelect/ModeSelect.tsx b/gui/src/components/ModeSelect/ModeSelect.tsx index 88a5490127c..fb84566f069 100644 --- a/gui/src/components/ModeSelect/ModeSelect.tsx +++ b/gui/src/components/ModeSelect/ModeSelect.tsx @@ -39,6 +39,8 @@ export function ModeSelect() { dispatch(setMode("plan")); } else if (mode === "plan") { dispatch(setMode("agent")); + } else if (mode === "agent") { + dispatch(setMode("aws-sdk-expert")); } else { dispatch(setMode("chat")); } @@ -96,7 +98,13 @@ export function ModeSelect() { > - {mode === "chat" ? "Chat" : mode === "agent" ? "Agent" : "Plan"} + {mode === "chat" + ? "Chat" + : mode === "agent" + ? "Agent" + : mode === "plan" + ? "Plan" + : "AWS SDK Expert"} + +
+ + AWS SDK Expert + + + +
+ {!isGoodAtAgentMode && notGreatAtAgent} + +
+
{`${metaKeyLabel} . for next mode`}
diff --git a/gui/src/redux/util/getBaseSystemMessage.ts b/gui/src/redux/util/getBaseSystemMessage.ts index c03493b158a..99032cefd40 100644 --- a/gui/src/redux/util/getBaseSystemMessage.ts +++ b/gui/src/redux/util/getBaseSystemMessage.ts @@ -1,6 +1,7 @@ import { ModelDescription, Tool } from "core"; import { DEFAULT_AGENT_SYSTEM_MESSAGE, + DEFAULT_AWS_SDK_EXPERT_SYSTEM_MESSAGE, DEFAULT_CHAT_SYSTEM_MESSAGE, DEFAULT_PLAN_SYSTEM_MESSAGE, } from "core/llm/defaultSystemMessages"; @@ -19,11 +20,13 @@ export function getBaseSystemMessage( baseMessage = model.baseAgentSystemMessage ?? DEFAULT_AGENT_SYSTEM_MESSAGE; } else if (messageMode === "plan") { baseMessage = model.basePlanSystemMessage ?? DEFAULT_PLAN_SYSTEM_MESSAGE; + } else if (messageMode === "aws-sdk-expert") { + baseMessage = DEFAULT_AWS_SDK_EXPERT_SYSTEM_MESSAGE; } else { baseMessage = model.baseChatSystemMessage ?? DEFAULT_CHAT_SYSTEM_MESSAGE; } - // Add no-tools warning for agent/plan modes when no tools are available + // Add no-tools warning for agent/plan/aws-sdk-expert modes when no tools are available if (messageMode !== "chat" && (!activeTools || activeTools.length === 0)) { baseMessage += NO_TOOL_WARNING; } diff --git a/manual-testing-sandbox/dynamodb/README.md b/manual-testing-sandbox/dynamodb/README.md new file mode 100644 index 00000000000..9bd1ebf2205 --- /dev/null +++ b/manual-testing-sandbox/dynamodb/README.md @@ -0,0 +1,301 @@ +# DynamoDB Integration with Go (AWS SDK v2) + +This example demonstrates how to integrate Amazon DynamoDB into your Go application using the AWS SDK for Go v2. + +## Features + +✅ **Complete CRUD Operations** + +- Create (PutItem) +- Read (GetItem) +- Update (UpdateItem) +- Delete (DeleteItem) + +✅ **Advanced Operations** + +- Batch Write (up to 25 items) +- Query with Pagination +- Scan with Filters + +✅ **Best Practices** + +- Proper error handling +- Context support +- Structured logging +- Type-safe attribute handling + +## Prerequisites + +1. **Go 1.21+** installed +2. **AWS Account** with DynamoDB access +3. **AWS Credentials** configured (via environment variables, IAM role, or credentials file) + +## Setup + +### 1. Install Dependencies + +```bash +cd dynamodb +go mod download +``` + +### 2. Create DynamoDB Table + +You need a DynamoDB table named `Users` with the following schema: + +**Primary Key:** + +- Partition Key: `UserID` (String) + +You can create it via AWS Console, CLI, or using the following AWS CLI command: + +```bash +aws dynamodb create-table \ + --table-name Users \ + --attribute-definitions \ + AttributeName=UserID,AttributeType=S \ + --key-schema \ + AttributeName=UserID,KeyType=HASH \ + --provisioned-throughput \ + ReadCapacityUnits=5,WriteCapacityUnits=5 \ + --region us-west-2 +``` + +### 3. Configure AWS Credentials + +**Option A: Environment Variables** + +```bash +export AWS_ACCESS_KEY_ID=your_access_key +export AWS_SECRET_ACCESS_KEY=your_secret_key +export AWS_REGION=us-west-2 +``` + +**Option B: AWS Credentials File** + +```bash +~/.aws/credentials +``` + +**Option C: IAM Role** (if running on EC2/ECS/Lambda) + +- The SDK will automatically use the instance/task role + +## Usage + +### Run the Example + +```bash +go run main.go +``` + +### Expected Output + +``` +✅ User inserted successfully +✅ Retrieved user: &{UserID:user123 Name:John Doe Email:john@example.com Age:30 Active:true} +✅ User updated successfully +✅ Found X active users +✅ Batch insert successful +✅ User deleted successfully +``` + +## Code Examples + +### Initialize Client + +```go +dbClient, err := NewDynamoDBClient("us-west-2", "Users") +if err != nil { + log.Fatal(err) +} +``` + +### Put Item + +```go +user := User{ + UserID: "user123", + Name: "John Doe", + Email: "john@example.com", + Age: 30, + Active: true, +} + +err := dbClient.PutUser(ctx, user) +``` + +### Get Item + +```go +user, err := dbClient.GetUser(ctx, "user123") +if err != nil { + log.Fatal(err) +} +fmt.Printf("User: %+v\n", user) +``` + +### Update Item + +```go +err := dbClient.UpdateUser(ctx, "user123", "newemail@example.com", 31) +``` + +### Delete Item + +```go +err := dbClient.DeleteUser(ctx, "user123") +``` + +### Batch Insert + +```go +users := []User{ + {UserID: "user001", Name: "Alice", Email: "alice@example.com", Age: 25, Active: true}, + {UserID: "user002", Name: "Bob", Email: "bob@example.com", Age: 28, Active: true}, +} + +err := dbClient.BatchPutUsers(ctx, users) +``` + +### Query with Pagination + +```go +activeUsers, err := dbClient.QueryActiveUsers(ctx) +``` + +## AWS SDK v2 Best Practices + +### ✅ Use IAM Roles Instead of Hardcoded Credentials + +```go +// The SDK automatically uses IAM roles when available +cfg, err := config.LoadDefaultConfig(context.TODO(), + config.WithRegion("us-west-2"), +) +``` + +### ✅ Implement Retry Logic with Exponential Backoff + +```go +import "github.com/aws/aws-sdk-go-v2/aws/retry" + +cfg, err := config.LoadDefaultConfig(context.TODO(), + config.WithRetryer(func() aws.Retryer { + return retry.AddWithMaxAttempts(retry.NewStandard(), 5) + }), +) +``` + +### ✅ Handle Pagination Properly + +```go +paginator := dynamodb.NewScanPaginator(client, input) + +for paginator.HasMorePages() { + page, err := paginator.NextPage(ctx) + // ... process page +} +``` + +### ✅ Use Context for Timeouts + +```go +ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) +defer cancel() + +result, err := client.GetItem(ctx, input) +``` + +### ✅ Batch Operations for Bulk Inserts/Deletes + +```go +// Process in batches of 25 (DynamoDB limit) +for i := 0; i < len(items); i += 25 { + batch := items[i:min(i+25, len(items))] + // ... batch write +} +``` + +## Common Errors and Solutions + +### Error: `ResourceNotFoundException` + +**Solution:** Create the DynamoDB table first (see Setup section) + +### Error: `ValidationException: One or more parameter values were invalid` + +**Solution:** Check that your attribute types match the table schema + +### Error: `ProvisionedThroughputExceededException` + +**Solution:** + +- Implement exponential backoff retry +- Consider using On-Demand billing mode +- Increase provisioned capacity + +### Error: `UnrecognizedClientException` + +**Solution:** Verify AWS credentials are properly configured + +## Performance Optimization + +### 1. **Use Batch Operations** + +```go +// ✅ Good: Batch write 25 items at once +BatchWriteItem() + +// ❌ Bad: 25 individual PutItem calls +``` + +### 2. **Enable Connection Pooling** + +```go +import awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http" + +cfg, err := config.LoadDefaultConfig(context.TODO(), + config.WithHTTPClient(awshttp.NewBuildableClient(). + WithTransportOptions(func(tr *http.Transport) { + tr.MaxIdleConns = 100 + tr.IdleConnTimeout = 90 * time.Second + }), + ), +) +``` + +### 3. **Use Query Instead of Scan** + +```go +// ✅ Good: Query with partition key +QueryInput{KeyConditionExpression: "UserID = :id"} + +// ❌ Bad: Scan entire table +ScanInput{FilterExpression: "UserID = :id"} +``` + +## Security Best Practices + +1. **Never hardcode credentials** - Use IAM roles, environment variables, or AWS SSM Parameter Store +2. **Enable encryption at rest** - Use AWS KMS for sensitive data +3. **Use VPC endpoints** - Keep traffic within AWS network +4. **Implement least privilege IAM policies** +5. **Enable CloudTrail logging** for audit trails + +## Cost Optimization + +1. **Use On-Demand pricing** for unpredictable workloads +2. **Enable Auto Scaling** for provisioned capacity +3. **Use Time to Live (TTL)** to automatically delete expired items +4. **Optimize queries** to reduce read/write capacity units + +## Additional Resources + +- [AWS SDK for Go v2 Documentation](https://aws.github.io/aws-sdk-go-v2/docs/) +- [DynamoDB Developer Guide](https://docs.aws.amazon.com/dynamodb/) +- [DynamoDB Best Practices](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/best-practices.html) + +## License + +MIT License - Feel free to use this code in your projects! diff --git a/manual-testing-sandbox/dynamodb/go.mod b/manual-testing-sandbox/dynamodb/go.mod new file mode 100644 index 00000000000..0933695da41 --- /dev/null +++ b/manual-testing-sandbox/dynamodb/go.mod @@ -0,0 +1,9 @@ +module github.com/yourusername/dynamodb-example + +go 1.21 + +require ( + github.com/aws/aws-sdk-go-v2 v1.30.0 + github.com/aws/aws-sdk-go-v2/config v1.27.0 + github.com/aws/aws-sdk-go-v2/service/dynamodb v1.34.0 +) diff --git a/manual-testing-sandbox/dynamodb/main.go b/manual-testing-sandbox/dynamodb/main.go new file mode 100644 index 00000000000..102a0f741c6 --- /dev/null +++ b/manual-testing-sandbox/dynamodb/main.go @@ -0,0 +1,352 @@ +package main + +import ( + "context" + "fmt" + "log" + "time" + + "github.com/aws/aws-sdk-go-v2/aws" + "github.com/aws/aws-sdk-go-v2/aws/retry" + "github.com/aws/aws-sdk-go-v2/config" + "github.com/aws/aws-sdk-go-v2/service/dynamodb" + "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" +) + +// User represents a user entity +type User struct { + UserID string + Name string + Email string + Age int + Active bool +} + +// DynamoDBClient wraps the DynamoDB service client +type DynamoDBClient struct { + client *dynamodb.Client + tableName string +} + +// RetryConfig holds retry configuration options +type RetryConfig struct { + MaxAttempts int + MaxBackoffDelay time.Duration + Mode aws.RetryMode // "standard", "adaptive" +} + +// DefaultRetryConfig returns the default retry configuration +func DefaultRetryConfig() RetryConfig { + return RetryConfig{ + MaxAttempts: 5, // Retry up to 5 times (default is 3) + MaxBackoffDelay: 20 * time.Second, // Max wait between retries + Mode: aws.RetryModeStandard, + } +} + +// AdaptiveRetryConfig returns adaptive retry configuration +// Adaptive mode uses machine learning to adjust retry behavior based on success rates +func AdaptiveRetryConfig() RetryConfig { + return RetryConfig{ + MaxAttempts: 5, + MaxBackoffDelay: 20 * time.Second, + Mode: aws.RetryModeAdaptive, + } +} + +// NewDynamoDBClient creates a new DynamoDB client with default retry configuration +func NewDynamoDBClient(region, tableName string) (*DynamoDBClient, error) { + return NewDynamoDBClientWithRetry(region, tableName, DefaultRetryConfig()) +} + +// NewDynamoDBClientWithRetry creates a new DynamoDB client with custom retry configuration +func NewDynamoDBClientWithRetry(region, tableName string, retryConfig RetryConfig) (*DynamoDBClient, error) { + cfg, err := config.LoadDefaultConfig(context.TODO(), + config.WithRegion(region), + // Set retry mode (standard or adaptive) + config.WithRetryMode(retryConfig.Mode), + // Configure custom retry behavior + config.WithRetryer(func() aws.Retryer { + // Start with the appropriate retry mode + var baseRetryer aws.Retryer + if retryConfig.Mode == aws.RetryModeAdaptive { + baseRetryer = retry.NewAdaptiveMode() + } else { + baseRetryer = retry.NewStandard() + } + + // Add max attempts + withMaxAttempts := retry.AddWithMaxAttempts(baseRetryer, retryConfig.MaxAttempts) + + // Add max backoff delay + withBackoff := retry.AddWithMaxBackoffDelay( + withMaxAttempts, + retryConfig.MaxBackoffDelay, + ) + + // Add custom retryable error codes specific to DynamoDB + return retry.AddWithErrorCodes( + withBackoff, + "ProvisionedThroughputExceededException", + "ThrottlingException", + "RequestLimitExceeded", + ) + }), + ) + if err != nil { + return nil, fmt.Errorf("unable to load SDK config: %w", err) + } + + client := dynamodb.NewFromConfig(cfg) + + log.Printf("✅ DynamoDB client initialized with retry config: MaxAttempts=%d, MaxBackoff=%v, Mode=%s", + retryConfig.MaxAttempts, retryConfig.MaxBackoffDelay, retryConfig.Mode) + + return &DynamoDBClient{ + client: client, + tableName: tableName, + }, nil +} + +// PutUser inserts or updates a user in DynamoDB +func (db *DynamoDBClient) PutUser(ctx context.Context, user User) error { + _, err := db.client.PutItem(ctx, &dynamodb.PutItemInput{ + TableName: aws.String(db.tableName), + Item: map[string]types.AttributeValue{ + "UserID": &types.AttributeValueMemberS{Value: user.UserID}, + "Name": &types.AttributeValueMemberS{Value: user.Name}, + "Email": &types.AttributeValueMemberS{Value: user.Email}, + "Age": &types.AttributeValueMemberN{Value: fmt.Sprintf("%d", user.Age)}, + "Active": &types.AttributeValueMemberBOOL{Value: user.Active}, + }, + }) + if err != nil { + return fmt.Errorf("failed to put item: %w", err) + } + + return nil +} + +// GetUser retrieves a user by ID from DynamoDB +func (db *DynamoDBClient) GetUser(ctx context.Context, userID string) (*User, error) { + result, err := db.client.GetItem(ctx, &dynamodb.GetItemInput{ + TableName: aws.String(db.tableName), + Key: map[string]types.AttributeValue{ + "UserID": &types.AttributeValueMemberS{Value: userID}, + }, + }) + if err != nil { + return nil, fmt.Errorf("failed to get item: %w", err) + } + + if result.Item == nil { + return nil, fmt.Errorf("user not found") + } + + // Parse the response + user := &User{ + UserID: result.Item["UserID"].(*types.AttributeValueMemberS).Value, + Name: result.Item["Name"].(*types.AttributeValueMemberS).Value, + Email: result.Item["Email"].(*types.AttributeValueMemberS).Value, + } + + // Parse age (number) + if ageAttr, ok := result.Item["Age"].(*types.AttributeValueMemberN); ok { + fmt.Sscanf(ageAttr.Value, "%d", &user.Age) + } + + // Parse active (boolean) + if activeAttr, ok := result.Item["Active"].(*types.AttributeValueMemberBOOL); ok { + user.Active = activeAttr.Value + } + + return user, nil +} + +// UpdateUser updates specific attributes of a user +func (db *DynamoDBClient) UpdateUser(ctx context.Context, userID, email string, age int) error { + _, err := db.client.UpdateItem(ctx, &dynamodb.UpdateItemInput{ + TableName: aws.String(db.tableName), + Key: map[string]types.AttributeValue{ + "UserID": &types.AttributeValueMemberS{Value: userID}, + }, + UpdateExpression: aws.String("SET Email = :email, Age = :age"), + ExpressionAttributeValues: map[string]types.AttributeValue{ + ":email": &types.AttributeValueMemberS{Value: email}, + ":age": &types.AttributeValueMemberN{Value: fmt.Sprintf("%d", age)}, + }, + }) + if err != nil { + return fmt.Errorf("failed to update item: %w", err) + } + + return nil +} + +// DeleteUser removes a user from DynamoDB +func (db *DynamoDBClient) DeleteUser(ctx context.Context, userID string) error { + _, err := db.client.DeleteItem(ctx, &dynamodb.DeleteItemInput{ + TableName: aws.String(db.tableName), + Key: map[string]types.AttributeValue{ + "UserID": &types.AttributeValueMemberS{Value: userID}, + }, + }) + if err != nil { + return fmt.Errorf("failed to delete item: %w", err) + } + + return nil +} + +// QueryActiveUsers queries all active users with pagination +func (db *DynamoDBClient) QueryActiveUsers(ctx context.Context) ([]User, error) { + // Note: This requires a Global Secondary Index (GSI) on 'Active' attribute + // For demonstration, we'll use Scan with filter (not recommended for production) + + input := &dynamodb.ScanInput{ + TableName: aws.String(db.tableName), + FilterExpression: aws.String("Active = :active"), + ExpressionAttributeValues: map[string]types.AttributeValue{ + ":active": &types.AttributeValueMemberBOOL{Value: true}, + }, + } + + paginator := dynamodb.NewScanPaginator(db.client, input) + + var users []User + for paginator.HasMorePages() { + page, err := paginator.NextPage(ctx) + if err != nil { + return nil, fmt.Errorf("failed to get page: %w", err) + } + + for _, item := range page.Items { + user := User{ + UserID: item["UserID"].(*types.AttributeValueMemberS).Value, + Name: item["Name"].(*types.AttributeValueMemberS).Value, + Email: item["Email"].(*types.AttributeValueMemberS).Value, + } + + if ageAttr, ok := item["Age"].(*types.AttributeValueMemberN); ok { + fmt.Sscanf(ageAttr.Value, "%d", &user.Age) + } + + if activeAttr, ok := item["Active"].(*types.AttributeValueMemberBOOL); ok { + user.Active = activeAttr.Value + } + + users = append(users, user) + } + } + + return users, nil +} + +// BatchPutUsers inserts multiple users in a single batch operation +func (db *DynamoDBClient) BatchPutUsers(ctx context.Context, users []User) error { + // DynamoDB allows max 25 items per batch write + batchSize := 25 + + for i := 0; i < len(users); i += batchSize { + end := i + batchSize + if end > len(users) { + end = len(users) + } + + batch := users[i:end] + writeRequests := make([]types.WriteRequest, len(batch)) + + for j, user := range batch { + writeRequests[j] = types.WriteRequest{ + PutRequest: &types.PutRequest{ + Item: map[string]types.AttributeValue{ + "UserID": &types.AttributeValueMemberS{Value: user.UserID}, + "Name": &types.AttributeValueMemberS{Value: user.Name}, + "Email": &types.AttributeValueMemberS{Value: user.Email}, + "Age": &types.AttributeValueMemberN{Value: fmt.Sprintf("%d", user.Age)}, + "Active": &types.AttributeValueMemberBOOL{Value: user.Active}, + }, + }, + } + } + + _, err := db.client.BatchWriteItem(ctx, &dynamodb.BatchWriteItemInput{ + RequestItems: map[string][]types.WriteRequest{ + db.tableName: writeRequests, + }, + }) + if err != nil { + return fmt.Errorf("failed to batch write items: %w", err) + } + } + + return nil +} + +func main() { + // Initialize DynamoDB client + dbClient, err := NewDynamoDBClient("us-west-2", "Users") + if err != nil { + log.Fatalf("Failed to create DynamoDB client: %v", err) + } + + ctx := context.TODO() + + // Example 1: Put a new user + user := User{ + UserID: "user123", + Name: "John Doe", + Email: "john@example.com", + Age: 30, + Active: true, + } + + if err := dbClient.PutUser(ctx, user); err != nil { + log.Printf("Failed to put user: %v", err) + } else { + fmt.Println("✅ User inserted successfully") + } + + // Example 2: Get the user + retrievedUser, err := dbClient.GetUser(ctx, "user123") + if err != nil { + log.Printf("Failed to get user: %v", err) + } else { + fmt.Printf("✅ Retrieved user: %+v\n", retrievedUser) + } + + // Example 3: Update user + if err := dbClient.UpdateUser(ctx, "user123", "john.doe@example.com", 31); err != nil { + log.Printf("Failed to update user: %v", err) + } else { + fmt.Println("✅ User updated successfully") + } + + // Example 4: Query active users + activeUsers, err := dbClient.QueryActiveUsers(ctx) + if err != nil { + log.Printf("Failed to query active users: %v", err) + } else { + fmt.Printf("✅ Found %d active users\n", len(activeUsers)) + } + + // Example 5: Batch insert users + batchUsers := []User{ + {UserID: "user001", Name: "Alice", Email: "alice@example.com", Age: 25, Active: true}, + {UserID: "user002", Name: "Bob", Email: "bob@example.com", Age: 28, Active: true}, + {UserID: "user003", Name: "Charlie", Email: "charlie@example.com", Age: 35, Active: false}, + } + + if err := dbClient.BatchPutUsers(ctx, batchUsers); err != nil { + log.Printf("Failed to batch insert users: %v", err) + } else { + fmt.Println("✅ Batch insert successful") + } + + // Example 6: Delete user + if err := dbClient.DeleteUser(ctx, "user123"); err != nil { + log.Printf("Failed to delete user: %v", err) + } else { + fmt.Println("✅ User deleted successfully") + } +} From df12f025622480ccbf0bcf4a3236f5c82352a482 Mon Sep 17 00:00:00 2001 From: HarshitZom Date: Fri, 17 Oct 2025 21:32:48 +0530 Subject: [PATCH 02/13] removed prompt and rules customisation --- .../components/AssistantAndOrgListbox/index.tsx | 5 ----- .../LumpToolbar/BlockSettingsTopToolbar.tsx | 17 ----------------- gui/src/pages/config/configTabs.tsx | 12 ------------ gui/src/util/navigation.ts | 2 -- 4 files changed, 36 deletions(-) diff --git a/gui/src/components/AssistantAndOrgListbox/index.tsx b/gui/src/components/AssistantAndOrgListbox/index.tsx index cf767c6c3fa..2febc18b6ae 100644 --- a/gui/src/components/AssistantAndOrgListbox/index.tsx +++ b/gui/src/components/AssistantAndOrgListbox/index.tsx @@ -90,11 +90,6 @@ export function AssistantAndOrgListbox({ close(); } - function onRulesConfig() { - navigate(CONFIG_ROUTES.RULES); - close(); - } - function onToolsConfig() { navigate(CONFIG_ROUTES.TOOLS); close(); diff --git a/gui/src/components/mainInput/Lump/LumpToolbar/BlockSettingsTopToolbar.tsx b/gui/src/components/mainInput/Lump/LumpToolbar/BlockSettingsTopToolbar.tsx index 2f022ea5a52..db842e35ee1 100644 --- a/gui/src/components/mainInput/Lump/LumpToolbar/BlockSettingsTopToolbar.tsx +++ b/gui/src/components/mainInput/Lump/LumpToolbar/BlockSettingsTopToolbar.tsx @@ -2,7 +2,6 @@ import { CubeIcon, ExclamationTriangleIcon, GiftIcon, - PencilIcon, WrenchScrewdriverIcon, } from "@heroicons/react/24/outline"; import { useContext } from "react"; @@ -43,16 +42,6 @@ export function BlockSettingsTopToolbar() { const { creditStatus, isUsingFreeTrial, refreshCreditStatus } = useCreditStatus(); - const handleRulesClick = () => { - if (selectedProfile) { - dispatch(setSelectedProfile(selectedProfile.id)); - ideMessenger.post("didChangeSelectedProfile", { - id: selectedProfile.id, - }); - } - navigate(CONFIG_ROUTES.RULES); - }; - const handleToolsClick = () => { if (selectedProfile) { dispatch(setSelectedProfile(selectedProfile.id)); @@ -114,12 +103,6 @@ export function BlockSettingsTopToolbar() { )} - - - - - - diff --git a/gui/src/pages/config/configTabs.tsx b/gui/src/pages/config/configTabs.tsx index b4687f1e33c..40319d88e8f 100644 --- a/gui/src/pages/config/configTabs.tsx +++ b/gui/src/pages/config/configTabs.tsx @@ -5,7 +5,6 @@ import { Cog6ToothIcon, CubeIcon, DocumentIcon, - PencilIcon, QuestionMarkCircleIcon, WrenchScrewdriverIcon, } from "@heroicons/react/24/outline"; @@ -15,7 +14,6 @@ import { HelpSection } from "./sections/HelpSection"; import { IndexingSettingsSection } from "./sections/IndexingSettingsSection"; import { ModelsSection } from "./sections/ModelsSection"; import { OrganizationsSection } from "./sections/OrganizationsSection"; -import { RulesSection } from "./sections/RulesSection"; import { ToolsSection } from "./sections/ToolsSection"; import { UserSettingsSection } from "./sections/UserSettingsSection"; @@ -60,16 +58,6 @@ export const topTabSections: TabSection[] = [ ), icon: , }, - { - id: "rules", - label: "Rules", - component: ( - - - - ), - icon: , - }, { id: "tools", label: "Tools", diff --git a/gui/src/util/navigation.ts b/gui/src/util/navigation.ts index fb35a9ac433..6e2dedacbf9 100644 --- a/gui/src/util/navigation.ts +++ b/gui/src/util/navigation.ts @@ -1,7 +1,6 @@ // Valid config tab names export type ConfigTab = | "models" - | "rules" | "tools" | "configs" | "organizations" @@ -27,7 +26,6 @@ export const buildConfigRoute = (tab?: ConfigTab): string => { // Typed config route builders for common tabs export const CONFIG_ROUTES = { MODELS: buildConfigRoute("models"), - RULES: buildConfigRoute("rules"), TOOLS: buildConfigRoute("tools"), CONFIGS: buildConfigRoute("configs"), ORGANIZATIONS: buildConfigRoute("organizations"), From 9bd1798570b3de18ed3b0bcc911eaa09ffd260f3 Mon Sep 17 00:00:00 2001 From: HarshitZom Date: Fri, 17 Oct 2025 21:55:17 +0530 Subject: [PATCH 03/13] removed lump --- AWS_SDK_EXPERT_ONLY_MODE_CHANGES.md | 186 ++++++++++++++++ core/config/default.ts | 2 +- core/config/profile/LocalProfileLoader.ts | 2 +- core/config/yaml/default.ts | 4 +- core/index.d.ts | 2 +- extensions/cli/src/util/yamlConfigUpdater.ts | 4 +- gui/src/components/ModeSelect/ModeSelect.tsx | 203 +----------------- .../components/mainInput/ContinueInputBox.tsx | 1 - gui/src/redux/selectors/selectActiveTools.ts | 33 +-- gui/src/redux/slices/sessionSlice.ts | 2 +- gui/src/redux/util/getBaseSystemMessage.ts | 24 +-- 11 files changed, 219 insertions(+), 244 deletions(-) create mode 100644 AWS_SDK_EXPERT_ONLY_MODE_CHANGES.md diff --git a/AWS_SDK_EXPERT_ONLY_MODE_CHANGES.md b/AWS_SDK_EXPERT_ONLY_MODE_CHANGES.md new file mode 100644 index 00000000000..6eab18940e1 --- /dev/null +++ b/AWS_SDK_EXPERT_ONLY_MODE_CHANGES.md @@ -0,0 +1,186 @@ +# AWS SDK Expert - Only Mode Implementation + +## Summary + +Successfully transformed Continue into a dedicated AWS SDK Expert tool by: + +1. Removing all other modes (Chat, Plan, Agent) +2. Making AWS SDK Expert the only and default mode +3. Updating config name from "Local Config" to "AWS SDK Expert" +4. Simplifying the UI to show only AWS SDK Expert mode + +--- + +## Files Modified + +### 1. Type Definitions + +**File:** `core/index.d.ts` + +- Changed `MessageModes` type from `"chat" | "agent" | "plan" | "aws-sdk-expert"` to just `"aws-sdk-expert"` +- This enforces that only AWS SDK Expert mode exists throughout the codebase + +### 2. Default Configuration Names + +**Files Updated:** + +- `core/config/yaml/default.ts` - Both `defaultConfigYaml` and `defaultConfigYamlJetBrains` +- `core/config/default.ts` - Main default config +- `extensions/cli/src/util/yamlConfigUpdater.ts` - CLI YAML config updater (2 locations) +- `core/config/profile/LocalProfileLoader.ts` - Profile loader title + +**Changes:** All instances of `name: "Local Config"` changed to `name: "AWS SDK Expert"` + +### 3. Default Mode in Session State + +**File:** `gui/src/redux/slices/sessionSlice.ts` + +- Changed default mode from `"agent"` to `"aws-sdk-expert"` +- This ensures all new sessions start in AWS SDK Expert mode + +### 4. Mode Selector UI Simplification + +**File:** `gui/src/components/ModeSelect/ModeSelect.tsx` + +- **Before:** Complex dropdown with Chat, Plan, Agent, and AWS SDK Expert options with mode cycling +- **After:** Simple static label showing "AWS SDK Expert" with an info tooltip +- Removed: + - All other mode options (Chat, Plan, Agent) + - Mode cycling functionality (Cmd/Ctrl + . shortcut) + - Dropdown selector + - Warning messages for incompatible models +- Kept: + - Mode icon display + - Tooltip explaining the mode + +### 5. System Message Selection + +**File:** `gui/src/redux/util/getBaseSystemMessage.ts` + +- **Before:** Complex logic to select between Chat, Plan, Agent, or AWS SDK Expert system messages +- **After:** Always returns `DEFAULT_AWS_SDK_EXPERT_SYSTEM_MESSAGE` +- Removed imports for other system messages +- Simplified logic - no mode checking needed + +### 6. Tool Selection Logic + +**File:** `gui/src/redux/selectors/selectActiveTools.ts` + +- **Before:** Different tool sets for Chat (none), Plan (read-only), and Agent (all) +- **After:** Always returns all enabled tools (AWS SDK Expert behavior) +- Removed mode parameter from selector +- Simplified to just filter based on tool policies + +--- + +## Behavior Changes + +### Before + +- 4 modes: Chat, Plan, Agent, AWS SDK Expert +- User could switch between modes +- Different tool availability per mode +- Mode cycling with Cmd/Ctrl + . shortcut +- Config showed as "Local Config" + +### After + +- 1 mode: AWS SDK Expert only +- No mode switching (mode is fixed) +- All tools always available +- No mode cycling shortcut +- Config shows as "AWS SDK Expert" + +--- + +## User Experience + +### What Users Will See: + +1. **Config Name**: "AWS SDK Expert" instead of "Local Config" in the dropdown +2. **Mode Selector**: Static label "AWS SDK Expert" (not a dropdown) +3. **Default Behavior**: Every session automatically starts in AWS SDK Expert mode +4. **System Prompt**: Always uses AWS SDK Expert specialized prompt +5. **Tool Access**: All tools available by default (read, write, execute) + +### What Users Will Notice: + +- No mode switching options +- Simpler, cleaner UI +- All queries automatically interpreted as AWS SDK related +- Consistent behavior across all sessions + +--- + +## AWS SDK Expert Mode Features (Unchanged) + +The core AWS SDK Expert mode functionality remains the same: + +- Interprets all queries as AWS SDK related +- Instructs agent to use Context7 MCP for latest documentation +- Provides working code examples with best practices +- Includes security considerations +- Handles errors properly with SDK-specific patterns + +--- + +## Testing Recommendations + +### Manual Testing: + +1. **Build and run** the application +2. **Verify config name**: Check that dropdown shows "AWS SDK Expert" +3. **Check mode selector**: Should show "AWS SDK Expert" as static label (no dropdown) +4. **Test queries**: Send AWS SDK queries and verify proper responses +5. **Check new sessions**: Ensure all new sessions start in AWS SDK Expert mode +6. **Verify tools**: All tools should be available in tool policies + +### Example Test Queries: + +``` +"How do I upload a file?" +Expected: S3 upload example + +"Send a message" +Expected: SQS/SNS example + +"Query a table" +Expected: DynamoDB example +``` + +--- + +## Technical Notes + +### Type Safety + +- TypeScript will now enforce that only `"aws-sdk-expert"` can be used as a mode +- Any code trying to use "chat", "plan", or "agent" will fail type checking + +### Backward Compatibility + +- Existing configs with `name: "Local Config"` will still work +- The system will use the default name "AWS SDK Expert" for new configs + +### Future Modifications + +If other modes need to be added back: + +1. Update `MessageModes` type in `core/index.d.ts` +2. Add mode options back to `ModeSelect.tsx` +3. Update `getBaseSystemMessage.ts` logic +4. Update `selectActiveTools.ts` logic +5. Add corresponding system messages in `defaultSystemMessages.ts` + +--- + +## Configuration Files Not Modified + +These files were intentionally not modified as they are test files or have minimal impact: + +- Test files referencing "Local Config" +- Documentation files +- Migration guides +- Error messages and logs that reference "local config" + +These can be updated if needed but don't affect core functionality. diff --git a/core/config/default.ts b/core/config/default.ts index 817bd791e97..2e136a409ce 100644 --- a/core/config/default.ts +++ b/core/config/default.ts @@ -1,7 +1,7 @@ import { ConfigYaml } from "@continuedev/config-yaml"; export const defaultConfig: ConfigYaml = { - name: "Local Config", + name: "AWS SDK Expert", version: "1.0.0", schema: "v1", models: [], diff --git a/core/config/profile/LocalProfileLoader.ts b/core/config/profile/LocalProfileLoader.ts index 9c57ae94c9c..0bf3550d934 100644 --- a/core/config/profile/LocalProfileLoader.ts +++ b/core/config/profile/LocalProfileLoader.ts @@ -32,7 +32,7 @@ export default class LocalProfileLoader implements IProfileLoader { iconUrl: "", title: overrideAssistantFile?.path ? getUriPathBasename(overrideAssistantFile.path) - : "Local Config", + : "AWS SDK Expert", errors: undefined, uri: overrideAssistantFile?.path ?? diff --git a/core/config/yaml/default.ts b/core/config/yaml/default.ts index 22d7d06854e..42d333d4454 100644 --- a/core/config/yaml/default.ts +++ b/core/config/yaml/default.ts @@ -4,7 +4,7 @@ import { AssistantUnrolled } from "@continuedev/config-yaml"; export const defaultConfigYaml: AssistantUnrolled = { models: [], context: [], - name: "Local Config", + name: "AWS SDK Expert", version: "1.0.0", schema: "v1", }; @@ -12,7 +12,7 @@ export const defaultConfigYaml: AssistantUnrolled = { export const defaultConfigYamlJetBrains: AssistantUnrolled = { models: [], context: [], - name: "Local Config", + name: "AWS SDK Expert", version: "1.0.0", schema: "v1", }; diff --git a/core/index.d.ts b/core/index.d.ts index ea4db0944a2..a694824ef72 100644 --- a/core/index.d.ts +++ b/core/index.d.ts @@ -462,7 +462,7 @@ export interface PromptLog { completion: string; } -export type MessageModes = "chat" | "agent" | "plan" | "aws-sdk-expert"; +export type MessageModes = "aws-sdk-expert"; export type ToolStatus = | "generating" // Tool call arguments are being streamed from the LLM diff --git a/extensions/cli/src/util/yamlConfigUpdater.ts b/extensions/cli/src/util/yamlConfigUpdater.ts index 4c249bb1f60..9d0273fe904 100644 --- a/extensions/cli/src/util/yamlConfigUpdater.ts +++ b/extensions/cli/src/util/yamlConfigUpdater.ts @@ -39,7 +39,7 @@ export function updateAnthropicModelInYaml( // If document is empty or has no content, create a new config if (!doc.contents || doc.contents === null) { const defaultConfig: ConfigStructure = { - name: "Local Config", + name: "AWS SDK Expert", version: "1.0.0", schema: "v1", models: [newModel], @@ -75,7 +75,7 @@ export function updateAnthropicModelInYaml( } catch { // If parsing fails completely, create a new config const defaultConfig: ConfigStructure = { - name: "Local Config", + name: "AWS SDK Expert", version: "1.0.0", schema: "v1", models: [newModel], diff --git a/gui/src/components/ModeSelect/ModeSelect.tsx b/gui/src/components/ModeSelect/ModeSelect.tsx index fb84566f069..4eabb3ce7b5 100644 --- a/gui/src/components/ModeSelect/ModeSelect.tsx +++ b/gui/src/components/ModeSelect/ModeSelect.tsx @@ -1,202 +1,17 @@ -import { - CheckIcon, - ChevronDownIcon, - ExclamationTriangleIcon, - InformationCircleIcon, -} from "@heroicons/react/24/outline"; -import { MessageModes } from "core"; -import { isRecommendedAgentModel } from "core/llm/toolSupport"; -import { capitalize } from "lodash"; -import { useCallback, useEffect, useMemo } from "react"; -import { useAppDispatch, useAppSelector } from "../../redux/hooks"; -import { selectSelectedChatModel } from "../../redux/slices/configSlice"; -import { setMode } from "../../redux/slices/sessionSlice"; -import { getFontSize, getMetaKeyLabel } from "../../util"; -import { ToolTip } from "../gui/Tooltip"; -import { useMainEditor } from "../mainInput/TipTapEditor"; -import { Listbox, ListboxButton, ListboxOption, ListboxOptions } from "../ui"; +import { useAppSelector } from "../../redux/hooks"; import { ModeIcon } from "./ModeIcon"; export function ModeSelect() { - const dispatch = useAppDispatch(); const mode = useAppSelector((store) => store.session.mode); - const selectedModel = useAppSelector(selectSelectedChatModel); - - const isGoodAtAgentMode = useMemo(() => { - if (!selectedModel) { - return undefined; - } - return isRecommendedAgentModel(selectedModel.model); - }, [selectedModel]); - - const { mainEditor } = useMainEditor(); - const metaKeyLabel = useMemo(() => { - return getMetaKeyLabel(); - }, []); - - const cycleMode = useCallback(() => { - if (mode === "chat") { - dispatch(setMode("plan")); - } else if (mode === "plan") { - dispatch(setMode("agent")); - } else if (mode === "agent") { - dispatch(setMode("aws-sdk-expert")); - } else { - dispatch(setMode("chat")); - } - // Only focus main editor if another one doesn't already have focus - if (!document.activeElement?.classList?.contains("ProseMirror")) { - mainEditor?.commands.focus(); - } - }, [mode, mainEditor]); - - const selectMode = useCallback( - (newMode: MessageModes) => { - if (newMode === mode) { - return; - } - - dispatch(setMode(newMode)); - - mainEditor?.commands.focus(); - }, - [mode, mainEditor], - ); - - useEffect(() => { - const handleKeyDown = (e: KeyboardEvent) => { - if (e.key === "." && (e.metaKey || e.ctrlKey)) { - e.preventDefault(); - void cycleMode(); - } - }; - - document.addEventListener("keydown", handleKeyDown); - return () => document.removeEventListener("keydown", handleKeyDown); - }, [cycleMode]); - - const notGreatAtAgent = ( - <> - - - - - ); + // Since we only have one mode (AWS SDK Expert), display it as a static label return ( - -
- - - - {mode === "chat" - ? "Chat" - : mode === "agent" - ? "Agent" - : mode === "plan" - ? "Plan" - : "AWS SDK Expert"} - - - - -
- - Chat - - - - - {getMetaKeyLabel()}L - -
- {mode === "chat" && } -
- -
- - Plan - - - -
- {!isGoodAtAgentMode && notGreatAtAgent} - -
- - -
- - Agent - - - -
- {!isGoodAtAgentMode && notGreatAtAgent} - -
- - -
- - AWS SDK Expert - - - -
- {!isGoodAtAgentMode && notGreatAtAgent} - -
- -
- {`${metaKeyLabel} . for next mode`} -
-
-
-
+
+ + AWS SDK Expert +
); } diff --git a/gui/src/components/mainInput/ContinueInputBox.tsx b/gui/src/components/mainInput/ContinueInputBox.tsx index da04a273b59..117919e5d81 100644 --- a/gui/src/components/mainInput/ContinueInputBox.tsx +++ b/gui/src/components/mainInput/ContinueInputBox.tsx @@ -113,7 +113,6 @@ function ContinueInputBox(props: ContinueInputBoxProps) { data-testid={`continue-input-box-${props.inputId}`} >
- {props.isMainInput && } store.session.mode, (store: RootState) => store.config.config.tools, (store: RootState) => store.ui.toolSettings, (store: RootState) => store.ui.toolGroupSettings, ], - (mode, tools, policies, groupPolicies): Tool[] => { - if (mode === "chat") { - return []; - } else { - const enabledTools = tools.filter((tool) => { - const toolPolicy = - policies[tool.function.name] ?? - tool.defaultToolPolicy ?? - DEFAULT_TOOL_SETTING; - return ( - toolPolicy !== "disabled" && groupPolicies[tool.group] !== "exclude" - ); - }); - if (mode === "plan") { - return enabledTools.filter( - (t) => t.group !== BUILT_IN_GROUP_NAME || t.readonly, - ); - } - return enabledTools; - } + (tools, policies, groupPolicies): Tool[] => { + // AWS SDK Expert mode always has all tools available + return tools.filter((tool) => { + const toolPolicy = + policies[tool.function.name] ?? + tool.defaultToolPolicy ?? + DEFAULT_TOOL_SETTING; + return ( + toolPolicy !== "disabled" && groupPolicies[tool.group] !== "exclude" + ); + }); }, ); diff --git a/gui/src/redux/slices/sessionSlice.ts b/gui/src/redux/slices/sessionSlice.ts index d4ebe48686d..057cf4a8602 100644 --- a/gui/src/redux/slices/sessionSlice.ts +++ b/gui/src/redux/slices/sessionSlice.ts @@ -233,7 +233,7 @@ export const INITIAL_SESSION_STATE: SessionState = { id: uuidv4(), streamAborter: new AbortController(), symbols: {}, - mode: "agent", + mode: "aws-sdk-expert", isInEdit: false, codeBlockApplyStates: { states: [], diff --git a/gui/src/redux/util/getBaseSystemMessage.ts b/gui/src/redux/util/getBaseSystemMessage.ts index 99032cefd40..2921e044a3b 100644 --- a/gui/src/redux/util/getBaseSystemMessage.ts +++ b/gui/src/redux/util/getBaseSystemMessage.ts @@ -1,10 +1,5 @@ import { ModelDescription, Tool } from "core"; -import { - DEFAULT_AGENT_SYSTEM_MESSAGE, - DEFAULT_AWS_SDK_EXPERT_SYSTEM_MESSAGE, - DEFAULT_CHAT_SYSTEM_MESSAGE, - DEFAULT_PLAN_SYSTEM_MESSAGE, -} from "core/llm/defaultSystemMessages"; +import { DEFAULT_AWS_SDK_EXPERT_SYSTEM_MESSAGE } from "core/llm/defaultSystemMessages"; export const NO_TOOL_WARNING = "\n\nTHE USER HAS NOT PROVIDED ANY TOOLS, DO NOT ATTEMPT TO USE ANY TOOLS. STOP AND LET THE USER KNOW THAT THERE ARE NO TOOLS AVAILABLE. The user can provide tools by enabling them in the Tool Policies section of the notch (wrench icon)"; @@ -14,20 +9,11 @@ export function getBaseSystemMessage( model: ModelDescription, activeTools?: Tool[], ): string { - let baseMessage: string; + // Always use AWS SDK Expert system message since it's the only mode + let baseMessage = DEFAULT_AWS_SDK_EXPERT_SYSTEM_MESSAGE; - if (messageMode === "agent") { - baseMessage = model.baseAgentSystemMessage ?? DEFAULT_AGENT_SYSTEM_MESSAGE; - } else if (messageMode === "plan") { - baseMessage = model.basePlanSystemMessage ?? DEFAULT_PLAN_SYSTEM_MESSAGE; - } else if (messageMode === "aws-sdk-expert") { - baseMessage = DEFAULT_AWS_SDK_EXPERT_SYSTEM_MESSAGE; - } else { - baseMessage = model.baseChatSystemMessage ?? DEFAULT_CHAT_SYSTEM_MESSAGE; - } - - // Add no-tools warning for agent/plan/aws-sdk-expert modes when no tools are available - if (messageMode !== "chat" && (!activeTools || activeTools.length === 0)) { + // Add no-tools warning when no tools are available + if (!activeTools || activeTools.length === 0) { baseMessage += NO_TOOL_WARNING; } From bf1b72a98763d39bc77468a10c3e02f67e39568b Mon Sep 17 00:00:00 2001 From: HarshitZom Date: Fri, 17 Oct 2025 22:12:17 +0530 Subject: [PATCH 04/13] UI cleaned up --- core/config/loadContextProviders.ts | 2 +- core/context/providers/index.ts | 2 +- .../AssistantOption.tsx | 2 +- .../OrganizationOption.tsx | 2 +- .../AssistantAndOrgListbox/index.tsx | 207 +----------------- .../StepContainer/ResponseActions.tsx | 10 - .../components/config/FatalErrorNotice.tsx | 4 +- .../LumpToolbar/BlockSettingsTopToolbar.tsx | 49 +---- .../TipTapEditor/utils/getSuggestion.ts | 22 +- .../components/modelSelection/ModelSelect.tsx | 4 +- gui/src/pages/config/configTabs.tsx | 60 +---- .../features/account/AccountDropdown.tsx | 29 +-- gui/src/pages/config/index.tsx | 2 +- .../config/sections/UserSettingsSection.tsx | 15 +- gui/src/util/navigation.ts | 13 +- 15 files changed, 53 insertions(+), 370 deletions(-) diff --git a/core/config/loadContextProviders.ts b/core/config/loadContextProviders.ts index e51051f0ad0..fdbd9cfa1ee 100644 --- a/core/config/loadContextProviders.ts +++ b/core/config/loadContextProviders.ts @@ -36,7 +36,7 @@ export function loadConfigContextProviders( new DiffContextProvider({}), new TerminalContextProvider({}), new ProblemsContextProvider({}), - new RulesContextProvider({}), + // new RulesContextProvider({}), ]; // Add from config diff --git a/core/context/providers/index.ts b/core/context/providers/index.ts index 1c0e980eda1..2d317969d00 100644 --- a/core/context/providers/index.ts +++ b/core/context/providers/index.ts @@ -71,7 +71,7 @@ export const Providers: (typeof BaseContextProvider)[] = [ MCPContextProvider, GitCommitContextProvider, ClipboardContextProvider, - RulesContextProvider, + // RulesContextProvider, ]; export function contextProviderClassFromName( diff --git a/gui/src/components/AssistantAndOrgListbox/AssistantOption.tsx b/gui/src/components/AssistantAndOrgListbox/AssistantOption.tsx index ec48c847417..bc1f2209ac1 100644 --- a/gui/src/components/AssistantAndOrgListbox/AssistantOption.tsx +++ b/gui/src/components/AssistantAndOrgListbox/AssistantOption.tsx @@ -87,7 +87,7 @@ export function AssistantOption({ className="text-error hover:enabled:text-error my-0 h-4 w-4 p-0" onClick={(e) => { e.stopPropagation(); - navigate(CONFIG_ROUTES.CONFIGS); + navigate(CONFIG_ROUTES.SETTINGS); }} > diff --git a/gui/src/components/AssistantAndOrgListbox/OrganizationOption.tsx b/gui/src/components/AssistantAndOrgListbox/OrganizationOption.tsx index 54699d332f6..48d5f01afd3 100644 --- a/gui/src/components/AssistantAndOrgListbox/OrganizationOption.tsx +++ b/gui/src/components/AssistantAndOrgListbox/OrganizationOption.tsx @@ -87,7 +87,7 @@ export function OrganizationOption({ className="text-description-muted hover:enabled:text-foreground my-0 h-4 w-4 p-0 opacity-0 transition-opacity group-hover:opacity-100" onClick={(e) => { e.stopPropagation(); - navigate(CONFIG_ROUTES.ORGANIZATIONS); + navigate(CONFIG_ROUTES.SETTINGS); onClose(); // Close the listbox }} > diff --git a/gui/src/components/AssistantAndOrgListbox/index.tsx b/gui/src/components/AssistantAndOrgListbox/index.tsx index 2febc18b6ae..93916ba02bd 100644 --- a/gui/src/components/AssistantAndOrgListbox/index.tsx +++ b/gui/src/components/AssistantAndOrgListbox/index.tsx @@ -1,10 +1,3 @@ -import { - ArrowPathIcon, - ArrowRightStartOnRectangleIcon, - Cog6ToothIcon, - PlusIcon, -} from "@heroicons/react/24/outline"; -import { isOnPremSession } from "core/control-plane/AuthTypes"; import { useContext, useEffect, useRef } from "react"; import { useNavigate } from "react-router-dom"; import { useAuth } from "../../context/Auth"; @@ -14,19 +7,9 @@ import { selectCurrentOrg, setSelectedProfile, } from "../../redux/slices/profilesSlice"; -import { getMetaKeyLabel, isMetaEquivalentKeyPressed } from "../../util"; -import { cn } from "../../util/cn"; +import { isMetaEquivalentKeyPressed } from "../../util"; import { CONFIG_ROUTES } from "../../util/navigation"; -import { - Button, - Listbox, - ListboxOptions, - Transition, - useFontSize, -} from "../ui"; -import { Divider } from "../ui/Divider"; -import { AssistantOptions } from "./AssistantOptions"; -import { OrganizationOptions } from "./OrganizationOptions"; +import { Listbox, ListboxOptions, Transition, useFontSize } from "../ui"; import { SelectedAssistantButton } from "./SelectedAssistantButton"; export interface AssistantAndOrgListboxProps { @@ -41,19 +24,8 @@ export function AssistantAndOrgListbox({ const listboxRef = useRef(null); const currentOrg = useAppSelector(selectCurrentOrg); const ideMessenger = useContext(IdeMessengerContext); - const { - profiles, - selectedProfile, - session, - logout, - login, - organizations, - refreshProfiles, - } = useAuth(); - const configLoading = useAppSelector((store) => store.config.loading); + const { profiles, selectedProfile } = useAuth(); const tinyFont = useFontSize(-4); - const shouldRenderOrgInfo = - session && organizations.length > 1 && !isOnPremSession(session); function close() { // Close the listbox by clicking outside or programmatically @@ -61,35 +33,6 @@ export function AssistantAndOrgListbox({ document.dispatchEvent(event); } - function onNewAssistant() { - if (session) { - void ideMessenger.request("controlPlane/openUrl", { - path: "/new", - orgSlug: currentOrg?.slug, - }); - } else { - void ideMessenger.request("config/newAssistantFile", undefined); - } - close(); - } - - function onNewOrganization() { - void ideMessenger.request("controlPlane/openUrl", { - path: "/organizations/new", - }); - close(); - } - - function onConfigsConfig() { - navigate(CONFIG_ROUTES.CONFIGS); - close(); - } - - function onOrganizationsConfig() { - navigate(CONFIG_ROUTES.ORGANIZATIONS); - close(); - } - function onToolsConfig() { navigate(CONFIG_ROUTES.TOOLS); close(); @@ -148,147 +91,11 @@ export function AssistantAndOrgListbox({ className="max-h-32 scale-x-[97%] overflow-y-auto pb-0" style={{ zIndex: 200 }} > -
- - Configs + {/* Minimal config display - no interactive options */} +
+ + AWS SDK Expert Mode -
- - -
-
- - - - {shouldRenderOrgInfo && ( - <> - -
- - Organizations - -
- - -
-
- - - - - - )} - - {/* Settings Section */} - {variant !== "sidebar" && ( -
- - {session ? ( - - ) : ( - - )} - - -
- )} - - {/* Bottom Actions */} -
-
- - {getMetaKeyLabel()} ⇧ ' to toggle config - -
diff --git a/gui/src/components/StepContainer/ResponseActions.tsx b/gui/src/components/StepContainer/ResponseActions.tsx index 0dc09a438be..b41dc22c036 100644 --- a/gui/src/components/StepContainer/ResponseActions.tsx +++ b/gui/src/components/StepContainer/ResponseActions.tsx @@ -85,16 +85,6 @@ export default function ResponseActions({
- {isLast && ruleGenerationSupported && ( - - - - )} - {isTruncated && ( { const configLoading = useAppSelector((state) => state.config.loading); const showConfigPage = () => { - navigate(CONFIG_ROUTES.CONFIGS); + navigate(CONFIG_ROUTES.SETTINGS); }; const currentPath = `${location.pathname}${location.search}`; @@ -64,7 +64,7 @@ export const FatalErrorIndicator = () => { Reload
)} - {currentPath !== CONFIG_ROUTES.CONFIGS && ( + {currentPath !== CONFIG_ROUTES.SETTINGS && (
View
diff --git a/gui/src/components/mainInput/Lump/LumpToolbar/BlockSettingsTopToolbar.tsx b/gui/src/components/mainInput/Lump/LumpToolbar/BlockSettingsTopToolbar.tsx index db842e35ee1..12c47939e77 100644 --- a/gui/src/components/mainInput/Lump/LumpToolbar/BlockSettingsTopToolbar.tsx +++ b/gui/src/components/mainInput/Lump/LumpToolbar/BlockSettingsTopToolbar.tsx @@ -1,9 +1,4 @@ -import { - CubeIcon, - ExclamationTriangleIcon, - GiftIcon, - WrenchScrewdriverIcon, -} from "@heroicons/react/24/outline"; +import { GiftIcon, WrenchScrewdriverIcon } from "@heroicons/react/24/outline"; import { useContext } from "react"; import { useNavigate } from "react-router-dom"; import { IdeMessengerContext } from "../../../../context/IdeMessenger"; @@ -27,7 +22,6 @@ export function BlockSettingsTopToolbar() { const dispatch = useAppDispatch(); const { selectedProfile } = useAuth(); - const configError = useAppSelector((store) => store.config.configError); const ideMessenger = useContext(IdeMessengerContext); const pendingToolCalls = useAppSelector(selectPendingToolCalls); @@ -37,8 +31,6 @@ export function BlockSettingsTopToolbar() { const hasActiveContent = pendingToolCalls.length > 0 || callingToolCalls.length > 0; - const shouldShowError = configError && configError?.length > 0; - const { creditStatus, isUsingFreeTrial, refreshCreditStatus } = useCreditStatus(); @@ -52,42 +44,9 @@ export function BlockSettingsTopToolbar() { navigate(CONFIG_ROUTES.TOOLS); }; - const handleModelsClick = () => { - if (selectedProfile) { - dispatch(setSelectedProfile(selectedProfile.id)); - ideMessenger.post("didChangeSelectedProfile", { - id: selectedProfile.id, - }); - } - navigate(CONFIG_ROUTES.MODELS); - }; - return (
- {shouldShowError && ( - -
navigate(CONFIG_ROUTES.CONFIGS)} - onKeyDown={(e) => { - if (e.key === "Enter" || e.key === " ") { - e.preventDefault(); - navigate(CONFIG_ROUTES.CONFIGS); - } - }} - data-testid="block-settings-toolbar-icon-error" - className="relative flex cursor-pointer select-none items-center rounded-full px-1.5 py-1 sm:px-1.5" - > -
-
- )} - {!hasActiveContent && (
{isUsingFreeTrial && ( @@ -108,12 +67,6 @@ export function BlockSettingsTopToolbar() { - - - - - -
)}
diff --git a/gui/src/components/mainInput/TipTapEditor/utils/getSuggestion.ts b/gui/src/components/mainInput/TipTapEditor/utils/getSuggestion.ts index 8c0613a4a14..5991d78af7e 100644 --- a/gui/src/components/mainInput/TipTapEditor/utils/getSuggestion.ts +++ b/gui/src/components/mainInput/TipTapEditor/utils/getSuggestion.ts @@ -163,17 +163,17 @@ export function getContextProviderDropdownOptions( .sort((c, _) => (c.id === "file" ? -1 : 1)) || []; if (contextProviderMatches.length) { - contextProviderMatches.push({ - title: "Add more context providers", - type: "action", - action: () => { - ideMessenger.post( - "openUrl", - "https://docs.continue.dev/customization/context-providers#built-in-context-providers", - ); - }, - description: "", - }); + // contextProviderMatches.push({ + // title: "Add more context providers", + // type: "action", + // action: () => { + // ideMessenger.post( + // "openUrl", + // "https://docs.continue.dev/customization/context-providers#built-in-context-providers", + // ); + // }, + // description: "", + // }); return contextProviderMatches; } diff --git a/gui/src/components/modelSelection/ModelSelect.tsx b/gui/src/components/modelSelection/ModelSelect.tsx index b4159314f2d..6dec47d9223 100644 --- a/gui/src/components/modelSelection/ModelSelect.tsx +++ b/gui/src/components/modelSelection/ModelSelect.tsx @@ -76,7 +76,7 @@ function ModelOption({ function handleConfigureClick(e: React.MouseEvent) { e.stopPropagation(); - navigate(CONFIG_ROUTES.MODELS); + navigate(CONFIG_ROUTES.SETTINGS); } return ( @@ -227,7 +227,7 @@ function ModelSelect() { closeDropDown(buttonRef.current); - navigate(CONFIG_ROUTES.MODELS); + navigate(CONFIG_ROUTES.SETTINGS); } const hasNoModels = allModels?.length === 0; diff --git a/gui/src/pages/config/configTabs.tsx b/gui/src/pages/config/configTabs.tsx index 40319d88e8f..33ed1278737 100644 --- a/gui/src/pages/config/configTabs.tsx +++ b/gui/src/pages/config/configTabs.tsx @@ -1,19 +1,11 @@ import { ArrowLeftIcon, - BuildingOfficeIcon, CircleStackIcon, Cog6ToothIcon, - CubeIcon, - DocumentIcon, - QuestionMarkCircleIcon, WrenchScrewdriverIcon, } from "@heroicons/react/24/outline"; import { ConfigSection } from "./components/ConfigSection"; -import { ConfigsSection } from "./sections/ConfigsSection"; -import { HelpSection } from "./sections/HelpSection"; import { IndexingSettingsSection } from "./sections/IndexingSettingsSection"; -import { ModelsSection } from "./sections/ModelsSection"; -import { OrganizationsSection } from "./sections/OrganizationsSection"; import { ToolsSection } from "./sections/ToolsSection"; import { UserSettingsSection } from "./sections/UserSettingsSection"; @@ -48,16 +40,6 @@ export const topTabSections: TabSection[] = [ id: "blocks", showTopDivider: true, tabs: [ - { - id: "models", - label: "Models", - component: ( - - - - ), - icon: , - }, { id: "tools", label: "Tools", @@ -72,37 +54,9 @@ export const topTabSections: TabSection[] = [ }, ], }, - { - id: "agents-orgs", - showTopDivider: true, - tabs: [ - { - id: "configs", - label: "Configs", - component: ( - - - - ), - icon: , - }, - { - id: "organizations", - label: "Organizations", - component: ( - - - - ), - icon: ( - - ), - }, - ], - }, { id: "indexing", - showTopDivider: true, + showTopDivider: false, tabs: [ { id: "indexing", @@ -134,18 +88,6 @@ export const bottomTabSections: TabSection[] = [ ), icon: , }, - { - id: "help", - label: "Help", - component: ( - - - - ), - icon: ( - - ), - }, ], }, ]; diff --git a/gui/src/pages/config/features/account/AccountDropdown.tsx b/gui/src/pages/config/features/account/AccountDropdown.tsx index 0a2e7fb272e..bc62a3dabf0 100644 --- a/gui/src/pages/config/features/account/AccountDropdown.tsx +++ b/gui/src/pages/config/features/account/AccountDropdown.tsx @@ -27,20 +27,21 @@ export function AccountDropdown() { } if (!session) { - return ( - - - - ); + // return ( + // + // + // + // ); + return; } return ( diff --git a/gui/src/pages/config/index.tsx b/gui/src/pages/config/index.tsx index d672b3d099c..289a4ede1b5 100644 --- a/gui/src/pages/config/index.tsx +++ b/gui/src/pages/config/index.tsx @@ -68,7 +68,7 @@ function ConfigPage() { /> ))} - + {/* */}
diff --git a/gui/src/pages/config/sections/UserSettingsSection.tsx b/gui/src/pages/config/sections/UserSettingsSection.tsx index afa56aca4ef..ea958924ee4 100644 --- a/gui/src/pages/config/sections/UserSettingsSection.tsx +++ b/gui/src/pages/config/sections/UserSettingsSection.tsx @@ -177,7 +177,7 @@ export function UserSettingsSection() { {/* Telemetry Settings */} -
+ {/*
@@ -193,7 +193,7 @@ export function UserSettingsSection() { />
-
+
*/} {/* Appearance Settings */}
@@ -217,7 +217,7 @@ export function UserSettingsSection() {
{/* Autocomplete Settings */} -
+ {/*
@@ -262,7 +262,8 @@ export function UserSettingsSection() { type="input" title="Disable autocomplete in files" description="List of comma-separated glob pattern to disable autocomplete in matching files." - placeholder="**/*.(txt,md)" + placeholder="**/ + /*.(txt,md)" value={formDisableAutocomplete} onChange={setFormDisableAutocomplete} onSubmit={handleDisableAutocompleteSubmit} @@ -274,10 +275,10 @@ export function UserSettingsSection() { />
-
+
*/} {/* Experimental Settings */} -
+ {/*
-
+
*/} diff --git a/gui/src/util/navigation.ts b/gui/src/util/navigation.ts index 6e2dedacbf9..65a3ee7a272 100644 --- a/gui/src/util/navigation.ts +++ b/gui/src/util/navigation.ts @@ -1,12 +1,5 @@ // Valid config tab names -export type ConfigTab = - | "models" - | "tools" - | "configs" - | "organizations" - | "indexing" - | "settings" - | "help"; +export type ConfigTab = "tools" | "indexing" | "settings"; // TODO: Move all the routes here export const ROUTES = { @@ -25,11 +18,7 @@ export const buildConfigRoute = (tab?: ConfigTab): string => { // Typed config route builders for common tabs export const CONFIG_ROUTES = { - MODELS: buildConfigRoute("models"), TOOLS: buildConfigRoute("tools"), - CONFIGS: buildConfigRoute("configs"), - ORGANIZATIONS: buildConfigRoute("organizations"), INDEXING: buildConfigRoute("indexing"), SETTINGS: buildConfigRoute("settings"), - HELP: buildConfigRoute("help"), } as const; From b12296023270861d53b6ca91bfa8aa6ff80502d9 Mon Sep 17 00:00:00 2001 From: HarshitZom Date: Fri, 17 Oct 2025 22:25:44 +0530 Subject: [PATCH 05/13] removed tool tips --- gui/src/components/mainInput/InputToolbar.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gui/src/components/mainInput/InputToolbar.tsx b/gui/src/components/mainInput/InputToolbar.tsx index e89eb145aaa..493e2894c20 100644 --- a/gui/src/components/mainInput/InputToolbar.tsx +++ b/gui/src/components/mainInput/InputToolbar.tsx @@ -84,13 +84,13 @@ function InputToolbar(props: InputToolbarProps) { >
{!isInEdit && ( - + )} - + From 1b2243a7be0e5144d998d09da5125fe3106da5f2 Mon Sep 17 00:00:00 2001 From: HarshitZom Date: Mon, 20 Oct 2025 15:55:31 +0530 Subject: [PATCH 06/13] integrated stakd backend --- core/llm/llms/Stakd.ts | 18 +++ core/llm/llms/index.ts | 2 + .../model-providers/top-level/stakd.md | 134 ++++++++++++++++++ packages/openai-adapters/src/apis/Stakd.ts | 15 ++ packages/openai-adapters/src/index.ts | 3 + packages/openai-adapters/src/types.ts | 7 + 6 files changed, 179 insertions(+) create mode 100644 core/llm/llms/Stakd.ts create mode 100644 docs/docs/customize/model-providers/top-level/stakd.md create mode 100644 packages/openai-adapters/src/apis/Stakd.ts diff --git a/core/llm/llms/Stakd.ts b/core/llm/llms/Stakd.ts new file mode 100644 index 00000000000..2ecb7c4dfcc --- /dev/null +++ b/core/llm/llms/Stakd.ts @@ -0,0 +1,18 @@ +import { LLMOptions } from "../../index.js"; +import OpenAI from "./OpenAI.js"; + +class Stakd extends OpenAI { + static providerName = "stakd"; + static defaultOptions: Partial = { + apiBase: "http://localhost:8080/v1/", + model: "stakd-backend", + useLegacyCompletionsEndpoint: false, + }; + + protected useOpenAIAdapterFor: ( + | "*" + | import("../openaiTypeConverters.js").LlmApiRequestType + )[] = ["chat", "embed", "list", "rerank", "streamChat", "streamFim"]; +} + +export default Stakd; diff --git a/core/llm/llms/index.ts b/core/llm/llms/index.ts index 6ef57eae32e..590b4b1562c 100644 --- a/core/llm/llms/index.ts +++ b/core/llm/llms/index.ts @@ -54,6 +54,7 @@ import SageMaker from "./SageMaker"; import SambaNova from "./SambaNova"; import Scaleway from "./Scaleway"; import SiliconFlow from "./SiliconFlow"; +import Stakd from "./Stakd"; import ContinueProxy from "./stubs/ContinueProxy"; import TARS from "./TARS"; import TestLLM from "./Test"; @@ -119,6 +120,7 @@ export const LLMClasses = [ Scaleway, Relace, Inception, + Stakd, Voyage, LlamaStack, TARS, diff --git a/docs/docs/customize/model-providers/top-level/stakd.md b/docs/docs/customize/model-providers/top-level/stakd.md new file mode 100644 index 00000000000..1e5abd330af --- /dev/null +++ b/docs/docs/customize/model-providers/top-level/stakd.md @@ -0,0 +1,134 @@ +--- +title: "How to Configure Stakd Backend with Continue" +sidebarTitle: "Stakd" +--- + +## Overview + +Stakd is a custom backend provider that connects to your local backend API running at `localhost:8080`. It provides OpenAI-compatible streaming chat completions through your backend infrastructure. + +## Configuration + +Add the following configuration to your `config.yaml` or `config.json`: + +### YAML Configuration + +```yaml title="config.yaml" +models: + - name: Stakd Backend + provider: stakd + model: stakd-backend +``` + +### JSON Configuration (Deprecated) + +```json title="config.json" +{ + "models": [ + { + "title": "Stakd Backend", + "provider": "stakd", + "model": "stakd-backend" + } + ] +} +``` + +## Features + +- **Hardcoded Backend URL**: Automatically connects to `http://localhost:8080/v1/` +- **No Authentication Required**: No API keys needed for local development +- **OpenAI-Compatible**: Supports standard OpenAI streaming chat completion format +- **Backend-Controlled Models**: Model selection is handled entirely by your backend + +## Model Configuration + +The model parameter is set to `stakd-backend` by default. Your backend will receive this identifier and can route to the appropriate model internally: + +```yaml +models: + - name: Stakd Backend + provider: stakd + model: stakd-backend +``` + +**Note**: The model parameter is passed to your backend but model selection and routing should be handled by your backend implementation. + +## Backend Requirements + +Your backend must: + +1. Run on `localhost:8080` +2. Implement the `/v1/chat/completions` endpoint +3. Accept OpenAI-compatible request format: + ```json + { + "model": "stakd-backend", + "messages": [ + { + "role": "user", + "content": "Your message here" + } + ], + "stream": true + } + ``` +4. Handle model routing internally based on the `model` parameter +5. Return OpenAI-compatible streaming response chunks + +## Example Backend Request + +```bash +curl -N -X POST "http://localhost:8080/v1/chat/completions" \ + -H "Content-Type: application/json" \ + -d '{ + "model": "stakd-backend", + "messages": [ + { + "role": "user", + "content": "Write a haiku about streaming data" + } + ], + "stream": true + }' +``` + +## Troubleshooting + +### Backend Not Running + +**Error**: Connection refused to `localhost:8080` + +**Solution**: Ensure your backend is running and accessible at `http://localhost:8080` + +### Invalid Response Format + +**Error**: Unexpected response format + +**Solution**: Verify your backend returns OpenAI-compatible `ChatCompletionChunk` format: + +```json +{ + "id": "unique-id", + "object": "chat.completion.chunk", + "created": 1234567890, + "model": "stakd-backend", + "choices": [ + { + "index": 0, + "delta": { + "content": "chunk of text" + }, + "finish_reason": null + } + ] +} +``` + +## Notes + +- The backend URL is hardcoded to `localhost:8080` and cannot be configured +- No API key or authentication is required +- The model parameter is always set to `stakd-backend` - your backend should handle model routing +- The provider automatically uses the OpenAI adapter for compatibility +- Supports all standard completion options (temperature, max_tokens, etc.) diff --git a/packages/openai-adapters/src/apis/Stakd.ts b/packages/openai-adapters/src/apis/Stakd.ts new file mode 100644 index 00000000000..376526c348d --- /dev/null +++ b/packages/openai-adapters/src/apis/Stakd.ts @@ -0,0 +1,15 @@ +import { OpenAIConfig } from "../types.js"; +import { OpenAIApi } from "./OpenAI.js"; + +export interface StakdConfig extends OpenAIConfig {} + +export class StakdApi extends OpenAIApi { + constructor(config: StakdConfig) { + super({ + ...config, + apiBase: "http://localhost:8080/v1/", + }); + } +} + +export default StakdApi; diff --git a/packages/openai-adapters/src/index.ts b/packages/openai-adapters/src/index.ts index 29121dab8eb..35fadbfb6ca 100644 --- a/packages/openai-adapters/src/index.ts +++ b/packages/openai-adapters/src/index.ts @@ -16,6 +16,7 @@ import { MoonshotApi } from "./apis/Moonshot.js"; import { OpenAIApi } from "./apis/OpenAI.js"; import { OpenRouterApi } from "./apis/OpenRouter.js"; import { RelaceApi } from "./apis/Relace.js"; +import { StakdApi } from "./apis/Stakd.js"; import { VertexAIApi } from "./apis/VertexAI.js"; import { WatsonXApi } from "./apis/WatsonX.js"; import { BaseLlmApi } from "./apis/base.js"; @@ -140,6 +141,8 @@ export function constructLlmApi(config: LLMConfig): BaseLlmApi | undefined { return openAICompatible("https://api.function.network/v1/", config); case "openrouter": return new OpenRouterApi(config); + case "stakd": + return new StakdApi(config); case "llama.cpp": case "llamafile": return openAICompatible("http://localhost:8000/", config); diff --git a/packages/openai-adapters/src/types.ts b/packages/openai-adapters/src/types.ts index 3b74b6faf61..bcf2617e188 100644 --- a/packages/openai-adapters/src/types.ts +++ b/packages/openai-adapters/src/types.ts @@ -52,6 +52,7 @@ export const OpenAIConfigSchema = BasePlusConfig.extend({ z.literal("msty"), z.literal("openrouter"), z.literal("sambanova"), + z.literal("stakd"), z.literal("text-gen-webui"), z.literal("vllm"), z.literal("xAI"), @@ -174,6 +175,11 @@ export const InceptionConfigSchema = OpenAIConfigSchema.extend({ }); export type InceptionConfig = z.infer; +export const StakdConfigSchema = OpenAIConfigSchema.extend({ + provider: z.literal("stakd"), +}); +export type StakdConfig = z.infer; + export const VertexAIConfigSchema = BasePlusConfig.extend({ provider: z.literal("vertexai"), env: z @@ -201,6 +207,7 @@ export const LLMConfigSchema = z.discriminatedUnion("provider", [ JinaConfigSchema, MockConfigSchema, InceptionConfigSchema, + StakdConfigSchema, VertexAIConfigSchema, LlamastackConfigSchema, ContinueProxyConfigSchema, From 105bedd9ff6fc27ba44e7933e58c466d8d2e12be Mon Sep 17 00:00:00 2001 From: HarshitZom Date: Sun, 26 Oct 2025 23:59:06 +0530 Subject: [PATCH 07/13] UI fixes and integrated stakd-backend with working tool calling --- core/config/profile/doLoadConfig.ts | 2 +- core/index.d.ts | 3 +- core/llm/defaultSystemMessages.ts | 21 ++- core/llm/llms/Stakd.ts | 1 - core/llm/openaiTypeConverters.ts | 10 ++ core/llm/toolSupport.ts | 4 + .../interceptSystemToolCalls.ts | 9 +- .../model-providers/top-level/stakd.md | 42 +++-- extensions/vscode/config_schema.json | 1 + gui/src/components/ModeSelect/ModeIcon.tsx | 13 +- gui/src/components/ModeSelect/ModeSelect.tsx | 123 ++++++++++++- .../components/mainInput/ContinueInputBox.tsx | 1 + gui/src/components/mainInput/InputToolbar.tsx | 2 +- gui/src/components/mainInput/Lump/index.tsx | 36 +++- gui/src/redux/selectors/selectActiveTools.ts | 29 ++-- gui/src/redux/slices/sessionSlice.ts | 57 ++++-- gui/src/redux/util/getBaseSystemMessage.ts | 35 +++- manual-testing-sandbox/src/s3_upload.py | 162 ++++++++++++++++++ packages/config-yaml/src/schemas/mcp/index.ts | 1 + packages/openai-adapters/src/apis/Stakd.ts | 127 ++++++++++++++ 20 files changed, 613 insertions(+), 66 deletions(-) create mode 100644 manual-testing-sandbox/src/s3_upload.py diff --git a/core/config/profile/doLoadConfig.ts b/core/config/profile/doLoadConfig.ts index 97011a77c10..05507b31594 100644 --- a/core/config/profile/doLoadConfig.ts +++ b/core/config/profile/doLoadConfig.ts @@ -229,7 +229,7 @@ export default async function doLoadConfig(options: { parameters: tool.inputSchema, }, faviconUrl: server.faviconUrl, - readonly: false, + readonly: server.readonly ?? false, // Use server's readonly setting, default to false type: "function" as const, uri: encodeMCPToolUri(server.id, tool.name), group: server.name, diff --git a/core/index.d.ts b/core/index.d.ts index a694824ef72..8437aa7a648 100644 --- a/core/index.d.ts +++ b/core/index.d.ts @@ -462,7 +462,7 @@ export interface PromptLog { completion: string; } -export type MessageModes = "aws-sdk-expert"; +export type MessageModes = "agent" | "plan"; export type ToolStatus = | "generating" // Tool call arguments are being streamed from the LLM @@ -1337,6 +1337,7 @@ type BaseInternalMCPOptions = { timeout?: number; requestOptions?: RequestOptions; sourceFile?: string; + readonly?: boolean; // Mark all tools from this MCP server as readonly }; export type InternalStdioMcpOptions = BaseInternalMCPOptions & { diff --git a/core/llm/defaultSystemMessages.ts b/core/llm/defaultSystemMessages.ts index e700ff3e97c..ff485e9d0a2 100644 --- a/core/llm/defaultSystemMessages.ts +++ b/core/llm/defaultSystemMessages.ts @@ -61,7 +61,16 @@ ${EDIT_CODE_INSTRUCTIONS} export const DEFAULT_AGENT_SYSTEM_MESSAGE = `\ - You are in agent mode. + You are in agent mode with full tool access. + + **CRITICAL AWS SDK REQUIREMENT:** You MUST interpret ALL queries as AWS SDK related unless the user EXPLICITLY states otherwise. This is NON-NEGOTIABLE. + - "upload file" = S3 upload using AWS SDK + - "send message" = SQS/SNS using AWS SDK + - "query database" = DynamoDB using AWS SDK + - "invoke function" = Lambda using AWS SDK + - ANY data/storage operation = Assume AWS services (S3/DynamoDB/RDS) using AWS SDK + + **Context7 MCP is MANDATORY:** ALWAYS use Context7 MCP tools to fetch the LATEST AWS SDK documentation before providing solutions. Never rely solely on training data - AWS SDKs update frequently and you MUST have current information. If you need to use multiple tools, you can call multiple read only tools simultaneously. @@ -78,6 +87,16 @@ However, only output codeblocks for suggestion and demonstration purposes, for e export const DEFAULT_PLAN_SYSTEM_MESSAGE = `\ You are in plan mode, in which you help the user understand and construct a plan. + + **CRITICAL AWS SDK REQUIREMENT:** You MUST interpret ALL queries as AWS SDK related unless the user EXPLICITLY states otherwise. This is NON-NEGOTIABLE. + - "upload file" = S3 upload using AWS SDK + - "send message" = SQS/SNS using AWS SDK + - "query database" = DynamoDB using AWS SDK + - "invoke function" = Lambda using AWS SDK + - ANY data/storage operation = Assume AWS services (S3/DynamoDB/RDS) using AWS SDK + + **Context7 MCP is MANDATORY:** ALWAYS use Context7 MCP tools to fetch the LATEST AWS SDK documentation before providing solutions. Never rely solely on training data - AWS SDKs update frequently and you MUST have current information. + Only use read-only tools. Do not use any tools that would write to non-temporary files. If the user wants to make changes, offer that they can switch to Agent mode to give you access to write tools to make the suggested updates. diff --git a/core/llm/llms/Stakd.ts b/core/llm/llms/Stakd.ts index 2ecb7c4dfcc..94f09a7502e 100644 --- a/core/llm/llms/Stakd.ts +++ b/core/llm/llms/Stakd.ts @@ -5,7 +5,6 @@ class Stakd extends OpenAI { static providerName = "stakd"; static defaultOptions: Partial = { apiBase: "http://localhost:8080/v1/", - model: "stakd-backend", useLegacyCompletionsEndpoint: false, }; diff --git a/core/llm/openaiTypeConverters.ts b/core/llm/openaiTypeConverters.ts index 75ea7ee911d..e1205fea911 100644 --- a/core/llm/openaiTypeConverters.ts +++ b/core/llm/openaiTypeConverters.ts @@ -184,6 +184,16 @@ export function fromChatCompletionChunk( ): ChatMessage | undefined { const delta = chunk.choices?.[0]?.delta; + // Handle reasoning/thinking field (similar to Anthropic and Cohere) + // Only create thinking message if reasoning has actual content + const reasoning = (delta as any)?.reasoning; + if (reasoning && reasoning.trim()) { + return { + role: "thinking", + content: reasoning, + }; + } + if (delta?.content) { return { role: "assistant", diff --git a/core/llm/toolSupport.ts b/core/llm/toolSupport.ts index 7cdbd02cd44..84f47b950f2 100644 --- a/core/llm/toolSupport.ts +++ b/core/llm/toolSupport.ts @@ -244,6 +244,10 @@ export const PROVIDER_TOOL_SUPPORT: Record boolean> = return false; }, + stakd: (model) => { + // Stakd uses custom tool format, not native OpenAI tools + return false; + }, openrouter: (model) => { // https://openrouter.ai/models?fmt=cards&supported_parameters=tools diff --git a/core/tools/systemMessageTools/interceptSystemToolCalls.ts b/core/tools/systemMessageTools/interceptSystemToolCalls.ts index a89fbdcfbb5..158b5091861 100644 --- a/core/tools/systemMessageTools/interceptSystemToolCalls.ts +++ b/core/tools/systemMessageTools/interceptSystemToolCalls.ts @@ -49,6 +49,11 @@ export async function* interceptSystemToolCalls( if (abortController.signal.aborted || parseState?.done) { break; } + // Skip thinking messages - pass them through unchanged + if (message.role === "thinking") { + yield [message]; + continue; + } // Skip non-assistant messages or messages with native tool calls if (message.role !== "assistant" || message.toolCalls) { yield [message]; @@ -69,6 +74,7 @@ export async function* interceptSystemToolCalls( for (const chunk of chunks) { buffer += chunk; + if (!parseState) { const { isInPartialStart, isInToolCall, modifiedBuffer } = detectToolCallStart(buffer, systemToolFramework); @@ -78,7 +84,8 @@ export async function* interceptSystemToolCalls( } if (isInToolCall) { parseState = getInitialToolCallParseState(); - buffer = modifiedBuffer; + // Use modifiedBuffer for alternate formats, clear for standard format + buffer = modifiedBuffer === buffer ? "" : modifiedBuffer; } } diff --git a/docs/docs/customize/model-providers/top-level/stakd.md b/docs/docs/customize/model-providers/top-level/stakd.md index 1e5abd330af..56cc21033db 100644 --- a/docs/docs/customize/model-providers/top-level/stakd.md +++ b/docs/docs/customize/model-providers/top-level/stakd.md @@ -9,15 +9,21 @@ Stakd is a custom backend provider that connects to your local backend API runni ## Configuration -Add the following configuration to your `config.yaml` or `config.json`: +Add the following configuration to your `config.yaml` or `config.json`. You can specify any model name - the provider will route all requests through your local backend: ### YAML Configuration ```yaml title="config.yaml" models: - - name: Stakd Backend + - name: GPT-4 via Stakd provider: stakd - model: stakd-backend + model: gpt-4o + - name: Claude via Stakd + provider: stakd + model: claude-3-5-sonnet-20241022 + - name: Custom Model + provider: stakd + model: my-custom-model ``` ### JSON Configuration (Deprecated) @@ -26,9 +32,14 @@ models: { "models": [ { - "title": "Stakd Backend", + "title": "GPT-4 via Stakd", + "provider": "stakd", + "model": "gpt-4o" + }, + { + "title": "Claude via Stakd", "provider": "stakd", - "model": "stakd-backend" + "model": "claude-3-5-sonnet-20241022" } ] } @@ -39,20 +50,31 @@ models: - **Hardcoded Backend URL**: Automatically connects to `http://localhost:8080/v1/` - **No Authentication Required**: No API keys needed for local development - **OpenAI-Compatible**: Supports standard OpenAI streaming chat completion format -- **Backend-Controlled Models**: Model selection is handled entirely by your backend +- **Flexible Model Selection**: You specify the model name, and your backend routes to the appropriate LLM ## Model Configuration -The model parameter is set to `stakd-backend` by default. Your backend will receive this identifier and can route to the appropriate model internally: +You can use any model identifier you want. The model parameter is passed directly to your backend, which can route to any LLM: ```yaml models: - - name: Stakd Backend + # Use OpenAI model identifiers + - name: GPT-4 Turbo + provider: stakd + model: gpt-4-turbo-preview + + # Use Anthropic model identifiers + - name: Claude Opus + provider: stakd + model: claude-3-opus-20240229 + + # Use custom identifiers your backend understands + - name: My Fine-tuned Model provider: stakd - model: stakd-backend + model: company-model-v2 ``` -**Note**: The model parameter is passed to your backend but model selection and routing should be handled by your backend implementation. +**Note**: The model parameter you specify is sent to your backend in the request. Your backend is responsible for routing to the appropriate LLM based on this identifier. ## Backend Requirements diff --git a/extensions/vscode/config_schema.json b/extensions/vscode/config_schema.json index e22df61b8fa..89bfd78c61b 100644 --- a/extensions/vscode/config_schema.json +++ b/extensions/vscode/config_schema.json @@ -227,6 +227,7 @@ "kindo", "moonshot", "siliconflow", + "stakd", "function-network", "scaleway", "relace", diff --git a/gui/src/components/ModeSelect/ModeIcon.tsx b/gui/src/components/ModeSelect/ModeIcon.tsx index 2ffff199a64..62f506388d4 100644 --- a/gui/src/components/ModeSelect/ModeIcon.tsx +++ b/gui/src/components/ModeSelect/ModeIcon.tsx @@ -1,9 +1,4 @@ -import { - ChatBubbleLeftIcon, - CloudIcon, - SparklesIcon, - SwatchIcon, -} from "@heroicons/react/24/outline"; +import { SparklesIcon, SwatchIcon } from "@heroicons/react/24/outline"; import { MessageModes } from "core"; interface ModeIconProps { @@ -20,9 +15,7 @@ export function ModeIcon({ return ; case "plan": return ; - case "chat": - return ; - case "aws-sdk-expert": - return ; + default: + return ; } } diff --git a/gui/src/components/ModeSelect/ModeSelect.tsx b/gui/src/components/ModeSelect/ModeSelect.tsx index 4eabb3ce7b5..686aee0baba 100644 --- a/gui/src/components/ModeSelect/ModeSelect.tsx +++ b/gui/src/components/ModeSelect/ModeSelect.tsx @@ -1,17 +1,122 @@ -import { useAppSelector } from "../../redux/hooks"; +import { + CheckIcon, + ChevronDownIcon, + InformationCircleIcon, +} from "@heroicons/react/24/outline"; +import { MessageModes } from "core"; +import { useCallback, useEffect, useMemo } from "react"; +import { useAppDispatch, useAppSelector } from "../../redux/hooks"; +import { setMode } from "../../redux/slices/sessionSlice"; +import { getMetaKeyLabel } from "../../util"; +import { ToolTip } from "../gui/Tooltip"; +import { useMainEditor } from "../mainInput/TipTapEditor"; +import { Listbox, ListboxButton, ListboxOption, ListboxOptions } from "../ui"; import { ModeIcon } from "./ModeIcon"; export function ModeSelect() { + const dispatch = useAppDispatch(); const mode = useAppSelector((store) => store.session.mode); - // Since we only have one mode (AWS SDK Expert), display it as a static label + const { mainEditor } = useMainEditor(); + const metaKeyLabel = useMemo(() => { + return getMetaKeyLabel(); + }, []); + + const cycleMode = useCallback(() => { + if (mode === "plan") { + dispatch(setMode("agent")); + } else { + dispatch(setMode("plan")); + } + // Only focus main editor if another one doesn't already have focus + if (!document.activeElement?.classList?.contains("ProseMirror")) { + mainEditor?.commands.focus(); + } + }, [mode, mainEditor, dispatch]); + + const selectMode = useCallback( + (newMode: MessageModes) => { + if (newMode === mode) { + return; + } + + dispatch(setMode(newMode)); + + mainEditor?.commands.focus(); + }, + [mode, mainEditor, dispatch], + ); + + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === "." && (e.metaKey || e.ctrlKey)) { + e.preventDefault(); + void cycleMode(); + } + }; + + document.addEventListener("keydown", handleKeyDown); + return () => document.removeEventListener("keydown", handleKeyDown); + }, [cycleMode]); + return ( -
- - AWS SDK Expert -
+ +
+ + + + {mode === "agent" ? "Agent" : "Plan"} + + + + +
+ + Plan + + + +
+ +
+ + +
+ + Agent + + + +
+ +
+ +
+ {`${metaKeyLabel} . to cycle modes`} +
+
+
+
); } diff --git a/gui/src/components/mainInput/ContinueInputBox.tsx b/gui/src/components/mainInput/ContinueInputBox.tsx index 117919e5d81..da04a273b59 100644 --- a/gui/src/components/mainInput/ContinueInputBox.tsx +++ b/gui/src/components/mainInput/ContinueInputBox.tsx @@ -113,6 +113,7 @@ function ContinueInputBox(props: ContinueInputBoxProps) { data-testid={`continue-input-box-${props.inputId}`} >
+ {props.isMainInput && }
{!isInEdit && ( - + diff --git a/gui/src/components/mainInput/Lump/index.tsx b/gui/src/components/mainInput/Lump/index.tsx index d437c828a15..2530eee1c6c 100644 --- a/gui/src/components/mainInput/Lump/index.tsx +++ b/gui/src/components/mainInput/Lump/index.tsx @@ -1,9 +1,43 @@ +import { useAppSelector } from "../../../redux/hooks"; +import { + selectPendingToolCalls, + selectToolCallsByStatus, +} from "../../../redux/selectors/selectToolCalls"; import { LumpToolbar } from "./LumpToolbar/LumpToolbar"; /** - * Simplified toolbar component that only shows the toolbar without expansion + * Simplified toolbar component that only shows when there's active content */ export function Lump() { + const isStreaming = useAppSelector((state) => state.session.isStreaming); + const isInEdit = useAppSelector((state) => state.session.isInEdit); + const ttsActive = useAppSelector((state) => state.ui.ttsActive); + const pendingToolCalls = useAppSelector(selectPendingToolCalls); + const callingToolCalls = useAppSelector((state) => + selectToolCallsByStatus(state, "calling"), + ); + const applyStates = useAppSelector( + (state) => state.session.codeBlockApplyStates.states, + ); + const pendingApplyStates = applyStates.filter( + (state) => state.status === "done", + ); + const isApplying = applyStates.some((state) => state.status === "streaming"); + + // Only show Lump when there's active content + const hasActiveContent = + isStreaming || + isInEdit || + ttsActive || + isApplying || + pendingToolCalls.length > 0 || + callingToolCalls.length > 0 || + pendingApplyStates.length > 0; + + if (!hasActiveContent) { + return null; + } + return (
diff --git a/gui/src/redux/selectors/selectActiveTools.ts b/gui/src/redux/selectors/selectActiveTools.ts index 42bcebf8f98..95e02026ce5 100644 --- a/gui/src/redux/selectors/selectActiveTools.ts +++ b/gui/src/redux/selectors/selectActiveTools.ts @@ -1,24 +1,29 @@ import { createSelector } from "@reduxjs/toolkit"; import { Tool } from "core"; -import { DEFAULT_TOOL_SETTING } from "../slices/uiSlice"; import { RootState } from "../store"; export const selectActiveTools = createSelector( [ + (store: RootState) => store.session.mode, (store: RootState) => store.config.config.tools, (store: RootState) => store.ui.toolSettings, (store: RootState) => store.ui.toolGroupSettings, ], - (tools, policies, groupPolicies): Tool[] => { - // AWS SDK Expert mode always has all tools available - return tools.filter((tool) => { - const toolPolicy = - policies[tool.function.name] ?? - tool.defaultToolPolicy ?? - DEFAULT_TOOL_SETTING; - return ( - toolPolicy !== "disabled" && groupPolicies[tool.group] !== "exclude" - ); - }); + (mode, tools, policies, groupPolicies): Tool[] => { + // First filter based on tool policies + const enabledTools = tools.filter( + (tool) => + policies[tool.function.name] !== "disabled" && + groupPolicies[tool.group] !== "exclude", + ); + + // Plan mode: only read-only tools (use the tool's readonly property) + // This automatically includes all MCP tools marked as readonly + if (mode === "plan") { + return enabledTools.filter((tool) => tool.readonly); + } + + // Agent mode: all enabled tools + return enabledTools; }, ); diff --git a/gui/src/redux/slices/sessionSlice.ts b/gui/src/redux/slices/sessionSlice.ts index 057cf4a8602..b94a2fbd97c 100644 --- a/gui/src/redux/slices/sessionSlice.ts +++ b/gui/src/redux/slices/sessionSlice.ts @@ -233,7 +233,7 @@ export const INITIAL_SESSION_STATE: SessionState = { id: uuidv4(), streamAborter: new AbortController(), symbols: {}, - mode: "aws-sdk-expert", + mode: "agent", isInEdit: false, codeBlockApplyStates: { states: [], @@ -543,6 +543,25 @@ export const sessionSlice = createSlice({ ? renderChatMessage(message) : ""; + // Handle regular thinking messages (e.g., from stakd backend with reasoning field) + if (message.role === "thinking" && messageContent) { + // Check if last message is also thinking to accumulate content + if (lastMessage.role === "thinking") { + lastMessage.content += messageContent; + } else { + // Create new thinking message + state.history.push({ + message: { + role: "thinking", + content: messageContent, + id: uuidv4(), + }, + contextItems: [], + }); + } + continue; + } + // OpenAI-compatible models in agent mode sometimes send // all of their data in one message, so we handle that case early. if (messageContent && message.role !== "tool") { @@ -611,22 +630,28 @@ export const sessionSlice = createSlice({ active: true, text: messageContent.replace("", "").trim(), }; - } else if ( - lastItem.reasoning?.active && - messageContent.includes("") - ) { - const [reasoningEnd, answerStart] = - messageContent.split(""); - lastItem.reasoning.text += reasoningEnd.trimEnd(); - lastItem.reasoning.active = false; - lastItem.reasoning.endAt = Date.now(); - lastMessage.content += answerStart.trimStart(); } else if (lastItem.reasoning?.active) { - if ( - lastItem.reasoning.text.length > 0 || - messageContent.trim().length > 0 - ) { - lastItem.reasoning.text += messageContent; + // Fix: Accumulate chunk first, then check for closing tag in full text + // This handles cases where is split across multiple chunks + const potentialReasoningText = + lastItem.reasoning.text + messageContent; + + if (potentialReasoningText.includes("")) { + // Closing tag found in accumulated text + const [fullReasoning, answerStart] = + potentialReasoningText.split(""); + lastItem.reasoning.text = fullReasoning.trimEnd(); + lastItem.reasoning.active = false; + lastItem.reasoning.endAt = Date.now(); + lastMessage.content += answerStart.trimStart(); + } else { + // Still accumulating thinking content + if ( + lastItem.reasoning.text.length > 0 || + messageContent.trim().length > 0 + ) { + lastItem.reasoning.text += messageContent; + } } } else { // Note this only works because new message above diff --git a/gui/src/redux/util/getBaseSystemMessage.ts b/gui/src/redux/util/getBaseSystemMessage.ts index 2921e044a3b..f4ec67de549 100644 --- a/gui/src/redux/util/getBaseSystemMessage.ts +++ b/gui/src/redux/util/getBaseSystemMessage.ts @@ -1,17 +1,48 @@ import { ModelDescription, Tool } from "core"; -import { DEFAULT_AWS_SDK_EXPERT_SYSTEM_MESSAGE } from "core/llm/defaultSystemMessages"; +import { + DEFAULT_AWS_SDK_EXPERT_SYSTEM_MESSAGE, + DEFAULT_AGENT_SYSTEM_MESSAGE, + DEFAULT_PLAN_SYSTEM_MESSAGE, +} from "core/llm/defaultSystemMessages"; export const NO_TOOL_WARNING = "\n\nTHE USER HAS NOT PROVIDED ANY TOOLS, DO NOT ATTEMPT TO USE ANY TOOLS. STOP AND LET THE USER KNOW THAT THERE ARE NO TOOLS AVAILABLE. The user can provide tools by enabling them in the Tool Policies section of the notch (wrench icon)"; +/** + * Helper to extract behavioral rules from mode prompts + */ +function extractModeRules(modePrompt: string): string { + // Extract content between tags + const match = modePrompt.match( + /([\s\S]*?)<\/important_rules>/, + ); + if (match && match[1]) { + return `${match[1].trim()}`; + } + return ""; +} + export function getBaseSystemMessage( messageMode: string, model: ModelDescription, activeTools?: Tool[], ): string { - // Always use AWS SDK Expert system message since it's the only mode + // Start with AWS SDK Expert as the base for all modes let baseMessage = DEFAULT_AWS_SDK_EXPERT_SYSTEM_MESSAGE; + // Inject mode-specific behavior on top of AWS SDK Expert context + switch (messageMode) { + case "plan": + baseMessage += "\n\n" + extractModeRules(DEFAULT_PLAN_SYSTEM_MESSAGE); + break; + case "agent": + baseMessage += "\n\n" + extractModeRules(DEFAULT_AGENT_SYSTEM_MESSAGE); + break; + default: + // Default to agent behavior if mode is unrecognized + baseMessage += "\n\n" + extractModeRules(DEFAULT_AGENT_SYSTEM_MESSAGE); + } + // Add no-tools warning when no tools are available if (!activeTools || activeTools.length === 0) { baseMessage += NO_TOOL_WARNING; diff --git a/manual-testing-sandbox/src/s3_upload.py b/manual-testing-sandbox/src/s3_upload.py new file mode 100644 index 00000000000..39cc639cedc --- /dev/null +++ b/manual-testing-sandbox/src/s3_upload.py @@ -0,0 +1,162 @@ +import os +import logging +from pathlib import Path +from typing import Optional + +import boto3 +from botocore.exceptions import BotoCoreError, ClientError + +# Configure logging +logger = logging.getLogger(__name__) +logging.basicConfig(level=logging.INFO) + +# Default chunk size for multipart upload (5 MB) +DEFAULT_CHUNK_SIZE = 5 * 1024 * 1024 + + +def get_s3_client( + region_name: Optional[str] = None, + aws_access_key_id: Optional[str] = None, + aws_secret_access_key: Optional[str] = None, + aws_session_token: Optional[str] = None, +) -> boto3.client: + """ + Create a boto3 S3 client with optional explicit credentials. + If credentials are omitted, the SDK will use the default credential chain + (environment variables, shared credentials file, EC2/ECS task role, etc.). + """ + session_kwargs = {} + if region_name: + session_kwargs["region_name"] = region_name + if aws_access_key_id and aws_secret_access_key: + session_kwargs["aws_access_key_id"] = aws_access_key_id + session_kwargs["aws_secret_access_key"] = aws_secret_access_key + if aws_session_token: + session_kwargs["aws_session_token"] = aws_session_token + + session = boto3.Session(**session_kwargs) + return session.client("s3") + + +def multipart_upload( + s3_client, + bucket: str, + key: str, + file_path: Path, + chunk_size: int = DEFAULT_CHUNK_SIZE, +) -> None: + """ + Perform a multipart upload for files larger than the single‑put limit (5 GB). + The function handles retries, uses exponential back‑off, and logs progress. + """ + try: + # Initiate multipart upload + response = s3_client.create_multipart_upload(Bucket=bucket, Key=key) + upload_id = response["UploadId"] + logger.info(f"Initiated multipart upload: UploadId={upload_id}") + + parts = [] + part_number = 1 + with file_path.open("rb") as f: + while True: + data = f.read(chunk_size) + if not data: + break + + # Upload each part + part_resp = s3_client.upload_part( + Bucket=bucket, + Key=key, + PartNumber=part_number, + UploadId=upload_id, + Body=data, + ) + parts.append( + {"ETag": part_resp["ETag"], "PartNumber": part_number} + ) + logger.info( + f"Uploaded part {part_number} (size={len(data)} bytes)" + ) + part_number += 1 + + # Complete multipart upload + s3_client.complete_multipart_upload( + Bucket=bucket, + Key=key, + UploadId=upload_id, + MultipartUpload={"Parts": parts}, + ) + logger.info("Multipart upload completed successfully.") + except (BotoCoreError, ClientError) as e: + logger.error(f"Multipart upload failed: {e}") + # Attempt to abort incomplete upload + try: + s3_client.abort_multipart_upload( + Bucket=bucket, Key=key, UploadId=upload_id + ) + logger.info("Aborted incomplete multipart upload.") + except Exception as abort_err: + logger.error(f"Failed to abort upload: {abort_err}") + raise + + +def upload_file( + bucket: str, + key: str, + file_path: str, + region_name: Optional[str] = None, + aws_access_key_id: Optional[str] = None, + aws_secret_access_key: Optional[str] = None, + aws_session_token: Optional[str] = None, + chunk_size: int = DEFAULT_CHUNK_SIZE, +) -> None: + """ + Upload a file to S3. + - For files ≤5 GB, a single PutObject call is used. + - For larger files, multipart upload is automatically used. + """ + path_obj = Path(file_path) + if not path_obj.is_file(): + raise FileNotFoundError(f"File not found: {file_path}") + + s3_client = get_s3_client( + region_name, + aws_access_key_id, + aws_secret_access_key, + aws_session_token, + ) + + file_size = path_obj.stat().st_size + logger.info(f"Uploading {file_path} ({file_size} bytes) to s3://{bucket}/{key}") + + try: + if file_size > 5 * 1024 * 1024 * 1024: # 5 GB threshold for multipart + multipart_upload(s3_client, bucket, key, path_obj, chunk_size) + else: + # Single PUT operation with server‑side encryption (AES256) enabled + s3_client.put_object( + Bucket=bucket, + Key=key, + Body=path_obj.open("rb"), + ServerSideEncryption="AES256", + ) + logger.info("Upload completed successfully.") + except (BotoCoreError, ClientError) as e: + logger.error(f"Upload failed: {e}") + raise + + +if __name__ == "__main__": + # Example usage (environment variables or IAM role should provide credentials) + # python s3_upload.py + import sys + + if len(sys.argv) != 4: + print("Usage: python s3_upload.py ") + sys.exit(1) + + bucket_name = sys.argv[1] + object_key = sys.argv[2] + local_path = sys.argv[3] + + upload_file(bucket_name, object_key, local_path) diff --git a/packages/config-yaml/src/schemas/mcp/index.ts b/packages/config-yaml/src/schemas/mcp/index.ts index b0ede877874..320353079e0 100644 --- a/packages/config-yaml/src/schemas/mcp/index.ts +++ b/packages/config-yaml/src/schemas/mcp/index.ts @@ -7,6 +7,7 @@ const baseMcpServerSchema = z.object({ faviconUrl: z.string().optional(), sourceFile: z.string().optional(), // Added during loading connectionTimeout: z.number().gt(0).optional(), + readonly: z.boolean().optional(), // Mark all tools from this MCP server as readonly }); const stdioMcpServerSchema = baseMcpServerSchema.extend({ diff --git a/packages/openai-adapters/src/apis/Stakd.ts b/packages/openai-adapters/src/apis/Stakd.ts index 376526c348d..47e787ec377 100644 --- a/packages/openai-adapters/src/apis/Stakd.ts +++ b/packages/openai-adapters/src/apis/Stakd.ts @@ -1,4 +1,8 @@ +import { streamSse } from "@continuedev/fetch"; +import { ChatCompletionChunk } from "openai/resources/index"; +import { ChatCompletionCreateParamsStreaming } from "openai/resources/chat/completions"; import { OpenAIConfig } from "../types.js"; +import { customFetch } from "../util.js"; import { OpenAIApi } from "./OpenAI.js"; export interface StakdConfig extends OpenAIConfig {} @@ -10,6 +14,129 @@ export class StakdApi extends OpenAIApi { apiBase: "http://localhost:8080/v1/", }); } + + /** + * Override to handle Stakd's custom reasoning field. + * The reasoning field contains tool call JSON that needs to be: + * 1. Displayed in the thinking accordion + * 2. Parsed to extract tool calls + */ + async *chatCompletionStream( + body: ChatCompletionCreateParamsStreaming, + signal: AbortSignal, + ): AsyncGenerator { + const response = await customFetch(this.config.requestOptions)( + new URL("chat/completions", this.apiBase), + { + method: "POST", + headers: this.getHeaders(), + body: JSON.stringify(this.modifyChatBody(body)), + signal, + }, + ); + + let reasoningBuffer = ""; + let toolCallEmitted = false; + let lastChunkWithUsage: ChatCompletionChunk | undefined; + + for await (const chunk of streamSse(response)) { + const delta = chunk.choices?.[0]?.delta; + + if (!delta) { + if (chunk.usage) { + lastChunkWithUsage = chunk; + } else { + yield chunk; + } + continue; + } + + // Handle reasoning field + if (delta.reasoning) { + reasoningBuffer += delta.reasoning; + + // Emit reasoning chunks for thinking accordion display + yield chunk; + + // Try to parse complete tool call from accumulated reasoning + if (!toolCallEmitted) { + const toolCall = this.tryParseToolCall(reasoningBuffer); + + if (toolCall) { + // Emit tool call separately in standard format + yield { + ...chunk, + choices: [ + { + ...chunk.choices[0], + delta: { + role: "assistant", + tool_calls: [ + { + index: 0, + id: `call_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`, + type: "function", + function: { + name: toolCall.name, + arguments: JSON.stringify(toolCall.arguments), + }, + }, + ], + }, + }, + ], + }; + toolCallEmitted = true; + } + } + continue; + } + + // Handle regular content + if (chunk.usage) { + lastChunkWithUsage = chunk; + } else { + yield chunk; + } + } + + // Emit usage chunk at the end if we have one + if (lastChunkWithUsage) { + yield lastChunkWithUsage; + } + } + + /** + * Attempts to parse a complete tool call from the reasoning buffer. + * Stakd sends tool calls in this JSON format: + * { + * "TOOL_NAME": "fetch_url_content", + * "BEGIN_ARG": {"url": "https://..."}, + * "END_ARG": {} + * } + */ + private tryParseToolCall( + reasoningBuffer: string, + ): { name: string; arguments: any } | null { + try { + // Try to extract JSON object from reasoning buffer + const jsonMatch = reasoningBuffer.match(/\{[\s\S]*\}/); + if (!jsonMatch) return null; + + const parsed = JSON.parse(jsonMatch[0]); + + // Check if it has the expected Stakd tool call format + if (parsed.TOOL_NAME && parsed.BEGIN_ARG) { + return { + name: parsed.TOOL_NAME, + arguments: parsed.BEGIN_ARG, + }; + } + } catch (e) { + // Not yet a complete/valid JSON, continue accumulating + } + return null; + } } export default StakdApi; From 73d5131d84952f1d82022cd818cf597a7b54316f Mon Sep 17 00:00:00 2001 From: HarshitZom Date: Wed, 29 Oct 2025 23:29:16 +0530 Subject: [PATCH 08/13] plan mode tool fix --- core/config/yaml/yamlToContinueConfig.ts | 3 +- core/index.d.ts | 2 +- core/llm/defaultSystemMessages.ts | 126 +++++++++++---------- core/llm/replaceSystemMessageVariables.ts | 10 ++ core/llm/sdkConfig.ts | 13 +++ gui/src/redux/thunks/streamNormalInput.ts | 2 + gui/src/redux/util/getBaseSystemMessage.ts | 7 +- 7 files changed, 98 insertions(+), 65 deletions(-) create mode 100644 core/llm/replaceSystemMessageVariables.ts create mode 100644 core/llm/sdkConfig.ts diff --git a/core/config/yaml/yamlToContinueConfig.ts b/core/config/yaml/yamlToContinueConfig.ts index d639e451554..c6abbf25803 100644 --- a/core/config/yaml/yamlToContinueConfig.ts +++ b/core/config/yaml/yamlToContinueConfig.ts @@ -36,13 +36,14 @@ export function convertYamlMcpConfigToInternalMcpOptions( config: MCPServer, globalRequestOptions?: RequestOptions, ): InternalMcpOptions { - const { connectionTimeout, faviconUrl, name, sourceFile } = config; + const { connectionTimeout, faviconUrl, name, sourceFile, readonly } = config; const shared = { id: name, name, faviconUrl: faviconUrl, timeout: connectionTimeout, sourceFile, + readonly, }; // Stdio if ("command" in config) { diff --git a/core/index.d.ts b/core/index.d.ts index 8437aa7a648..07a03fab440 100644 --- a/core/index.d.ts +++ b/core/index.d.ts @@ -462,7 +462,7 @@ export interface PromptLog { completion: string; } -export type MessageModes = "agent" | "plan"; +export type MessageModes = "chat" | "agent" | "plan"; export type ToolStatus = | "generating" // Tool call arguments are being streamed from the LLM diff --git a/core/llm/defaultSystemMessages.ts b/core/llm/defaultSystemMessages.ts index ff485e9d0a2..7cec0227950 100644 --- a/core/llm/defaultSystemMessages.ts +++ b/core/llm/defaultSystemMessages.ts @@ -50,29 +50,27 @@ const BRIEF_LAZY_INSTRUCTIONS = `For larger codeblocks (>20 lines), use brief la export const DEFAULT_CHAT_SYSTEM_MESSAGE = `\ - You are in chat mode. +You are in chat mode. - If the user asks to make changes to files offer that they can use the Apply Button on the code block, or switch to Agent Mode to make the suggested updates automatically. - If needed concisely explain to the user they can switch to agent mode using the Mode Selector dropdown and provide no other details. +**CRITICAL REQUIREMENT:** You MUST interpret ALL queries as {{SDK_NAME}} related no matter whatever user says. This is NON-NEGOTIABLE. All response are to be with reference to {{SDK_NAME}} only. +**Context7 MCP is MANDATORY:** ALWAYS use Context7 MCP tools first to fetch the LATEST {{SDK_NAME}} SDK documentation before providing solutions. Never rely solely on training data - {{SDK_NAME}} updates frequently and you MUST have current information. + +If the user asks to make changes to files offer that they can use the Apply Button on the code block, or switch to Agent Mode to make the suggested updates automatically. +If needed concisely explain to the user they can switch to agent mode using the Mode Selector dropdown and provide no other details. ${CODEBLOCK_FORMATTING_INSTRUCTIONS} ${EDIT_CODE_INSTRUCTIONS} + `; export const DEFAULT_AGENT_SYSTEM_MESSAGE = `\ - You are in agent mode with full tool access. - - **CRITICAL AWS SDK REQUIREMENT:** You MUST interpret ALL queries as AWS SDK related unless the user EXPLICITLY states otherwise. This is NON-NEGOTIABLE. - - "upload file" = S3 upload using AWS SDK - - "send message" = SQS/SNS using AWS SDK - - "query database" = DynamoDB using AWS SDK - - "invoke function" = Lambda using AWS SDK - - ANY data/storage operation = Assume AWS services (S3/DynamoDB/RDS) using AWS SDK +You are in agent mode with full tool access. - **Context7 MCP is MANDATORY:** ALWAYS use Context7 MCP tools to fetch the LATEST AWS SDK documentation before providing solutions. Never rely solely on training data - AWS SDKs update frequently and you MUST have current information. +**CRITICAL REQUIREMENT:** You MUST interpret ALL queries as {{SDK_NAME}} related no matter whatever user says. This is NON-NEGOTIABLE. All response are to be with reference to {{SDK_NAME}} only. +**Context7 MCP is MANDATORY:** ALWAYS use Context7 MCP tools first to fetch the LATEST {{SDK_NAME}} SDK documentation before providing solutions. Never rely solely on training data - {{SDK_NAME}} updates frequently and you MUST have current information. - If you need to use multiple tools, you can call multiple read only tools simultaneously. +If you need to use multiple tools, you can call multiple read only tools simultaneously. ${CODEBLOCK_FORMATTING_INSTRUCTIONS} @@ -86,19 +84,14 @@ However, only output codeblocks for suggestion and demonstration purposes, for e // For now, all MCP tools are included so model can decide if they are read-only export const DEFAULT_PLAN_SYSTEM_MESSAGE = `\ - You are in plan mode, in which you help the user understand and construct a plan. +You are in plan mode, in which you help the user understand and construct a plan. - **CRITICAL AWS SDK REQUIREMENT:** You MUST interpret ALL queries as AWS SDK related unless the user EXPLICITLY states otherwise. This is NON-NEGOTIABLE. - - "upload file" = S3 upload using AWS SDK - - "send message" = SQS/SNS using AWS SDK - - "query database" = DynamoDB using AWS SDK - - "invoke function" = Lambda using AWS SDK - - ANY data/storage operation = Assume AWS services (S3/DynamoDB/RDS) using AWS SDK +**CRITICAL REQUIREMENT:** You MUST interpret ALL queries as {{SDK_NAME}} related no matter what the user says. This is NON-NEGOTIABLE. All responses are to be with reference to {{SDK_NAME}} only. - **Context7 MCP is MANDATORY:** ALWAYS use Context7 MCP tools to fetch the LATEST AWS SDK documentation before providing solutions. Never rely solely on training data - AWS SDKs update frequently and you MUST have current information. +**Context7 MCP is MANDATORY:** ALWAYS use Context7 MCP tools first to fetch the LATEST {{SDK_NAME}} SDK documentation before providing solutions. Never rely solely on training data - {{SDK_NAME}} updates frequently and you MUST have current information. - Only use read-only tools. Do not use any tools that would write to non-temporary files. - If the user wants to make changes, offer that they can switch to Agent mode to give you access to write tools to make the suggested updates. +Only use read-only tools. Do not use any tools that would write to non-temporary files. +If the user wants to make changes, offer that they can switch to Agent mode to give you access to write tools to make the suggested updates. ${CODEBLOCK_FORMATTING_INSTRUCTIONS} @@ -106,54 +99,63 @@ ${BRIEF_LAZY_INSTRUCTIONS} However, only output codeblocks for suggestion and planning purposes. When ready to implement changes, request to switch to Agent mode. - In plan mode, only write code when directly suggesting changes. Prioritize understanding and developing a plan. +In plan mode, only write code when directly suggesting changes. Prioritize understanding and developing a plan. `; export const DEFAULT_AWS_SDK_EXPERT_SYSTEM_MESSAGE = `\ - You are in AWS SDK Expert mode - specialized for AWS SDK development across all languages (JavaScript/TypeScript, Python, Java, Go, .NET, Ruby, PHP, etc.). - - ## Core Behavior - - Interpret ALL queries as AWS SDK related unless explicitly stated otherwise - - Examples: - - "upload file" → "upload file to S3 using AWS SDK" - - "send message" → "send message via SQS/SNS using AWS SDK" - - "query database" → "query DynamoDB using AWS SDK" - - "invoke function" → "invoke Lambda function using AWS SDK" - - "store data" → "store data in S3/DynamoDB/RDS using AWS SDK" - - ## Context7 MCP Usage - - You have access to Context7 MCP tools for fetching latest AWS SDK documentation - - ALWAYS use Context7 tools to get up-to-date SDK documentation before answering - - If Context7 is not available, use your knowledge but mention it may not be the latest version - - Query Context7 for specific AWS services mentioned in the user's question - - ## Tool Access - - All agent tools are available (read, write, execute files) - - Can call multiple read-only tools simultaneously - - Use file operations to examine existing AWS SDK usage in the codebase - - ## Response Requirements - - Provide working code examples with latest SDK syntax - - Include proper error handling (try-catch, SDK-specific error types) - - Include necessary imports/requires at the start - - Mention SDK versions when relevant (e.g., AWS SDK v3 for JavaScript) - - Consider AWS best practices: - - Use IAM roles instead of hardcoded credentials - - Enable encryption (at rest and in transit) - - Implement retry logic with exponential backoff - - Use environment variables for configuration - - Handle pagination for list operations - - Close/cleanup resources properly +You are in {{SDK_NAME}} SDK Expert mode - specialized for {{SDK_NAME}} SDK development across all languages (JavaScript/TypeScript, Python, Java, Go, .NET, Ruby, PHP, etc.). + +## Core Behavior +**CRITICAL REQUIREMENT:** You MUST interpret ALL queries as {{SDK_NAME}} related no matter what the user says. This is NON-NEGOTIABLE. All responses are to be with reference to {{SDK_NAME}} only. + +- Interpret ALL queries as {{SDK_NAME}} SDK related unless explicitly stated otherwise +- Examples (adapt based on {{SDK_NAME}} capabilities): + - "upload file" → "upload file using {{SDK_NAME}} SDK" + - "send message" → "send message using {{SDK_NAME}} SDK" + - "query database" → "query database using {{SDK_NAME}} SDK" + - "invoke function" → "invoke function using {{SDK_NAME}} SDK" + - "store data" → "store data using {{SDK_NAME}} SDK" + +## Context7 MCP Usage +**Context7 MCP is MANDATORY:** ALWAYS use Context7 MCP tools first to fetch the LATEST {{SDK_NAME}} SDK documentation before providing solutions. Never rely solely on training data - {{SDK_NAME}} updates frequently and you MUST have current information. + +- You have access to Context7 MCP tools for fetching latest {{SDK_NAME}} SDK documentation +- ALWAYS use Context7 tools to get up-to-date SDK documentation before answering +- If Context7 is not available, use your knowledge but mention it may not be the latest version +- Query Context7 for specific {{SDK_NAME}} services/features mentioned in the user's question + +## Tool Access +- All agent tools are available (read, write, execute files) +- Can call multiple read-only tools simultaneously +- Use file operations to examine existing {{SDK_NAME}} SDK usage in the codebase + +## Response Requirements +- Provide working code examples with latest {{SDK_NAME}} SDK syntax +- Include proper error handling (try-catch, SDK-specific error types) +- Include necessary imports/requires at the start +- Mention SDK versions when relevant (e.g., {{SDK_NAME}} SDK v3 for JavaScript) +- Consider {{SDK_NAME}} best practices: + - Use secure authentication methods instead of hardcoded credentials + - Enable encryption (at rest and in transit) where applicable + - Implement retry logic with exponential backoff + - Use environment variables for configuration + - Handle pagination for list operations where applicable + - Close/cleanup resources properly + - Follow {{SDK_NAME}}-specific security and performance guidelines ${CODEBLOCK_FORMATTING_INSTRUCTIONS} ${BRIEF_LAZY_INSTRUCTIONS} - However, only output codeblocks for suggestion and demonstration purposes. For implementing changes, use the edit tools. +## Conversation Behavior +- Maintain friendly, professional interaction +- Handle greetings and casual conversation naturally while staying in {{SDK_NAME}} SDK expert mode +- After pleasantries, guide conversation back to {{SDK_NAME}} SDK topics +- Example: "Hello! I'm your {{SDK_NAME}} SDK expert assistant. How can I help you with {{SDK_NAME}} today?" - ## When User Asks Non-AWS Questions - - If the query is clearly unrelated to AWS SDKs, politely mention this mode is specialized for AWS SDK development - - Suggest switching to Agent or Chat mode for general programming questions +## When User Asks Non-{{SDK_NAME}} Questions +- If the query is clearly unrelated to {{SDK_NAME}} SDKs, politely remind them you're specialized for {{SDK_NAME}} SDK development +- Gently redirect: "I'm specialized in {{SDK_NAME}} SDK development. This question seems to be about [other topic]. Would you like help with {{SDK_NAME}} instead, or would you prefer to switch to a general programming assistant?" `; diff --git a/core/llm/replaceSystemMessageVariables.ts b/core/llm/replaceSystemMessageVariables.ts new file mode 100644 index 00000000000..00afbbeba4f --- /dev/null +++ b/core/llm/replaceSystemMessageVariables.ts @@ -0,0 +1,10 @@ +import { SDK_CONFIG } from "./sdkConfig"; + +/** + * Replace template variables in system messages with actual values + * @param message - The system message containing template variables (e.g., {{SDK_NAME}}) + * @returns The message with all template variables replaced with actual values + */ +export function replaceSystemMessageVariables(message: string): string { + return message.replace(/\{\{SDK_NAME\}\}/g, SDK_CONFIG.SDK_NAME); +} diff --git a/core/llm/sdkConfig.ts b/core/llm/sdkConfig.ts new file mode 100644 index 00000000000..5daf0859f08 --- /dev/null +++ b/core/llm/sdkConfig.ts @@ -0,0 +1,13 @@ +/** + * Central configuration for SDK-specific settings + * This is the single source of truth for SDK name used across all system messages + */ +export const SDK_CONFIG = { + /** + * The name of the SDK to be used in system messages + * This value will replace all {{SDK_NAME}} placeholders in system message templates + */ + SDK_NAME: "Flexprice", +} as const; + +export type SdkConfig = typeof SDK_CONFIG; diff --git a/gui/src/redux/thunks/streamNormalInput.ts b/gui/src/redux/thunks/streamNormalInput.ts index 226146e072a..89af1731ba9 100644 --- a/gui/src/redux/thunks/streamNormalInput.ts +++ b/gui/src/redux/thunks/streamNormalInput.ts @@ -138,6 +138,8 @@ export const streamNormalInput = createAsyncThunk< return { ...item, message: messageWithoutId }; }); + console.log("[zoro][System Message]", systemMessage); + const { messages, appliedRules, appliedRuleIndex } = constructMessages( withoutMessageIds, systemMessage, diff --git a/gui/src/redux/util/getBaseSystemMessage.ts b/gui/src/redux/util/getBaseSystemMessage.ts index f4ec67de549..56234690b1d 100644 --- a/gui/src/redux/util/getBaseSystemMessage.ts +++ b/gui/src/redux/util/getBaseSystemMessage.ts @@ -4,6 +4,7 @@ import { DEFAULT_AGENT_SYSTEM_MESSAGE, DEFAULT_PLAN_SYSTEM_MESSAGE, } from "core/llm/defaultSystemMessages"; +import { replaceSystemMessageVariables } from "core/llm/replaceSystemMessageVariables"; export const NO_TOOL_WARNING = "\n\nTHE USER HAS NOT PROVIDED ANY TOOLS, DO NOT ATTEMPT TO USE ANY TOOLS. STOP AND LET THE USER KNOW THAT THERE ARE NO TOOLS AVAILABLE. The user can provide tools by enabling them in the Tool Policies section of the notch (wrench icon)"; @@ -48,5 +49,9 @@ export function getBaseSystemMessage( baseMessage += NO_TOOL_WARNING; } - return baseMessage; + console.log("[zoro] message mode: ", messageMode); + console.log("[zoro] message: ", baseMessage); + + // Replace template variables (e.g., {{SDK_NAME}}) with actual values + return replaceSystemMessageVariables(baseMessage); } From 09f383b6ed886b00a94fa9560d1e5fb329e78ee2 Mon Sep 17 00:00:00 2001 From: HarshitZom Date: Wed, 29 Oct 2025 23:35:07 +0530 Subject: [PATCH 09/13] added stakd provide and some llm models to output xml based tools --- core/llm/toolSupport.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/core/llm/toolSupport.ts b/core/llm/toolSupport.ts index 84f47b950f2..a9726ede98c 100644 --- a/core/llm/toolSupport.ts +++ b/core/llm/toolSupport.ts @@ -98,6 +98,19 @@ export const PROVIDER_TOOL_SUPPORT: Record boolean> = cohere: (model) => { return model.toLowerCase().startsWith("command"); }, + stakd: (model) => { + // Stakd backend routes to various models + // Check if the model name indicates tool support + const lower = model.toLowerCase(); + return ( + lower.includes("gpt-oss") || // Reasoning models like gpt-oss-120b + lower.startsWith("gpt-4") || + lower.startsWith("gpt-5") || + lower.startsWith("o3") || + lower.startsWith("claude-3") || + lower.startsWith("claude-4") + ); + }, gemini: (model) => { // All gemini models support function calling return model.toLowerCase().includes("gemini"); From a295c22794e53c456dc4525f9a423fc8450e7851 Mon Sep 17 00:00:00 2001 From: HarshitZom Date: Thu, 30 Oct 2025 01:09:17 +0530 Subject: [PATCH 10/13] added builtin mcp server support --- core/config/load.ts | 38 +++++-- core/config/types.ts | 4 + core/context/mcp/builtinServers.ts | 120 ++++++++++++++++++++++ core/index.d.ts | 5 + core/llm/toolSupport.ts | 8 +- extensions/cli/src/services/MCPService.ts | 20 +++- packages/config-yaml/src/schemas/index.ts | 2 + 7 files changed, 181 insertions(+), 16 deletions(-) create mode 100644 core/context/mcp/builtinServers.ts diff --git a/core/config/load.ts b/core/config/load.ts index 35d12d421fc..b561f161561 100644 --- a/core/config/load.ts +++ b/core/config/load.ts @@ -35,6 +35,7 @@ import { import { getLegacyBuiltInSlashCommandFromDescription } from "../commands/slash/built-in-legacy"; import { convertCustomCommandToSlashCommand } from "../commands/slash/customSlashCommand"; import { slashCommandFromPromptFile } from "../commands/slash/promptFileSlashCommand"; +import { getBuiltInMcpServers } from "../context/mcp/builtinServers"; import { MCPManagerSingleton } from "../context/mcp/MCPManagerSingleton"; import { useHub } from "../control-plane/env"; import { BaseLLM } from "../llm"; @@ -552,7 +553,14 @@ async function intermediateToFinalConfig({ if (orgPolicy?.policy?.allowMcpServers === false) { await mcpManager.shutdown(); } else { - const mcpOptions: InternalMcpOptions[] = ( + // Load built-in MCP servers (like Context7) + const builtInServers = getBuiltInMcpServers( + config.disableBuiltInMcpServers ?? [], + config.disableAllBuiltInMcpServers ?? false, + ); + + // Load user-configured MCP servers + const userMcpOptions: InternalMcpOptions[] = ( config.experimental?.modelContextProtocolServers ?? [] ).map((server, index) => ({ id: `continue-mcp-server-${index + 1}`, @@ -565,14 +573,28 @@ async function intermediateToFinalConfig({ ), ...server.transport, })); - const { errors: jsonMcpErrors, mcpServers } = await loadJsonMcpConfigs( - ide, - true, - config.requestOptions, - ); + + // Load JSON-based MCP configs + const { errors: jsonMcpErrors, mcpServers: jsonMcpServers } = + await loadJsonMcpConfigs(ide, true, config.requestOptions); errors.push(...jsonMcpErrors); - mcpOptions.push(...mcpServers); - mcpManager.setConnections(mcpOptions, false); + + // Merge servers with deduplication: user configs override built-ins + // Create a map for deduplication by server name + const serverMap = new Map(); + + // Add built-in servers first + builtInServers.forEach((server) => { + serverMap.set(server.name.toLowerCase(), server); + }); + + // Override with user configs (both experimental and JSON) + [...userMcpOptions, ...jsonMcpServers].forEach((server) => { + serverMap.set(server.name.toLowerCase(), server); + }); + + const allMcpOptions = Array.from(serverMap.values()); + mcpManager.setConnections(allMcpOptions, false); } // Handle experimental modelRole config values for apply and edit diff --git a/core/config/types.ts b/core/config/types.ts index d9f58aca6e2..3b51e9aff40 100644 --- a/core/config/types.ts +++ b/core/config/types.ts @@ -1208,6 +1208,10 @@ declare global { experimental?: ExperimentalConfig; /** Analytics configuration */ analytics?: AnalyticsConfig; + /** Array of built-in MCP server IDs to disable (e.g., ['builtin-context7']) */ + disableBuiltInMcpServers?: string[]; + /** If set to true, all built-in MCP servers will be disabled */ + disableAllBuiltInMcpServers?: boolean; } // in the actual Continue source code diff --git a/core/context/mcp/builtinServers.ts b/core/context/mcp/builtinServers.ts new file mode 100644 index 00000000000..3d25777b48a --- /dev/null +++ b/core/context/mcp/builtinServers.ts @@ -0,0 +1,120 @@ +import { InternalMcpOptions } from "../.."; + +/** + * Built-in MCP servers that are available by default in Continue. + * These servers are loaded automatically unless explicitly disabled by the user. + */ +export interface BuiltInMcpServerConfig { + id: string; + name: string; + description?: string; + type: "streamable-http" | "sse" | "stdio"; + url?: string; + command?: string; + args?: string[]; + readonly?: boolean; + apiKey?: string; + env?: Record; + enabledByDefault: boolean; +} + +/** + * Context7 MCP Server - Provides up-to-date documentation for libraries and frameworks + * using semantic search and retrieval from Context7's documentation index. + */ +export const CONTEXT7_SERVER: BuiltInMcpServerConfig = { + id: "builtin-context7", + name: "Context7", + description: + "Search up-to-date documentation for libraries and frameworks. Should always be called to get latest information about Flexprice.", + type: "streamable-http", + url: "https://mcp.context7.com/mcp", + readonly: true, + apiKey: "ctx7sk-ad277c04-2ae2-4a09-b5f3-e85807f2d4de", + enabledByDefault: true, +}; + +/** + * All built-in MCP servers available in Continue. + * Add new built-in servers to this array. + */ +export const BUILTIN_MCP_SERVERS: BuiltInMcpServerConfig[] = [CONTEXT7_SERVER]; + +/** + * Convert a built-in server configuration to InternalMcpOptions format. + */ +export function convertBuiltInToInternalOptions( + server: BuiltInMcpServerConfig, +): InternalMcpOptions { + const base = { + id: server.id, + name: server.name, + readonly: server.readonly, + isBuiltIn: true, + }; + + if (server.type === "stdio" && server.command) { + return { + ...base, + type: "stdio" as const, + command: server.command, + args: server.args, + env: server.env, + }; + } else if (server.url) { + const options: InternalMcpOptions = { + ...base, + type: server.type as "streamable-http" | "sse", + url: server.url, + }; + + // Add API key to request options if provided + if (server.apiKey) { + options.requestOptions = { + headers: { + Authorization: `Bearer ${server.apiKey}`, + }, + }; + } + + return options; + } + + throw new Error(`Invalid built-in server configuration: ${server.id}`); +} + +/** + * Get all built-in MCP servers that should be loaded based on user configuration. + * @param disabledServerIds Array of built-in server IDs to disable + * @param disableAll If true, all built-in servers are disabled + * @returns Array of InternalMcpOptions for enabled built-in servers + */ +export function getBuiltInMcpServers( + disabledServerIds: string[] = [], + disableAll: boolean = false, +): InternalMcpOptions[] { + if (disableAll) { + return []; + } + + return BUILTIN_MCP_SERVERS.filter( + (server) => + server.enabledByDefault && !disabledServerIds.includes(server.id), + ).map(convertBuiltInToInternalOptions); +} + +/** + * Check if a server ID is a built-in server. + */ +export function isBuiltInServerId(id: string): boolean { + return BUILTIN_MCP_SERVERS.some((server) => server.id === id); +} + +/** + * Get a built-in server configuration by ID. + */ +export function getBuiltInServerById( + id: string, +): BuiltInMcpServerConfig | undefined { + return BUILTIN_MCP_SERVERS.find((server) => server.id === id); +} diff --git a/core/index.d.ts b/core/index.d.ts index 07a03fab440..657213c1deb 100644 --- a/core/index.d.ts +++ b/core/index.d.ts @@ -1338,6 +1338,7 @@ type BaseInternalMCPOptions = { requestOptions?: RequestOptions; sourceFile?: string; readonly?: boolean; // Mark all tools from this MCP server as readonly + isBuiltIn?: boolean; // Mark if this is a built-in MCP server }; export type InternalStdioMcpOptions = BaseInternalMCPOptions & { @@ -1750,6 +1751,10 @@ export interface Config { analytics?: AnalyticsConfig; docs?: SiteIndexingConfig[]; data?: DataDestination[]; + /** Array of built-in MCP server IDs to disable (e.g., ['builtin-context7']) */ + disableBuiltInMcpServers?: string[]; + /** If set to true, all built-in MCP servers will be disabled */ + disableAllBuiltInMcpServers?: boolean; } // in the actual Continue source code diff --git a/core/llm/toolSupport.ts b/core/llm/toolSupport.ts index a9726ede98c..2f025103966 100644 --- a/core/llm/toolSupport.ts +++ b/core/llm/toolSupport.ts @@ -257,10 +257,10 @@ export const PROVIDER_TOOL_SUPPORT: Record boolean> = return false; }, - stakd: (model) => { - // Stakd uses custom tool format, not native OpenAI tools - return false; - }, + // stakd: (model) => { + // // Stakd uses custom tool format, not native OpenAI tools + // return false; + // }, openrouter: (model) => { // https://openrouter.ai/models?fmt=cards&supported_parameters=tools diff --git a/extensions/cli/src/services/MCPService.ts b/extensions/cli/src/services/MCPService.ts index 08190e26796..15fc3725e20 100644 --- a/extensions/cli/src/services/MCPService.ts +++ b/extensions/cli/src/services/MCPService.ts @@ -12,6 +12,7 @@ import { StdioMcpServer, } from "node_modules/@continuedev/config-yaml/dist/schemas/mcp/index.js"; +import { getBuiltInMcpServers } from "../../../../core/context/mcp/builtinServers.js"; import { getErrorString } from "../util/error.js"; import { logger } from "../util/logger.js"; @@ -72,9 +73,20 @@ export class MCPService assistant: AssistantConfig, waitForConnections = false, ): Promise { + // Load built-in MCP servers + const builtInServers = getBuiltInMcpServers( + assistant.disableBuiltInMcpServers ?? [], + assistant.disableAllBuiltInMcpServers ?? false, + ); + + // Merge with user-configured servers + const allServers = [...builtInServers, ...(assistant.mcpServers ?? [])]; + logger.debug("Initializing MCPService", { configName: assistant.name, - serverCount: assistant.mcpServers?.length || 0, + builtInServerCount: builtInServers.length, + userServerCount: assistant.mcpServers?.length || 0, + totalServerCount: allServers.length, }); await this.shutdownConnections(); @@ -82,12 +94,12 @@ export class MCPService this.assistant = assistant; this.connections.clear(); - if (assistant.mcpServers?.length) { + if (allServers.length) { logger.debug("Starting MCP server connections", { - serverCount: assistant.mcpServers.length, + serverCount: allServers.length, }); } - const connectionPromises = assistant.mcpServers?.map(async (config) => { + const connectionPromises = allServers.map(async (config) => { if (config) { return await this.connectServer(config); } diff --git a/packages/config-yaml/src/schemas/index.ts b/packages/config-yaml/src/schemas/index.ts index d8af2456e56..089af479f82 100644 --- a/packages/config-yaml/src/schemas/index.ts +++ b/packages/config-yaml/src/schemas/index.ts @@ -261,6 +261,8 @@ export const configSchema = z.object({ api_key: z.string().optional(), env: envRecord.optional(), requestOptions: requestOptionsSchema.optional(), + disableBuiltInMcpServers: z.array(z.string()).optional(), + disableAllBuiltInMcpServers: z.boolean().optional(), }); export type Config = z.infer; From bd89b0a5e8cbd5a24083dccbf4df5f89e1a69a55 Mon Sep 17 00:00:00 2001 From: HarshitZom Date: Thu, 30 Oct 2025 02:04:04 +0530 Subject: [PATCH 11/13] builtin mcp integration fix --- core/config/load.ts | 4 ++++ core/config/yaml/loadYaml.ts | 41 +++++++++++++++++++++++++++++------- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/core/config/load.ts b/core/config/load.ts index b561f161561..2ce29a7e0ba 100644 --- a/core/config/load.ts +++ b/core/config/load.ts @@ -595,6 +595,10 @@ async function intermediateToFinalConfig({ const allMcpOptions = Array.from(serverMap.values()); mcpManager.setConnections(allMcpOptions, false); + + // Wait for MCP servers to connect before proceeding with config + // This ensures built-in servers like Context7 are ready and their tools are available + await mcpManager.refreshConnections(false); } // Handle experimental modelRole config values for apply and edit diff --git a/core/config/yaml/loadYaml.ts b/core/config/yaml/loadYaml.ts index 44af239df88..5d284b464b9 100644 --- a/core/config/yaml/loadYaml.ts +++ b/core/config/yaml/loadYaml.ts @@ -32,6 +32,7 @@ import { modifyAnyConfigWithSharedConfig } from "../sharedConfig"; import { convertPromptBlockToSlashCommand } from "../../commands/slash/promptBlockSlashCommand"; import { slashCommandFromPromptFile } from "../../commands/slash/promptFileSlashCommand"; +import { getBuiltInMcpServers } from "../../context/mcp/builtinServers"; import { loadJsonMcpConfigs } from "../../context/mcp/json/loadJsonMcpConfigs"; import { getControlPlaneEnvSync } from "../../control-plane/env"; import { PolicySingleton } from "../../control-plane/PolicySingleton"; @@ -394,18 +395,42 @@ export async function configYamlToContinueConfig(options: { if (orgPolicy?.policy?.allowMcpServers === false) { await mcpManager.shutdown(); } else { - const mcpOptions: InternalMcpOptions[] = (config.mcpServers ?? []).map( + // Load built-in MCP servers (like Context7) + // Note: Built-in servers are always loaded for YAML configs + // Users can configure individual server settings if needed + const builtInServers = getBuiltInMcpServers([], false); + + // Load user-configured MCP servers from YAML + const userMcpOptions: InternalMcpOptions[] = (config.mcpServers ?? []).map( (server) => convertYamlMcpConfigToInternalMcpOptions(server, config.requestOptions), ); - const { errors: jsonMcpErrors, mcpServers } = await loadJsonMcpConfigs( - ide, - true, - config.requestOptions, - ); + + // Load JSON-based MCP configs + const { errors: jsonMcpErrors, mcpServers: jsonMcpServers } = + await loadJsonMcpConfigs(ide, true, config.requestOptions); localErrors.push(...jsonMcpErrors); - mcpOptions.push(...mcpServers); - mcpManager.setConnections(mcpOptions, false, { ide }); + + // Merge servers with deduplication: user configs override built-ins + // Create a map for deduplication by server name + const serverMap = new Map(); + + // Add built-in servers first + builtInServers.forEach((server) => { + serverMap.set(server.name.toLowerCase(), server); + }); + + // Override with user configs (both YAML and JSON) + [...userMcpOptions, ...jsonMcpServers].forEach((server) => { + serverMap.set(server.name.toLowerCase(), server); + }); + + const allMcpOptions = Array.from(serverMap.values()); + mcpManager.setConnections(allMcpOptions, false, { ide }); + + // Wait for MCP servers to connect before proceeding with config + // This ensures built-in servers like Context7 are ready and their tools are available + await mcpManager.refreshConnections(false); } return { config: continueConfig, errors: localErrors }; From 2d0c7f9df01ea3d6b71aabfda539922a2aa14145 Mon Sep 17 00:00:00 2001 From: HarshitZom Date: Sat, 1 Nov 2025 16:04:52 +0530 Subject: [PATCH 12/13] stream ending logic fix - might be wron --- .../rules/bigger-picture-description-rules.md | 19 + .stitch/rules/colors.md | 63 + .stitch/rules/continue-specificity.md | 9 + .stitch/rules/css-units.md | 5 + .stitch/rules/dev-data-guide.md | 200 + .../rules/documentation-description-rule.md | 8 + .stitch/rules/documentation-standards.md | 111 + .../rules/github-pr-documentation-updater.md | 7 + .stitch/rules/gui-link-opening.md | 10 + .../rules/intellij-plugin-test-execution.md | 20 + .stitch/rules/llm-specificity.md | 11 + .../migrate-styled-components-to-tailwind.md | 5 + .stitch/rules/mintlify-formatting.md | 100 + .stitch/rules/navigating-responses.md | 4 + .stitch/rules/new-protocol-message.md | 36 + .stitch/rules/no-any-types.md | 5 + .stitch/rules/overeager.md | 5 + .stitch/rules/personality.md | 6 + .stitch/rules/programming-principles.md | 6 + .stitch/rules/pure-function-unit-tests.md | 1 + .stitch/rules/test-running-guide.md | 25 + .stitch/rules/typescript-enum-usage.md | 6 + .stitch/rules/unit-testing-rules.md | 37 + .../vs-code-commands-helper-functions.md | 5 + .../flexprice/connections.test.ts | 498 ++ .../flexprice/connections.ts | 200 + .../flexprice/package-lock.json | 5141 +++++++++++++++++ manual-testing-sandbox/flexprice/package.json | 57 + manual-testing-sandbox/flexprice/server.ts | 419 ++ .../flexprice/src/middleware/interceptors.ts | 276 + .../flexprice/src/utils/logger.ts | 136 + .../flexprice/tsconfig.json | 24 + packages/openai-adapters/src/apis/Stakd.ts | 10 + 33 files changed, 7465 insertions(+) create mode 100644 .stitch/rules/bigger-picture-description-rules.md create mode 100644 .stitch/rules/colors.md create mode 100644 .stitch/rules/continue-specificity.md create mode 100644 .stitch/rules/css-units.md create mode 100644 .stitch/rules/dev-data-guide.md create mode 100644 .stitch/rules/documentation-description-rule.md create mode 100644 .stitch/rules/documentation-standards.md create mode 100644 .stitch/rules/github-pr-documentation-updater.md create mode 100644 .stitch/rules/gui-link-opening.md create mode 100644 .stitch/rules/intellij-plugin-test-execution.md create mode 100644 .stitch/rules/llm-specificity.md create mode 100644 .stitch/rules/migrate-styled-components-to-tailwind.md create mode 100644 .stitch/rules/mintlify-formatting.md create mode 100644 .stitch/rules/navigating-responses.md create mode 100644 .stitch/rules/new-protocol-message.md create mode 100644 .stitch/rules/no-any-types.md create mode 100644 .stitch/rules/overeager.md create mode 100644 .stitch/rules/personality.md create mode 100644 .stitch/rules/programming-principles.md create mode 100644 .stitch/rules/pure-function-unit-tests.md create mode 100644 .stitch/rules/test-running-guide.md create mode 100644 .stitch/rules/typescript-enum-usage.md create mode 100644 .stitch/rules/unit-testing-rules.md create mode 100644 .stitch/rules/vs-code-commands-helper-functions.md create mode 100644 manual-testing-sandbox/flexprice/connections.test.ts create mode 100644 manual-testing-sandbox/flexprice/connections.ts create mode 100644 manual-testing-sandbox/flexprice/package-lock.json create mode 100644 manual-testing-sandbox/flexprice/package.json create mode 100644 manual-testing-sandbox/flexprice/server.ts create mode 100644 manual-testing-sandbox/flexprice/src/middleware/interceptors.ts create mode 100644 manual-testing-sandbox/flexprice/src/utils/logger.ts create mode 100644 manual-testing-sandbox/flexprice/tsconfig.json diff --git a/.stitch/rules/bigger-picture-description-rules.md b/.stitch/rules/bigger-picture-description-rules.md new file mode 100644 index 00000000000..b1a759abebe --- /dev/null +++ b/.stitch/rules/bigger-picture-description-rules.md @@ -0,0 +1,19 @@ +--- +name: Bigger Picture Description Rules +description: Guidelines for explaining how code works in context +alwaysApply: false +--- + +When a user asks how a certain part of the code works: + +## 1. Describe what the code does in isolation + +Explain the functionality of the code without considering its interactions with other parts of the codebase. + +## 2. Describe interactions with other parts of the codebase + +If the code interacts with other parts of the codebase, describe how the code is imported and used in other parts of the codebase. + +## 3. Include parent function for clarity + +When describing each use-case, include the parent function for clarity. diff --git a/.stitch/rules/colors.md b/.stitch/rules/colors.md new file mode 100644 index 00000000000..0d031cbd0c0 --- /dev/null +++ b/.stitch/rules/colors.md @@ -0,0 +1,63 @@ +--- +name: Extension Color Themes +description: Guidelines for using theme colors in GUI components +alwaysApply: false +globs: "gui/**/*.tsx" +--- + +When adding colors to components, use tailwind color classes. +Do NOT use explicit colors like text-gray-400. Instead, use theme colors. + +## Available theme colors + +### Normal text + +- `foreground`, `description`, `description-muted` + +### Other text, icons, etc + +- `success`, `warning`, `error`, `accent`, `link` + +### General components + +- `background`, `border`, `border-focus` + +### Specific components + +#### Button + +- `primary`, `primary-foreground`, `primary-hover` +- `secondary`, `secondary-foreground`, `secondary-hover` + +#### Input + +- `input`, `input-foreground`, `input-border`, `input-placeholder` + +#### Badge + +- `badge`, `badge-foreground` + +#### List/Dropdown items + +- `list-hover`, `list-active`, `list-active-foreground` + +#### Code Editor + +- `editor`, `editor-foreground` + +## Usage examples + +Any of these colors can be used in tailwind classes: + +- `bg-primary` +- `text-success` +- `border-error` +- `hover:bg-list-hover` + +## Excluded colors + +The following less-used colors are excluded from this guide: + +- Command (only used by tip-tap): `command`, `command-foreground`, `command-border`, `command-border-focus` +- Find widget colors: `find-match`, `find-match-selected` +- `table-oddRow` diff --git a/.stitch/rules/continue-specificity.md b/.stitch/rules/continue-specificity.md new file mode 100644 index 00000000000..4579e24441b --- /dev/null +++ b/.stitch/rules/continue-specificity.md @@ -0,0 +1,9 @@ +--- +globs: /**/*. +description: General questions about code completion should be answered specific to Continue +--- + +# Continue Specificity + +- In chat mode, if the user asks generally about code completion or developer tools, answer specifically regarding Continue and not other similar software. +- Keep all suggestions and comments concentrated on Continue, unless the user asks otherwise. If the user does this, answer with no particular specificity to Continue. diff --git a/.stitch/rules/css-units.md b/.stitch/rules/css-units.md new file mode 100644 index 00000000000..a270a383ddc --- /dev/null +++ b/.stitch/rules/css-units.md @@ -0,0 +1,5 @@ +--- +globs: "gui/**/*.tsx" +--- + +You should try to use the `rem` CSS unit whenever possible for scalability instead of `px`. diff --git a/.stitch/rules/dev-data-guide.md b/.stitch/rules/dev-data-guide.md new file mode 100644 index 00000000000..7321b93c089 --- /dev/null +++ b/.stitch/rules/dev-data-guide.md @@ -0,0 +1,200 @@ +--- +alwaysApply: false +--- + +# Continue Development Data (Dev Data) Guide + +## Overview + +Development data (dev data) captures detailed information about how developers interact with LLM-aided development tools. Unlike basic telemetry, dev data includes lots of details into the complete software development workflow, including code context, user interactions, and development patterns. + +## Core Architecture + +### Primary Implementation Files + +- **`/core/data/log.ts`**: Main `DataLogger` class - singleton for event logging and remote transmission +- **`/packages/config-yaml/src/schemas/data/`**: Schema definitions for all event types + +### Storage Locations + +- **Default storage**: `~/.continue/dev_data/` +- **Event files**: `~/.continue/dev_data/{version}/{eventName}.jsonl` + +## Event Types and Schemas + +### Core Event Types + +1. **`tokensGenerated`**: LLM token usage tracking +2. **`autocomplete`**: Code completion interactions +3. **`chatInteraction`**: Chat-based development assistance +4. **`editInteraction`**: Code editing sessions +5. **`editOutcome`**: Results of edit operations +6. **`nextEditOutcome`**: Next Edit feature outcomes +7. **`chatFeedback`**: User feedback on AI responses +8. **`toolUsage`**: Tool interaction statistics +9. **`quickEdit`**: Quick edit functionality usage + +### Schema Versioning + +- **Version 0.1.0**: Initial schema implementation +- **Version 0.2.0**: Current schema with expanded fields and metadata +- **Schema files**: Located in `/packages/config-yaml/src/schemas/data/` + +### Base Schema Structure + +All events inherit from a base schema (`/packages/config-yaml/src/schemas/data/base.ts`): + +```typescript +{ + eventName: string, + schema: string, + timestamp: string, + userId: string, + userAgent: string, + selectedProfileId: string +} +``` + +## Key Integration Points + +### Autocomplete System + +- **File**: `/core/autocomplete/util/AutocompleteLoggingService.ts` +- **Purpose**: Tracks code completion acceptance/rejection, timing, and cache hits +- **Integration**: Called from autocomplete engine when completions are shown/accepted + +### Chat Interface + +- **Integration**: Chat interactions logged through `DataLogger.logDevData()` +- **Data**: Includes prompts, responses, context, and user feedback +- **Privacy**: Can be configured to exclude code content + +### Edit Features + +- **Files**: `/extensions/vscode/src/extension/EditOutcomeTracker.ts`, `/core/nextEdit/NextEditLoggingService.ts` +- **Purpose**: Track edit suggestions, acceptance rates, and outcomes +- **Integration**: Embedded in edit workflow to capture user decisions + +### LLM Token Tracking + +- **File**: `/core/llm/index.ts` +- **Purpose**: Track token usage across all LLM providers +- **Storage**: SQLite database for efficient querying and reporting + +## Configuration and Customization + +### Configuration Structure + +Dev data is configured through `data` blocks in your Continue config: + +```yaml +data: + - name: "Local Development Data" + destination: "file:///Users/developer/.continue/dev_data" + schema: "0.2.0" + level: "all" + events: ["autocomplete", "chatInteraction", "editOutcome"] + + - name: "Team Analytics" + destination: "https://analytics.yourcompany.com/api/events" + schema: "0.2.0" + level: "noCode" + apiKey: "your-api-key-here" + events: ["tokensGenerated", "toolUsage"] +``` + +### Configuration Options + +- **`destination`**: Where to send data (`file://` for local, `http://`/`https://` for remote) +- **`schema`**: Schema version to use (`"0.1.0"` or `"0.2.0"`) +- **`level`**: Data detail level (`"all"` includes code, `"noCode"` excludes code content) +- **`events`**: Array of event types to collect +- **`apiKey`**: Authentication for remote endpoints + +### Privacy Controls + +- **`"all"` level**: Includes code content (prefixes, suffixes, completions) +- **`"noCode"` level**: Excludes code content, only metadata and metrics +- **Local-first**: Data is always stored locally, remote transmission is optional + +## Making Changes to Dev Data + +### Adding New Event Types + +1. **Create schema**: Add new event schema in `/packages/config-yaml/src/schemas/data/` +2. **Update index**: Add to schema aggregator in `/packages/config-yaml/src/schemas/data/index.ts` +3. **Implement logging**: Add logging calls in relevant service files +4. **Update version**: Consider schema version bump if breaking changes + +### Modifying Existing Events + +1. **Schema changes**: Update schema files in `/packages/config-yaml/src/schemas/data/` +2. **Backward compatibility**: Ensure changes don't break existing data consumers +3. **Version management**: Increment schema version for breaking changes +4. **Test thoroughly**: Validate schema changes with existing data + +### Adding New Logging Points + +1. **Import DataLogger**: `import { DataLogger } from "core/data/log"` +2. **Log events**: Call `DataLogger.getInstance().logDevData(eventName, data)` +3. **Follow patterns**: Use existing logging services as examples +4. **Validate data**: Ensure logged data matches schema requirements + +### Debugging Dev Data Issues + +1. **Check local storage**: Verify files are being created in `~/.continue/dev_data/` +2. **Validate schemas**: Ensure event data matches expected schema format +3. **Review configuration**: Check `data` blocks in Continue config +4. **Test endpoints**: Verify remote endpoints are reachable and accepting data + +## Best Practices + +### When Adding New Events + +- Follow existing naming conventions for event types +- Include sufficient context for analysis without oversharing sensitive data +- Consider privacy implications and respect user configuration levels +- Add appropriate error handling and logging + +### When Modifying Schemas + +- Maintain backward compatibility when possible +- Document schema changes thoroughly +- Consider impact on existing data consumers +- Test with real development data + +### When Integrating Logging + +- Use the singleton pattern: `DataLogger.getInstance()` +- Log events at appropriate points in user workflow +- Respect user privacy settings and configuration +- Handle errors gracefully without disrupting user experience + +## Common Patterns + +### Service-Based Logging + +Most dev data logging follows a service pattern: + +```typescript +export class FeatureLoggingService { + private dataLogger = DataLogger.getInstance(); + + logFeatureUsage(data: FeatureUsageData) { + this.dataLogger.logDevData("featureUsage", data); + } +} +``` + +### Event-Driven Logging + +Events are typically logged at key interaction points: + +```typescript +// When user accepts autocomplete +onAutocompleteAccepted(completion: CompletionData) { + AutocompleteLoggingService.getInstance().logAutocompleteAccepted(completion); +} +``` + +This guide provides the foundation for understanding and working with Continue's dev data system. Always prioritize user privacy and follow established patterns when making changes. diff --git a/.stitch/rules/documentation-description-rule.md b/.stitch/rules/documentation-description-rule.md new file mode 100644 index 00000000000..17f62d2d0c2 --- /dev/null +++ b/.stitch/rules/documentation-description-rule.md @@ -0,0 +1,8 @@ +--- +globs: docs/**/*.{md,mdx} +description: This rule applies to all documentation files to ensure consistent + SEO optimization and improve discoverability. It helps users and search + engines understand the content of each page before reading it. +--- + +Every file in the docs folder must include a 'description' field in its frontmatter that accurately summarizes the content of the page in 100-160 characters. The description should be concise, keyword-rich, and explain what users will learn or accomplish from the page. diff --git a/.stitch/rules/documentation-standards.md b/.stitch/rules/documentation-standards.md new file mode 100644 index 00000000000..b93aefd71a9 --- /dev/null +++ b/.stitch/rules/documentation-standards.md @@ -0,0 +1,111 @@ +--- +globs: docs/\*_/_.{md,mdx} +description: This style guide should be used as a reference for maintaining consistency across all Continue documentation +alwaysApply: false +--- + +# Continue Documentation Style Guide + +## Overview + +## Writing Tone & Voice + +### Conversational and Direct + +- Follow Mintlify documentation standards +- Use simple, conversational language that gets straight to the point +- Avoid overly technical jargon when simpler terms work +- Write as if speaking directly to the developer using the tool +- Keep paragraphs concise and scannable + +**Example:** +✅ "You send it a question, and it replies with an answer" +❌ "The system processes user queries and generates corresponding responses" + +### Helpful and Instructional + +- Focus on helping users accomplish their goals +- Use active voice and imperative mood for instructions +- Assume users want to get things done quickly +- Include relevant Admonition components for tips, warnings, and info + +**Example:** +✅ "Press cmd/ctrl + L to begin a new session" +❌ "A new session can be initiated by pressing cmd/ctrl + L" + +### Practical and Task-Oriented + +- Emphasize what users can accomplish with each feature +- Lead with benefits and use cases before diving into mechanics +- Keep explanations grounded in real-world scenarios + +## Content Structure + +### Page Organization + +1. **Visual Introduction**: Lead with GIFs or images showing the feature in action +2. **Purpose Statement**: Brief explanation of what the feature does and when to use it +3. **Step-by-Step Instructions**: Clear, actionable steps with keyboard shortcuts +4. **Platform-Specific Notes**: Separate sections for VS Code and JetBrains when needed +5. **Additional Tips**: Advanced usage or troubleshooting notes + +### Section Headers + +- Use consistent heading hierarchy starting with h2 (##) +- Include YAML frontmatter with title, description, and keywords +- Use action-oriented headers that describe what users will do +- Format: "Verb + object" (e.g., "Type a request and press enter") +- Keep headers concise but descriptive +- Use title case + +**Examples:** +✅ "Highlight code and activate" +✅ "Accept or reject changes" +✅ "Switch between different models" + +### Lists and Steps + +- Use numbered lists for sequential steps +- Use bullet points for feature lists or options +- Keep list items parallel in structure +- Start action items with verbs + +## Technical Writing Standards + +### Code and Keyboard Shortcuts + +- Use `backticks` for inline code elements +- Format keyboard shortcuts consistently: `cmd/ctrl + L` +- Always provide shortcuts for Mac/Windows/Linux +- Use code blocks for configuration examples with proper syntax highlighting + +### Cross-References + +- Link to related sections using descriptive anchor text +- Use relative links to other documentation pages +- Format: `[descriptive text](/path/to/page)` + +### Platform Differences + +- Always address both VS Code and JetBrains when applicable +- Use clear subheadings to separate platform-specific instructions +- Lead with the more common platform (typically VS Code) when both are covered + +## Language Conventions + +### Terminology + +- **Consistent Terms**: Use the same terms throughout (e.g., "LLM" not "AI model" in some places) +- **Product Names**: Capitalize product names correctly (VS Code, JetBrains, Continue) +- **Feature Names**: Use consistent capitalization for Continue features (Chat, Edit, Agent, Autocomplete) + +### Abbreviations + +- Spell out acronyms on first use, then use abbreviation consistently +- Common abbreviations: LLM, IDE, API, URL + +### Pronouns + +- Use "you" to address the user directly +- Use "it" to refer to the tool/model +- Avoid "we" unless referring to the Continue team diff --git a/.stitch/rules/github-pr-documentation-updater.md b/.stitch/rules/github-pr-documentation-updater.md new file mode 100644 index 00000000000..3560ccdad44 --- /dev/null +++ b/.stitch/rules/github-pr-documentation-updater.md @@ -0,0 +1,7 @@ +--- +name: Update docs from GitHub PR +description: Provide a PR link to update docs based on +alwaysApply: false +--- + +When provided with a GitHub PR URL, use the GitHub CLI (`gh pr view --json title,body,commits,files,additions,deletions,changedFiles`) to fetch comprehensive PR details including title, description, commits, changed files, and diff statistics. Analyze the PR content to identify what documentation in the `docs` folder needs to be updated, created, or modified. Update relevant documentation files to reflect the changes, new features, bug fixes, or improvements described in the PR. Ensure documentation changes are accurate, well-structured, and maintain consistency with existing docs formatting and style. diff --git a/.stitch/rules/gui-link-opening.md b/.stitch/rules/gui-link-opening.md new file mode 100644 index 00000000000..db791e67a9a --- /dev/null +++ b/.stitch/rules/gui-link-opening.md @@ -0,0 +1,10 @@ +--- +globs: gui/**/* +description: Ensures consistent URL opening behavior in GUI components using the + IDE messenger pattern +alwaysApply: false +--- + +# GUI Link Opening + +When adding functionality to open external links in GUI components, use `ideMessenger.post("openUrl", url)` where `ideMessenger` is obtained from `useContext(IdeMessengerContext)` diff --git a/.stitch/rules/intellij-plugin-test-execution.md b/.stitch/rules/intellij-plugin-test-execution.md new file mode 100644 index 00000000000..8b35745a6d0 --- /dev/null +++ b/.stitch/rules/intellij-plugin-test-execution.md @@ -0,0 +1,20 @@ +--- +name: IntelliJ Plugin Test Execution +description: Guidelines for running IntelliJ plugin tests with Gradle +alwaysApply: false +globs: extensions/intellij/**/*Test.kt +--- + +Run IntelliJ plugin tests using Gradle with the fully qualified test class or method name: + +## Run test class + +```bash +./gradlew test --tests "com.github.continuedev.continueintellijextension.unit.ApplyToFileHandlerTest" +``` + +## Run specific test method + +```bash +./gradlew test --tests "com.github.continuedev.continueintellijextension.unit.ApplyToFileHandlerTest.should*" +``` diff --git a/.stitch/rules/llm-specificity.md b/.stitch/rules/llm-specificity.md new file mode 100644 index 00000000000..4ff15c9e774 --- /dev/null +++ b/.stitch/rules/llm-specificity.md @@ -0,0 +1,11 @@ +--- +globs: core/llm/llms/**/*.{ts,test.ts} +description: Tailor recommendations for LLM code based on which specific LLM is being used. +--- + +# LLM Model Specificity + +- Refer to the file name and names of big classes to determine which LLM is being used in a file. +- Ground all observations and recommendations with knowledge of that LLM. +- Consider items such as context length, architecture, speed, and such. +- Pay attention to the parent classes in these files. diff --git a/.stitch/rules/migrate-styled-components-to-tailwind.md b/.stitch/rules/migrate-styled-components-to-tailwind.md new file mode 100644 index 00000000000..0432f9a8d2a --- /dev/null +++ b/.stitch/rules/migrate-styled-components-to-tailwind.md @@ -0,0 +1,5 @@ +--- +alwaysApply: false +--- + +Convert all of the styled components in this file into tailwind CSS. If a variable is used that is not already in @theme.ts and @tailwind.config.cjs, then you should figure out where it comes from and try adding that so it can be used. Wherever a function is called to interpolate a value, you can just use inline `styles={{ ... }}`. For ternaries, you could use @cn.ts. diff --git a/.stitch/rules/mintlify-formatting.md b/.stitch/rules/mintlify-formatting.md new file mode 100644 index 00000000000..4df9bdb9146 --- /dev/null +++ b/.stitch/rules/mintlify-formatting.md @@ -0,0 +1,100 @@ +# Mintlify Documentation Formatting Rules + +## Component Formatting + +When working with Mintlify documentation components (Card, Info, Tip, Note, Warning, etc.), follow these formatting guidelines: + +### Bullet Points and Lists + +1. **Always add a blank line** after the opening component tag and before the closing tag +2. **Indent content** by 2 spaces within components +3. **Use proper list formatting** with each item on its own line: + - Start lists on a new line after introductory text + - Use `-` for unordered lists + - Maintain consistent indentation + +### Examples + +#### ✅ Correct Formatting: + +```mdx + + + This is the content with proper formatting: + - First bullet point + - Second bullet point + - Third bullet point + + +``` + +```mdx + + + Important information here: + - Point one + - Point two + - Point three + + +``` + +#### ❌ Incorrect Formatting: + +```mdx + + This is wrong: - All bullets - On one line - Bad formatting + +``` + +### Component-Specific Rules + +1. **Card Components**: Always include blank lines and proper indentation +2. **Info/Tip/Note/Warning**: Format lists as bullet points, not inline +3. **CardGroup**: Each Card within should follow the same formatting rules +4. **Code Blocks**: Within components, maintain proper indentation + +### Links in Lists + +When including links in bullet points: +```mdx +- [Link Text](url): Description of the link +``` + +### Nested Components + +For nested components, maintain proper indentation levels: +```mdx + + + + Content here: + - Bullet one + - Bullet two + + + + + + More content: + - Another bullet + - Final bullet + + + +``` + +## Application + +These rules apply to all `.mdx` files in the `docs/` directory, particularly: +- Guide documents +- Cookbook documents +- Reference documentation +- Any Mintlify-powered documentation + +## Automation Note + +When using Continue or other AI assistants to generate or modify documentation: +- Always format Mintlify components according to these rules +- Review generated content for proper formatting +- Apply these rules consistently across all documentation \ No newline at end of file diff --git a/.stitch/rules/navigating-responses.md b/.stitch/rules/navigating-responses.md new file mode 100644 index 00000000000..dadbd0f8078 --- /dev/null +++ b/.stitch/rules/navigating-responses.md @@ -0,0 +1,4 @@ +If the user's request is vague and you don't have enough information to confidently answer, you can either + +- In Chat OR Agent mode, stop and ask questions to clarify before proceeding +- In Agent mode, use tools to discover more information, starting with the glob/grep tools if available. diff --git a/.stitch/rules/new-protocol-message.md b/.stitch/rules/new-protocol-message.md new file mode 100644 index 00000000000..b01961242d4 --- /dev/null +++ b/.stitch/rules/new-protocol-message.md @@ -0,0 +1,36 @@ +--- +name: New protocol message +description: Create a new protocol message from core/gui/ide to core/gui/ide +alwaysApply: false +--- + +Create a new protocol message by taking the following steps: + +## 1. Define the message type + +Add your new message type definition to the appropriate file in the `protocol/` directory with correct TypeScript typing. + +## 2. Check for duplicates + +Verify that no existing message type already provides the same functionality. + +## 3. Add to passThrough (if webview ↔ core) + +If your message is between webview and core, add it to `core/protocol/passThrough.ts`. + +## 4. Add to IntelliJ constants (if webview ↔ core) + +If your message is between webview and core, add it to `extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/constants/MessageTypes.kt`. + +## 5. Implement the message handler + +Implement the message in the appropriate location: + +- **Messages to core**: `core/core.ts` +- **Messages to GUI**: `useWebviewListener` hook +- **Messages to VS Code IDE**: `VsCodeMessenger.ts` +- **Messages to JetBrains IDE**: `IdeProtocolClient.kt` + +## 6. Test the implementation + +Verify that your message works correctly in both VS Code and JetBrains IDEs if applicable. diff --git a/.stitch/rules/no-any-types.md b/.stitch/rules/no-any-types.md new file mode 100644 index 00000000000..1ec5f4f3e56 --- /dev/null +++ b/.stitch/rules/no-any-types.md @@ -0,0 +1,5 @@ +--- +globs: "**/*.{ts,tsx}" +--- + +Avoid using the `any` type wherever possible. Use unknown or find the correct type. The only acceptable place to use any is when typecasting for test mocks, and even then it's better to avoid and provide a proper mock. diff --git a/.stitch/rules/overeager.md b/.stitch/rules/overeager.md new file mode 100644 index 00000000000..729088aad97 --- /dev/null +++ b/.stitch/rules/overeager.md @@ -0,0 +1,5 @@ +--- +name: Don't be overeager +--- + +Avoid over-eagerly adding new features. You should solve the problem at hand and then can propose further work. diff --git a/.stitch/rules/personality.md b/.stitch/rules/personality.md new file mode 100644 index 00000000000..c68d0e35cc2 --- /dev/null +++ b/.stitch/rules/personality.md @@ -0,0 +1,6 @@ +--- +name: Personality Rules +description: Conversational and personality guidelines +--- + +When the user challenges your output or asks a question, don't be overly-amiable (e.g. responding "You're right" all the time). Focus on correctness and be willing to tell the user they are wrong. diff --git a/.stitch/rules/programming-principles.md b/.stitch/rules/programming-principles.md new file mode 100644 index 00000000000..0485f4f6874 --- /dev/null +++ b/.stitch/rules/programming-principles.md @@ -0,0 +1,6 @@ +--- +name: Programming principles +description: Guidelines for coding fundamentals in this project +--- + +Use functional programming paradigms whenever possible. Modifying existing classes or creating singletons where needed is acceptable. but otherwise, use functions. diff --git a/.stitch/rules/pure-function-unit-tests.md b/.stitch/rules/pure-function-unit-tests.md new file mode 100644 index 00000000000..68a1ff9838d --- /dev/null +++ b/.stitch/rules/pure-function-unit-tests.md @@ -0,0 +1 @@ +Always create comprehensive unit tests for new pure functions. Tests should cover normal cases, edge cases, and boundary conditions. diff --git a/.stitch/rules/test-running-guide.md b/.stitch/rules/test-running-guide.md new file mode 100644 index 00000000000..47918587808 --- /dev/null +++ b/.stitch/rules/test-running-guide.md @@ -0,0 +1,25 @@ +--- +globs: ["gui/**/*", "core/**/*"] +description: Provides test running instructions for GUI and core folders +alwaysApply: false +--- + +When working with test files, use the following commands to run tests: + +GUI folder tests: + +- Run all tests: `cd gui && npm test` + +Core folder tests: + +- Run Jest tests: `cd core && npm test` +- Run Vitest tests: `cd core && npm run vitest` + +Test file patterns: + +- GUI: _.test.ts or_.test.tsx files use Vitest +- Core: _.test.ts files use Jest,_.vitest.ts files use Vitest + +Best practices: + +- We are transitioning to vitest, so use that when creating new tests diff --git a/.stitch/rules/typescript-enum-usage.md b/.stitch/rules/typescript-enum-usage.md new file mode 100644 index 00000000000..34728c1d5af --- /dev/null +++ b/.stitch/rules/typescript-enum-usage.md @@ -0,0 +1,6 @@ +--- +globs: "**/*.{ts,tsx}" +alwaysApply: false +--- + +Use enums instead of simple string unions when possible in TypeScript code diff --git a/.stitch/rules/unit-testing-rules.md b/.stitch/rules/unit-testing-rules.md new file mode 100644 index 00000000000..bd9fb0109c7 --- /dev/null +++ b/.stitch/rules/unit-testing-rules.md @@ -0,0 +1,37 @@ +--- +name: Unit Testing Rules +description: Guidelines for unit testing in this project +alwaysApply: false +--- + +For unit testing in this project: + +## 1. Testing frameworks + +The project uses Vitest and Jest for testing. Prefer Vitest. + +## 2. Test execution location + +Run tests from within the specific package directory (e.g., `cd core && ..`). + +## 3. Vitest tests + +- Test files follow the pattern `*.vitest.ts` +- Run tests using `vitest` from within the specific package/module directory: + ```bash + cd [directory] && vitest -- [test file path] + ``` + +## 4. Jest tests + +- Test files follow the pattern `*.test.ts` +- Run tests using `npm test` from within the specific package/module directory: + ```bash + cd [directory] && npm test -- [test file path] + ``` +- The test script uses experimental VM modules via NODE_OPTIONS flag + +## 5. Test structure + +- Write tests as top-level `test()` functions - DO NOT use `describe()` blocks +- Include the function name being tested in the test description for clarity diff --git a/.stitch/rules/vs-code-commands-helper-functions.md b/.stitch/rules/vs-code-commands-helper-functions.md new file mode 100644 index 00000000000..bf2e0a8da47 --- /dev/null +++ b/.stitch/rules/vs-code-commands-helper-functions.md @@ -0,0 +1,5 @@ +--- +globs: "extensions/vscode/src/commands.ts" +--- + +When adding new commands to the commands map, always create a separate helper function for the command logic instead of defining it inline. Follow the pattern of existing commands like `streamInlineEdit` - define the helper function below the commands map, then call it from within the command entry. diff --git a/manual-testing-sandbox/flexprice/connections.test.ts b/manual-testing-sandbox/flexprice/connections.test.ts new file mode 100644 index 00000000000..69f54bf6fc8 --- /dev/null +++ b/manual-testing-sandbox/flexprice/connections.test.ts @@ -0,0 +1,498 @@ +// Polyfill fetch for Node.js test environment +import fetch from 'node-fetch'; +globalThis.fetch = fetch as any; + +import { jest } from '@jest/globals'; +import { createConnection, getConnections, updateConnection, deleteConnection } from './connections'; + +// Mock the fetch module +describe('Flexprice Connections API CRUD Operations', () => { + const mockResponse = { + id: 'conn-test-id', + name: 'Test Connection', + provider_type: 'stripe', + status: 'published', + sync_config: { + invoice: { inbound: true, outbound: true }, + plan: { inbound: true, outbound: true }, + subscription: { inbound: true, outbound: true }, + }, + created_at: '2023-10-27T10:00:00Z', + created_by: 'test-user', + updated_at: '2023-10-27T10:00:00Z', + updated_by: 'test-user', + environment_id: 'env-test', + tenant_id: 'tenant-test', + }; + + const mockConnectionsList = { + connections: [mockResponse], + limit: 1, + offset: 0, + total: 1, + }; + + beforeEach(() => { + jest.clearAllMocks(); + process.env.FLEXPRICE_API_KEY = 'test-api-key'; + // Mock fetch globally + globalThis.fetch = jest.fn() as any; + }); + + describe('createConnection', () => { + it('should create a new connection successfully', async () => { + (globalThis.fetch as jest.MockedFunction).mockResolvedValueOnce({ + ok: true, + json: async () => mockResponse, + } as any); + + const createData = { + name: 'Test Connection', + provider_type: 'stripe' as const, + sync_config: { + invoice: { inbound: true, outbound: true }, + plan: { inbound: true, outbound: true }, + subscription: { inbound: true, outbound: true }, + }, + }; + + const result = await createConnection(createData); + expect(result).toEqual(mockResponse); + expect(globalThis.fetch).toHaveBeenCalledWith('https://api.cloud.flexprice.io/v1/connections', { + method: 'POST', + body: JSON.stringify(createData), + headers: { + 'x-api-key': 'test-api-key', + 'Content-Type': 'application/json', + }, + }); + }); + + it('should handle API errors', async () => { + (globalThis.fetch as jest.MockedFunction).mockResolvedValueOnce({ + ok: false, + status: 400, + json: async () => ({ error: { details: 'Invalid request' } }), + } as any); + + const createData = { + name: '', + provider_type: 'stripe' as const, + sync_config: { + invoice: { inbound: true, outbound: true }, + plan: { inbound: true, outbound: true }, + subscription: { inbound: true, outbound: true }, + }, + }; + + await expect(createConnection(createData)).rejects.toThrow('Flexprice API Error 400: Invalid request'); + }, 10000); + + it('should handle 401 Unauthorized errors', async () => { + (globalThis.fetch as jest.MockedFunction).mockResolvedValueOnce({ + ok: false, + status: 401, + json: async () => ({ error: { details: 'Unauthorized' } }), + } as any); + + const createData = { + name: 'Test Connection', + provider_type: 'stripe' as const, + sync_config: { + invoice: { inbound: true, outbound: true }, + plan: { inbound: true, outbound: true }, + subscription: { inbound: true, outbound: true }, + }, + }; + + await expect(createConnection(createData)).rejects.toThrow('Flexprice API Error 401: Unauthorized'); + }, 10000); + + it('should handle 403 Forbidden errors', async () => { + (globalThis.fetch as jest.MockedFunction).mockResolvedValueOnce({ + ok: false, + status: 403, + json: async () => ({ error: { details: 'Forbidden' } }), + } as any); + + const createData = { + name: 'Test Connection', + provider_type: 'stripe' as const, + sync_config: { + invoice: { inbound: true, outbound: true }, + plan: { inbound: true, outbound: true }, + subscription: { inbound: true, outbound: true }, + }, + }; + + await expect(createConnection(createData)).rejects.toThrow('Flexprice API Error 403: Forbidden'); + }, 10000); + + it('should handle 500 Internal Server Error', async () => { + (globalThis.fetch as jest.MockedFunction).mockResolvedValueOnce({ + ok: false, + status: 500, + json: async () => ({ error: { details: 'Internal Server Error' } }), + } as any); + + const createData = { + name: 'Test Connection', + provider_type: 'stripe' as const, + sync_config: { + invoice: { inbound: true, outbound: true }, + plan: { inbound: true, outbound: true }, + subscription: { inbound: true, outbound: true }, + }, + }; + + await expect(createConnection(createData)).rejects.toThrow('Flexprice API Error 500: Internal Server Error'); + }, 10000); + + it('should retry on network errors', async () => { + (globalThis.fetch as jest.MockedFunction) + .mockRejectedValueOnce(new Error('Network error')) + .mockRejectedValueOnce(new Error('Network error')) + .mockResolvedValueOnce({ + ok: true, + json: async () => mockResponse, + } as any); + + const createData = { + name: 'Test Connection', + provider_type: 'stripe' as const, + sync_config: { + invoice: { inbound: true, outbound: true }, + plan: { inbound: true, outbound: true }, + subscription: { inbound: true, outbound: true }, + }, + }; + + const result = await createConnection(createData); + expect(result).toEqual(mockResponse); + expect(globalThis.fetch).toHaveBeenCalledTimes(3); + }, 10000); + }); + + describe('Error Scenarios', () => { + beforeEach(() => { + jest.clearAllMocks(); + globalThis.fetch = jest.fn() as any; + }); + + describe('Missing API Key', () => { + it('should handle missing API key', async () => { + delete process.env.FLEXPRICE_API_KEY; + + (globalThis.fetch as jest.MockedFunction).mockResolvedValueOnce({ + ok: false, + status: 401, + json: async () => ({ error: { details: 'Unauthorized' } }), + } as any); + + const createData = { + name: 'Test Connection', + provider_type: 'stripe' as const, + sync_config: { + invoice: { inbound: true, outbound: true }, + plan: { inbound: true, outbound: true }, + subscription: { inbound: true, outbound: true }, + }, + }; + + await expect(createConnection(createData)).rejects.toThrow('Flexprice API Error 401: Unauthorized'); + }, 10000); + }); + + describe('Different Provider Types', () => { + it('should create connection with flexprice provider', async () => { + process.env.FLEXPRICE_API_KEY = 'test-api-key'; + + (globalThis.fetch as jest.MockedFunction).mockResolvedValueOnce({ + ok: true, + json: async () => ({ ...mockResponse, provider_type: 'flexprice' }), + } as any); + + const createData = { + name: 'Flexprice Connection', + provider_type: 'flexprice' as const, + sync_config: { + invoice: { inbound: true, outbound: true }, + plan: { inbound: true, outbound: true }, + subscription: { inbound: true, outbound: true }, + }, + }; + + const result = await createConnection(createData); + expect(result.provider_type).toBe('flexprice'); + }); + + it('should create connection with s3 provider', async () => { + (globalThis.fetch as jest.MockedFunction).mockResolvedValueOnce({ + ok: true, + json: async () => ({ ...mockResponse, provider_type: 's3' }), + } as any); + + const createData = { + name: 'S3 Connection', + provider_type: 's3' as const, + sync_config: { + invoice: { inbound: true, outbound: true }, + plan: { inbound: true, outbound: true }, + subscription: { inbound: true, outbound: true }, + }, + }; + + const result = await createConnection(createData); + expect(result.provider_type).toBe('s3'); + }); + }); + + describe('Sync Configuration Variations', () => { + it('should handle partial sync config', async () => { + (globalThis.fetch as jest.MockedFunction).mockResolvedValueOnce({ + ok: true, + json: async () => ({ + ...mockResponse, + sync_config: { + invoice: { inbound: true }, + plan: { outbound: false }, + subscription: { inbound: false, outbound: true }, + }, + }), + } as any); + + const createData = { + name: 'Partial Sync Connection', + provider_type: 'stripe' as const, + sync_config: { + invoice: { inbound: true }, + plan: { outbound: false }, + subscription: { inbound: false, outbound: true }, + }, + }; + + const result = await createConnection(createData); + expect(result.sync_config.invoice.inbound).toBe(true); + expect(result.sync_config.plan.outbound).toBe(false); + expect(result.sync_config.subscription.inbound).toBe(false); + expect(result.sync_config.subscription.outbound).toBe(true); + }); + }); + + describe('Network and Timeout Scenarios', () => { + it('should handle network timeout in getConnections', async () => { + (globalThis.fetch as jest.MockedFunction) + .mockRejectedValueOnce(new Error('Network timeout')) + .mockRejectedValueOnce(new Error('Network timeout')) + .mockRejectedValueOnce(new Error('Network timeout')); + + await expect(getConnections()).rejects.toThrow('Network timeout'); + expect(globalThis.fetch).toHaveBeenCalledTimes(3); + }, 10000); + + it('should handle network timeout in updateConnection', async () => { + (globalThis.fetch as jest.MockedFunction) + .mockRejectedValueOnce(new Error('Network timeout')) + .mockRejectedValueOnce(new Error('Network timeout')) + .mockRejectedValueOnce(new Error('Network timeout')); + + const updates = { name: 'Updated Name' }; + await expect(updateConnection('conn-test-id', updates)).rejects.toThrow('Network timeout'); + expect(globalThis.fetch).toHaveBeenCalledTimes(3); + }, 10000); + + it('should handle network timeout in deleteConnection', async () => { + (globalThis.fetch as jest.MockedFunction) + .mockRejectedValueOnce(new Error('Network timeout')) + .mockRejectedValueOnce(new Error('Network timeout')) + .mockRejectedValueOnce(new Error('Network timeout')); + + await expect(deleteConnection('conn-test-id')).rejects.toThrow('Network timeout'); + expect(globalThis.fetch).toHaveBeenCalledTimes(3); + }, 10000); + }); + + describe('Malformed Data', () => { + it('should handle malformed JSON response', async () => { + (globalThis.fetch as jest.MockedFunction).mockResolvedValueOnce({ + ok: true, + json: async () => { + throw new Error('Invalid JSON'); + }, + } as any); + + await expect(getConnections()).rejects.toThrow('Invalid JSON'); + }, 10000); + + it('should handle unexpected response structure', async () => { + (globalThis.fetch as jest.MockedFunction).mockResolvedValueOnce({ + ok: true, + json: async () => 'unexpected string response', + } as any); + + const result = await getConnections(); + expect(result).toBe('unexpected string response'); + }); + }); + }); + + describe('getConnections', () => { + it('should fetch connections successfully', async () => { + (globalThis.fetch as jest.MockedFunction).mockResolvedValueOnce({ + ok: true, + json: async () => mockConnectionsList, + } as any); + + const searchParams = { limit: 10, status: 'published' }; + const result = await getConnections(searchParams); + expect(result).toEqual(mockConnectionsList); + expect(globalThis.fetch).toHaveBeenCalledWith('https://api.cloud.flexprice.io/v1/connections/search', { + method: 'POST', + body: JSON.stringify(searchParams), + headers: { + 'x-api-key': 'test-api-key', + 'Content-Type': 'application/json', + }, + }); + }); + + it('should use default empty params when none provided', async () => { + (globalThis.fetch as jest.MockedFunction).mockResolvedValueOnce({ + ok: true, + json: async () => mockConnectionsList, + } as any); + + await getConnections(); + expect(globalThis.fetch).toHaveBeenCalledWith('https://api.cloud.flexprice.io/v1/connections/search', expect.objectContaining({ + body: JSON.stringify({}), + })); + }); + + it('should handle search with multiple parameters', async () => { + (globalThis.fetch as jest.MockedFunction).mockResolvedValueOnce({ + ok: true, + json: async () => mockConnectionsList, + } as any); + + const searchParams = { + limit: 20, + offset: 10, + status: 'published', + provider_type: 'stripe', + order: 'desc' as const, + }; + const result = await getConnections(searchParams); + expect(result).toEqual(mockConnectionsList); + expect(globalThis.fetch).toHaveBeenCalledWith('https://api.cloud.flexprice.io/v1/connections/search', { + method: 'POST', + body: JSON.stringify(searchParams), + headers: { + 'x-api-key': 'test-api-key', + 'Content-Type': 'application/json', + }, + }); + }); + + it('should handle search with filters', async () => { + (globalThis.fetch as jest.MockedFunction).mockResolvedValueOnce({ + ok: true, + json: async () => mockConnectionsList, + } as any); + + const searchParams = { + filters: [ + { + data_type: 'connection', + field: 'name', + operator: 'contains', + value: 'test', + }, + ], + limit: 5, + }; + const result = await getConnections(searchParams); + expect(result).toEqual(mockConnectionsList); + expect(globalThis.fetch).toHaveBeenCalledWith('https://api.cloud.flexprice.io/v1/connections/search', { + method: 'POST', + body: JSON.stringify(searchParams), + headers: { + 'x-api-key': 'test-api-key', + 'Content-Type': 'application/json', + }, + }); + }); + }); + + describe('updateConnection', () => { + it('should update a connection successfully', async () => { + (globalThis.fetch as jest.MockedFunction).mockResolvedValueOnce({ + ok: true, + json: async () => ({ ...mockResponse, name: 'Updated Connection' }), + } as any); + + const updates = { name: 'Updated Connection' }; + const result = await updateConnection('conn-test-id', updates); + expect(result.name).toBe('Updated Connection'); + expect(globalThis.fetch).toHaveBeenCalledWith('https://api.cloud.flexprice.io/v1/connections/conn-test-id', { + method: 'PUT', + body: JSON.stringify(updates), + headers: { + 'x-api-key': 'test-api-key', + 'Content-Type': 'application/json', + }, + }); + }); + + it('should handle partial updates', async () => { + (globalThis.fetch as jest.MockedFunction).mockResolvedValueOnce({ + ok: true, + json: async () => ({ ...mockResponse, sync_config: { ...mockResponse.sync_config, plan: { inbound: false, outbound: true } } }), + } as any); + + const updates = { sync_config: { invoice: { inbound: true, outbound: true }, plan: { inbound: false, outbound: true }, subscription: { inbound: true, outbound: true } } }; + const result = await updateConnection('conn-test-id', updates); + expect(result.sync_config.plan.inbound).toBe(false); + expect(result.sync_config.plan.outbound).toBe(true); + }); + + it('should handle update errors', async () => { + (globalThis.fetch as jest.MockedFunction).mockResolvedValueOnce({ + ok: false, + status: 404, + json: async () => ({ error: { details: 'Connection not found' } }), + } as any); + + const updates = { name: 'Updated Name' }; + await expect(updateConnection('non-existent-id', updates)).rejects.toThrow('Flexprice API Error 404: Connection not found'); + }, 10000); + }); + + describe('deleteConnection', () => { + it('should delete a connection successfully', async () => { + (globalThis.fetch as jest.MockedFunction).mockResolvedValueOnce({ + ok: true, + json: async () => ({}), + } as any); + + const result = await deleteConnection('conn-test-id'); + expect(result).toEqual({ success: true }); + expect(globalThis.fetch).toHaveBeenCalledWith('https://api.cloud.flexprice.io/v1/connections/conn-test-id', { + method: 'DELETE', + headers: { + 'x-api-key': 'test-api-key', + 'Content-Type': 'application/json', + }, + }); + }); + + it('should handle delete errors', async () => { + (globalThis.fetch as jest.MockedFunction).mockResolvedValueOnce({ + ok: false, + status: 404, + json: async () => ({ error: { details: 'Connection not found' } }), + } as any); + + await expect(deleteConnection('non-existent-id')).rejects.toThrow('Flexprice API Error 404: Connection not found'); + }, 10000); + }); +}); diff --git a/manual-testing-sandbox/flexprice/connections.ts b/manual-testing-sandbox/flexprice/connections.ts new file mode 100644 index 00000000000..216d66b7edb --- /dev/null +++ b/manual-testing-sandbox/flexprice/connections.ts @@ -0,0 +1,200 @@ +// Flexprice SDK base URL and API key (use environment variables in production) +const FLEXPRICE_BASE_URL = 'https://api.cloud.flexprice.io/v1'; + +// Use global fetch for testing compatibility +const getFetch = (): typeof fetch => { + return globalThis.fetch; +}; + +/** + * Generic fetch wrapper with retry logic and error handling + */ +async function flexpriceFetch(endpoint: string, options: RequestInit = {}, retries: number = 3): Promise { + const url = `${FLEXPRICE_BASE_URL}${endpoint}`; + const apiKey = process.env.FLEXPRICE_API_KEY; + const config: RequestInit = { + ...options, + headers: { + 'x-api-key': apiKey || '', + 'Content-Type': 'application/json', + ...options.headers, + }, + }; + + for (let attempt = 1; attempt <= retries; attempt++) { + try { + const fetchFn = getFetch(); + const response = await fetchFn(url, config); + if (!response.ok) { + const errorData = await response.json() as { error?: { details?: string } }; + throw new Error(`Flexprice API Error ${response.status}: ${errorData.error?.details || 'Unknown error'}`); + } + return await response.json(); + } catch (error) { + if (attempt === retries) throw error; + // Exponential backoff + await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000)); + } + } +} + +/** + * Types based on Flexprice API docs + */ +interface SyncConfig { + invoice: { inbound?: boolean; outbound?: boolean }; + plan: { inbound?: boolean; outbound?: boolean }; + subscription: { inbound?: boolean; outbound?: boolean }; +} + +interface CreateConnectionRequest { + name: string; + provider_type: 'flexprice' | 'stripe' | 's3'; + encrypted_secret_data?: Record; // Specific to provider + metadata?: Record; + sync_config: SyncConfig; +} + +interface ConnectionResponse { + id: string; + name: string; + provider_type: string; + status: string; + sync_config: SyncConfig; + created_at: string; + created_by: string; + updated_at: string; + updated_by: string; + environment_id: string; + tenant_id: string; + metadata?: Record; +} + +interface UpdateConnectionRequest { + name?: string; + metadata?: Record; + sync_config?: SyncConfig; +} + +interface SearchConnectionsRequest { + connection_ids?: string[]; + end_time?: string; + expand?: string; + filters?: Array<{ + data_type: string; + field: string; + operator: string; + value: unknown; + }>; + limit?: number; + offset?: number; + order?: 'asc' | 'desc'; + provider_type?: string; + sort?: Array<{ direction: 'asc' | 'desc'; field: string }>; + start_time?: string; + status?: string; +} + +/** + * CREATE: Create a new connection + */ +export async function createConnection(data: CreateConnectionRequest): Promise { + try { + const response = await flexpriceFetch('/connections', { + method: 'POST', + body: JSON.stringify(data), + }); + return response as ConnectionResponse; + } catch (error) { + console.error('Error creating connection:', error); + throw error; + } +} + +/** + * READ: List/Search connections + */ +export async function getConnections(searchParams: SearchConnectionsRequest = {}): Promise<{ + connections: ConnectionResponse[]; + limit: number; + offset: number; + total: number; +}> { + try { + const response = await flexpriceFetch('/connections/search', { + method: 'POST', + body: JSON.stringify(searchParams), + }); + return response as { + connections: ConnectionResponse[]; + limit: number; + offset: number; + total: number; + }; + } catch (error) { + console.error('Error fetching connections:', error); + throw error; + } +} + +/** + * UPDATE: Update an existing connection by ID + */ +export async function updateConnection(id: string, updates: UpdateConnectionRequest): Promise { + try { + const response = await flexpriceFetch(`/connections/${id}`, { + method: 'PUT', + body: JSON.stringify(updates), + }); + return response as ConnectionResponse; + } catch (error) { + console.error('Error updating connection:', error); + throw error; + } +} + +/** + * DELETE: Delete a connection by ID (assumed based on CRUD convention) + */ +export async function deleteConnection(id: string): Promise<{ success: boolean }> { + try { + const response = await flexpriceFetch(`/connections/${id}`, { + method: 'DELETE', + }); + return { success: true }; // Assuming 204 No Content on success + } catch (error) { + console.error('Error deleting connection:', error); + throw error; + } +} + +// Example usage (for demonstration): +/* +// Create a connection +const newConn = await createConnection({ + name: 'My Stripe Connection', + provider_type: 'stripe', + encrypted_secret_data: { + stripe: { + account_id: 'acct_123', + publishable_key: 'pk_live_...', + secret_key: 'sk_live_...', + webhook_secret: 'whsec_...', + }, + }, + sync_config: { + invoice: { inbound: true, outbound: true }, + plan: { inbound: true, outbound: true }, + subscription: { inbound: true, outbound: true }, + }, +}); + +// List connections +const connections = await getConnections({ limit: 10 }); + +// Update a connection +const updated = await updateConnection('conn-id', { name: 'Updated Name' }); + +// Delete a connection +await deleteConnection('conn-id'); +*/ diff --git a/manual-testing-sandbox/flexprice/package-lock.json b/manual-testing-sandbox/flexprice/package-lock.json new file mode 100644 index 00000000000..8f19cc6f261 --- /dev/null +++ b/manual-testing-sandbox/flexprice/package-lock.json @@ -0,0 +1,5141 @@ +{ + "name": "flexprice-connections", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "flexprice-connections", + "version": "1.0.0", + "dependencies": { + "cors": "^2.8.5", + "dotenv": "^17.2.3", + "express": "^5.1.0", + "express-rate-limit": "^8.2.1", + "helmet": "^8.1.0", + "node-fetch": "^2.6.7", + "winston": "^3.18.3" + }, + "devDependencies": { + "@jest/globals": "^29.7.0", + "@types/jest": "^29.5.12", + "@types/node": "^20.12.7", + "@types/node-fetch": "^2.6.9", + "jest": "^29.7.0", + "jest-environment-node": "^29.7.0", + "ts-jest": "^29.1.2", + "typescript": "^5.4.5" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.5.tgz", + "integrity": "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz", + "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.28.3", + "@babel/helpers": "^7.28.4", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.5.tgz", + "integrity": "sha512-3EwLFhZ38J4VyIP6WNtt2kUdW9dokXA9Cr4IVIFHuCpZ3H8/YFOl5JjZHisrn1fATPBmKKqXzDFvh9fUwHz6CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.28.5", + "@babel/types": "^7.28.5", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", + "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.28.3" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", + "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", + "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", + "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.5.tgz", + "integrity": "sha512-TCCj4t55U90khlYkVV/0TfkJkAkUg3jZFA3Neb7unZT8CPok7iiRfaX0F+WnqWqt7OxhOn0uBKXCw4lbL8W0aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.5", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.5", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.5", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@colors/colors": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@dabh/diagnostics": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", + "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", + "license": "MIT", + "dependencies": { + "@so-ric/colorspace": "^1.1.6", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@so-ric/colorspace": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", + "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", + "license": "MIT", + "dependencies": { + "color": "^5.0.2", + "text-hex": "1.0.x" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "node_modules/@types/jest": { + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "expect": "^29.0.0", + "pretty-format": "^29.0.0" + } + }, + "node_modules/@types/node": { + "version": "20.19.24", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.24.tgz", + "integrity": "sha512-FE5u0ezmi6y9OZEzlJfg37mqqf6ZDSF2V/NLjUyGrR9uTZ7Sb9F7bLNZ03S4XVUNRWGA7Ck4c1kK+YnuWjl+DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/node-fetch": { + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "form-data": "^4.0.4" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "license": "MIT" + }, + "node_modules/@types/yargs": { + "version": "17.0.34", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.34.tgz", + "integrity": "sha512-KExbHVa92aJpw9WDQvzBaGVE2/Pz+pLZQloT2hjL8IqsZnV62rlPOYvNnLmf/L2dyllfVUOVBj64M0z/46eR2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/accepts/node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "license": "MIT" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.8.22", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.22.tgz", + "integrity": "sha512-/tk9kky/d8T8CTXIQYASLyhAxR5VwL3zct1oAoVTaOUHwrmsGnfbRwNdEq+vOl2BN8i3PcDdP0o4Q+jjKQoFbQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/body-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", + "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.0", + "http-errors": "^2.0.0", + "iconv-lite": "^0.6.3", + "on-finished": "^2.4.1", + "qs": "^6.14.0", + "raw-body": "^3.0.0", + "type-is": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.27.0.tgz", + "integrity": "sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.8.19", + "caniuse-lite": "^1.0.30001751", + "electron-to-chromium": "^1.5.238", + "node-releases": "^2.0.26", + "update-browserslist-db": "^1.1.4" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-json-stable-stringify": "2.x" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001752", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001752.tgz", + "integrity": "sha512-vKUk7beoukxE47P5gcVNKkDRzXdVofotshHwfR9vmpeFKxmI5PBpgOMC18LUJUA/DvJ70Y7RveasIBraqsyO/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cjs-module-lexer": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" + } + }, + "node_modules/collect-v8-coverage": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/color": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/color/-/color-5.0.2.tgz", + "integrity": "sha512-e2hz5BzbUPcYlIRHo8ieAhYgoajrJr+hWoceg6E345TPsATMUKqDgzt8fSXZJJbxfpiPzkWyphz8yn8At7q3fA==", + "license": "MIT", + "dependencies": { + "color-convert": "^3.0.1", + "color-string": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-string": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.2.tgz", + "integrity": "sha512-RxmjYxbWemV9gKu4zPgiZagUxbH3RQpEIO77XoSSX0ivgABDZ+h8Zuash/EMFLTI4N9QgFPOJ6JQpPZKFxa+dA==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/color-string/node_modules/color-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz", + "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.2.tgz", + "integrity": "sha512-UNqkvCDXstVck3kdowtOTWROIJQwafjOfXSmddoDrXo4cewMKmusCeF22Q24zvjR8nwWib/3S/dfyzPItPEiJg==", + "license": "MIT", + "dependencies": { + "color-name": "^2.0.0" + }, + "engines": { + "node": ">=14.6" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.0.2.tgz", + "integrity": "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A==", + "license": "MIT", + "engines": { + "node": ">=12.20" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/content-disposition": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", + "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" + }, + "bin": { + "create-jest": "bin/create-jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/dedent": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.0.tgz", + "integrity": "sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "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": ">= 0.8" + } + }, + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/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.244", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.244.tgz", + "integrity": "sha512-OszpBN7xZX4vWMPJwB9illkN/znA8M36GQqQxi6MNy9axWxhOfJyZZJtSLQCpEFLHP2xK33BiWx9aIuIEXVCcw==", + "dev": true, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "license": "MIT" + }, + "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/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "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/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/express": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", + "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.0", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.2.1.tgz", + "integrity": "sha512-PCZEIEIxqwhzw4KF0n7QF4QqruVTcF73O5kFKUnGOyjbCCgizBBiFaYpd/fnBLUMPw/BWw9OsiN7GgrNYr7j6g==", + "license": "MIT", + "dependencies": { + "ip-address": "10.0.1" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/express/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bser": "2.1.1" + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", + "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "license": "MIT" + }, + "node_modules/form-data": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/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/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "license": "ISC" + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "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/helmet": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/helmet/-/helmet-8.1.0.tgz", + "integrity": "sha512-jOiHyAZsmnr8LqoPGmCjYAaiuWwjAPLgY8ZX2XrmHawt99/u1y6RgrZMTeoPfpUbV96HOalYgz1qzkRbw54Pmg==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "license": "MIT", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=10.17.0" + } + }, + "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/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.0.1.tgz", + "integrity": "sha512-NWv9YLW4PoW2B7xtzaS3NCot75m6nK7Icdv0o3lfMceJVRfSoQwqD4wEH5rLwoKJwUiZ/rfpiVBhnaF0FK4HoA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/logform": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "license": "MIT", + "dependencies": { + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "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==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "license": "MIT", + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-to-regexp": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" + }, + "node_modules/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.1.tgz", + "integrity": "sha512-9G8cA+tuMS75+6G/TzW8OtLzmBDMo8p1JRxN5AZ+LAp8uxGA8V8GZm4GQ4/N5QNQEnLmg6SS7wyuSmbKepiKqA==", + "license": "MIT", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.7.0", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/raw-body/node_modules/iconv-lite": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.0.tgz", + "integrity": "sha512-cf6L2Ds3h57VVmkZe+Pn+5APsT7FpqJtEhhieDCvrE2MK5Qk9MyffgQyuxQTm6BChfeZNtcOLHp9IcWRVcIcBQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/resolve.exports": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/send": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", + "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.5", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "mime-types": "^3.0.1", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/send/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/send/node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "dev": true, + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "char-regex": "^1.0.2", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", + "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-final-newline": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "license": "MIT" + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/triple-beam": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/ts-jest": { + "version": "29.4.5", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.5.tgz", + "integrity": "sha512-HO3GyiWn2qvTQA4kTgjDcXiMwYQt68a1Y8+JuLRVpdIzm+UOLSHgl/XqR4c6nzJkq5rOkjc02O2I7P7l/Yof0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.8", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.3", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <6" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/transform": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jest-util": { + "optional": true + } + } + }, + "node_modules/ts-jest/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "license": "MIT", + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", + "dev": true, + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.4.tgz", + "integrity": "sha512-q0SPT4xyU84saUX+tomz1WLkxUbuaJnR1xWt17M7fJtEJigJeWUNGUqrauFXsHnqev9y9JTRGwk13tFBuKby4A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "license": "BSD-2-Clause" + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "license": "MIT", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/winston": { + "version": "3.18.3", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.18.3.tgz", + "integrity": "sha512-NoBZauFNNWENgsnC9YpgyYwOVrl2m58PpQ8lNHjV3kosGs7KJ7Npk9pCUE+WJlawVSe8mykWDKWFSVfs3QO9ww==", + "license": "MIT", + "dependencies": { + "@colors/colors": "^1.6.0", + "@dabh/diagnostics": "^2.0.8", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.7.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.9.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "license": "MIT", + "dependencies": { + "logform": "^2.7.0", + "readable-stream": "^3.6.2", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/manual-testing-sandbox/flexprice/package.json b/manual-testing-sandbox/flexprice/package.json new file mode 100644 index 00000000000..e7410e37c8c --- /dev/null +++ b/manual-testing-sandbox/flexprice/package.json @@ -0,0 +1,57 @@ +{ + "name": "flexprice-connections", + "version": "1.0.0", + "description": "Flexprice Connections API CRUD operations and tests", + "type": "module", + "main": "connections.ts", + "scripts": { + "test": "jest", + "test:watch": "jest --watch" + }, + "dependencies": { + "cors": "^2.8.5", + "dotenv": "^17.2.3", + "express": "^5.1.0", + "express-rate-limit": "^8.2.1", + "helmet": "^8.1.0", + "node-fetch": "^2.6.7", + "winston": "^3.18.3" + }, + "devDependencies": { + "@jest/globals": "^29.7.0", + "@types/jest": "^29.5.12", + "@types/node": "^20.12.7", + "@types/node-fetch": "^2.6.9", + "jest": "^29.7.0", + "jest-environment-node": "^29.7.0", + "ts-jest": "^29.1.2", + "typescript": "^5.4.5" + }, + "jest": { + "preset": "ts-jest", + "testEnvironment": "node", + "roots": [ + "" + ], + "testRegex": "test\\.ts$", + "transform": { + "^.+\\.ts$": [ + "ts-jest", + { + "useESM": true + } + ] + }, + "moduleFileExtensions": [ + "ts", + "tsx", + "js", + "jsx", + "json", + "node" + ], + "extensionsToTreatAsEsm": [ + ".ts" + ] + } +} diff --git a/manual-testing-sandbox/flexprice/server.ts b/manual-testing-sandbox/flexprice/server.ts new file mode 100644 index 00000000000..484d50a3702 --- /dev/null +++ b/manual-testing-sandbox/flexprice/server.ts @@ -0,0 +1,419 @@ +import express, { Request, Response, NextFunction } from 'express'; +import rateLimit from 'express-rate-limit'; +import cors from 'cors'; +import helmet from 'helmet'; +import { config } from 'dotenv'; + +// Load environment variables +config(); + +import { + createConnection, + getConnections, + updateConnection, + deleteConnection, + CreateConnectionRequest, + UpdateConnectionRequest, + SearchConnectionsRequest +} from './connections.js'; + +// Import refactored logger +import logger, { Logger } from './utils/logger.js'; + +// Import interceptors +import { + requestInterceptor, + responseInterceptor, + errorInterceptor, + performanceInterceptor, + securityHeadersInterceptor, + requestIdInterceptor, + correlationIdInterceptor +} from './middleware/interceptors.js'; + +// Create context-specific loggers +const serverLogger = new Logger('Server'); +const apiLogger = new Logger('API'); +const authLogger = new Logger('Authentication'); +const rateLimitLogger = new Logger('RateLimit'); + +// Initialize Express app +const app = express(); +const PORT = process.env.PORT || 3000; + +// Security middleware +app.use(helmet({ + contentSecurityPolicy: false, // Disable if conflicting with API responses + crossOriginEmbedderPolicy: false +})); + +// CORS configuration +app.use(cors({ + origin: process.env.ALLOWED_ORIGINS ? process.env.ALLOWED_ORIGINS.split(',') : ['http://localhost:3000'], + credentials: true, + methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], + allowedHeaders: ['Content-Type', 'Authorization', 'x-api-key'] +})); + +// Body parsing middleware +app.use(express.json({ limit: '10mb' })); +app.use(express.urlencoded({ extended: true, limit: '10mb' })); + +// Apply interceptors +app.use(requestIdInterceptor); +app.use(correlationIdInterceptor); +app.use(securityHeadersInterceptor); +app.use(requestInterceptor({ + logRequests: true, + sanitizeHeaders: true, + trackPerformance: true, + customHeaders: { + 'X-Powered-By': 'Flexprice-SDK', + 'X-API-Version': '1.0.0' + } +})); +app.use(responseInterceptor({ + logResponses: true, + trackPerformance: true +})); +app.use(performanceInterceptor(1000)); // Warn if requests take > 1 second + +// Request logging is now handled by interceptors + +// Rate limiting configuration +const apiKeyRateLimit = rateLimit({ + windowMs: parseInt(process.env.RATE_LIMIT_WINDOW_MS || '900000'), // 15 minutes default + max: parseInt(process.env.RATE_LIMIT_MAX || '100'), // limit each IP to 100 requests per windowMs + message: { + error: 'Too many requests from this IP, please try again later.', + retryAfter: Math.ceil(parseInt(process.env.RATE_LIMIT_WINDOW_MS || '900000') / 1000) + }, + standardHeaders: true, // Return rate limit info in the `RateLimit-*` headers + legacyHeaders: false, // Disable X-RateLimit-* headers + keyGenerator: (req: Request) => { + // Use API key as primary identifier, fallback to IP + const apiKey = req.header('x-api-key'); + return apiKey || req.ip; + }, + handler: (req: Request, res: Response) => { + rateLimitLogger.warn('Rate limit exceeded', { + ip: req.ip, + requestId: (req as any).requestId, + url: req.url, + method: req.method + }); + res.status(429).json({ + success: false, + error: 'Too many requests', + message: 'Rate limit exceeded. Please try again later.', + retryAfter: Math.ceil(parseInt(process.env.RATE_LIMIT_WINDOW_MS || '900000') / 1000), + requestId: (req as any).requestId + }); + } +}); + +// Stricter rate limiting for write operations +const writeOperationsRateLimit = rateLimit({ + windowMs: parseInt(process.env.WRITE_RATE_LIMIT_WINDOW_MS || '300000'), // 5 minutes + max: parseInt(process.env.WRITE_RATE_LIMIT_MAX || '20'), // 20 write operations per 5 minutes + message: { + error: 'Too many write operations', + retryAfter: Math.ceil(parseInt(process.env.WRITE_RATE_LIMIT_WINDOW_MS || '300000') / 1000) + }, + standardHeaders: true, + keyGenerator: (req: Request) => { + const apiKey = req.header('x-api-key'); + return apiKey || req.ip; + }, + handler: (req: Request, res: Response) => { + rateLimitLogger.warn('Write rate limit exceeded', { + ip: req.ip, + requestId: (req as any).requestId, + operation: req.method, + url: req.url + }); + res.status(429).json({ + success: false, + error: 'Too many write operations', + message: 'Write operation rate limit exceeded. Please try again later.', + retryAfter: Math.ceil(parseInt(process.env.WRITE_RATE_LIMIT_WINDOW_MS || '300000') / 1000), + requestId: (req as any).requestId + }); + } +}); + +// API key validation middleware +const validateApiKey = (req: Request, res: Response, next: NextFunction) => { + const apiKey = req.header('x-api-key'); + const requestId = (req as any).requestId; + + if (!apiKey) { + authLogger.warn('Request without API key', { + ip: req.ip, + url: req.url, + requestId + }); + return res.status(401).json({ + success: false, + error: 'Authentication required', + message: 'API key is required', + requestId + }); + } + + // Basic validation + if (apiKey.length < 10) { + authLogger.warn('Invalid API key format', { + ip: req.ip, + apiKeyLength: apiKey.length, + requestId + }); + return res.status(401).json({ + success: false, + error: 'Invalid API key', + message: 'API key format is invalid', + requestId + }); + } + + // Attach validated API key context + (req as any).apiKeyValidated = true; + + authLogger.debug('API key validated', { + requestId, + url: req.url + }); + + next(); +}; + +// Legacy error handler (now uses errorInterceptor) +const errorHandler = errorInterceptor; + +// Input validation middleware +const validateRequestBody = (schema: Record) => { + return (req: Request, res: Response, next: NextFunction) => { + const errors: string[] = []; + + for (const [field, rules] of Object.entries(schema)) { + const value = req.body[field]; + + if (rules.required && (value === undefined || value === null)) { + errors.push(`${field} is required`); + continue; + } + + if (value !== undefined && value !== null) { + if (rules.type && typeof value !== rules.type) { + errors.push(`${field} must be of type ${rules.type}`); + } + + if (rules.minLength && typeof value === 'string' && value.length < rules.minLength) { + errors.push(`${field} must be at least ${rules.minLength} characters`); + } + + if (rules.enum && rules.enum.length && !rules.enum.includes(value)) { + errors.push(`${field} must be one of: ${rules.enum.join(', ')}`); + } + } + } + + if (errors.length > 0) { + apiLogger.debug('Request validation failed', { + errors, + requestId: (req as any).requestId, + url: req.url + }); + return res.status(400).json({ + success: false, + error: 'Validation failed', + message: errors.join(', '), + errors, + requestId: (req as any).requestId + }); + } + + next(); + }; +}; + +// Apply rate limiting +app.use('/api/', apiKeyRateLimit); + +// Health check endpoint +app.get('/health', (req: Request, res: Response) => { + res.json({ + status: 'healthy', + timestamp: new Date().toISOString(), + version: process.env.npm_package_version || '1.0.0' + }); +}); + +// API Routes +app.post('/api/connections', + validateApiKey, + writeOperationsRateLimit, + validateRequestBody({ + name: { required: true, type: 'string', minLength: 1 }, + provider_type: { required: true, type: 'string', enum: ['flexprice', 'stripe', 's3'] }, + sync_config: { required: true, type: 'object' } + }), + async (req: Request, res: Response, next: NextFunction) => { + try { + const data: CreateConnectionRequest = req.body; + const requestId = (req as any).requestId; + + apiLogger.sdkOperation('createConnection', { + providerType: data.provider_type, + requestId + }); + + const result = await createConnection(data); + + apiLogger.info('Connection created successfully', { + connectionId: result.id, + requestId + }); + + res.status(201).json({ + success: true, + data: result, + requestId + }); + } catch (error: any) { + apiLogger.error('Create connection failed', { + error: error.message, + requestId: (req as any).requestId + }); + next(error); + } + } +); + +app.post('/api/connections/search', + validateApiKey, + async (req: Request, res: Response, next: NextFunction) => { + try { + const searchParams: SearchConnectionsRequest = req.body; + const requestId = (req as any).requestId; + + apiLogger.sdkOperation('searchConnections', { + filters: searchParams.filters?.length || 0, + requestId + }); + + const result = await getConnections(searchParams); + + apiLogger.info('Connection search completed', { + count: result.connections.length, + requestId + }); + + res.json({ + success: true, + data: result, + requestId + }); + } catch (error: any) { + apiLogger.error('Search connections failed', { + error: error.message, + requestId: (req as any).requestId + }); + next(error); + } + } +); + +app.put('/api/connections/:id', + validateApiKey, + writeOperationsRateLimit, + validateRequestBody({ + name: { type: 'string', minLength: 1 }, + metadata: { type: 'object' }, + sync_config: { type: 'object' } + }), + async (req: Request, res: Response, next: NextFunction) => { + try { + const { id } = req.params; + const updates: UpdateConnectionRequest = req.body; + const requestId = (req as any).requestId; + + apiLogger.sdkOperation('updateConnection', { + connectionId: id, + updateFields: Object.keys(updates), + requestId + }); + + const result = await updateConnection(id, updates); + + apiLogger.info('Connection updated successfully', { + connectionId: id, + requestId + }); + + res.json({ + success: true, + data: result, + requestId + }); + } catch (error: any) { + apiLogger.error('Update connection failed', { + connectionId: req.params.id, + error: error.message, + requestId: (req as any).requestId + }); + next(error); + } + } +); + +app.delete('/api/connections/:id', + validateApiKey, + writeOperationsRateLimit, + async (req: Request, res: Response, next: NextFunction) => { + try { + const { id } = req.params; + const requestId = (req as any).requestId; + + apiLogger.sdkOperation('deleteConnection', { + connectionId: id, + requestId + }); + + const result = await deleteConnection(id); + + apiLogger.info('Connection deleted successfully', { + connectionId: id, + requestId + }); + + res.json({ + success: true, + data: result, + requestId + }); + } catch (error: any) { + apiLogger.error('Delete connection failed', { + connectionId: req.params.id, + error: error.message, + requestId: (req as any).requestId + }); + next(error); + } + } +); + +// Apply error handling +app.use(errorHandler); + +// Start server +if (process.env.NODE_ENV !== 'test') { + app.listen(PORT, () => { + serverLogger.info(`Flexprice Connections API server running on port ${PORT}`); + serverLogger.info(`Environment: ${process.env.NODE_ENV || 'development'}`); + serverLogger.info(`Rate limit: ${process.env.RATE_LIMIT_MAX || '100'} requests per ${Math.ceil(parseInt(process.env.RATE_LIMIT_WINDOW_MS || '900000') / 60000)} minutes`); + serverLogger.info('All Flexprice SDK interceptors active'); + }); +} + +export default app; \ No newline at end of file diff --git a/manual-testing-sandbox/flexprice/src/middleware/interceptors.ts b/manual-testing-sandbox/flexprice/src/middleware/interceptors.ts new file mode 100644 index 00000000000..80d277b5d42 --- /dev/null +++ b/manual-testing-sandbox/flexprice/src/middleware/interceptors.ts @@ -0,0 +1,276 @@ +import { Request, Response, NextFunction } from 'express'; +import { Logger } from '../utils/logger.js'; + +const logger = new Logger('Interceptors'); + +/** + * Request/Response Interceptor Configuration + */ +export interface InterceptorConfig { + logRequests?: boolean; + logResponses?: boolean; + sanitizeHeaders?: boolean; + trackPerformance?: boolean; + customHeaders?: Record; +} + +/** + * Request Interceptor + * Captures and logs incoming requests with Flexprice SDK context + */ +export const requestInterceptor = (config: InterceptorConfig = {}) => { + const { + logRequests = true, + sanitizeHeaders = true, + trackPerformance = true, + customHeaders = {} + } = config; + + return (req: Request, res: Response, next: NextFunction) => { + // Attach request metadata + const requestId = `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + (req as any).requestId = requestId; + (req as any).startTime = Date.now(); + + // Add custom headers to response + Object.entries(customHeaders).forEach(([key, value]) => { + res.setHeader(key, value); + }); + + // Add request ID header + res.setHeader('X-Request-ID', requestId); + + if (logRequests) { + const sanitizedHeaders = sanitizeHeaders + ? sanitizeSensitiveData(req.headers) + : req.headers; + + logger.apiRequest(req.method, req.url, { + requestId, + ip: req.ip, + userAgent: req.get('User-Agent'), + headers: sanitizedHeaders, + query: req.query, + body: sanitizeHeaders ? sanitizeSensitiveData(req.body) : req.body + }); + } + + next(); + }; +}; + +/** + * Response Interceptor + * Captures and logs outgoing responses with performance metrics + */ +export const responseInterceptor = (config: InterceptorConfig = {}) => { + const { + logResponses = true, + trackPerformance = true + } = config; + + return (req: Request, res: Response, next: NextFunction) => { + // Capture original send function + const originalSend = res.send; + const originalJson = res.json; + + // Override send method + res.send = function (data: any): Response { + logResponse(req, res, data); + return originalSend.call(this, data); + }; + + // Override json method + res.json = function (data: any): Response { + logResponse(req, res, data); + return originalJson.call(this, data); + }; + + function logResponse(req: Request, res: Response, data: any) { + if (logResponses) { + const duration = trackPerformance && (req as any).startTime + ? Date.now() - (req as any).startTime + : 0; + + logger.apiResponse(req.method, req.url, res.statusCode, duration, { + requestId: (req as any).requestId, + contentType: res.get('Content-Type'), + contentLength: res.get('Content-Length') || (data ? JSON.stringify(data).length : 0), + success: res.statusCode >= 200 && res.statusCode < 300 + }); + } + } + + next(); + }; +}; + +/** + * Error Interceptor + * Captures and formats errors with Flexprice SDK context + */ +export const errorInterceptor = (err: Error, req: Request, res: Response, next: NextFunction) => { + const requestId = (req as any).requestId || 'unknown'; + const duration = (req as any).startTime ? Date.now() - (req as any).startTime : 0; + + // Log the error + logger.error('Request error occurred', { + requestId, + error: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined, + method: req.method, + url: req.url, + ip: req.ip, + duration + }); + + // Determine error status code + const statusCode = (err as any).statusCode || (err as any).status || 500; + + // Send error response + res.status(statusCode).json({ + success: false, + error: { + message: process.env.NODE_ENV === 'development' ? err.message : 'An error occurred', + code: (err as any).code || 'INTERNAL_SERVER_ERROR', + requestId, + timestamp: new Date().toISOString() + } + }); +}; + +/** + * Performance Monitoring Interceptor + * Tracks and logs performance metrics for Flexprice SDK operations + */ +export const performanceInterceptor = (threshold: number = 1000) => { + return (req: Request, res: Response, next: NextFunction) => { + const startTime = Date.now(); + + res.on('finish', () => { + const duration = Date.now() - startTime; + + if (duration > threshold) { + logger.warn('Slow request detected', { + requestId: (req as any).requestId, + method: req.method, + url: req.url, + duration, + threshold, + statusCode: res.statusCode + }); + } + + // Log performance metrics + if (process.env.LOG_LEVEL === 'debug') { + logger.debug('Request performance', { + requestId: (req as any).requestId, + method: req.method, + url: req.url, + duration, + statusCode: res.statusCode, + contentLength: res.get('Content-Length') + }); + } + }); + + next(); + }; +}; + +/** + * Security Headers Interceptor + * Adds security headers to all responses for Flexprice SDK protection + */ +export const securityHeadersInterceptor = (req: Request, res: Response, next: NextFunction) => { + // Add security headers + res.setHeader('X-Content-Type-Options', 'nosniff'); + res.setHeader('X-Frame-Options', 'DENY'); + res.setHeader('X-XSS-Protection', '1; mode=block'); + res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains'); + + // Remove powered-by header + res.removeHeader('X-Powered-By'); + + next(); +}; + +/** + * Request ID Interceptor + * Ensures all requests have a unique ID for tracing through Flexprice SDK + */ +export const requestIdInterceptor = (req: Request, res: Response, next: NextFunction) => { + const requestId = req.get('X-Request-ID') || + `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + + (req as any).requestId = requestId; + res.setHeader('X-Request-ID', requestId); + + next(); +}; + +/** + * Sanitize sensitive data from objects + */ +function sanitizeSensitiveData(obj: any): any { + if (!obj || typeof obj !== 'object') { + return obj; + } + + const sensitiveKeys = [ + 'password', + 'token', + 'apikey', + 'api-key', + 'x-api-key', + 'authorization', + 'secret', + 'credential', + 'private' + ]; + + const sanitized = Array.isArray(obj) ? [...obj] : { ...obj }; + + for (const key in sanitized) { + const lowerKey = key.toLowerCase(); + + if (sensitiveKeys.some(sensitive => lowerKey.includes(sensitive))) { + sanitized[key] = '[REDACTED]'; + } else if (typeof sanitized[key] === 'object' && sanitized[key] !== null) { + sanitized[key] = sanitizeSensitiveData(sanitized[key]); + } + } + + return sanitized; +} + +/** + * Correlation ID Interceptor + * Tracks requests across multiple Flexprice SDK services + */ +export const correlationIdInterceptor = (req: Request, res: Response, next: NextFunction) => { + const correlationId = req.get('X-Correlation-ID') || + `corr_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; + + (req as any).correlationId = correlationId; + res.setHeader('X-Correlation-ID', correlationId); + + logger.debug('Correlation ID assigned', { + correlationId, + requestId: (req as any).requestId, + url: req.url + }); + + next(); +}; + +export default { + requestInterceptor, + responseInterceptor, + errorInterceptor, + performanceInterceptor, + securityHeadersInterceptor, + requestIdInterceptor, + correlationIdInterceptor +}; +``` \ No newline at end of file diff --git a/manual-testing-sandbox/flexprice/src/utils/logger.ts b/manual-testing-sandbox/flexprice/src/utils/logger.ts new file mode 100644 index 00000000000..c722ce681f5 --- /dev/null +++ b/manual-testing-sandbox/flexprice/src/utils/logger.ts @@ -0,0 +1,136 @@ +import winston from 'winston'; +import path from 'path'; +import fs from 'fs'; + +/** + * Flexprice SDK Logger Utility + * Provides structured logging with multiple transports and log levels + */ + +// Ensure logs directory exists +const logsDir = path.join(process.cwd(), 'logs'); +if (!fs.existsSync(logsDir)) { + fs.mkdirSync(logsDir, { recursive: true }); +} + +// Custom log format +const customFormat = winston.format.printf(({ timestamp, level, message, service, ...meta }) => { + const metaStr = Object.keys(meta).length ? ` ${JSON.stringify(meta, null, 2)}` : ''; + return `${timestamp} [${service}] ${level}: ${message}${metaStr}`; +}); + +// Create logger instance +const logger = winston.createLogger({ + level: process.env.LOG_LEVEL || 'info', + format: winston.format.combine( + winston.format.timestamp({ + format: 'YYYY-MM-DD HH:mm:ss' + }), + winston.format.errors({ stack: true }), + winston.format.splat(), + winston.format.json() + ), + defaultMeta: { service: 'flexprice-connections-server' }, + transports: [ + // Console transport with colorization for development + new winston.transports.Console({ + format: winston.format.combine( + winston.format.colorize(), + customFormat + ) + }) + ] +}); + +// Add file transports for production +if (process.env.NODE_ENV === 'production') { + logger.add( + new winston.transports.File({ + filename: path.join(logsDir, 'server-error.log'), + level: 'error', + maxsize: 5242880, // 5MB + maxFiles: 5 + }) + ); + + logger.add( + new winston.transports.File({ + filename: path.join(logsDir, 'server-combined.log'), + maxsize: 5242880, // 5MB + maxFiles: 5 + }) + ); +} + +/** + * Logger wrapper with additional utility methods + */ +export class Logger { + private context: string; + + constructor(context: string = 'default') { + this.context = context; + } + + private formatMessage(message: string, meta?: Record) { + return { + message, + context: this.context, + ...meta + }; + } + + info(message: string, meta?: Record) { + logger.info(this.formatMessage(message, meta)); + } + + error(message: string, meta?: Record) { + logger.error(this.formatMessage(message, meta)); + } + + warn(message: string, meta?: Record) { + logger.warn(this.formatMessage(message, meta)); + } + + debug(message: string, meta?: Record) { + logger.debug(this.formatMessage(message, meta)); + } + + verbose(message: string, meta?: Record) { + logger.verbose(this.formatMessage(message, meta)); + } + + /** + * Log Flexprice SDK specific operations + */ + sdkOperation(operation: string, meta?: Record) { + this.info(`Flexprice SDK Operation: ${operation}`, { + ...meta, + sdkOperation: true + }); + } + + /** + * Log API requests + */ + apiRequest(method: string, url: string, meta?: Record) { + this.info(`API Request: ${method} ${url}`, { + ...meta, + apiRequest: true + }); + } + + /** + * Log API responses + */ + apiResponse(method: string, url: string, statusCode: number, duration: number, meta?: Record) { + this.info(`API Response: ${method} ${url} - ${statusCode}`, { + ...meta, + statusCode, + duration, + apiResponse: true + }); + } +} + +export default new Logger('FlexpriceServer'); \ No newline at end of file diff --git a/manual-testing-sandbox/flexprice/tsconfig.json b/manual-testing-sandbox/flexprice/tsconfig.json new file mode 100644 index 00000000000..3123ab28fc2 --- /dev/null +++ b/manual-testing-sandbox/flexprice/tsconfig.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "target": "ES2020", + "lib": ["ES2020"], + "module": "ESNext", + "moduleResolution": "node", + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "types": ["jest", "@types/jest", "@types/node", "@types/node-fetch"] + }, + "include": [ + "*.ts", + "*.tsx" + ], + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/packages/openai-adapters/src/apis/Stakd.ts b/packages/openai-adapters/src/apis/Stakd.ts index 47e787ec377..08df052819f 100644 --- a/packages/openai-adapters/src/apis/Stakd.ts +++ b/packages/openai-adapters/src/apis/Stakd.ts @@ -41,6 +41,16 @@ export class StakdApi extends OpenAIApi { for await (const chunk of streamSse(response)) { const delta = chunk.choices?.[0]?.delta; + const finishReason = chunk.choices?.[0]?.finish_reason; + + // Check for stream completion: finish_reason present with no more content + if (finishReason && finishReason !== "null" && (!delta || (!delta.content && !delta.reasoning))) { + // This is the final chunk signaling completion + if (chunk.usage) { + lastChunkWithUsage = chunk; + } + break; + } if (!delta) { if (chunk.usage) { From e4a2bc2ea5948cb04f8c5ce60a339168fba63605 Mon Sep 17 00:00:00 2001 From: HarshitZom Date: Sat, 1 Nov 2025 18:04:19 +0530 Subject: [PATCH 13/13] removed hardcoded secret keys --- core/context/mcp/builtinServers.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/context/mcp/builtinServers.ts b/core/context/mcp/builtinServers.ts index 3d25777b48a..2bdb78f2a5f 100644 --- a/core/context/mcp/builtinServers.ts +++ b/core/context/mcp/builtinServers.ts @@ -30,7 +30,7 @@ export const CONTEXT7_SERVER: BuiltInMcpServerConfig = { type: "streamable-http", url: "https://mcp.context7.com/mcp", readonly: true, - apiKey: "ctx7sk-ad277c04-2ae2-4a09-b5f3-e85807f2d4de", + apiKey: process.env.CONTEXT7_API_KEY, enabledByDefault: true, };