diff --git a/.gitignore b/.gitignore index 659b93b..7ca3bdd 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,3 @@ tests/__pycache__ infra/.terraform infra/.terraform.lock.hcl venv -infra/email/.terraform -infra/email/.terraform.lock.hcl -infra/email/node_modules -infra/email/email_processor.zip diff --git a/infra/email/email_processor.js b/infra/email/email_processor.js deleted file mode 100644 index 07e9923..0000000 --- a/infra/email/email_processor.js +++ /dev/null @@ -1,180 +0,0 @@ -const { SESClient, SendEmailCommand } = require('@aws-sdk/client-ses'); -const { S3Client, GetObjectCommand, PutObjectCommand } = require('@aws-sdk/client-s3'); -const { getSignedUrl } = require('@aws-sdk/s3-request-presigner'); -const { simpleParser } = require('mailparser'); - -// Initialize clients -const sesClient = new SESClient({ region: process.env.AWS_REGION || 'us-west-2' }); -const s3Client = new S3Client({ region: process.env.AWS_REGION || 'us-west-2' }); - -exports.handler = async (event) => { - try { - // Extract S3 event information - if (!event.Records || !event.Records[0] || !event.Records[0].s3) { - throw new Error('Invalid S3 event structure'); - } - - const s3Record = event.Records[0].s3; - const bucketName = s3Record.bucket.name; - const objectKey = s3Record.object.key; - - // Filter out attachment files to prevent infinite loops - if (objectKey.startsWith('attachments/')) { - console.log(`Skipping attachment file: ${objectKey}`); - return { - statusCode: 200, - body: JSON.stringify({ - message: 'Skipped attachment file', - reason: 'Object is in attachments/ prefix', - objectKey: objectKey - }) - }; - } - - console.log(`Processing email: ${objectKey}`); - - // Get the email content from S3 - const getObjectParams = { - Bucket: bucketName, - Key: objectKey - }; - - const s3Response = await s3Client.send(new GetObjectCommand(getObjectParams)); - const emailContent = await s3Response.Body.transformToString(); - - // Extract recipient from the object key path - const pathParts = objectKey.split('/'); - const recipient = pathParts[0]; // e.g., "calculator" - - const forwardEmail = process.env.FORWARD_EMAIL; - const domainName = process.env.DOMAIN_NAME || '127local.net'; - const environment = process.env.ENVIRONMENT || 'production'; - - // Parse the email using mailparser - const parsedEmail = await simpleParser(emailContent); - - // Extract email details - const subject = parsedEmail.subject || 'No Subject'; - const from = parsedEmail.from?.text || 'unknown@example.com'; - const emailBody = parsedEmail.text || parsedEmail.html || 'No body content'; - - // Create a clean, properly formatted forwarded email - const forwardSubject = `[${environment.toUpperCase()}] FWD: ${subject}`; - - // Build the email body with proper formatting - let forwardBody = `Email forwarded from ${from} to ${recipient}@${domainName} (${environment.toUpperCase()})\n`; - forwardBody += `Forwarded at: ${new Date().toISOString()}\n`; - forwardBody += `\n--- Original Email ---\n\n`; - forwardBody += `Subject: ${subject}\n`; - forwardBody += `From: ${from}\n`; - forwardBody += `\n${emailBody}`; - - // Process attachments and generate download links - let attachmentLinks = []; - if (parsedEmail.attachments && parsedEmail.attachments.length > 0) { - console.log(`Processing ${parsedEmail.attachments.length} attachments...`); - - for (const attachment of parsedEmail.attachments) { - try { - // Create unique filename for S3 - const timestamp = Date.now(); - const safeFilename = attachment.filename.replace(/[^a-zA-Z0-9.-]/g, '_'); - const s3Key = `attachments/${recipient}/${timestamp}-${safeFilename}`; - - // Upload attachment to S3 - const uploadParams = { - Bucket: bucketName, - Key: s3Key, - Body: attachment.content, - ContentType: attachment.contentType, - Metadata: { - originalEmail: objectKey, - recipient: recipient, - sender: from, - originalFilename: attachment.filename - } - }; - - await s3Client.send(new PutObjectCommand(uploadParams)); - console.log(`Uploaded: ${attachment.filename}`); - - // Generate pre-signed download URL (expires in 7 days) - const downloadUrl = await getSignedUrl( - s3Client, - new GetObjectCommand({ - Bucket: bucketName, - Key: s3Key - }), - { expiresIn: 604800 } // 7 days - ); - - attachmentLinks.push({ - filename: attachment.filename, - type: attachment.contentType, - size: attachment.size, - downloadUrl: downloadUrl - }); - - } catch (error) { - console.error(`Failed to process attachment ${attachment.filename}:`, error); - // Continue with other attachments - } - } - } - - // Add attachment information with download links - if (attachmentLinks.length > 0) { - forwardBody += `\n\n--- Attachments (${attachmentLinks.length}) ---\n`; - attachmentLinks.forEach((attachment) => { - forwardBody += `β€’ ${attachment.filename} (${attachment.type}, ${attachment.size} bytes)\n`; - forwardBody += ` Download: ${attachment.downloadUrl}\n`; - forwardBody += ` Note: Download link expires in 7 days\n\n`; - }); - } - - // Create the forwarded email using SendEmail - const forwardParams = { - Source: `noreply@${domainName}`, - Destination: { - ToAddresses: [forwardEmail] - }, - Message: { - Subject: { - Data: forwardSubject - }, - Body: { - Text: { - Data: forwardBody - } - } - } - }; - - // Send the properly formatted email - const result = await sesClient.send(new SendEmailCommand(forwardParams)); - console.log(`Email forwarded successfully: ${result.MessageId}`); - - return { - statusCode: 200, - body: JSON.stringify({ - message: 'Email forwarded successfully', - messageId: result.MessageId, - originalRecipient: recipient, - forwardedTo: forwardEmail, - s3Location: `${bucketName}/${objectKey}` - }) - }; - - } catch (error) { - console.error('Error processing email:', error); - - return { - statusCode: 500, - body: JSON.stringify({ - error: 'Failed to process email', - message: error.message, - stack: error.stack - }) - }; - } -}; diff --git a/infra/email/main.tf b/infra/email/main.tf deleted file mode 100644 index 442cd21..0000000 --- a/infra/email/main.tf +++ /dev/null @@ -1,308 +0,0 @@ -terraform { - required_version = ">= 1.0" - required_providers { - aws = { - source = "hashicorp/aws" - version = "~> 5.0" - } - random = { - source = "hashicorp/random" - version = "~> 3.0" - } - } - - backend "s3" { - bucket = "calculator-127local-net-terraform-state" - key = "email/infrastructure/terraform.tfstate" - region = "us-west-2" - } -} - -provider "aws" { - region = var.aws_region - - default_tags { - tags = local.common_tags - } -} - -# Common tags for all resources -locals { - common_tags = { - Project = "calculator-127local-net" - Environment = "production" - ManagedBy = "terraform" - Owner = "will@aerenserve.net" - Purpose = "email-forwarding" - CostCenter = "calculator-services" - } -} - -# S3 bucket for email storage -resource "aws_s3_bucket" "email_storage" { - bucket = "calculator-127local-net-emails-${random_string.bucket_suffix.result}" - - tags = merge(local.common_tags, { - Name = "Calculator Email Storage" - }) -} - -resource "random_string" "bucket_suffix" { - length = 8 - special = false - upper = false -} - -# S3 bucket versioning -resource "aws_s3_bucket_versioning" "email_storage" { - bucket = aws_s3_bucket.email_storage.id - versioning_configuration { - status = "Enabled" - } -} - -# S3 bucket server-side encryption -resource "aws_s3_bucket_server_side_encryption_configuration" "email_storage" { - bucket = aws_s3_bucket.email_storage.id - - rule { - apply_server_side_encryption_by_default { - sse_algorithm = "AES256" - } - } -} - -# S3 bucket public access block -resource "aws_s3_bucket_public_access_block" "email_storage" { - bucket = aws_s3_bucket.email_storage.id - - block_public_acls = true - block_public_policy = true - ignore_public_acls = true - restrict_public_buckets = true -} - -# S3 bucket policy to allow SES to write emails -resource "aws_s3_bucket_policy" "email_storage" { - bucket = aws_s3_bucket.email_storage.id - - policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Sid = "AllowSESToWriteEmails" - Effect = "Allow" - Principal = { - Service = "ses.amazonaws.com" - } - Action = [ - "s3:PutObject" - ] - Resource = "${aws_s3_bucket.email_storage.arn}/*" - Condition = { - StringEquals = { - "aws:Referer" = var.aws_account_id - } - } - } - ] - }) -} - -# SES Domain identity for 127local.net -resource "aws_ses_domain_identity" "calculator" { - domain = var.domain_name -} - -# SES Domain DKIM for 127local.net -resource "aws_ses_domain_dkim" "calculator" { - domain = aws_ses_domain_identity.calculator.domain -} - -# Route53 records for SES DKIM validation -resource "aws_route53_record" "ses_dkim" { - count = 3 - zone_id = var.route53_zone_id - name = "${element(aws_ses_domain_dkim.calculator.dkim_tokens, count.index)}._domainkey.${var.domain_name}" - type = "CNAME" - ttl = "600" - records = ["${element(aws_ses_domain_dkim.calculator.dkim_tokens, count.index)}.dkim.amazonses.com"] -} - -# MX record for email receiving -resource "aws_route53_record" "ses_mx" { - zone_id = var.route53_zone_id - name = var.domain_name - type = "MX" - ttl = "300" - records = ["10 inbound-smtp.us-west-2.amazonaws.com"] -} - -# SES Email receiving rule set -resource "aws_ses_receipt_rule_set" "calculator" { - rule_set_name = "calculator-main-rule-set" -} - -# SES Receipt rule for calculator@127local.net -resource "aws_ses_receipt_rule" "calculator_forward" { - name = "calculator-forward" - rule_set_name = aws_ses_receipt_rule_set.calculator.rule_set_name - recipients = ["calculator@${var.domain_name}"] - enabled = true - scan_enabled = true - tls_policy = "Optional" - - add_header_action { - header_name = "X-Forwarded-To" - header_value = var.forward_email - position = 1 - } - - s3_action { - bucket_name = aws_s3_bucket.email_storage.bucket - object_key_prefix = "calculator/" - position = 2 - } -} - -# Set this rule set as active -resource "aws_ses_active_receipt_rule_set" "calculator" { - rule_set_name = aws_ses_receipt_rule_set.calculator.rule_set_name -} - -# Lambda function for email processing -resource "aws_lambda_function" "email_processor" { - filename = "email_processor.zip" - function_name = "calculator-email-processor" - role = aws_iam_role.lambda_role.arn - handler = "email_processor.handler" - source_code_hash = data.archive_file.email_processor_zip.output_base64sha256 - runtime = "nodejs18.x" - timeout = 30 - - environment { - variables = { - FORWARD_EMAIL = var.forward_email - DOMAIN_NAME = var.domain_name - ENVIRONMENT = "production" - } - } - - tags = merge(local.common_tags, { - Name = "Calculator Email Processor" - }) -} - -# Create zip file for Lambda function with dependencies -data "archive_file" "email_processor_zip" { - type = "zip" - source_dir = "." - output_path = "email_processor.zip" - excludes = ["email_processor.zip", "*.tf", "*.tfvars", "README.md", ".terraform", ".terraform.lock.hcl"] -} - -# IAM role for Lambda function -resource "aws_iam_role" "lambda_role" { - name = "calculator-email-processor-role" - - assume_role_policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Action = "sts:AssumeRole" - Effect = "Allow" - Principal = { - Service = "lambda.amazonaws.com" - } - } - ] - }) - - tags = local.common_tags -} - -# IAM policy for Lambda function -resource "aws_iam_role_policy" "lambda_policy" { - name = "calculator-email-processor-policy" - role = aws_iam_role.lambda_role.id - - policy = jsonencode({ - Version = "2012-10-17" - Statement = [ - { - Effect = "Allow" - Action = [ - "logs:CreateLogGroup", - "logs:CreateLogStream", - "logs:PutLogEvents" - ] - Resource = "arn:aws:logs:*:*:*" - }, - { - Effect = "Allow" - Action = [ - "s3:GetObject", - "s3:PutObject" - ] - Resource = "${aws_s3_bucket.email_storage.arn}/*" - }, - { - Effect = "Allow" - Action = [ - "s3:ListBucket" - ] - Resource = aws_s3_bucket.email_storage.arn - }, - { - Effect = "Allow" - Action = [ - "ses:SendEmail", - "ses:SendRawEmail" - ] - Resource = "*" - } - ] - }) -} - -# S3 bucket notification to trigger Lambda -resource "aws_s3_bucket_notification" "email_processor" { - bucket = aws_s3_bucket.email_storage.id - - lambda_function { - lambda_function_arn = aws_lambda_function.email_processor.arn - events = ["s3:ObjectCreated:*"] - filter_prefix = "calculator/" - filter_suffix = "" - } -} - -# Lambda permission for S3 to invoke the function -resource "aws_lambda_permission" "allow_s3" { - statement_id = "AllowExecutionFromS3Bucket" - action = "lambda:InvokeFunction" - function_name = aws_lambda_function.email_processor.function_name - principal = "s3.amazonaws.com" - source_arn = aws_s3_bucket.email_storage.arn -} - -# Outputs -output "email_storage_bucket_name" { - value = aws_s3_bucket.email_storage.bucket -} - -output "ses_domain_identity" { - value = aws_ses_domain_identity.calculator.domain -} - -output "ses_receipt_rule_set_name" { - value = aws_ses_receipt_rule_set.calculator.rule_set_name -} - -output "lambda_function_name" { - value = aws_lambda_function.email_processor.function_name -} - -output "dkim_tokens" { - value = aws_ses_domain_dkim.calculator.dkim_tokens -} diff --git a/infra/email/package-lock.json b/infra/email/package-lock.json deleted file mode 100644 index ddea225..0000000 --- a/infra/email/package-lock.json +++ /dev/null @@ -1,2045 +0,0 @@ -{ - "name": "calculator-email-processor", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "calculator-email-processor", - "version": "1.0.0", - "dependencies": { - "@aws-sdk/client-s3": "^3.0.0", - "@aws-sdk/client-ses": "^3.0.0", - "@aws-sdk/s3-request-presigner": "^3.0.0", - "mailparser": "^3.6.5" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-crypto/crc32": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", - "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-crypto/crc32c": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", - "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha1-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", - "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", - "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-js": "^5.2.0", - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", - "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-crypto/supports-web-crypto": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", - "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", - "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-s3": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.879.0.tgz", - "integrity": "sha512-1bD2Do/OdCIzl72ncHKYamDhPijUErLYpuLvciyYD4Ywt4cVLHjWtVIqb22XOOHYYHE3NqHMd4uRhvXMlsBRoQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha1-browser": "5.2.0", - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.879.0", - "@aws-sdk/credential-provider-node": "3.879.0", - "@aws-sdk/middleware-bucket-endpoint": "3.873.0", - "@aws-sdk/middleware-expect-continue": "3.873.0", - "@aws-sdk/middleware-flexible-checksums": "3.879.0", - "@aws-sdk/middleware-host-header": "3.873.0", - "@aws-sdk/middleware-location-constraint": "3.873.0", - "@aws-sdk/middleware-logger": "3.876.0", - "@aws-sdk/middleware-recursion-detection": "3.873.0", - "@aws-sdk/middleware-sdk-s3": "3.879.0", - "@aws-sdk/middleware-ssec": "3.873.0", - "@aws-sdk/middleware-user-agent": "3.879.0", - "@aws-sdk/region-config-resolver": "3.873.0", - "@aws-sdk/signature-v4-multi-region": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-endpoints": "3.879.0", - "@aws-sdk/util-user-agent-browser": "3.873.0", - "@aws-sdk/util-user-agent-node": "3.879.0", - "@aws-sdk/xml-builder": "3.873.0", - "@smithy/config-resolver": "^4.1.5", - "@smithy/core": "^3.9.0", - "@smithy/eventstream-serde-browser": "^4.0.5", - "@smithy/eventstream-serde-config-resolver": "^4.1.3", - "@smithy/eventstream-serde-node": "^4.0.5", - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/hash-blob-browser": "^4.0.5", - "@smithy/hash-node": "^4.0.5", - "@smithy/hash-stream-node": "^4.0.5", - "@smithy/invalid-dependency": "^4.0.5", - "@smithy/md5-js": "^4.0.5", - "@smithy/middleware-content-length": "^4.0.5", - "@smithy/middleware-endpoint": "^4.1.19", - "@smithy/middleware-retry": "^4.1.20", - "@smithy/middleware-serde": "^4.0.9", - "@smithy/middleware-stack": "^4.0.5", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.5.0", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.27", - "@smithy/util-defaults-mode-node": "^4.0.27", - "@smithy/util-endpoints": "^3.0.7", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-retry": "^4.0.7", - "@smithy/util-stream": "^4.2.4", - "@smithy/util-utf8": "^4.0.0", - "@smithy/util-waiter": "^4.0.7", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-ses": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-ses/-/client-ses-3.879.0.tgz", - "integrity": "sha512-6yydcKf01tXAIsya5YBOcznvGN4DN8crLEuYC0jwG+67loCeq2HZMO1rL3ouaIllCSgmO0l7KHDK62BQr3Z3Zg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.879.0", - "@aws-sdk/credential-provider-node": "3.879.0", - "@aws-sdk/middleware-host-header": "3.873.0", - "@aws-sdk/middleware-logger": "3.876.0", - "@aws-sdk/middleware-recursion-detection": "3.873.0", - "@aws-sdk/middleware-user-agent": "3.879.0", - "@aws-sdk/region-config-resolver": "3.873.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-endpoints": "3.879.0", - "@aws-sdk/util-user-agent-browser": "3.873.0", - "@aws-sdk/util-user-agent-node": "3.879.0", - "@smithy/config-resolver": "^4.1.5", - "@smithy/core": "^3.9.0", - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/hash-node": "^4.0.5", - "@smithy/invalid-dependency": "^4.0.5", - "@smithy/middleware-content-length": "^4.0.5", - "@smithy/middleware-endpoint": "^4.1.19", - "@smithy/middleware-retry": "^4.1.20", - "@smithy/middleware-serde": "^4.0.9", - "@smithy/middleware-stack": "^4.0.5", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.5.0", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.27", - "@smithy/util-defaults-mode-node": "^4.0.27", - "@smithy/util-endpoints": "^3.0.7", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-retry": "^4.0.7", - "@smithy/util-utf8": "^4.0.0", - "@smithy/util-waiter": "^4.0.7", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.879.0.tgz", - "integrity": "sha512-+Pc3OYFpRYpKLKRreovPM63FPPud1/SF9vemwIJfz6KwsBCJdvg7vYD1xLSIp5DVZLeetgf4reCyAA5ImBfZuw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.879.0", - "@aws-sdk/middleware-host-header": "3.873.0", - "@aws-sdk/middleware-logger": "3.876.0", - "@aws-sdk/middleware-recursion-detection": "3.873.0", - "@aws-sdk/middleware-user-agent": "3.879.0", - "@aws-sdk/region-config-resolver": "3.873.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-endpoints": "3.879.0", - "@aws-sdk/util-user-agent-browser": "3.873.0", - "@aws-sdk/util-user-agent-node": "3.879.0", - "@smithy/config-resolver": "^4.1.5", - "@smithy/core": "^3.9.0", - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/hash-node": "^4.0.5", - "@smithy/invalid-dependency": "^4.0.5", - "@smithy/middleware-content-length": "^4.0.5", - "@smithy/middleware-endpoint": "^4.1.19", - "@smithy/middleware-retry": "^4.1.20", - "@smithy/middleware-serde": "^4.0.9", - "@smithy/middleware-stack": "^4.0.5", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.5.0", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.27", - "@smithy/util-defaults-mode-node": "^4.0.27", - "@smithy/util-endpoints": "^3.0.7", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-retry": "^4.0.7", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/core": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.879.0.tgz", - "integrity": "sha512-AhNmLCrx980LsK+SfPXGh7YqTyZxsK0Qmy18mWmkfY0TSq7WLaSDB5zdQbgbnQCACCHy8DUYXbi4KsjlIhv3PA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@aws-sdk/xml-builder": "3.873.0", - "@smithy/core": "^3.9.0", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/property-provider": "^4.0.5", - "@smithy/protocol-http": "^5.1.3", - "@smithy/signature-v4": "^5.1.3", - "@smithy/smithy-client": "^4.5.0", - "@smithy/types": "^4.3.2", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-utf8": "^4.0.0", - "fast-xml-parser": "5.2.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.879.0.tgz", - "integrity": "sha512-JgG7A8SSbr5IiCYL8kk39Y9chdSB5GPwBorDW8V8mr19G9L+qd6ohED4fAocoNFaDnYJ5wGAHhCfSJjzcsPBVQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@smithy/property-provider": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.879.0.tgz", - "integrity": "sha512-2hM5ByLpyK+qORUexjtYyDZsgxVCCUiJQZRMGkNXFEGz6zTpbjfTIWoh3zRgWHEBiqyPIyfEy50eIF69WshcuA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/property-provider": "^4.0.5", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.5.0", - "@smithy/types": "^4.3.2", - "@smithy/util-stream": "^4.2.4", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.879.0.tgz", - "integrity": "sha512-07M8zfb73KmMBqVO5/V3Ea9kqDspMX0fO0kaI1bsjWI6ngnMye8jCE0/sIhmkVAI0aU709VA0g+Bzlopnw9EoQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.879.0", - "@aws-sdk/credential-provider-env": "3.879.0", - "@aws-sdk/credential-provider-http": "3.879.0", - "@aws-sdk/credential-provider-process": "3.879.0", - "@aws-sdk/credential-provider-sso": "3.879.0", - "@aws-sdk/credential-provider-web-identity": "3.879.0", - "@aws-sdk/nested-clients": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@smithy/credential-provider-imds": "^4.0.7", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.879.0.tgz", - "integrity": "sha512-FYaAqJbnSTrVL2iZkNDj2hj5087yMv2RN2GA8DJhe7iOJjzhzRojrtlfpWeJg6IhK0sBKDH+YXbdeexCzUJvtA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.879.0", - "@aws-sdk/credential-provider-http": "3.879.0", - "@aws-sdk/credential-provider-ini": "3.879.0", - "@aws-sdk/credential-provider-process": "3.879.0", - "@aws-sdk/credential-provider-sso": "3.879.0", - "@aws-sdk/credential-provider-web-identity": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@smithy/credential-provider-imds": "^4.0.7", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.879.0.tgz", - "integrity": "sha512-7r360x1VyEt35Sm1JFOzww2WpnfJNBbvvnzoyLt7WRfK0S/AfsuWhu5ltJ80QvJ0R3AiSNbG+q/btG2IHhDYPQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.879.0.tgz", - "integrity": "sha512-gd27B0NsgtKlaPNARj4IX7F7US5NuU691rGm0EUSkDsM7TctvJULighKoHzPxDQlrDbVI11PW4WtKS/Zg5zPlQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-sso": "3.879.0", - "@aws-sdk/core": "3.879.0", - "@aws-sdk/token-providers": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.879.0.tgz", - "integrity": "sha512-Jy4uPFfGzHk1Mxy+/Wr43vuw9yXsE2yiF4e4598vc3aJfO0YtA2nSfbKD3PNKRORwXbeKqWPfph9SCKQpWoxEg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.879.0", - "@aws-sdk/nested-clients": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@smithy/property-provider": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.873.0.tgz", - "integrity": "sha512-b4bvr0QdADeTUs+lPc9Z48kXzbKHXQKgTvxx/jXDgSW9tv4KmYPO1gIj6Z9dcrBkRWQuUtSW3Tu2S5n6pe+zeg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-arn-parser": "3.873.0", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "@smithy/util-config-provider": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.873.0.tgz", - "integrity": "sha512-GIqoc8WgRcf/opBOZXFLmplJQKwOMjiOMmDz9gQkaJ8FiVJoAp8EGVmK2TOWZMQUYsavvHYsHaor5R2xwPoGVg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.879.0.tgz", - "integrity": "sha512-U1rcWToy2rlQPQLsx5h73uTC1XYo/JpnlJGCc3Iw7b1qrK8Mke4+rgMPKCfnXELD5TTazGrbT03frxH4Y1Ycvw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/crc32": "5.2.0", - "@aws-crypto/crc32c": "5.2.0", - "@aws-crypto/util": "5.2.0", - "@aws-sdk/core": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@smithy/is-array-buffer": "^4.0.0", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-stream": "^4.2.4", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.873.0.tgz", - "integrity": "sha512-KZ/W1uruWtMOs7D5j3KquOxzCnV79KQW9MjJFZM/M0l6KI8J6V3718MXxFHsTjUE4fpdV6SeCNLV1lwGygsjJA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-location-constraint": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.873.0.tgz", - "integrity": "sha512-r+hIaORsW/8rq6wieDordXnA/eAu7xAPLue2InhoEX6ML7irP52BgiibHLpt9R0psiCzIHhju8qqKa4pJOrmiw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-logger": { - "version": "3.876.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.876.0.tgz", - "integrity": "sha512-cpWJhOuMSyz9oV25Z/CMHCBTgafDCbv7fHR80nlRrPdPZ8ETNsahwRgltXP1QJJ8r3X/c1kwpOR7tc+RabVzNA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.873.0.tgz", - "integrity": "sha512-OtgY8EXOzRdEWR//WfPkA/fXl0+WwE8hq0y9iw2caNyKPtca85dzrrZWnPqyBK/cpImosrpR1iKMYr41XshsCg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.879.0.tgz", - "integrity": "sha512-ZTpLr2AbZcCsEzu18YCtB8Tp8tjAWHT0ccfwy3HiL6g9ncuSMW+7BVi1hDYmBidFwpPbnnIMtM0db3pDMR6/WA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-arn-parser": "3.873.0", - "@smithy/core": "^3.9.0", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/protocol-http": "^5.1.3", - "@smithy/signature-v4": "^5.1.3", - "@smithy/smithy-client": "^4.5.0", - "@smithy/types": "^4.3.2", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-stream": "^4.2.4", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-ssec": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.873.0.tgz", - "integrity": "sha512-AF55J94BoiuzN7g3hahy0dXTVZahVi8XxRBLgzNp6yQf0KTng+hb/V9UQZVYY1GZaDczvvvnqC54RGe9OZZ9zQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.879.0.tgz", - "integrity": "sha512-DDSV8228lQxeMAFKnigkd0fHzzn5aauZMYC3CSj6e5/qE7+9OwpkUcjHfb7HZ9KWG6L2/70aKZXHqiJ4xKhOZw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-endpoints": "3.879.0", - "@smithy/core": "^3.9.0", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/nested-clients": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.879.0.tgz", - "integrity": "sha512-7+n9NpIz9QtKYnxmw1fHi9C8o0GrX8LbBR4D50c7bH6Iq5+XdSuL5AFOWWQ5cMD0JhqYYJhK/fJsVau3nUtC4g==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.879.0", - "@aws-sdk/middleware-host-header": "3.873.0", - "@aws-sdk/middleware-logger": "3.876.0", - "@aws-sdk/middleware-recursion-detection": "3.873.0", - "@aws-sdk/middleware-user-agent": "3.879.0", - "@aws-sdk/region-config-resolver": "3.873.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-endpoints": "3.879.0", - "@aws-sdk/util-user-agent-browser": "3.873.0", - "@aws-sdk/util-user-agent-node": "3.879.0", - "@smithy/config-resolver": "^4.1.5", - "@smithy/core": "^3.9.0", - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/hash-node": "^4.0.5", - "@smithy/invalid-dependency": "^4.0.5", - "@smithy/middleware-content-length": "^4.0.5", - "@smithy/middleware-endpoint": "^4.1.19", - "@smithy/middleware-retry": "^4.1.20", - "@smithy/middleware-serde": "^4.0.9", - "@smithy/middleware-stack": "^4.0.5", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.5.0", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.27", - "@smithy/util-defaults-mode-node": "^4.0.27", - "@smithy/util-endpoints": "^3.0.7", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-retry": "^4.0.7", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.873.0.tgz", - "integrity": "sha512-q9sPoef+BBG6PJnc4x60vK/bfVwvRWsPgcoQyIra057S/QGjq5VkjvNk6H8xedf6vnKlXNBwq9BaANBXnldUJg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/types": "^4.3.2", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/s3-request-presigner": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.879.0.tgz", - "integrity": "sha512-WNUrY4UW1ZAkBiSq9HnhJcG/1NdrEy37DDxqE8u0OdIZHhbgU1x1r4iXgQssAZhV6D+Ib70oiQGtPSH/lXeMKg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/signature-v4-multi-region": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-format-url": "3.873.0", - "@smithy/middleware-endpoint": "^4.1.19", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.5.0", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.879.0.tgz", - "integrity": "sha512-MDsw0EWOHyKac75X3gD8tLWtmPuRliS/s4IhWRhsdDCU13wewHIs5IlA5B65kT6ISf49yEIalEH3FHUSVqdmIQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@smithy/protocol-http": "^5.1.3", - "@smithy/signature-v4": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/token-providers": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.879.0.tgz", - "integrity": "sha512-47J7sCwXdnw9plRZNAGVkNEOlSiLb/kR2slnDIHRK9NB/ECKsoqgz5OZQJ9E2f0yqOs8zSNJjn3T01KxpgW8Qw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.879.0", - "@aws-sdk/nested-clients": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/types": { - "version": "3.862.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.862.0.tgz", - "integrity": "sha512-Bei+RL0cDxxV+lW2UezLbCYYNeJm6Nzee0TpW0FfyTRBhH9C1XQh4+x+IClriXvgBnRquTMMYsmJfvx8iyLKrg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-arn-parser": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.873.0.tgz", - "integrity": "sha512-qag+VTqnJWDn8zTAXX4wiVioa0hZDQMtbZcGRERVnLar4/3/VIKBhxX2XibNQXFu1ufgcRn4YntT/XEPecFWcg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-endpoints": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.879.0.tgz", - "integrity": "sha512-aVAJwGecYoEmbEFju3127TyJDF9qJsKDUUTRMDuS8tGn+QiWQFnfInmbt+el9GU1gEJupNTXV+E3e74y51fb7A==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-endpoints": "^3.0.7", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-format-url": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.873.0.tgz", - "integrity": "sha512-v//b9jFnhzTKKV3HFTw2MakdM22uBAs2lBov51BWmFXuFtSTdBLrR7zgfetQPE3PVkFai0cmtJQPdc3MX+T/cQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/querystring-builder": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-locate-window": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.873.0.tgz", - "integrity": "sha512-xcVhZF6svjM5Rj89T1WzkjQmrTF6dpR2UvIHPMTnSZoNe6CixejPZ6f0JJ2kAhO8H+dUHwNBlsUgOTIKiK/Syg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.873.0.tgz", - "integrity": "sha512-AcRdbK6o19yehEcywI43blIBhOCSo6UgyWcuOJX5CFF8k39xm1ILCjQlRRjchLAxWrm0lU0Q7XV90RiMMFMZtA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/types": "^4.3.2", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.879.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.879.0.tgz", - "integrity": "sha512-A5KGc1S+CJRzYnuxJQQmH1BtGsz46AgyHkqReKfGiNQA8ET/9y9LQ5t2ABqnSBHHIh3+MiCcQSkUZ0S3rTodrQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/middleware-user-agent": "3.879.0", - "@aws-sdk/types": "3.862.0", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@aws-sdk/xml-builder": { - "version": "3.873.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.873.0.tgz", - "integrity": "sha512-kLO7k7cGJ6KaHiExSJWojZurF7SnGMDHXRuQunFnEoD0n1yB6Lqy/S/zHiQ7oJnBhPr9q0TW9qFkrsZb1Uc54w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@selderee/plugin-htmlparser2": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.11.0.tgz", - "integrity": "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==", - "license": "MIT", - "dependencies": { - "domhandler": "^5.0.3", - "selderee": "^0.11.0" - }, - "funding": { - "url": "https://ko-fi.com/killymxi" - } - }, - "node_modules/@smithy/abort-controller": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.5.tgz", - "integrity": "sha512-jcrqdTQurIrBbUm4W2YdLVMQDoL0sA9DTxYd2s+R/y+2U9NLOP7Xf/YqfSg1FZhlZIYEnvk2mwbyvIfdLEPo8g==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/chunked-blob-reader": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.0.0.tgz", - "integrity": "sha512-+sKqDBQqb036hh4NPaUiEkYFkTUGYzRsn3EuFhyfQfMy6oGHEUJDurLP9Ufb5dasr/XiAmPNMr6wa9afjQB+Gw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/chunked-blob-reader-native": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.0.0.tgz", - "integrity": "sha512-R9wM2yPmfEMsUmlMlIgSzOyICs0x9uu7UTHoccMyt7BWw8shcGM8HqB355+BZCPBcySvbTYMs62EgEQkNxz2ig==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-base64": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/config-resolver": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.5.tgz", - "integrity": "sha512-viuHMxBAqydkB0AfWwHIdwf/PRH2z5KHGUzqyRtS/Wv+n3IHI993Sk76VCA7dD/+GzgGOmlJDITfPcJC1nIVIw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.1.4", - "@smithy/types": "^4.3.2", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/core": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.9.0.tgz", - "integrity": "sha512-B/GknvCfS3llXd/b++hcrwIuqnEozQDnRL4sBmOac5/z/dr0/yG1PURNPOyU4Lsiy1IyTj8scPxVqRs5dYWf6A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/middleware-serde": "^4.0.9", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-stream": "^4.2.4", - "@smithy/util-utf8": "^4.0.0", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/credential-provider-imds": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.7.tgz", - "integrity": "sha512-dDzrMXA8d8riFNiPvytxn0mNwR4B3h8lgrQ5UjAGu6T9z/kRg/Xncf4tEQHE/+t25sY8IH3CowcmWi+1U5B1Gw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.1.4", - "@smithy/property-provider": "^4.0.5", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-codec": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.0.5.tgz", - "integrity": "sha512-miEUN+nz2UTNoRYRhRqVTJCx7jMeILdAurStT2XoS+mhokkmz1xAPp95DFW9Gxt4iF2VBqpeF9HbTQ3kY1viOA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^4.3.2", - "@smithy/util-hex-encoding": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-browser": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.0.5.tgz", - "integrity": "sha512-LCUQUVTbM6HFKzImYlSB9w4xafZmpdmZsOh9rIl7riPC3osCgGFVP+wwvYVw6pXda9PPT9TcEZxaq3XE81EdJQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-serde-universal": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.1.3.tgz", - "integrity": "sha512-yTTzw2jZjn/MbHu1pURbHdpjGbCuMHWncNBpJnQAPxOVnFUAbSIUSwafiphVDjNV93TdBJWmeVAds7yl5QCkcA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-node": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.0.5.tgz", - "integrity": "sha512-lGS10urI4CNzz6YlTe5EYG0YOpsSp3ra8MXyco4aqSkQDuyZPIw2hcaxDU82OUVtK7UY9hrSvgWtpsW5D4rb4g==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-serde-universal": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-universal": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.0.5.tgz", - "integrity": "sha512-JFnmu4SU36YYw3DIBVao3FsJh4Uw65vVDIqlWT4LzR6gXA0F3KP0IXFKKJrhaVzCBhAuMsrUUaT5I+/4ZhF7aw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-codec": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/fetch-http-handler": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.1.1.tgz", - "integrity": "sha512-61WjM0PWmZJR+SnmzaKI7t7G0UkkNFboDpzIdzSoy7TByUzlxo18Qlh9s71qug4AY4hlH/CwXdubMtkcNEb/sQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.1.3", - "@smithy/querystring-builder": "^4.0.5", - "@smithy/types": "^4.3.2", - "@smithy/util-base64": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/hash-blob-browser": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.0.5.tgz", - "integrity": "sha512-F7MmCd3FH/Q2edhcKd+qulWkwfChHbc9nhguBlVjSUE6hVHhec3q6uPQ+0u69S6ppvLtR3eStfCuEKMXBXhvvA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/chunked-blob-reader": "^5.0.0", - "@smithy/chunked-blob-reader-native": "^4.0.0", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/hash-node": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.5.tgz", - "integrity": "sha512-cv1HHkKhpyRb6ahD8Vcfb2Hgz67vNIXEp2vnhzfxLFGRukLCNEA5QdsorbUEzXma1Rco0u3rx5VTqbM06GcZqQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/hash-stream-node": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.0.5.tgz", - "integrity": "sha512-IJuDS3+VfWB67UC0GU0uYBG/TA30w+PlOaSo0GPm9UHS88A6rCP6uZxNjNYiyRtOcjv7TXn/60cW8ox1yuZsLg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/invalid-dependency": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.5.tgz", - "integrity": "sha512-IVnb78Qtf7EJpoEVo7qJ8BEXQwgC4n3igeJNNKEj/MLYtapnx8A67Zt/J3RXAj2xSO1910zk0LdFiygSemuLow==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/is-array-buffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", - "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/md5-js": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.0.5.tgz", - "integrity": "sha512-8n2XCwdUbGr8W/XhMTaxILkVlw2QebkVTn5tm3HOcbPbOpWg89zr6dPXsH8xbeTsbTXlJvlJNTQsKAIoqQGbdA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-content-length": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.5.tgz", - "integrity": "sha512-l1jlNZoYzoCC7p0zCtBDE5OBXZ95yMKlRlftooE5jPWQn4YBPLgsp+oeHp7iMHaTGoUdFqmHOPa8c9G3gBsRpQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-endpoint": { - "version": "4.1.19", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.19.tgz", - "integrity": "sha512-EAlEPncqo03siNZJ9Tm6adKCQ+sw5fNU8ncxWwaH0zTCwMPsgmERTi6CEKaermZdgJb+4Yvh0NFm36HeO4PGgQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.9.0", - "@smithy/middleware-serde": "^4.0.9", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-middleware": "^4.0.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-retry": { - "version": "4.1.20", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.20.tgz", - "integrity": "sha512-T3maNEm3Masae99eFdx1Q7PIqBBEVOvRd5hralqKZNeIivnoGNx5OFtI3DiZ5gCjUkl0mNondlzSXeVxkinh7Q==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.1.4", - "@smithy/protocol-http": "^5.1.3", - "@smithy/service-error-classification": "^4.0.7", - "@smithy/smithy-client": "^4.5.0", - "@smithy/types": "^4.3.2", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-retry": "^4.0.7", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-serde": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.9.tgz", - "integrity": "sha512-uAFFR4dpeoJPGz8x9mhxp+RPjo5wW0QEEIPPPbLXiRRWeCATf/Km3gKIVR5vaP8bN1kgsPhcEeh+IZvUlBv6Xg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-stack": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.5.tgz", - "integrity": "sha512-/yoHDXZPh3ocRVyeWQFvC44u8seu3eYzZRveCMfgMOBcNKnAmOvjbL9+Cp5XKSIi9iYA9PECUuW2teDAk8T+OQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/node-config-provider": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.1.4.tgz", - "integrity": "sha512-+UDQV/k42jLEPPHSn39l0Bmc4sB1xtdI9Gd47fzo/0PbXzJ7ylgaOByVjF5EeQIumkepnrJyfx86dPa9p47Y+w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/node-http-handler": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.1.1.tgz", - "integrity": "sha512-RHnlHqFpoVdjSPPiYy/t40Zovf3BBHc2oemgD7VsVTFFZrU5erFFe0n52OANZZ/5sbshgD93sOh5r6I35Xmpaw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^4.0.5", - "@smithy/protocol-http": "^5.1.3", - "@smithy/querystring-builder": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/property-provider": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.5.tgz", - "integrity": "sha512-R/bswf59T/n9ZgfgUICAZoWYKBHcsVDurAGX88zsiUtOTA/xUAPyiT+qkNCPwFn43pZqN84M4MiUsbSGQmgFIQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/protocol-http": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.3.tgz", - "integrity": "sha512-fCJd2ZR7D22XhDY0l+92pUag/7je2BztPRQ01gU5bMChcyI0rlly7QFibnYHzcxDvccMjlpM/Q1ev8ceRIb48w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/querystring-builder": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.5.tgz", - "integrity": "sha512-NJeSCU57piZ56c+/wY+AbAw6rxCCAOZLCIniRE7wqvndqxcKKDOXzwWjrY7wGKEISfhL9gBbAaWWgHsUGedk+A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "@smithy/util-uri-escape": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/querystring-parser": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.5.tgz", - "integrity": "sha512-6SV7md2CzNG/WUeTjVe6Dj8noH32r4MnUeFKZrnVYsQxpGSIcphAanQMayi8jJLZAWm6pdM9ZXvKCpWOsIGg0w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/service-error-classification": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.7.tgz", - "integrity": "sha512-XvRHOipqpwNhEjDf2L5gJowZEm5nsxC16pAZOeEcsygdjv9A2jdOh3YoDQvOXBGTsaJk6mNWtzWalOB9976Wlg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.5.tgz", - "integrity": "sha512-YVVwehRDuehgoXdEL4r1tAAzdaDgaC9EQvhK0lEbfnbrd0bd5+CTQumbdPryX3J2shT7ZqQE+jPW4lmNBAB8JQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/signature-v4": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.3.tgz", - "integrity": "sha512-mARDSXSEgllNzMw6N+mC+r1AQlEBO3meEAkR/UlfAgnMzJUB3goRBWgip1EAMG99wh36MDqzo86SfIX5Y+VEaw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^4.0.0", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "@smithy/util-hex-encoding": "^4.0.0", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-uri-escape": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/smithy-client": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.5.0.tgz", - "integrity": "sha512-ZSdE3vl0MuVbEwJBxSftm0J5nL/gw76xp5WF13zW9cN18MFuFXD5/LV0QD8P+sCU5bSWGyy6CTgUupE1HhOo1A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.9.0", - "@smithy/middleware-endpoint": "^4.1.19", - "@smithy/middleware-stack": "^4.0.5", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "@smithy/util-stream": "^4.2.4", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/types": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.2.tgz", - "integrity": "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/url-parser": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.5.tgz", - "integrity": "sha512-j+733Um7f1/DXjYhCbvNXABV53NyCRRA54C7bNEIxNPs0YjfRxeMKjjgm2jvTYrciZyCjsicHwQ6Q0ylo+NAUw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/querystring-parser": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-base64": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", - "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-body-length-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", - "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-body-length-node": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", - "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-buffer-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", - "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-config-provider": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", - "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.0.27", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.27.tgz", - "integrity": "sha512-i/Fu6AFT5014VJNgWxKomBJP/GB5uuOsM4iHdcmplLm8B1eAqnRItw4lT2qpdO+mf+6TFmf6dGcggGLAVMZJsQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^4.0.5", - "@smithy/smithy-client": "^4.5.0", - "@smithy/types": "^4.3.2", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.0.27", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.27.tgz", - "integrity": "sha512-3W0qClMyxl/ELqTA39aNw1N+pN0IjpXT7lPFvZ8zTxqVFP7XCpACB9QufmN4FQtd39xbgS7/Lekn7LmDa63I5w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/config-resolver": "^4.1.5", - "@smithy/credential-provider-imds": "^4.0.7", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/property-provider": "^4.0.5", - "@smithy/smithy-client": "^4.5.0", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-endpoints": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.7.tgz", - "integrity": "sha512-klGBP+RpBp6V5JbrY2C/VKnHXn3d5V2YrifZbmMY8os7M6m8wdYFoO6w/fe5VkP+YVwrEktW3IWYaSQVNZJ8oQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.1.4", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-hex-encoding": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", - "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-middleware": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.5.tgz", - "integrity": "sha512-N40PfqsZHRSsByGB81HhSo+uvMxEHT+9e255S53pfBw/wI6WKDI7Jw9oyu5tJTLwZzV5DsMha3ji8jk9dsHmQQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-retry": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.7.tgz", - "integrity": "sha512-TTO6rt0ppK70alZpkjwy+3nQlTiqNfoXja+qwuAchIEAIoSZW8Qyd76dvBv3I5bCpE38APafG23Y/u270NspiQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/service-error-classification": "^4.0.7", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-stream": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.4.tgz", - "integrity": "sha512-vSKnvNZX2BXzl0U2RgCLOwWaAP9x/ddd/XobPK02pCbzRm5s55M53uwb1rl/Ts7RXZvdJZerPkA+en2FDghLuQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/types": "^4.3.2", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-hex-encoding": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-uri-escape": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", - "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-utf8": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", - "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-waiter": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.7.tgz", - "integrity": "sha512-mYqtQXPmrwvUljaHyGxYUIIRI3qjBTEb/f5QFi3A6VlxhpmZd5mWXn9W+qUkf2pVE1Hv3SqxefiZOPGdxmO64A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@types/uuid": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", - "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", - "license": "MIT" - }, - "node_modules/bowser": { - "version": "2.12.1", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.12.1.tgz", - "integrity": "sha512-z4rE2Gxh7tvshQ4hluIT7XcFrgLIQaw9X3A+kTTRdovCz5PMukm/0QC/BKSYPj3omF5Qfypn9O/c5kgpmvYUCw==", - "license": "MIT" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/dom-serializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", - "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.2", - "entities": "^4.2.0" - }, - "funding": { - "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" - } - }, - "node_modules/domelementtype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", - "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "BSD-2-Clause" - }, - "node_modules/domhandler": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", - "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", - "license": "BSD-2-Clause", - "dependencies": { - "domelementtype": "^2.3.0" - }, - "engines": { - "node": ">= 4" - }, - "funding": { - "url": "https://github.com/fb55/domhandler?sponsor=1" - } - }, - "node_modules/domutils": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", - "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", - "license": "BSD-2-Clause", - "dependencies": { - "dom-serializer": "^2.0.0", - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3" - }, - "funding": { - "url": "https://github.com/fb55/domutils?sponsor=1" - } - }, - "node_modules/encoding-japanese": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/encoding-japanese/-/encoding-japanese-2.2.0.tgz", - "integrity": "sha512-EuJWwlHPZ1LbADuKTClvHtwbaFn4rOD+dRAbWysqEOXRc2Uui0hJInNJrsdH0c+OhJA4nrCBdSkW4DD5YxAo6A==", - "license": "MIT", - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/entities": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", - "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/fast-xml-parser": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", - "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "strnum": "^2.1.0" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/he": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", - "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "license": "MIT", - "bin": { - "he": "bin/he" - } - }, - "node_modules/html-to-text": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz", - "integrity": "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==", - "license": "MIT", - "dependencies": { - "@selderee/plugin-htmlparser2": "^0.11.0", - "deepmerge": "^4.3.1", - "dom-serializer": "^2.0.0", - "htmlparser2": "^8.0.2", - "selderee": "^0.11.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/htmlparser2": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-8.0.2.tgz", - "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==", - "funding": [ - "https://github.com/fb55/htmlparser2?sponsor=1", - { - "type": "github", - "url": "https://github.com/sponsors/fb55" - } - ], - "license": "MIT", - "dependencies": { - "domelementtype": "^2.3.0", - "domhandler": "^5.0.3", - "domutils": "^3.0.1", - "entities": "^4.4.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/leac": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/leac/-/leac-0.6.0.tgz", - "integrity": "sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==", - "license": "MIT", - "funding": { - "url": "https://ko-fi.com/killymxi" - } - }, - "node_modules/libbase64": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/libbase64/-/libbase64-1.3.0.tgz", - "integrity": "sha512-GgOXd0Eo6phYgh0DJtjQ2tO8dc0IVINtZJeARPeiIJqge+HdsWSuaDTe8ztQ7j/cONByDZ3zeB325AHiv5O0dg==", - "license": "MIT" - }, - "node_modules/libmime": { - "version": "5.3.7", - "resolved": "https://registry.npmjs.org/libmime/-/libmime-5.3.7.tgz", - "integrity": "sha512-FlDb3Wtha8P01kTL3P9M+ZDNDWPKPmKHWaU/cG/lg5pfuAwdflVpZE+wm9m7pKmC5ww6s+zTxBKS1p6yl3KpSw==", - "license": "MIT", - "dependencies": { - "encoding-japanese": "2.2.0", - "iconv-lite": "0.6.3", - "libbase64": "1.3.0", - "libqp": "2.1.1" - } - }, - "node_modules/libqp": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/libqp/-/libqp-2.1.1.tgz", - "integrity": "sha512-0Wd+GPz1O134cP62YU2GTOPNA7Qgl09XwCqM5zpBv87ERCXdfDtyKXvV7c9U22yWJh44QZqBocFnXN11K96qow==", - "license": "MIT" - }, - "node_modules/linkify-it": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", - "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", - "license": "MIT", - "dependencies": { - "uc.micro": "^2.0.0" - } - }, - "node_modules/mailparser": { - "version": "3.7.4", - "resolved": "https://registry.npmjs.org/mailparser/-/mailparser-3.7.4.tgz", - "integrity": "sha512-Beh4yyR4jLq3CZZ32asajByrXnW8dLyKCAQD3WvtTiBnMtFWhxO+wa93F6sJNjDmfjxXs4NRNjw3XAGLqZR3Vg==", - "license": "MIT", - "dependencies": { - "encoding-japanese": "2.2.0", - "he": "1.2.0", - "html-to-text": "9.0.5", - "iconv-lite": "0.6.3", - "libmime": "5.3.7", - "linkify-it": "5.0.0", - "mailsplit": "5.4.5", - "nodemailer": "7.0.4", - "punycode.js": "2.3.1", - "tlds": "1.259.0" - } - }, - "node_modules/mailsplit": { - "version": "5.4.5", - "resolved": "https://registry.npmjs.org/mailsplit/-/mailsplit-5.4.5.tgz", - "integrity": "sha512-oMfhmvclR689IIaQmIcR5nODnZRRVwAKtqFT407TIvmhX2OLUBnshUTcxzQBt3+96sZVDud9NfSe1NxAkUNXEQ==", - "license": "(MIT OR EUPL-1.1+)", - "dependencies": { - "libbase64": "1.3.0", - "libmime": "5.3.7", - "libqp": "2.1.1" - } - }, - "node_modules/nodemailer": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.4.tgz", - "integrity": "sha512-9O00Vh89/Ld2EcVCqJ/etd7u20UhME0f/NToPfArwPEe1Don1zy4mAIz6ariRr7mJ2RDxtaDzN0WJVdVXPtZaw==", - "license": "MIT-0", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/parseley": { - "version": "0.12.1", - "resolved": "https://registry.npmjs.org/parseley/-/parseley-0.12.1.tgz", - "integrity": "sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==", - "license": "MIT", - "dependencies": { - "leac": "^0.6.0", - "peberminta": "^0.9.0" - }, - "funding": { - "url": "https://ko-fi.com/killymxi" - } - }, - "node_modules/peberminta": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/peberminta/-/peberminta-0.9.0.tgz", - "integrity": "sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==", - "license": "MIT", - "funding": { - "url": "https://ko-fi.com/killymxi" - } - }, - "node_modules/punycode.js": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", - "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "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/selderee": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz", - "integrity": "sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==", - "license": "MIT", - "dependencies": { - "parseley": "^0.12.0" - }, - "funding": { - "url": "https://ko-fi.com/killymxi" - } - }, - "node_modules/strnum": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", - "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, - "node_modules/tlds": { - "version": "1.259.0", - "resolved": "https://registry.npmjs.org/tlds/-/tlds-1.259.0.tgz", - "integrity": "sha512-AldGGlDP0PNgwppe2quAvuBl18UcjuNtOnDuUkqhd6ipPqrYYBt3aTxK1QTsBVknk97lS2JcafWMghjGWFtunw==", - "license": "MIT", - "bin": { - "tlds": "bin.js" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/uc.micro": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", - "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", - "license": "MIT" - }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - } - } -} diff --git a/infra/email/package.json b/infra/email/package.json deleted file mode 100644 index bc08916..0000000 --- a/infra/email/package.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "calculator-email-processor", - "version": "1.0.0", - "description": "Lambda function to process and forward emails for calculator.127local.net", - "main": "email_processor.js", - "dependencies": { - "@aws-sdk/client-ses": "^3.0.0", - "@aws-sdk/client-s3": "^3.0.0", - "@aws-sdk/s3-request-presigner": "^3.0.0", - "mailparser": "^3.6.5" - }, - "engines": { - "node": ">=18.0.0" - } -} diff --git a/infra/email/setup.sh b/infra/email/setup.sh deleted file mode 100644 index 1a0512a..0000000 --- a/infra/email/setup.sh +++ /dev/null @@ -1,58 +0,0 @@ -#!/bin/bash - -# Calculator Email Infrastructure Setup Script - -set -e - -echo "πŸš€ Setting up Calculator Email Infrastructure..." - -# Check if we're in the right directory -if [ ! -f "main.tf" ]; then - echo "❌ Error: main.tf not found. Please run this script from infra/email directory" - exit 1 -fi - -# Install npm dependencies for Lambda function -echo "πŸ“¦ Installing Lambda dependencies..." -if [ ! -d "node_modules" ]; then - npm install -else - echo "βœ… Dependencies already installed" -fi - -# Initialize Terraform if needed -if [ ! -d ".terraform" ]; then - echo "πŸ”§ Initializing Terraform..." - terraform init -else - echo "βœ… Terraform already initialized" -fi - -# Plan the infrastructure setup -echo "πŸ“‹ Planning infrastructure setup..." -terraform plan - -# Ask for confirmation -echo "" -read -p "πŸ€” Do you want to create this infrastructure? (y/N): " -n 1 -r -echo -if [[ $REPLY =~ ^[Yy]$ ]]; then - echo "πŸš€ Creating infrastructure..." - terraform apply -auto-approve - - echo "" - echo "βœ… Infrastructure setup complete!" - echo "" - echo "πŸ“‹ Next steps:" - echo "1. Go to AWS SES Console and verify the 127local.net domain identity" - echo "2. Wait for DKIM verification (should happen automatically)" - echo "3. Test by sending an email to calculator@127local.net" - echo "" - echo "πŸ” Useful commands:" - echo " terraform output # Show all outputs" - echo " terraform output dkim_tokens # Show DKIM tokens" - echo " terraform output lambda_function_name # Show Lambda function name" -else - echo "❌ Infrastructure setup cancelled" - exit 1 -fi diff --git a/infra/email/terraform.tfvars b/infra/email/terraform.tfvars deleted file mode 100644 index 0bedd93..0000000 --- a/infra/email/terraform.tfvars +++ /dev/null @@ -1,3 +0,0 @@ -aws_region = "us-west-2" -domain_name = "127local.net" -route53_zone_id = "Z001158010D1XENOLOOMC" diff --git a/infra/email/variables.tf b/infra/email/variables.tf deleted file mode 100644 index 8c90298..0000000 --- a/infra/email/variables.tf +++ /dev/null @@ -1,25 +0,0 @@ -variable "aws_region" { - description = "AWS region for resources" - type = string - default = "us-west-2" -} - -variable "domain_name" { - description = "Domain name for email forwarding" - type = string -} - -variable "forward_email" { - description = "Email address to forward incoming emails to" - type = string -} - -variable "route53_zone_id" { - description = "Route53 hosted zone ID for the domain" - type = string -} - -variable "aws_account_id" { - description = "AWS Account ID for SES S3 bucket policy" - type = string -} diff --git a/public/calculators/currency.js b/public/calculators/currency.js index 7f02f4a..6edcfd0 100644 --- a/public/calculators/currency.js +++ b/public/calculators/currency.js @@ -107,28 +107,10 @@ export default { cursor: pointer; transition: background 0.2s; `; - fetchBtn.addEventListener('mouseenter', () => fetchBtn.style.opacity = '0.9'); - fetchBtn.addEventListener('mouseleave', () => fetchBtn.style.opacity = '1'); + fetchBtn.addEventListener('mouseenter', () => fetchBtn.style.background = 'var(--accent-hover)'); + fetchBtn.addEventListener('mouseleave', () => fetchBtn.style.background = 'var(--accent)'); ui.append(fetchBtn); - // External API notice - const noticeDiv = document.createElement('div'); - noticeDiv.style.cssText = ` - margin: 10px 0; - padding: 12px; - background: var(--k-bg); - border: 1px solid var(--border); - border-radius: 6px; - font-size: 13px; - color: var(--muted); - line-height: 1.4; - `; - noticeDiv.innerHTML = ` - External API Notice: Updating exchange rates requires an external call to exchangerate-api.com. - Your browser will make a request to fetch the latest rates. Your IP address will be visible to the API provider. - `; - ui.append(noticeDiv); - const out = document.createElement('div'); out.className = 'result'; out.style.cssText = ` diff --git a/public/calculators/subnet.js b/public/calculators/subnet.js index ff07306..8986937 100644 --- a/public/calculators/subnet.js +++ b/public/calculators/subnet.js @@ -53,9 +53,9 @@ export default { - / + / `; @@ -97,10 +97,6 @@ export default { ipv6CidrLabel.style.cssText = 'display: block; margin-bottom: 8px; font-weight: 500; color: var(--text);'; ipv6CidrLabel.textContent = 'CIDR Prefix Length'; - const ipv6CidrSpan = document.createElement('span'); - ipv6CidrSpan.style.cssText = 'color: var(--muted); margin-right: 10px;'; - ipv6CidrSpan.textContent = '/'; - const ipv6CidrInput = document.createElement('input'); ipv6CidrInput.type = 'number'; ipv6CidrInput.name = 'ipv6Cidr'; @@ -109,9 +105,13 @@ export default { ipv6CidrInput.max = '128'; ipv6CidrInput.style.cssText = 'width: 200px; padding: 12px; border: 1px solid var(--border); border-radius: 8px; font-size: 16px;'; + const ipv6CidrSpan = document.createElement('span'); + ipv6CidrSpan.style.cssText = 'color: var(--muted); margin-left: 10px;'; + ipv6CidrSpan.textContent = '/'; + ipv6CidrContainer.appendChild(ipv6CidrLabel); - ipv6CidrContainer.appendChild(ipv6CidrSpan); ipv6CidrContainer.appendChild(ipv6CidrInput); + ipv6CidrContainer.appendChild(ipv6CidrSpan); // Add all elements to IPv6 section ipv6Section.appendChild(ipv6AddressContainer); @@ -142,328 +142,6 @@ export default { return 'Unknown'; } - function getRFCNetworkInfo(ip, cidr) { - const ipLong = ipToLong(ip); - const parts = ip.split('.'); - const firstOctet = parseInt(parts[0]); - const secondOctet = parseInt(parts[1]); - - // Check for specific RFC-defined ranges - if (firstOctet === 0) { - return { - type: 'Current Network', - description: 'RFC 1122: "This host on this network" - used only as source address', - rfc: 'RFC 1122', - cidr: '0.0.0.0/8' - }; - } - - if (firstOctet === 10) { - return { - type: 'Private Network', - description: 'RFC 1918: Private IP address range for Class A networks', - rfc: 'RFC 1918', - cidr: '10.0.0.0/8' - }; - } - - if (firstOctet === 127) { - return { - type: 'Loopback', - description: 'RFC 1122: Loopback addresses - packets sent to this address are processed locally', - rfc: 'RFC 1122', - cidr: '127.0.0.0/8' - }; - } - - if (firstOctet === 169 && secondOctet === 254) { - return { - type: 'Link-Local', - description: 'RFC 3927: Automatic Private IP Addressing (APIPA) - used when DHCP fails', - rfc: 'RFC 3927', - cidr: '169.254.0.0/16' - }; - } - - if (firstOctet === 172 && secondOctet >= 16 && secondOctet <= 31) { - return { - type: 'Private Network', - description: 'RFC 1918: Private IP address range for Class B networks', - rfc: 'RFC 1918', - cidr: '172.16.0.0/12' - }; - } - - if (firstOctet === 192 && secondOctet === 168) { - return { - type: 'Private Network', - description: 'RFC 1918: Private IP address range for Class C networks', - rfc: 'RFC 1918', - cidr: '192.168.0.0/16' - }; - } - - if (firstOctet === 192 && secondOctet === 0 && parseInt(parts[2]) === 0) { - return { - type: 'IETF Protocol Assignments', - description: 'RFC 5736: Reserved for IETF protocol assignments', - rfc: 'RFC 5736', - cidr: '192.0.0.0/24' - }; - } - - if (firstOctet === 192 && secondOctet === 0 && parseInt(parts[2]) === 2) { - return { - type: 'Test-Net', - description: 'RFC 5737: Documentation and example code (TEST-NET-1)', - rfc: 'RFC 5737', - cidr: '192.0.2.0/24' - }; - } - - if (firstOctet === 192 && secondOctet === 88 && parseInt(parts[2]) === 99) { - return { - type: '6to4 Relay', - description: 'RFC 3068: IPv6 to IPv4 relay anycast addresses', - rfc: 'RFC 3068', - cidr: '192.88.99.0/24' - }; - } - - if (firstOctet === 198 && secondOctet === 18) { - return { - type: 'Benchmark Testing', - description: 'RFC 2544: Network interconnect device benchmark testing', - rfc: 'RFC 2544', - cidr: '198.18.0.0/15' - }; - } - - if (firstOctet === 198 && secondOctet === 51 && parseInt(parts[2]) === 100) { - return { - type: 'Test-Net', - description: 'RFC 5737: Documentation and example code (TEST-NET-2)', - rfc: 'RFC 5737', - cidr: '198.51.100.0/24' - }; - } - - if (firstOctet === 203 && secondOctet === 0 && parseInt(parts[2]) === 113) { - return { - type: 'Test-Net', - description: 'RFC 5737: Documentation and example code (TEST-NET-3)', - rfc: 'RFC 5737', - cidr: '203.0.113.0/24' - }; - } - - if (firstOctet === 100 && secondOctet >= 64 && secondOctet <= 127) { - return { - type: 'CGNAT', - description: 'RFC 6598: Carrier-Grade NAT (CGN) shared address space', - rfc: 'RFC 6598', - cidr: '100.64.0.0/10' - }; - } - - if (firstOctet >= 224 && firstOctet <= 239) { - return { - type: 'Multicast', - description: 'RFC 1112: Multicast addresses - used for one-to-many communication', - rfc: 'RFC 1112', - cidr: '224.0.0.0/4' - }; - } - - if (firstOctet >= 240 && firstOctet <= 255) { - return { - type: 'Reserved', - description: 'RFC 1112: Reserved for future use (formerly Class E)', - rfc: 'RFC 1112', - cidr: '240.0.0.0/4' - }; - } - - if (ipLong === 0xFFFFFFFF) { - return { - type: 'Broadcast', - description: 'RFC 919: Limited broadcast address - reaches all hosts on the local network', - rfc: 'RFC 919' - }; - } - - // Check for other broadcast addresses based on CIDR - if (cidr < 32) { - const networkLong = (ipLong & ((0xFFFFFFFF << (32 - cidr)) >>> 0)) >>> 0; - const broadcastLong = (networkLong | (~((0xFFFFFFFF << (32 - cidr)) >>> 0)) >>> 0) >>> 0; - if (ipLong === broadcastLong) { - return { - type: 'Network Broadcast', - description: `Broadcast address for /${cidr} network - reaches all hosts in this subnet`, - rfc: 'RFC 919' - }; - } - } - - // Default case - public IP - return { - type: 'Public IP', - description: 'Globally routable IP address on the Internet', - rfc: null - }; - } - - function getIPv6NetworkInfo(ipv6, cidr) { - const expanded = expandIPv6(ipv6); - const parts = expanded.split(':'); - - // Convert to BigInt for comparison - let ipv6Long = 0n; - for (let i = 0; i < 8; i++) { - const part = parseInt(parts[i], 16); - ipv6Long = (ipv6Long << 16n) + BigInt(part); - } - - // Check for specific IPv6 reserved ranges - - // ::/128 - Unspecified address - if (ipv6Long === 0n) { - return { - type: 'Unspecified', - description: 'RFC 4291: Unspecified address - used only as source address', - rfc: 'RFC 4291', - cidr: '::/128' - }; - } - - // ::1/128 - Loopback - if (ipv6Long === 1n) { - return { - type: 'Loopback', - description: 'RFC 4291: Loopback address - equivalent to IPv4 127.0.0.1', - rfc: 'RFC 4291', - cidr: '::1/128' - }; - } - - // 2000::/3 - Global Unicast (public IPv6) - if ((ipv6Long >> 125n) === 4n) { // First 3 bits are 001 - return { - type: 'Global Unicast', - description: 'RFC 4291: Globally routable IPv6 addresses (public Internet)', - rfc: 'RFC 4291', - cidr: '2000::/3' - }; - } - - // fc00::/7 - Unique Local Address (ULA) - if ((ipv6Long >> 121n) === 0x7Dn) { // First 7 bits are 1111110 - return { - type: 'Unique Local Address', - description: 'RFC 4193: Private IPv6 addresses (equivalent to IPv4 private ranges)', - rfc: 'RFC 4193', - cidr: 'fc00::/7' - }; - } - - // fe80::/10 - Link-Local - if ((ipv6Long >> 118n) === 0xFE80n >> 6n) { // First 10 bits are 1111111010 - return { - type: 'Link-Local', - description: 'RFC 4291: Link-local addresses - valid only on the local network segment', - rfc: 'RFC 4291', - cidr: 'fe80::/10' - }; - } - - // ff00::/8 - Multicast - if ((ipv6Long >> 120n) === 0xFFn) { // First 8 bits are 11111111 - return { - type: 'Multicast', - description: 'RFC 4291: IPv6 multicast addresses - used for one-to-many communication', - rfc: 'RFC 4291', - cidr: 'ff00::/8' - }; - } - - // 2001:db8::/32 - Documentation - if ((ipv6Long >> 96n) === 0x20010DB8n) { - return { - type: 'Documentation', - description: 'RFC 3849: Reserved for documentation and example code', - rfc: 'RFC 3849', - cidr: '2001:db8::/32' - }; - } - - // 2001::/32 - Teredo - if ((ipv6Long >> 96n) === 0x20010000n) { - return { - type: 'Teredo', - description: 'RFC 4380: Teredo tunneling - IPv6 over UDP over IPv4', - rfc: 'RFC 4380', - cidr: '2001::/32' - }; - } - - // 2002::/16 - 6to4 - if ((ipv6Long >> 112n) === 0x2002n) { - return { - type: '6to4', - description: 'RFC 3056: 6to4 tunneling - automatic IPv6 over IPv4 tunneling', - rfc: 'RFC 3056', - cidr: '2002::/16' - }; - } - - // 64:ff9b::/96 - IPv4-IPv6 Translation - if ((ipv6Long >> 96n) === 0x64FF9B000000000000000000n) { - return { - type: 'IPv4-IPv6 Translation', - description: 'RFC 6052: Well-known prefix for IPv4-IPv6 translation', - rfc: 'RFC 6052', - cidr: '64:ff9b::/96' - }; - } - - // 100::/64 - Discard-Only - if ((ipv6Long >> 64n) === 0x100000000000000n) { - return { - type: 'Discard-Only', - description: 'RFC 6666: Discard-only address block - packets are discarded', - rfc: 'RFC 6666', - cidr: '100::/64' - }; - } - - // ::ffff:0:0/96 - IPv4-mapped IPv6 - if ((ipv6Long >> 96n) === 0xFFFF00000000n) { - return { - type: 'IPv4-mapped IPv6', - description: 'RFC 4291: IPv4 addresses mapped to IPv6 format', - rfc: 'RFC 4291', - cidr: '::ffff:0:0/96' - }; - } - - // ::/96 - IPv4-compatible IPv6 (deprecated) - if ((ipv6Long >> 96n) === 0n && (ipv6Long & 0xFFFFFFFF00000000n) === 0n) { - return { - type: 'IPv4-compatible IPv6', - description: 'RFC 4291: IPv4-compatible IPv6 addresses (deprecated)', - rfc: 'RFC 4291', - cidr: '::/96' - }; - } - - // Default case - other reserved or unknown - return { - type: 'Reserved/Unknown', - description: 'Reserved or unassigned IPv6 address range', - rfc: null - }; - } - function ipToLong(ip) { return ip.split('.').reduce((acc, octet) => (acc << 8) + parseInt(octet), 0) >>> 0; } @@ -697,52 +375,19 @@ export default { const networkSize = Math.pow(2, 32 - cidr); const baseLong = ipToLong(baseIP); - // Calculate the base network for the IP based on the CIDR - // For larger subnets (CIDR < 16), we need to find the appropriate base network - let baseNetworkLong; - let maxNetworks; + // Calculate the base network for the IP (up to /16 level) + // For example: 192.168.1.0 -> 192.168.0.0 (base /16 network) + const baseNetworkLong = baseLong & ((0xFFFFFFFF << (32 - 16)) >>> 0); + const networkLong = baseNetworkLong; - if (cidr <= 8) { - // For /8 and larger, use /8 as the base (Class A) - baseNetworkLong = baseLong & ((0xFFFFFFFF << (32 - 8)) >>> 0); - maxNetworks = Math.min(64, Math.floor(16777216 / networkSize)); // 2^24 / networkSize - } else if (cidr <= 16) { - // For /9 to /16, use /16 as the base (Class B) - baseNetworkLong = baseLong & ((0xFFFFFFFF << (32 - 16)) >>> 0); - maxNetworks = Math.min(64, Math.floor(65536 / networkSize)); // 2^16 / networkSize - } else { - // For /17 and smaller, use the actual network as base - baseNetworkLong = baseLong & ((0xFFFFFFFF << (32 - cidr)) >>> 0); - maxNetworks = Math.min(64, Math.floor(65536 / networkSize)); - } - - // Show up to 64 networks, but limit based on what makes sense - const count = Math.min(maxNetworks, 64); + // Show up to 64 networks + const count = Math.min(64, Math.floor(65536 / networkSize)); for (let i = 0; i < count; i++) { - const networkAddr = baseNetworkLong + (i * networkSize); + const networkAddr = networkLong + (i * networkSize); const broadcastAddr = networkAddr + networkSize - 1; - - // Handle edge cases for host calculations - let firstHost, lastHost; - - if (cidr === 32) { - // /32 - single host, no usable hosts - firstHost = networkAddr; - lastHost = networkAddr; - } else if (cidr === 31) { - // /31 - point-to-point, no usable hosts - firstHost = networkAddr; - lastHost = broadcastAddr; - } else if (cidr === 30) { - // /30 - 4 total hosts, 2 usable - firstHost = networkAddr + 1; - lastHost = broadcastAddr - 1; - } else { - // Normal case - calculate usable hosts - firstHost = networkAddr + 1; - lastHost = broadcastAddr - 1; - } + const firstHost = networkAddr + 1; + const lastHost = broadcastAddr - 1; networks.push({ network: longToIp(networkAddr), @@ -807,27 +452,13 @@ export default { lastHostLong = broadcastLong - 1; } - // Calculate total possible networks based on CIDR + // Calculate total possible networks const networkSize = Math.pow(2, 32 - calculatedCidr); - let totalPossibleNetworks; - - if (calculatedCidr <= 8) { - // For /8 and larger, calculate based on Class A space - totalPossibleNetworks = Math.floor(16777216 / networkSize); // 2^24 / networkSize - } else if (calculatedCidr <= 16) { - // For /9 to /16, calculate based on Class B space - totalPossibleNetworks = Math.floor(65536 / networkSize); // 2^16 / networkSize - } else { - // For /17 and smaller, use the standard calculation - totalPossibleNetworks = Math.floor(65536 / networkSize); - } + const totalPossibleNetworks = Math.floor(65536 / networkSize); // Generate available networks table const availableNetworks = generateAvailableNetworks(ipAddress, calculatedCidr); - // Get RFC network information - const rfcInfo = getRFCNetworkInfo(ipAddress, calculatedCidr); - out.innerHTML = `
IPv4 Subnet Information @@ -874,22 +505,6 @@ export default {
-
-

Network Type Information

-
- Network Type: ${rfcInfo.type} -
-
- Description: ${rfcInfo.description} -
- ${rfcInfo.cidr ? `
- RFC Range: ${rfcInfo.cidr} -
` : ''} - ${rfcInfo.rfc ? `
- RFC Reference: ${rfcInfo.rfc} -
` : ''} -
-

Binary Representation

@@ -1002,9 +617,6 @@ export default { }); } - // Get IPv6 network information - const ipv6NetworkInfo = getIPv6NetworkInfo(ipv6Address, ipv6Cidr); - // Format large numbers for display function formatBigInt(num) { if (num < BigInt(1e6)) { @@ -1076,22 +688,6 @@ export default {
-
-

Network Type Information

-
- Network Type: ${ipv6NetworkInfo.type} -
-
- Description: ${ipv6NetworkInfo.description} -
- ${ipv6NetworkInfo.cidr ? `
- RFC Range: ${ipv6NetworkInfo.cidr} -
` : ''} - ${ipv6NetworkInfo.rfc ? `
- RFC Reference: ${ipv6NetworkInfo.rfc} -
` : ''} -
-

Available Networks

diff --git a/public/css/styles.css b/public/css/styles.css index aca385d..77f57af 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -123,20 +123,16 @@ html,body{margin:0;background:var(--bg);color:var(--text);font:16px/1.5 system-u display: block; } - /* Mobile overlay - separate element */ - .mobile-nav-overlay { + /* Add mobile overlay */ + .sidenav::before { + content: ''; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0, 0, 0, 0.5); - z-index: 99; - display: none; - } - - .mobile-nav-overlay.active { - display: block; + z-index: -1; } /* Adjust main content spacing for mobile */ @@ -280,21 +276,14 @@ input,select,textarea{width:100%;background:transparent;color:var(--text);border align-items: center; } -.footer-links { - display: flex; - gap: 16px; - align-items: center; -} - -.contact-link, .source-link { color: var(--accent); text-decoration: none; font-weight: 500; transition: color 0.2s ease; + margin-left: auto; } -.contact-link:hover, .source-link:hover { color: var(--accent2); text-decoration: underline; diff --git a/public/index.html b/public/index.html index bc8ff1a..b21196e 100644 --- a/public/index.html +++ b/public/index.html @@ -29,17 +29,11 @@
- - -
diff --git a/public/js/app.js b/public/js/app.js index e27223a..b009f43 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -1,50 +1,30 @@ import {el, initTheme, enhanceSelects} from './util.js'; const CALCS = [ - // Financial calculators { id:'interest', name:'Interest (Simple & Compound)', about:'Simple/compound interest', path:'../calculators/interest.js' }, - { id:'currency', name:'Currency Converter', about:'Convert between currencies', path:'../calculators/currency.js' }, - - // Storage/System Administration { id:'raid', name:'RAID', about:'Usable capacity', path:'../calculators/raid.js' }, - { id:'zfs', name:'ZFS', about:'Pool configuration & performance', path:'../calculators/zfs.js' }, - - // Network/System Administration { id:'bandwidth', name:'Bandwidth', about:'Bits↔bytes unit conv.', path:'../calculators/bandwidth.js' }, - { id:'subnet', name:'IP Subnet', about:'IPv4/IPv6 subnet calculations', path:'../calculators/subnet.js' }, - - // Specialized Tools - { id:'nmea', name:'NMEA', about:'0183 XOR checksum', path:'../calculators/nmea.js' } + { id:'nmea', name:'NMEA', about:'0183 XOR checksum', path:'../calculators/nmea.js' }, + { id:'currency', name:'Currency Converter', about:'Convert between currencies', path:'../calculators/currency.js' }, + { id:'zfs', name:'ZFS', about:'Pool configuration & performance', path:'../calculators/zfs.js' }, + { id:'subnet', name:'IP Subnet', about:'IPv4/IPv6 subnet calculations', path:'../calculators/subnet.js' } ]; const navEl = document.getElementById('nav'); const viewEl = document.getElementById('view'); const themeBtn= document.getElementById('themeToggle'); const navToggleBtn = document.getElementById('navToggle'); -const mobileNavOverlay = document.getElementById('mobileNavOverlay'); initTheme(themeBtn); // Mobile navigation toggle navToggleBtn.addEventListener('click', () => { navEl.classList.toggle('mobile-active'); - mobileNavOverlay.classList.toggle('active'); -}); - -// Close mobile nav when clicking overlay -mobileNavOverlay.addEventListener('click', () => { - navEl.classList.remove('mobile-active'); - mobileNavOverlay.classList.remove('active'); }); // Close mobile nav when clicking outside document.addEventListener('click', (e) => { - // Only close if nav is currently open - if (navEl.classList.contains('mobile-active')) { - // Close if clicking outside both nav and toggle button - if (!navEl.contains(e.target) && !navToggleBtn.contains(e.target)) { - navEl.classList.remove('mobile-active'); - mobileNavOverlay.classList.remove('active'); - } + if (!navEl.contains(e.target) && !navToggleBtn.contains(e.target)) { + navEl.classList.remove('mobile-active'); } }); @@ -53,7 +33,6 @@ navEl.addEventListener('click', (e) => { const a = e.target.closest('a[data-calc]'); if (a) { navEl.classList.remove('mobile-active'); - mobileNavOverlay.classList.remove('active'); } }); diff --git a/tests/test_subnet.py b/tests/test_subnet.py index b365f25..759420b 100644 --- a/tests/test_subnet.py +++ b/tests/test_subnet.py @@ -759,50 +759,6 @@ class TestSubnetCalculator: # Verify network class is correct assert f"Network Class: {expected_class}" in result_text, f"Failed for {ip_addr}: expected {expected_class}" - def test_subnet_ipv4_network_class_cidr_independence(self, calculator_page): - """Test that network class is determined by IP address only, not CIDR""" - calculator_page.get("http://localhost:8008/subnet") - - # Wait for calculator to load - WebDriverWait(calculator_page, 10).until( - EC.presence_of_element_located((By.CSS_SELECTOR, "input[name='ipAddress']")) - ) - - ip_input = calculator_page.find_element(By.CSS_SELECTOR, "input[name='ipAddress']") - cidr_input = calculator_page.find_element(By.CSS_SELECTOR, "input[name='cidr']") - - # Test that network class remains the same regardless of CIDR - test_cases = [ - ("10.0.0.1", "Class A", [8, 16, 24, 30, 32]), # Class A IP with different CIDRs - ("172.16.0.1", "Class B", [8, 16, 24, 30, 32]), # Class B IP with different CIDRs - ("192.168.1.1", "Class C", [8, 16, 24, 30, 32]), # Class C IP with different CIDRs - ("224.0.0.1", "Class D", [8, 16, 24, 30, 32]), # Class D IP with different CIDRs - ("240.0.0.1", "Class E", [8, 16, 24, 30, 32]), # Class E IP with different CIDRs - ] - - for ip_addr, expected_class, cidr_values in test_cases: - # Set the IP address once - ip_input.clear() - ip_input.send_keys(ip_addr) - - # Test with different CIDR values - for cidr in cidr_values: - cidr_input.clear() - cidr_input.send_keys(str(cidr)) - - # Wait for results to update - WebDriverWait(calculator_page, 10).until( - lambda driver: f"Network Class: {expected_class}" in self._get_subnet_result(driver) - ) - - result_text = self._get_subnet_result(calculator_page) - - # Verify network class remains the same regardless of CIDR - assert f"Network Class: {expected_class}" in result_text, f"Failed for {ip_addr} with CIDR /{cidr}: expected {expected_class}" - - # Also verify that the CIDR is correctly applied (different from network class) - assert f"CIDR Notation: /{cidr}" in result_text, f"CIDR /{cidr} not applied correctly for {ip_addr}" - def test_subnet_cidr_mask_conversion_edge_cases(self, calculator_page): """Test CIDR to mask conversion for all edge cases""" calculator_page.get("http://localhost:8008/subnet") @@ -855,141 +811,6 @@ class TestSubnetCalculator: actual_cidr = cidr_input.get_attribute("value") assert actual_cidr == str(cidr), f"Mask {expected_mask} should map to /{cidr}, got /{actual_cidr}" - def test_subnet_large_cidr_networks_table(self, calculator_page): - """Test that subnet calculator displays networks table for large CIDR values like /10, /8""" - calculator_page.get("http://localhost:8008/subnet") - - # Wait for calculator to load - WebDriverWait(calculator_page, 10).until( - EC.presence_of_element_located((By.CSS_SELECTOR, "input[name='ipAddress']")) - ) - - ip_input = calculator_page.find_element(By.CSS_SELECTOR, "input[name='ipAddress']") - cidr_input = calculator_page.find_element(By.CSS_SELECTOR, "input[name='cidr']") - - # Test with /10 (large subnet) - ip_input.clear() - ip_input.send_keys("10.0.0.1") - cidr_input.clear() - cidr_input.send_keys("10") - - # Wait for results - WebDriverWait(calculator_page, 10).until( - EC.presence_of_element_located((By.CLASS_NAME, "result")) - ) - - result_text = self._get_subnet_result(calculator_page) - - # Verify that the networks table is displayed - assert "Available Networks" in result_text, "Available Networks table should be displayed for /10" - assert "Network" in result_text, "Network column should be present" - assert "First Host" in result_text, "First Host column should be present" - assert "Last Host" in result_text, "Last Host column should be present" - assert "Broadcast" in result_text, "Broadcast column should be present" - - # Verify that we get multiple networks (should be many for /10) - assert "Showing" in result_text, "Should show count of networks" - assert "of" in result_text, "Should show total possible networks" - - # Test with /8 (even larger subnet) - cidr_input.clear() - cidr_input.send_keys("8") - - # Wait for results to update - WebDriverWait(calculator_page, 10).until( - lambda driver: "Available Networks" in self._get_subnet_result(driver) - ) - - result_text = self._get_subnet_result(calculator_page) - - # Verify that the networks table is still displayed for /8 - assert "Available Networks" in result_text, "Available Networks table should be displayed for /8" - assert "Network" in result_text, "Network column should be present for /8" - - # Test with /6 (very large subnet) - cidr_input.clear() - cidr_input.send_keys("6") - - # Wait for results to update - WebDriverWait(calculator_page, 10).until( - lambda driver: "Available Networks" in self._get_subnet_result(driver) - ) - - result_text = self._get_subnet_result(calculator_page) - - # Verify that the networks table is still displayed for /6 - assert "Available Networks" in result_text, "Available Networks table should be displayed for /6" - assert "Network" in result_text, "Network column should be present for /6" - - def test_subnet_rfc_network_detection(self, calculator_page): - """Test RFC network type detection and display""" - calculator_page.get("http://localhost:8008/subnet") - - # Wait for calculator to load - WebDriverWait(calculator_page, 10).until( - EC.presence_of_element_located((By.CSS_SELECTOR, "input[name='ipAddress']")) - ) - - ip_input = calculator_page.find_element(By.CSS_SELECTOR, "input[name='ipAddress']") - cidr_input = calculator_page.find_element(By.CSS_SELECTOR, "input[name='cidr']") - - # Test cases for different RFC network types - test_cases = [ - ("10.0.0.1", "Private Network", "RFC 1918", "10.0.0.0/8"), - ("192.168.1.1", "Private Network", "RFC 1918", "192.168.0.0/16"), - ("172.16.0.1", "Private Network", "RFC 1918", "172.16.0.0/12"), - ("127.0.0.1", "Loopback", "RFC 1122", "127.0.0.0/8"), - ("169.254.1.1", "Link-Local", "RFC 3927", "169.254.0.0/16"), - ("100.64.1.1", "CGNAT", "RFC 6598", "100.64.0.0/10"), - ("192.0.2.1", "Test-Net", "RFC 5737", "192.0.2.0/24"), - ("224.0.0.1", "Multicast", "RFC 1112", "224.0.0.0/4"), - ("8.8.8.8", "Public IP", None, None), # Google DNS - ] - - for ip_addr, expected_type, expected_rfc, expected_cidr in test_cases: - # Set the IP address - ip_input.clear() - ip_input.send_keys(ip_addr) - cidr_input.clear() - cidr_input.send_keys("24") - - # Wait for results - WebDriverWait(calculator_page, 10).until( - EC.presence_of_element_located((By.CLASS_NAME, "result")) - ) - - result_text = self._get_subnet_result(calculator_page) - - # Verify Network Type Information section is displayed - assert "Network Type Information" in result_text, f"Network Type Information section should be displayed for {ip_addr}" - assert "Network Type:" in result_text, f"Network Type should be displayed for {ip_addr}" - assert "Description:" in result_text, f"Description should be displayed for {ip_addr}" - - # Verify the network type is correct - assert f"Network Type: {expected_type}" in result_text, f"Expected {expected_type} for {ip_addr}, got: {result_text}" - - # Verify RFC reference if expected - if expected_rfc: - assert f"RFC Reference: {expected_rfc}" in result_text, f"Expected RFC {expected_rfc} for {ip_addr}" - else: - # For public IPs, RFC reference should not be shown - assert "RFC Reference:" not in result_text, f"RFC Reference should not be shown for public IP {ip_addr}" - - # Verify RFC range (CIDR notation) if expected - if expected_cidr: - assert f"RFC Range: {expected_cidr}" in result_text, f"Expected RFC Range {expected_cidr} for {ip_addr}" - else: - # For public IPs, RFC range should not be shown - assert "RFC Range:" not in result_text, f"RFC Range should not be shown for public IP {ip_addr}" - - # Note: IPv6 RFC network detection test is commented out due to test environment issues - # with IPv6 mode switching. The functionality works in the actual application. - # def test_subnet_ipv6_rfc_network_detection(self, calculator_page): - # """Test IPv6 RFC network type detection and display""" - # # This test would verify IPv6 network type detection but is disabled - # # due to test environment issues with IPv6 mode switching - # pass - def _get_subnet_result(self, driver): """Helper method to get subnet calculation result text""" result_element = driver.find_element(By.CLASS_NAME, "result")