From 71bc0edc50dbd0c99a1cd05651f8861c0a8f7e28 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 19:11:37 -0500 Subject: [PATCH 01/25] Setup multiregion s3 for assets bucket --- terraform/modules/assets/main.tf | 45 +++++++++++++++++++++++++++ terraform/modules/assets/variables.tf | 14 +++++++++ 2 files changed, 59 insertions(+) create mode 100644 terraform/modules/assets/main.tf create mode 100644 terraform/modules/assets/variables.tf diff --git a/terraform/modules/assets/main.tf b/terraform/modules/assets/main.tf new file mode 100644 index 00000000..a5a3e610 --- /dev/null +++ b/terraform/modules/assets/main.tf @@ -0,0 +1,45 @@ +data "aws_caller_identity" "current" {} +data "aws_region" "current" {} + +locals { + asset_bucket_prefix = "${data.aws_caller_identity.current.account_id}-${var.ProjectId}" +} + +module "buckets" { + source = "git::https://github.com/acm-uiuc/terraform-modules.git//multiregion-s3?ref=99de4c350d1e35931f94499e0c06cbf29d0d5b8a" + Region1 = var.PrimaryRegion + Region2 = var.SecondaryRegion + BucketPrefix = local.asset_bucket_prefix +} + +resource "aws_s3_bucket_lifecycle_configuration" "expire_noncurrent" { + for_each = module.buckets.buckets_info + bucket = each.value.id + + rule { + id = "expire-noncurrent-versions" + status = "Enabled" + + noncurrent_version_expiration { + noncurrent_days = 3 + } + } + + rule { + id = "expire-delete-markers" + status = "Enabled" + + expiration { + expired_object_delete_marker = true + } + } + + rule { + id = "abort-incomplete-multipart" + status = "Enabled" + + abort_incomplete_multipart_upload { + days_after_initiation = 3 + } + } +} diff --git a/terraform/modules/assets/variables.tf b/terraform/modules/assets/variables.tf new file mode 100644 index 00000000..d7a93e1f --- /dev/null +++ b/terraform/modules/assets/variables.tf @@ -0,0 +1,14 @@ +variable "PrimaryRegion" { + type = string + default = "us-east-2" +} + +variable "SecondaryRegion" { + type = string + default = "us-west-2" +} + +variable "ProjectId" { + type = string + description = "Prefix before each resource" +} From 0af7c04e6d8819160bdbf90506556e5db0a886c2 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 19:20:49 -0500 Subject: [PATCH 02/25] Deploy assets bucket --- terraform/envs/prod/main.tf | 5 +++++ terraform/envs/qa/main.tf | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/terraform/envs/prod/main.tf b/terraform/envs/prod/main.tf index eb0b76ae..4d3402e6 100644 --- a/terraform/envs/prod/main.tf +++ b/terraform/envs/prod/main.tf @@ -127,6 +127,11 @@ module "frontend" { LinkryEdgeFunctionArn = module.lambdas.linkry_redirect_function_arn } +module "assets" { + source = "../../modules/assets" + ProjectId = var.ProjectId +} + resource "aws_lambda_event_source_mapping" "queue_consumer" { region = "us-east-2" depends_on = [module.lambdas, module.sqs_queues] diff --git a/terraform/envs/qa/main.tf b/terraform/envs/qa/main.tf index c9653ebe..f9ad43cc 100644 --- a/terraform/envs/qa/main.tf +++ b/terraform/envs/qa/main.tf @@ -130,6 +130,11 @@ module "frontend" { LinkryEdgeFunctionArn = module.lambdas.linkry_redirect_function_arn } +module "assets" { + source = "../../modules/assets" + ProjectId = var.ProjectId +} + // Multi-Region Failover: US-West-2 module "lambdas_usw2" { From dee9d82d19b81ac5dd9d608b8af2981bb7d9c62b Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 19:24:04 -0500 Subject: [PATCH 03/25] New --- src/api/routes/roomRequests.ts | 19 +++++++++++++++---- src/common/types/roomRequest.ts | 3 ++- terraform/modules/assets/main.tf | 2 +- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/api/routes/roomRequests.ts b/src/api/routes/roomRequests.ts index d2d29e79..79f3c762 100644 --- a/src/api/routes/roomRequests.ts +++ b/src/api/routes/roomRequests.ts @@ -2,12 +2,8 @@ import { FastifyPluginAsync } from "fastify"; import rateLimiter from "api/plugins/rateLimiter.js"; import { formatStatus, - roomGetResponse, - RoomRequestFormValues, - roomRequestPostResponse, roomRequestSchema, RoomRequestStatus, - RoomRequestStatusUpdatePostBody, roomRequestStatusUpdateRequest, } from "common/types/roomRequest.js"; import { AppRoles } from "common/roles.js"; @@ -59,6 +55,18 @@ const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => { semesterId, }), body: roomRequestStatusUpdateRequest, + response: { + 201: { + description: "The room request status was updated.", + content: { + "application/json": { + schema: z.object({ + uploadUrl: z.optional(z.url()), + }), + }, + }, + }, + }, }), ), onRequest: fastify.authorizeFromSchema, @@ -177,6 +185,9 @@ const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => { request.log.info( `Queued room reservation email to SQS with message ID ${result.MessageId}`, ); + if (request.body.attachmentFilename) { + request.log.info("Creating presigned URL to store file to"); + } return reply.status(201).send(); }, ); diff --git a/src/common/types/roomRequest.ts b/src/common/types/roomRequest.ts index 1334a021..f8e939ca 100644 --- a/src/common/types/roomRequest.ts +++ b/src/common/types/roomRequest.ts @@ -131,7 +131,8 @@ export enum RoomRequestStatus { } export const roomRequestStatusUpdateRequest = z.object({ - status: z.nativeEnum(RoomRequestStatus), + status: z.enum(RoomRequestStatus), + attachmentFilename: z.optional(z.string().max(100)), notes: z.string().min(1).max(1000) }); diff --git a/terraform/modules/assets/main.tf b/terraform/modules/assets/main.tf index a5a3e610..39fc8fed 100644 --- a/terraform/modules/assets/main.tf +++ b/terraform/modules/assets/main.tf @@ -6,7 +6,7 @@ locals { } module "buckets" { - source = "git::https://github.com/acm-uiuc/terraform-modules.git//multiregion-s3?ref=99de4c350d1e35931f94499e0c06cbf29d0d5b8a" + source = "git::https://github.com/acm-uiuc/terraform-modules.git//multiregion-s3?ref=976e9eb8f8a29746b02d9e85d78d2952e3548bf9" Region1 = var.PrimaryRegion Region2 = var.SecondaryRegion BucketPrefix = local.asset_bucket_prefix From 69f86811d052b6b3191b476cd658a5a7d16a9e72 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 19:27:17 -0500 Subject: [PATCH 04/25] Fix bucket name --- terraform/modules/assets/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/modules/assets/main.tf b/terraform/modules/assets/main.tf index 39fc8fed..409f4967 100644 --- a/terraform/modules/assets/main.tf +++ b/terraform/modules/assets/main.tf @@ -2,7 +2,7 @@ data "aws_caller_identity" "current" {} data "aws_region" "current" {} locals { - asset_bucket_prefix = "${data.aws_caller_identity.current.account_id}-${var.ProjectId}" + asset_bucket_prefix = "${data.aws_caller_identity.current.account_id}-${var.ProjectId}-assets" } module "buckets" { From ad951edcde42efd0dd85966b048c863d75403411 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 19:34:54 -0500 Subject: [PATCH 05/25] Retain data for longer --- src/common/constants.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/common/constants.ts b/src/common/constants.ts index 2eb2ed23..96648860 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -1,6 +1,6 @@ export const STRIPE_LINK_RETENTION_DAYS = 90; // this number of days after the link is deactivated. export const AUDIT_LOG_RETENTION_DAYS = 365; -export const ROOM_RESERVATION_RETENTION_DAYS = 730; -export const FULFILLED_PURCHASES_RETENTION_DAYS = 730; // ticketing/merch: after the purchase is marked as fulfilled. +export const ROOM_RESERVATION_RETENTION_DAYS = 1460; +export const FULFILLED_PURCHASES_RETENTION_DAYS = 1460; // ticketing/merch: after the purchase is marked as fulfilled. export const EVENTS_EXPIRY_AFTER_LAST_OCCURRENCE_DAYS = 1460; // hold events for 4 years after last occurrence // we keep data longer for historical analytics purposes From d15139ab38534d31ce1a158a18d58cc3bb2b44b7 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 19:35:14 -0500 Subject: [PATCH 06/25] Clarify comment --- src/common/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/constants.ts b/src/common/constants.ts index 96648860..32bc6978 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -3,4 +3,4 @@ export const AUDIT_LOG_RETENTION_DAYS = 365; export const ROOM_RESERVATION_RETENTION_DAYS = 1460; export const FULFILLED_PURCHASES_RETENTION_DAYS = 1460; // ticketing/merch: after the purchase is marked as fulfilled. export const EVENTS_EXPIRY_AFTER_LAST_OCCURRENCE_DAYS = 1460; // hold events for 4 years after last occurrence -// we keep data longer for historical analytics purposes +// we keep data longer for historical analytics purposes in S3 as needed From 70c92ef440eb0223c548b8c2f4f95096e8e40142 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 19:36:11 -0500 Subject: [PATCH 07/25] Enable auto intelligent tiering --- terraform/modules/assets/main.tf | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/terraform/modules/assets/main.tf b/terraform/modules/assets/main.tf index 409f4967..4e03ba02 100644 --- a/terraform/modules/assets/main.tf +++ b/terraform/modules/assets/main.tf @@ -43,3 +43,14 @@ resource "aws_s3_bucket_lifecycle_configuration" "expire_noncurrent" { } } } + +resource "aws_s3_bucket_intelligent_tiering_configuration" "tiering" { + for_each = module.buckets.buckets_info + bucket = each.value.id + name = "EntireBucketIntelligentTiering" + + tiering { + access_tier = "ARCHIVE_ACCESS" + days = 1 + } +} From b5e39c04fefd32d071d9db3d46c97fabf8d57bcd Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 19:41:41 -0500 Subject: [PATCH 08/25] Fix terraform deploy --- src/common/config.ts | 7 +++++-- terraform/modules/assets/main.tf | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/common/config.ts b/src/common/config.ts index 5ffb0251..bfc191b6 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -33,6 +33,7 @@ export type ConfigType = { OrgAdminGithubParentTeam: number; GithubIdpSyncEnabled: boolean GithubOrgId: number; + AssetsBucketId: string; }; export type GenericConfigType = { @@ -142,7 +143,8 @@ const environmentConfig: EnvironmentConfigType = { GithubOrgName: "acm-uiuc-testing", GithubOrgId: 235748315, OrgAdminGithubParentTeam: 14420860, - GithubIdpSyncEnabled: false + GithubIdpSyncEnabled: false, + AssetsBucketId: `427040638965-infra-core-api-assets-${genericConfig.AwsRegion}` }, prod: { UserFacingUrl: "https://core.acm.illinois.edu", @@ -174,7 +176,8 @@ const environmentConfig: EnvironmentConfigType = { GithubOrgName: "acm-uiuc", GithubOrgId: 425738, OrgAdminGithubParentTeam: 12025214, - GithubIdpSyncEnabled: true + GithubIdpSyncEnabled: true, + AssetsBucketId: `298118738376-infra-core-api-assets-${genericConfig.AwsRegion}` }, }; diff --git a/terraform/modules/assets/main.tf b/terraform/modules/assets/main.tf index 4e03ba02..32c70a8a 100644 --- a/terraform/modules/assets/main.tf +++ b/terraform/modules/assets/main.tf @@ -14,6 +14,7 @@ module "buckets" { resource "aws_s3_bucket_lifecycle_configuration" "expire_noncurrent" { for_each = module.buckets.buckets_info + region = each.key bucket = each.value.id rule { @@ -47,6 +48,7 @@ resource "aws_s3_bucket_lifecycle_configuration" "expire_noncurrent" { resource "aws_s3_bucket_intelligent_tiering_configuration" "tiering" { for_each = module.buckets.buckets_info bucket = each.value.id + region = each.key name = "EntireBucketIntelligentTiering" tiering { From 2966bcf574d532d161e12f96eb578b2fa0b2ea61 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 20:02:54 -0500 Subject: [PATCH 09/25] Setup s3 presigned URL uploads --- src/api/functions/s3.ts | 41 ++ src/api/package.json | 2 + src/api/routes/roomRequests.ts | 33 +- src/api/types.d.ts | 2 + src/common/types/roomRequest.ts | 11 +- yarn.lock | 936 +++++++++++++++++++++++++++++++- 6 files changed, 1019 insertions(+), 6 deletions(-) create mode 100644 src/api/functions/s3.ts diff --git a/src/api/functions/s3.ts b/src/api/functions/s3.ts new file mode 100644 index 00000000..99dff8e9 --- /dev/null +++ b/src/api/functions/s3.ts @@ -0,0 +1,41 @@ +import { PutObjectCommand, type S3Client } from "@aws-sdk/client-s3"; +import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; +import { InternalServerError } from "common/errors/index.js"; + +export type CreatePresignedPutInputs = { + s3client: S3Client; + bucketName: string; + key: string; + length: number; + mimeType: string; + md5hash: string; // Must be a base64-encoded MD5 hash + urlExpiresIn?: number; +}; + +export async function createPresignedPut({ + s3client, + bucketName, + key, + length, + mimeType, + md5hash, + urlExpiresIn, +}: CreatePresignedPutInputs) { + const command = new PutObjectCommand({ + Bucket: bucketName, + Key: key, + ContentLength: length, + ContentType: mimeType, + ContentMD5: md5hash, + }); + + const expiresIn = urlExpiresIn || 900; + + try { + return await getSignedUrl(s3client, command, { expiresIn }); + } catch (err) { + throw new InternalServerError({ + message: "Could not create S3 upload presigned url.", + }); + } +} diff --git a/src/api/package.json b/src/api/package.json index 384e51c5..5f9fb36b 100644 --- a/src/api/package.json +++ b/src/api/package.json @@ -15,6 +15,8 @@ "prettier:write": "prettier --write *.ts **/*.ts" }, "dependencies": { + "@aws-sdk/s3-request-presigner": "^3.914.0", + "@aws-sdk/client-s3": "^3.914.0", "@aws-sdk/client-dynamodb": "^3.914.0", "@aws-sdk/client-lambda": "^3.914.0", "@aws-sdk/client-secrets-manager": "^3.914.0", diff --git a/src/api/routes/roomRequests.ts b/src/api/routes/roomRequests.ts index 79f3c762..26e3233f 100644 --- a/src/api/routes/roomRequests.ts +++ b/src/api/routes/roomRequests.ts @@ -33,6 +33,8 @@ import { nonEmptyCommaSeparatedStringSchema, } from "common/utils.js"; import { ROOM_RESERVATION_RETENTION_DAYS } from "common/constants.js"; +import { createPresignedPut } from "api/functions/s3.js"; +import { S3Client } from "@aws-sdk/client-s3"; const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => { await fastify.register(rateLimiter, { @@ -79,6 +81,9 @@ const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => { } const requestId = request.params.requestId; const semesterId = request.params.semesterId; + const attachmentS3key = request.body.attachmentInfo + ? `roomRequests/${requestId}/${request.body.status}/${request.body.attachmentInfo.filename}` + : undefined; const getReservationData = new QueryCommand({ TableName: genericConfig.RoomRequestsStatusTableName, KeyConditionExpression: "requestId = :requestId", @@ -91,6 +96,28 @@ const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => { ":requestId": { S: requestId }, }, }); + let uploadUrl: string | undefined = undefined; + if (request.body.attachmentInfo) { + const { md5hash, fileSizeBytes, contentType } = + request.body.attachmentInfo; + request.log.info( + request.body.attachmentInfo, + `Creating presigned URL to store attachment`, + ); + if (!fastify.s3Client) { + fastify.s3Client = new S3Client({ + region: genericConfig.AwsRegion, + }); + } + uploadUrl = await createPresignedPut({ + s3client: fastify.s3Client, + key: attachmentS3key!, + bucketName: fastify.environmentConfig.AssetsBucketId, + length: fileSizeBytes, + mimeType: contentType, + md5hash, + }); + } const createdNotified = await fastify.dynamoClient.send(getReservationData); if (!createdNotified.Items || createdNotified.Count === 0) { @@ -116,6 +143,7 @@ const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => { expiresAt: Math.floor(Date.now() / 1000) + 86400 * ROOM_RESERVATION_RETENTION_DAYS, + attachmentS3key, ...request.body, }, { removeUndefinedValues: true }, @@ -185,10 +213,7 @@ const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => { request.log.info( `Queued room reservation email to SQS with message ID ${result.MessageId}`, ); - if (request.body.attachmentFilename) { - request.log.info("Creating presigned URL to store file to"); - } - return reply.status(201).send(); + return reply.status(201).send({ uploadUrl }); }, ); fastify.withTypeProvider().get( diff --git a/src/api/types.d.ts b/src/api/types.d.ts index 8213f193..44f1377f 100644 --- a/src/api/types.d.ts +++ b/src/api/types.d.ts @@ -9,6 +9,7 @@ import { SecretsManagerClient } from "@aws-sdk/client-secrets-manager"; import { SQSClient } from "@aws-sdk/client-sqs"; import { AvailableAuthorizationPolicy } from "common/policies/definition.js"; import type RedisModule from "ioredis"; +import { type S3Client } from "@aws-sdk/client-s3"; export type Redis = RedisModule.default; export type ValidLoggers = FastifyBaseLogger | pino.Logger; @@ -42,6 +43,7 @@ declare module "fastify" { nodeCache: NodeCache; dynamoClient: DynamoDBClient; sqsClient?: SQSClient; + s3Client?: S3Client; redisClient: Redis; secretsManagerClient: SecretsManagerClient; secretConfig: SecretConfig | (SecretConfig & SecretTesting); diff --git a/src/common/types/roomRequest.ts b/src/common/types/roomRequest.ts index f8e939ca..ccadb4ee 100644 --- a/src/common/types/roomRequest.ts +++ b/src/common/types/roomRequest.ts @@ -1,6 +1,8 @@ import * as z from "zod/v4"; import { AllOrganizationNameList } from "@acm-uiuc/js-shared"; import { illinoisSemesterId } from "./generic.js" +export const validMimeTypes = ['application/pdf', 'image/jpeg', 'image/heic', 'image/pdf'] +export const maxAttachmentSizeBytes = 1e7; // 10MB export const eventThemeOptions = [ "Arts & Music", @@ -130,9 +132,16 @@ export enum RoomRequestStatus { REJECTED_BY_UIUC = "rejected_by_uiuc", } +export const roomRequestStatusAttachmentInfo = z.object({ + filename: z.string().min(1).max(100), + md5hash: z.string().length(32), + fileSizeBytes: z.number().min(1).max(maxAttachmentSizeBytes), + contentType: z.enum(validMimeTypes) +}) + export const roomRequestStatusUpdateRequest = z.object({ status: z.enum(RoomRequestStatus), - attachmentFilename: z.optional(z.string().max(100)), + attachmentInfo: z.optional(roomRequestStatusAttachmentInfo), notes: z.string().min(1).max(1000) }); diff --git a/yarn.lock b/yarn.lock index 4bdf35fa..8330b2a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -40,6 +40,27 @@ "@aws-sdk/types" "^3.222.0" tslib "^2.6.2" +"@aws-crypto/crc32c@5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz#4e34aab7f419307821509a98b9b08e84e0c1917e" + integrity sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag== + dependencies: + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + tslib "^2.6.2" + +"@aws-crypto/sha1-browser@5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz#b0ee2d2821d3861f017e965ef3b4cb38e3b6a0f4" + integrity sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg== + 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" + "@aws-crypto/sha256-browser@5.2.0": version "5.2.0" resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz#153895ef1dba6f9fce38af550e0ef58988eb649e" @@ -69,7 +90,7 @@ dependencies: tslib "^2.6.2" -"@aws-crypto/util@^5.2.0": +"@aws-crypto/util@5.2.0", "@aws-crypto/util@^5.2.0": version "5.2.0" resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-5.2.0.tgz#71284c9cffe7927ddadac793c14f14886d3876da" integrity sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ== @@ -221,6 +242,69 @@ "@smithy/util-waiter" "^4.2.3" tslib "^2.6.2" +"@aws-sdk/client-s3@^3.914.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.922.0.tgz#5752a59ad5cf4e370ede00774f1e09e9c0af6235" + integrity sha512-SZRaZUUAHCWfEyBf4SRSPd29ko4uFoJpfd0E/w1meE68XhFB52FTtz/71UqYcwqZmN+s7oUNFFZT+DE/dnQSEA== + 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.922.0" + "@aws-sdk/credential-provider-node" "3.922.0" + "@aws-sdk/middleware-bucket-endpoint" "3.922.0" + "@aws-sdk/middleware-expect-continue" "3.922.0" + "@aws-sdk/middleware-flexible-checksums" "3.922.0" + "@aws-sdk/middleware-host-header" "3.922.0" + "@aws-sdk/middleware-location-constraint" "3.922.0" + "@aws-sdk/middleware-logger" "3.922.0" + "@aws-sdk/middleware-recursion-detection" "3.922.0" + "@aws-sdk/middleware-sdk-s3" "3.922.0" + "@aws-sdk/middleware-ssec" "3.922.0" + "@aws-sdk/middleware-user-agent" "3.922.0" + "@aws-sdk/region-config-resolver" "3.922.0" + "@aws-sdk/signature-v4-multi-region" "3.922.0" + "@aws-sdk/types" "3.922.0" + "@aws-sdk/util-endpoints" "3.922.0" + "@aws-sdk/util-user-agent-browser" "3.922.0" + "@aws-sdk/util-user-agent-node" "3.922.0" + "@aws-sdk/xml-builder" "3.921.0" + "@smithy/config-resolver" "^4.4.1" + "@smithy/core" "^3.17.2" + "@smithy/eventstream-serde-browser" "^4.2.4" + "@smithy/eventstream-serde-config-resolver" "^4.3.4" + "@smithy/eventstream-serde-node" "^4.2.4" + "@smithy/fetch-http-handler" "^5.3.5" + "@smithy/hash-blob-browser" "^4.2.5" + "@smithy/hash-node" "^4.2.4" + "@smithy/hash-stream-node" "^4.2.4" + "@smithy/invalid-dependency" "^4.2.4" + "@smithy/md5-js" "^4.2.4" + "@smithy/middleware-content-length" "^4.2.4" + "@smithy/middleware-endpoint" "^4.3.6" + "@smithy/middleware-retry" "^4.4.6" + "@smithy/middleware-serde" "^4.2.4" + "@smithy/middleware-stack" "^4.2.4" + "@smithy/node-config-provider" "^4.3.4" + "@smithy/node-http-handler" "^4.4.4" + "@smithy/protocol-http" "^5.3.4" + "@smithy/smithy-client" "^4.9.2" + "@smithy/types" "^4.8.1" + "@smithy/url-parser" "^4.2.4" + "@smithy/util-base64" "^4.3.0" + "@smithy/util-body-length-browser" "^4.2.0" + "@smithy/util-body-length-node" "^4.2.1" + "@smithy/util-defaults-mode-browser" "^4.3.5" + "@smithy/util-defaults-mode-node" "^4.2.7" + "@smithy/util-endpoints" "^3.2.4" + "@smithy/util-middleware" "^4.2.4" + "@smithy/util-retry" "^4.2.4" + "@smithy/util-stream" "^4.5.5" + "@smithy/util-utf8" "^4.2.0" + "@smithy/util-waiter" "^4.2.4" + "@smithy/uuid" "^1.1.0" + tslib "^2.6.2" + "@aws-sdk/client-secrets-manager@^3.914.0": version "3.917.0" resolved "https://registry.yarnpkg.com/@aws-sdk/client-secrets-manager/-/client-secrets-manager-3.917.0.tgz#474fee3f74810ec7c1bf8b0127922558515dbca4" @@ -448,6 +532,50 @@ "@smithy/util-utf8" "^4.2.0" tslib "^2.6.2" +"@aws-sdk/client-sso@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.922.0.tgz#5f19518fdf496e0d3cc55e98755132b7cc25d5d9" + integrity sha512-jdHs7uy7cSpiMvrxhYmqHyJxgK7hyqw4plG8OQ4YTBpq0SbfAxdoOuOkwJ1IVUUQho4otR1xYYjiX/8e8J8qwQ== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.922.0" + "@aws-sdk/middleware-host-header" "3.922.0" + "@aws-sdk/middleware-logger" "3.922.0" + "@aws-sdk/middleware-recursion-detection" "3.922.0" + "@aws-sdk/middleware-user-agent" "3.922.0" + "@aws-sdk/region-config-resolver" "3.922.0" + "@aws-sdk/types" "3.922.0" + "@aws-sdk/util-endpoints" "3.922.0" + "@aws-sdk/util-user-agent-browser" "3.922.0" + "@aws-sdk/util-user-agent-node" "3.922.0" + "@smithy/config-resolver" "^4.4.1" + "@smithy/core" "^3.17.2" + "@smithy/fetch-http-handler" "^5.3.5" + "@smithy/hash-node" "^4.2.4" + "@smithy/invalid-dependency" "^4.2.4" + "@smithy/middleware-content-length" "^4.2.4" + "@smithy/middleware-endpoint" "^4.3.6" + "@smithy/middleware-retry" "^4.4.6" + "@smithy/middleware-serde" "^4.2.4" + "@smithy/middleware-stack" "^4.2.4" + "@smithy/node-config-provider" "^4.3.4" + "@smithy/node-http-handler" "^4.4.4" + "@smithy/protocol-http" "^5.3.4" + "@smithy/smithy-client" "^4.9.2" + "@smithy/types" "^4.8.1" + "@smithy/url-parser" "^4.2.4" + "@smithy/util-base64" "^4.3.0" + "@smithy/util-body-length-browser" "^4.2.0" + "@smithy/util-body-length-node" "^4.2.1" + "@smithy/util-defaults-mode-browser" "^4.3.5" + "@smithy/util-defaults-mode-node" "^4.2.7" + "@smithy/util-endpoints" "^3.2.4" + "@smithy/util-middleware" "^4.2.4" + "@smithy/util-retry" "^4.2.4" + "@smithy/util-utf8" "^4.2.0" + tslib "^2.6.2" + "@aws-sdk/client-sts@^3.914.0": version "3.917.0" resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.917.0.tgz#34316d92a4be7950a84f9ff9157aedf0dab05f34" @@ -531,6 +659,25 @@ "@smithy/util-utf8" "^4.2.0" tslib "^2.6.2" +"@aws-sdk/core@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.922.0.tgz#6df3b04145202567a15e2b83cc83ef15590c828a" + integrity sha512-EvfP4cqJfpO3L2v5vkIlTkMesPtRwWlMfsaW6Tpfm7iYfBOuTi6jx60pMDMTyJNVfh6cGmXwh/kj1jQdR+w99Q== + dependencies: + "@aws-sdk/types" "3.922.0" + "@aws-sdk/xml-builder" "3.921.0" + "@smithy/core" "^3.17.2" + "@smithy/node-config-provider" "^4.3.4" + "@smithy/property-provider" "^4.2.4" + "@smithy/protocol-http" "^5.3.4" + "@smithy/signature-v4" "^5.3.4" + "@smithy/smithy-client" "^4.9.2" + "@smithy/types" "^4.8.1" + "@smithy/util-base64" "^4.3.0" + "@smithy/util-middleware" "^4.2.4" + "@smithy/util-utf8" "^4.2.0" + tslib "^2.6.2" + "@aws-sdk/credential-provider-env@3.914.0": version "3.914.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.914.0.tgz#fb2b87f8c59fbb833d33918ae8861cde3f9e514f" @@ -553,6 +700,17 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@aws-sdk/credential-provider-env@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.922.0.tgz#c0f7a0ae77c4f9d1c6494aec4a149bbc74cc8578" + integrity sha512-WikGQpKkROJSK3D3E7odPjZ8tU7WJp5/TgGdRuZw3izsHUeH48xMv6IznafpRTmvHcjAbDQj4U3CJZNAzOK/OQ== + dependencies: + "@aws-sdk/core" "3.922.0" + "@aws-sdk/types" "3.922.0" + "@smithy/property-provider" "^4.2.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@aws-sdk/credential-provider-http@3.914.0": version "3.914.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.914.0.tgz#61df7fab6b12f0791ee9dbd5b856326b5629eaf5" @@ -585,6 +743,22 @@ "@smithy/util-stream" "^4.5.4" tslib "^2.6.2" +"@aws-sdk/credential-provider-http@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.922.0.tgz#d957bf8f2c0fb07a27eaee561e5bc94d6f86cb73" + integrity sha512-i72DgHMK7ydAEqdzU0Duqh60Q8W59EZmRJ73y0Y5oFmNOqnYsAI+UXyOoCsubp+Dkr6+yOwAn1gPt1XGE9Aowg== + dependencies: + "@aws-sdk/core" "3.922.0" + "@aws-sdk/types" "3.922.0" + "@smithy/fetch-http-handler" "^5.3.5" + "@smithy/node-http-handler" "^4.4.4" + "@smithy/property-provider" "^4.2.4" + "@smithy/protocol-http" "^5.3.4" + "@smithy/smithy-client" "^4.9.2" + "@smithy/types" "^4.8.1" + "@smithy/util-stream" "^4.5.5" + tslib "^2.6.2" + "@aws-sdk/credential-provider-ini@3.914.0": version "3.914.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.914.0.tgz#b3cbe799c33ae0f88627d26c12c5c0102e457f9b" @@ -623,6 +797,25 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@aws-sdk/credential-provider-ini@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.922.0.tgz#67348aa54e0548e7bcaea2a54eac6af18e84f0d7" + integrity sha512-bVF+pI5UCLNkvbiZr/t2fgTtv84s8FCdOGAPxQiQcw5qOZywNuuCCY3wIIchmQr6GJr8YFkEp5LgDCac5EC5aQ== + dependencies: + "@aws-sdk/core" "3.922.0" + "@aws-sdk/credential-provider-env" "3.922.0" + "@aws-sdk/credential-provider-http" "3.922.0" + "@aws-sdk/credential-provider-process" "3.922.0" + "@aws-sdk/credential-provider-sso" "3.922.0" + "@aws-sdk/credential-provider-web-identity" "3.922.0" + "@aws-sdk/nested-clients" "3.922.0" + "@aws-sdk/types" "3.922.0" + "@smithy/credential-provider-imds" "^4.2.4" + "@smithy/property-provider" "^4.2.4" + "@smithy/shared-ini-file-loader" "^4.3.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@aws-sdk/credential-provider-node@3.914.0": version "3.914.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.914.0.tgz#6b692ed7049427ebaddf289394795270256dde9e" @@ -659,6 +852,24 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@aws-sdk/credential-provider-node@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.922.0.tgz#279eb69f1b932556a8b94d52af13375baeaac4a2" + integrity sha512-agCwaD6mBihToHkjycL8ObIS2XOnWypWZZWhJSoWyHwFrhEKz1zGvgylK9Dc711oUfU+zU6J8e0JPKNJMNb3BQ== + dependencies: + "@aws-sdk/credential-provider-env" "3.922.0" + "@aws-sdk/credential-provider-http" "3.922.0" + "@aws-sdk/credential-provider-ini" "3.922.0" + "@aws-sdk/credential-provider-process" "3.922.0" + "@aws-sdk/credential-provider-sso" "3.922.0" + "@aws-sdk/credential-provider-web-identity" "3.922.0" + "@aws-sdk/types" "3.922.0" + "@smithy/credential-provider-imds" "^4.2.4" + "@smithy/property-provider" "^4.2.4" + "@smithy/shared-ini-file-loader" "^4.3.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@aws-sdk/credential-provider-process@3.914.0": version "3.914.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.914.0.tgz#67bc9c330e1aed34d0f667b970ef9460524ebe60" @@ -683,6 +894,18 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@aws-sdk/credential-provider-process@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.922.0.tgz#23f5b0923d9a9bb89dac3fa9aeb27c9eebf1a19c" + integrity sha512-1DZOYezT6okslpvMW7oA2q+y17CJd4fxjNFH0jtThfswdh9CtG62+wxenqO+NExttq0UMaKisrkZiVrYQBTShw== + dependencies: + "@aws-sdk/core" "3.922.0" + "@aws-sdk/types" "3.922.0" + "@smithy/property-provider" "^4.2.4" + "@smithy/shared-ini-file-loader" "^4.3.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@aws-sdk/credential-provider-sso@3.914.0": version "3.914.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.914.0.tgz#14b5780ef743d1689d4bc708cb3691f7d1e48bec" @@ -711,6 +934,20 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@aws-sdk/credential-provider-sso@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.922.0.tgz#64a2e8f44e4aa8fdacbaa6c536495971109926d2" + integrity sha512-nbD3G3hShTYxLCkKMqLkLPtKwAAfxdY/k9jHtZmVBFXek2T6tQrqZHKxlAu+fd23Ga4/Aik7DLQQx1RA1a5ipg== + dependencies: + "@aws-sdk/client-sso" "3.922.0" + "@aws-sdk/core" "3.922.0" + "@aws-sdk/token-providers" "3.922.0" + "@aws-sdk/types" "3.922.0" + "@smithy/property-provider" "^4.2.4" + "@smithy/shared-ini-file-loader" "^4.3.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@aws-sdk/credential-provider-web-identity@3.914.0": version "3.914.0" resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.914.0.tgz#12d85b37514a6fc1dcd21e5f23d94e1dab003e50" @@ -737,6 +974,19 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@aws-sdk/credential-provider-web-identity@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.922.0.tgz#d7757693c8f7cec62a0c34b90fea45be4023cf23" + integrity sha512-wjGIhgMHGGQfQTdFaJphNOKyAL8wZs6znJdHADPVURmgR+EWLyN/0fDO1u7wx8xaLMZpbHIFWBEvf9TritR/cQ== + dependencies: + "@aws-sdk/core" "3.922.0" + "@aws-sdk/nested-clients" "3.922.0" + "@aws-sdk/types" "3.922.0" + "@smithy/property-provider" "^4.2.4" + "@smithy/shared-ini-file-loader" "^4.3.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@aws-sdk/crt-loader@3.916.0": version "3.916.0" resolved "https://registry.yarnpkg.com/@aws-sdk/crt-loader/-/crt-loader-3.916.0.tgz#b7f49f4f7653eee54d4bfcaa39cd268fc5718810" @@ -754,6 +1004,19 @@ mnemonist "0.38.3" tslib "^2.6.2" +"@aws-sdk/middleware-bucket-endpoint@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.922.0.tgz#417efd18e8af948e694c5be751bde6d631138b3d" + integrity sha512-Dpr2YeOaLFqt3q1hocwBesynE3x8/dXZqXZRuzSX/9/VQcwYBFChHAm4mTAl4zuvArtDbLrwzWSxmOWYZGtq5w== + dependencies: + "@aws-sdk/types" "3.922.0" + "@aws-sdk/util-arn-parser" "3.893.0" + "@smithy/node-config-provider" "^4.3.4" + "@smithy/protocol-http" "^5.3.4" + "@smithy/types" "^4.8.1" + "@smithy/util-config-provider" "^4.2.0" + tslib "^2.6.2" + "@aws-sdk/middleware-endpoint-discovery@3.914.0": version "3.914.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.914.0.tgz#aa6784bbbae77048ecb0a251ed473165ca882e24" @@ -766,6 +1029,35 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@aws-sdk/middleware-expect-continue@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.922.0.tgz#02f0b0402fcb8974765b3e7d20f43753bd05738c" + integrity sha512-xmnLWMtmHJHJBupSWMUEW1gyxuRIeQ1Ov2xa8Tqq77fPr4Ft2AluEwiDMaZIMHoAvpxWKEEt9Si59Li7GIA+bQ== + dependencies: + "@aws-sdk/types" "3.922.0" + "@smithy/protocol-http" "^5.3.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + +"@aws-sdk/middleware-flexible-checksums@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.922.0.tgz#41abb38c5baa8626a843aa57cfcb904a4d71427a" + integrity sha512-G363np7YcJhf+gBucskdv8cOTbs2TRwocEzRupuqDIooGDlLBlfJrvwehdgtWR8l53yjJR3zcHvGrVPTe2h8Nw== + dependencies: + "@aws-crypto/crc32" "5.2.0" + "@aws-crypto/crc32c" "5.2.0" + "@aws-crypto/util" "5.2.0" + "@aws-sdk/core" "3.922.0" + "@aws-sdk/types" "3.922.0" + "@smithy/is-array-buffer" "^4.2.0" + "@smithy/node-config-provider" "^4.3.4" + "@smithy/protocol-http" "^5.3.4" + "@smithy/types" "^4.8.1" + "@smithy/util-middleware" "^4.2.4" + "@smithy/util-stream" "^4.5.5" + "@smithy/util-utf8" "^4.2.0" + tslib "^2.6.2" + "@aws-sdk/middleware-host-header@3.914.0": version "3.914.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.914.0.tgz#7e962c3d18c1ecc98606eab09a98dcf1b3402835" @@ -776,6 +1068,25 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@aws-sdk/middleware-host-header@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.922.0.tgz#f19621fd19764f7eb0a33795ce0f43402080e394" + integrity sha512-HPquFgBnq/KqKRVkiuCt97PmWbKtxQ5iUNLEc6FIviqOoZTmaYG3EDsIbuFBz9C4RHJU4FKLmHL2bL3FEId6AA== + dependencies: + "@aws-sdk/types" "3.922.0" + "@smithy/protocol-http" "^5.3.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + +"@aws-sdk/middleware-location-constraint@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.922.0.tgz#c455d40e3ab49014a1193fbcb2bf29885d345b7c" + integrity sha512-T4iqd7WQ2DDjCH/0s50mnhdoX+IJns83ZE+3zj9IDlpU0N2aq8R91IG890qTfYkUEdP9yRm0xir/CNed+v6Dew== + dependencies: + "@aws-sdk/types" "3.922.0" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@aws-sdk/middleware-logger@3.914.0": version "3.914.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.914.0.tgz#222d50ec69447715d6954eb6db0029f11576227b" @@ -785,6 +1096,15 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@aws-sdk/middleware-logger@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.922.0.tgz#3a43e2b7ec72b043751a7fd45f0514db77756be9" + integrity sha512-AkvYO6b80FBm5/kk2E636zNNcNgjztNNUxpqVx+huyGn9ZqGTzS4kLqW2hO6CBe5APzVtPCtiQsXL24nzuOlAg== + dependencies: + "@aws-sdk/types" "3.922.0" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@aws-sdk/middleware-recursion-detection@3.914.0": version "3.914.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.914.0.tgz#bf65759cf303f271b22770e7f9675034b4ced946" @@ -796,6 +1116,17 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@aws-sdk/middleware-recursion-detection@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.922.0.tgz#cca89bd926ad05893f9b99b253fa50a6b6c7b829" + integrity sha512-TtSCEDonV/9R0VhVlCpxZbp/9sxQvTTRKzIf8LxW3uXpby6Wl8IxEciBJlxmSkoqxh542WRcko7NYODlvL/gDA== + dependencies: + "@aws-sdk/types" "3.922.0" + "@aws/lambda-invoke-store" "^0.1.1" + "@smithy/protocol-http" "^5.3.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@aws-sdk/middleware-sdk-s3@3.916.0": version "3.916.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.916.0.tgz#5c1cc4645186b3c0f7ac5f6a897885af0b62198e" @@ -816,6 +1147,26 @@ "@smithy/util-utf8" "^4.2.0" tslib "^2.6.2" +"@aws-sdk/middleware-sdk-s3@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.922.0.tgz#11875f1b848070413815ca34f1b8e93a36fa351a" + integrity sha512-ygg8lME1oFAbsH42ed2wtGqfHLoT5irgx6VC4X98j79fV1qXEwwwbqMsAiMQ/HJehpjqAFRVsHox3MHLN48Z5A== + dependencies: + "@aws-sdk/core" "3.922.0" + "@aws-sdk/types" "3.922.0" + "@aws-sdk/util-arn-parser" "3.893.0" + "@smithy/core" "^3.17.2" + "@smithy/node-config-provider" "^4.3.4" + "@smithy/protocol-http" "^5.3.4" + "@smithy/signature-v4" "^5.3.4" + "@smithy/smithy-client" "^4.9.2" + "@smithy/types" "^4.8.1" + "@smithy/util-config-provider" "^4.2.0" + "@smithy/util-middleware" "^4.2.4" + "@smithy/util-stream" "^4.5.5" + "@smithy/util-utf8" "^4.2.0" + tslib "^2.6.2" + "@aws-sdk/middleware-sdk-sqs@3.916.0": version "3.916.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-sqs/-/middleware-sdk-sqs-3.916.0.tgz#7a5566003fcbcf18f99f8c0bd413fcf5a0672afa" @@ -828,6 +1179,15 @@ "@smithy/util-utf8" "^4.2.0" tslib "^2.6.2" +"@aws-sdk/middleware-ssec@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-ssec/-/middleware-ssec-3.922.0.tgz#1c56b2619cdd604e97203148030f299980494008" + integrity sha512-eHvSJZTSRJO+/tjjGD6ocnPc8q9o3m26+qbwQTu/4V6yOJQ1q+xkDZNqwJQphL+CodYaQ7uljp8g1Ji/AN3D9w== + dependencies: + "@aws-sdk/types" "3.922.0" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@aws-sdk/middleware-user-agent@3.914.0": version "3.914.0" resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.914.0.tgz#8274febe22529078fbfd0e97ba151af9407097ea" @@ -854,6 +1214,19 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@aws-sdk/middleware-user-agent@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.922.0.tgz#b1e109ce3a0c30d44360bf0f707013a3a36d3f78" + integrity sha512-N4Qx/9KP3oVQBJOrSghhz8iZFtUC2NNeSZt88hpPhbqAEAtuX8aD8OzVcpnAtrwWqy82Yd2YTxlkqMGkgqnBsQ== + dependencies: + "@aws-sdk/core" "3.922.0" + "@aws-sdk/types" "3.922.0" + "@aws-sdk/util-endpoints" "3.922.0" + "@smithy/core" "^3.17.2" + "@smithy/protocol-http" "^5.3.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@aws-sdk/nested-clients@3.914.0": version "3.914.0" resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.914.0.tgz#02e676f637ecdf4f891c6d23941ca7cae61e76f1" @@ -942,6 +1315,50 @@ "@smithy/util-utf8" "^4.2.0" tslib "^2.6.2" +"@aws-sdk/nested-clients@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/nested-clients/-/nested-clients-3.922.0.tgz#1b66f1505fb0d6442e67239c5596159ba871cfff" + integrity sha512-uYvKCF1TGh/MuJ4TMqmUM0Csuao02HawcseG4LUDyxdUsd/EFuxalWq1Cx4fKZQ2K8F504efZBjctMAMNY+l7A== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.922.0" + "@aws-sdk/middleware-host-header" "3.922.0" + "@aws-sdk/middleware-logger" "3.922.0" + "@aws-sdk/middleware-recursion-detection" "3.922.0" + "@aws-sdk/middleware-user-agent" "3.922.0" + "@aws-sdk/region-config-resolver" "3.922.0" + "@aws-sdk/types" "3.922.0" + "@aws-sdk/util-endpoints" "3.922.0" + "@aws-sdk/util-user-agent-browser" "3.922.0" + "@aws-sdk/util-user-agent-node" "3.922.0" + "@smithy/config-resolver" "^4.4.1" + "@smithy/core" "^3.17.2" + "@smithy/fetch-http-handler" "^5.3.5" + "@smithy/hash-node" "^4.2.4" + "@smithy/invalid-dependency" "^4.2.4" + "@smithy/middleware-content-length" "^4.2.4" + "@smithy/middleware-endpoint" "^4.3.6" + "@smithy/middleware-retry" "^4.4.6" + "@smithy/middleware-serde" "^4.2.4" + "@smithy/middleware-stack" "^4.2.4" + "@smithy/node-config-provider" "^4.3.4" + "@smithy/node-http-handler" "^4.4.4" + "@smithy/protocol-http" "^5.3.4" + "@smithy/smithy-client" "^4.9.2" + "@smithy/types" "^4.8.1" + "@smithy/url-parser" "^4.2.4" + "@smithy/util-base64" "^4.3.0" + "@smithy/util-body-length-browser" "^4.2.0" + "@smithy/util-body-length-node" "^4.2.1" + "@smithy/util-defaults-mode-browser" "^4.3.5" + "@smithy/util-defaults-mode-node" "^4.2.7" + "@smithy/util-endpoints" "^3.2.4" + "@smithy/util-middleware" "^4.2.4" + "@smithy/util-retry" "^4.2.4" + "@smithy/util-utf8" "^4.2.0" + tslib "^2.6.2" + "@aws-sdk/region-config-resolver@3.914.0": version "3.914.0" resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.914.0.tgz#b6d2825081195ce1c634b8c92b1e19b08f140008" @@ -952,6 +1369,31 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@aws-sdk/region-config-resolver@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.922.0.tgz#55bf45c6d4d344b8393528c7e33576f923bb1b1d" + integrity sha512-44Y/rNNwhngR2KHp6gkx//TOr56/hx6s4l+XLjOqH7EBCHL7XhnrT1y92L+DLiroVr1tCSmO8eHQwBv0Y2+mvw== + dependencies: + "@aws-sdk/types" "3.922.0" + "@smithy/config-resolver" "^4.4.1" + "@smithy/node-config-provider" "^4.3.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + +"@aws-sdk/s3-request-presigner@^3.914.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.922.0.tgz#f71ad9565d2f1e790ed258c192e3844430ce484c" + integrity sha512-x/WZXOMAN10X/hbjHnaXjtU34RmV3/eJMiHoJsohquSgz8+pfRN1DeK65oa/XPoKCMPfV31RfHSzCduligHfsQ== + dependencies: + "@aws-sdk/signature-v4-multi-region" "3.922.0" + "@aws-sdk/types" "3.922.0" + "@aws-sdk/util-format-url" "3.922.0" + "@smithy/middleware-endpoint" "^4.3.6" + "@smithy/protocol-http" "^5.3.4" + "@smithy/smithy-client" "^4.9.2" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@aws-sdk/signature-v4-crt@^3.914.0": version "3.916.0" resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-crt/-/signature-v4-crt-3.916.0.tgz#43d32b49260bacd25305b92424ff5f52bceba1c2" @@ -978,6 +1420,18 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@aws-sdk/signature-v4-multi-region@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.922.0.tgz#37e30499d805ac0cffbd14b448f7d2a58bbea132" + integrity sha512-mmsgEEL5pE+A7gFYiJMDBCLVciaXq4EFI5iAP7bPpnHvOplnNOYxVy2IreKMllGvrfjVyLnwxzZYlo5zZ65FWg== + dependencies: + "@aws-sdk/middleware-sdk-s3" "3.922.0" + "@aws-sdk/types" "3.922.0" + "@smithy/protocol-http" "^5.3.4" + "@smithy/signature-v4" "^5.3.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@aws-sdk/token-providers@3.914.0": version "3.914.0" resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.914.0.tgz#576651c6c2a3b2b9e2435461ee81c35d4b7a6e49" @@ -1004,6 +1458,19 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@aws-sdk/token-providers@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.922.0.tgz#0ae050e9b713b942397fa290ce269df91013c8cc" + integrity sha512-/inmPnjZE0ZBE16zaCowAvouSx05FJ7p6BQYuzlJ8vxEU0sS0Hf8fvhuiRnN9V9eDUPIBY+/5EjbMWygXL4wlQ== + dependencies: + "@aws-sdk/core" "3.922.0" + "@aws-sdk/nested-clients" "3.922.0" + "@aws-sdk/types" "3.922.0" + "@smithy/property-provider" "^4.2.4" + "@smithy/shared-ini-file-loader" "^4.3.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@aws-sdk/types@3.914.0", "@aws-sdk/types@^3.222.0": version "3.914.0" resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.914.0.tgz#175cf9a4b2267aafbb110fe1316e6827de951fdb" @@ -1012,6 +1479,14 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@aws-sdk/types@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.922.0.tgz#e92daf55272171caac8dba9d425786646466d935" + integrity sha512-eLA6XjVobAUAMivvM7DBL79mnHyrm+32TkXNWZua5mnxF+6kQCfblKKJvxMZLGosO53/Ex46ogim8IY5Nbqv2w== + dependencies: + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@aws-sdk/util-arn-parser@3.893.0": version "3.893.0" resolved "https://registry.yarnpkg.com/@aws-sdk/util-arn-parser/-/util-arn-parser-3.893.0.tgz#fcc9b792744b9da597662891c2422dda83881d8d" @@ -1048,6 +1523,27 @@ "@smithy/util-endpoints" "^3.2.3" tslib "^2.6.2" +"@aws-sdk/util-endpoints@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.922.0.tgz#817457d6a78ce366bdb7b201c638dba5ffdbfe60" + integrity sha512-4ZdQCSuNMY8HMlR1YN4MRDdXuKd+uQTeKIr5/pIM+g3TjInZoj8imvXudjcrFGA63UF3t92YVTkBq88mg58RXQ== + dependencies: + "@aws-sdk/types" "3.922.0" + "@smithy/types" "^4.8.1" + "@smithy/url-parser" "^4.2.4" + "@smithy/util-endpoints" "^3.2.4" + tslib "^2.6.2" + +"@aws-sdk/util-format-url@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-format-url/-/util-format-url-3.922.0.tgz#a1b91b180d75a73dd8eedee0913a60153673392e" + integrity sha512-UYLWPvZEd6TYilNkrQrIeXh2bXZsY3ighYErSEjD24f3JQhg0XdXoR/QHIE8licHu2qFrTRM6yi9LH1GY6X0cg== + dependencies: + "@aws-sdk/types" "3.922.0" + "@smithy/querystring-builder" "^4.2.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@aws-sdk/util-locate-window@^3.0.0": version "3.804.0" resolved "https://registry.yarnpkg.com/@aws-sdk/util-locate-window/-/util-locate-window-3.804.0.tgz#a2ee8dc5d9c98276986e8e1ba03c0c84d9afb0f5" @@ -1065,6 +1561,16 @@ bowser "^2.11.0" tslib "^2.6.2" +"@aws-sdk/util-user-agent-browser@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.922.0.tgz#734bbe74d34c3fbdb96aca80a151d3d7e7e87c30" + integrity sha512-qOJAERZ3Plj1st7M4Q5henl5FRpE30uLm6L9edZqZXGR6c7ry9jzexWamWVpQ4H4xVAVmiO9dIEBAfbq4mduOA== + dependencies: + "@aws-sdk/types" "3.922.0" + "@smithy/types" "^4.8.1" + bowser "^2.11.0" + tslib "^2.6.2" + "@aws-sdk/util-user-agent-node@3.914.0": version "3.914.0" resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.914.0.tgz#2c8842ccb7423882c3ec244fe797dd7e4be9c19d" @@ -1087,6 +1593,17 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@aws-sdk/util-user-agent-node@3.922.0": + version "3.922.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.922.0.tgz#52dd951b314a3e7097d181664a3f79a1e56921ce" + integrity sha512-NrPe/Rsr5kcGunkog0eBV+bY0inkRELsD2SacC4lQZvZiXf8VJ2Y7j+Yq1tB+h+FPLsdt3v9wItIvDf/laAm0Q== + dependencies: + "@aws-sdk/middleware-user-agent" "3.922.0" + "@aws-sdk/types" "3.922.0" + "@smithy/node-config-provider" "^4.3.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@aws-sdk/util-utf8-browser@^3.259.0": version "3.259.0" resolved "https://registry.yarnpkg.com/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz#3275a6f5eb334f96ca76635b961d3c50259fd9ff" @@ -1103,11 +1620,25 @@ fast-xml-parser "5.2.5" tslib "^2.6.2" +"@aws-sdk/xml-builder@3.921.0": + version "3.921.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/xml-builder/-/xml-builder-3.921.0.tgz#e4d4d21b09341648b598d720c602ee76d7a84594" + integrity sha512-LVHg0jgjyicKKvpNIEMXIMr1EBViESxcPkqfOlT+X1FkmUMTNZEEVF18tOJg4m4hV5vxtkWcqtr4IEeWa1C41Q== + dependencies: + "@smithy/types" "^4.8.1" + fast-xml-parser "5.2.5" + tslib "^2.6.2" + "@aws/lambda-invoke-store@^0.0.1": version "0.0.1" resolved "https://registry.yarnpkg.com/@aws/lambda-invoke-store/-/lambda-invoke-store-0.0.1.tgz#92d792a7dda250dfcb902e13228f37a81be57c8f" integrity sha512-ORHRQ2tmvnBXc8t/X9Z8IcSbBA4xTLKuN873FopzklHMeqBst7YG0d+AX97inkvDX+NChYtSr+qGfcqGFaI8Zw== +"@aws/lambda-invoke-store@^0.1.1": + version "0.1.1" + resolved "https://registry.yarnpkg.com/@aws/lambda-invoke-store/-/lambda-invoke-store-0.1.1.tgz#2e67f17040b930bde00a79ffb484eb9e77472b06" + integrity sha512-RcLam17LdlbSOSp9VxmUu1eI6Mwxp+OwhD2QhiSNmNCzoDb0EeUXTD2n/WbcnrAYMGlmf05th6QYq23VqvJqpA== + "@azure/msal-browser@^4.25.1": version "4.25.1" resolved "https://registry.yarnpkg.com/@azure/msal-browser/-/msal-browser-4.25.1.tgz#33e6b05aebc61ba1e508634b1f9a47ffa472221d" @@ -2579,6 +3110,29 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@smithy/abort-controller@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@smithy/abort-controller/-/abort-controller-4.2.4.tgz#8031d32aea69c714eae49c1f43ce0ea60481d2d3" + integrity sha512-Z4DUr/AkgyFf1bOThW2HwzREagee0sB5ycl+hDiSZOfRLW8ZgrOjDi6g8mHH19yyU5E2A/64W3z6SMIf5XiUSQ== + dependencies: + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + +"@smithy/chunked-blob-reader-native@^4.2.1": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.1.tgz#380266951d746b522b4ab2b16bfea6b451147b41" + integrity sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ== + dependencies: + "@smithy/util-base64" "^4.3.0" + tslib "^2.6.2" + +"@smithy/chunked-blob-reader@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.2.0.tgz#776fec5eaa5ab5fa70d0d0174b7402420b24559c" + integrity sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA== + dependencies: + tslib "^2.6.2" + "@smithy/config-resolver@^4.4.0": version "4.4.0" resolved "https://registry.yarnpkg.com/@smithy/config-resolver/-/config-resolver-4.4.0.tgz#9a33b7dd9b7e0475802acef53f41555257e104cd" @@ -2591,6 +3145,18 @@ "@smithy/util-middleware" "^4.2.3" tslib "^2.6.2" +"@smithy/config-resolver@^4.4.1": + version "4.4.1" + resolved "https://registry.yarnpkg.com/@smithy/config-resolver/-/config-resolver-4.4.1.tgz#dcf9321841d44912455d4a0d8c4e554aa97af921" + integrity sha512-BciDJ5hkyYEGBBKMbjGB1A/Zq8bYZ41Zo9BMnGdKF6QD1fY4zIkYx6zui/0CHaVGnv6h0iy8y4rnPX9CPCAPyQ== + dependencies: + "@smithy/node-config-provider" "^4.3.4" + "@smithy/types" "^4.8.1" + "@smithy/util-config-provider" "^4.2.0" + "@smithy/util-endpoints" "^3.2.4" + "@smithy/util-middleware" "^4.2.4" + tslib "^2.6.2" + "@smithy/core@^3.17.0", "@smithy/core@^3.17.1": version "3.17.1" resolved "https://registry.yarnpkg.com/@smithy/core/-/core-3.17.1.tgz#644aa4046b31c82d2c17276bcef2c6b78245dfeb" @@ -2607,6 +3173,22 @@ "@smithy/uuid" "^1.1.0" tslib "^2.6.2" +"@smithy/core@^3.17.2": + version "3.17.2" + resolved "https://registry.yarnpkg.com/@smithy/core/-/core-3.17.2.tgz#bd27762dfd9f61e60b2789a20fa0dfd647827e98" + integrity sha512-n3g4Nl1Te+qGPDbNFAYf+smkRVB+JhFsGy9uJXXZQEufoP4u0r+WLh6KvTDolCswaagysDc/afS1yvb2jnj1gQ== + dependencies: + "@smithy/middleware-serde" "^4.2.4" + "@smithy/protocol-http" "^5.3.4" + "@smithy/types" "^4.8.1" + "@smithy/util-base64" "^4.3.0" + "@smithy/util-body-length-browser" "^4.2.0" + "@smithy/util-middleware" "^4.2.4" + "@smithy/util-stream" "^4.5.5" + "@smithy/util-utf8" "^4.2.0" + "@smithy/uuid" "^1.1.0" + tslib "^2.6.2" + "@smithy/credential-provider-imds@^4.2.3": version "4.2.3" resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.3.tgz#b35d0d1f1b28f415e06282999eba2d53eb10a1c5" @@ -2618,6 +3200,17 @@ "@smithy/url-parser" "^4.2.3" tslib "^2.6.2" +"@smithy/credential-provider-imds@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.4.tgz#eb2ab999136c97d942e69638e6126a3c4d8cf79d" + integrity sha512-YVNMjhdz2pVto5bRdux7GMs0x1m0Afz3OcQy/4Yf9DH4fWOtroGH7uLvs7ZmDyoBJzLdegtIPpXrpJOZWvUXdw== + dependencies: + "@smithy/node-config-provider" "^4.3.4" + "@smithy/property-provider" "^4.2.4" + "@smithy/types" "^4.8.1" + "@smithy/url-parser" "^4.2.4" + tslib "^2.6.2" + "@smithy/eventstream-codec@^4.2.3": version "4.2.3" resolved "https://registry.yarnpkg.com/@smithy/eventstream-codec/-/eventstream-codec-4.2.3.tgz#dd65d9050c322f0805ba62749a3801985a2f5394" @@ -2628,6 +3221,16 @@ "@smithy/util-hex-encoding" "^4.2.0" tslib "^2.6.2" +"@smithy/eventstream-codec@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-codec/-/eventstream-codec-4.2.4.tgz#f9cc680b156d3fac4cc631a8b0159f5e87205143" + integrity sha512-aV8blR9RBDKrOlZVgjOdmOibTC2sBXNiT7WA558b4MPdsLTV6sbyc1WIE9QiIuYMJjYtnPLciefoqSW8Gi+MZQ== + dependencies: + "@aws-crypto/crc32" "5.2.0" + "@smithy/types" "^4.8.1" + "@smithy/util-hex-encoding" "^4.2.0" + tslib "^2.6.2" + "@smithy/eventstream-serde-browser@^4.2.3": version "4.2.3" resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.3.tgz#57fb9c10daac12647a0b97ef04330d706cbe9494" @@ -2637,6 +3240,15 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@smithy/eventstream-serde-browser@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.4.tgz#6aa94f14dd4d3376cb3389a0f6f245994e9e97c7" + integrity sha512-d5T7ZS3J/r8P/PDjgmCcutmNxnSRvPH1U6iHeXjzI50sMr78GLmFcrczLw33Ap92oEKqa4CLrkAPeSSOqvGdUA== + dependencies: + "@smithy/eventstream-serde-universal" "^4.2.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@smithy/eventstream-serde-config-resolver@^4.3.3": version "4.3.3" resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.3.tgz#ca1a7d272ae939aee303da40aa476656d785f75f" @@ -2645,6 +3257,14 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@smithy/eventstream-serde-config-resolver@^4.3.4": + version "4.3.4" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.4.tgz#6ddd88c57274a6fe72e11bfd5ac858977573dc46" + integrity sha512-lxfDT0UuSc1HqltOGsTEAlZ6H29gpfDSdEPTapD5G63RbnYToZ+ezjzdonCCH90j5tRRCw3aLXVbiZaBW3VRVg== + dependencies: + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@smithy/eventstream-serde-node@^4.2.3": version "4.2.3" resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.3.tgz#f1b33bb576bf7222b6bd6bc2ad845068ccf53f16" @@ -2654,6 +3274,15 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@smithy/eventstream-serde-node@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.4.tgz#61934c44c511bec5b07cfbbf59a2282806cd2ff8" + integrity sha512-TPhiGByWnYyzcpU/K3pO5V7QgtXYpE0NaJPEZBCa1Y5jlw5SjqzMSbFiLb+ZkJhqoQc0ImGyVINqnq1ze0ZRcQ== + dependencies: + "@smithy/eventstream-serde-universal" "^4.2.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@smithy/eventstream-serde-universal@^4.2.3": version "4.2.3" resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.3.tgz#86194daa2cd2496e413723465360d80f32ad7252" @@ -2663,6 +3292,15 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@smithy/eventstream-serde-universal@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.4.tgz#7c19762047b429d53af4664dc1168482706b4ee7" + integrity sha512-GNI/IXaY/XBB1SkGBFmbW033uWA0tj085eCxYih0eccUe/PFR7+UBQv9HNDk2fD9TJu7UVsCWsH99TkpEPSOzQ== + dependencies: + "@smithy/eventstream-codec" "^4.2.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@smithy/fetch-http-handler@^5.3.4": version "5.3.4" resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.4.tgz#af6dd2f63550494c84ef029a5ceda81ef46965d3" @@ -2674,6 +3312,27 @@ "@smithy/util-base64" "^4.3.0" tslib "^2.6.2" +"@smithy/fetch-http-handler@^5.3.5": + version "5.3.5" + resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.5.tgz#5cfea38d9a1519741c7147fea10a4a064de03f66" + integrity sha512-mg83SM3FLI8Sa2ooTJbsh5MFfyMTyNRwxqpKHmE0ICRIa66Aodv80DMsTQI02xBLVJ0hckwqTRr5IGAbbWuFLQ== + dependencies: + "@smithy/protocol-http" "^5.3.4" + "@smithy/querystring-builder" "^4.2.4" + "@smithy/types" "^4.8.1" + "@smithy/util-base64" "^4.3.0" + tslib "^2.6.2" + +"@smithy/hash-blob-browser@^4.2.5": + version "4.2.5" + resolved "https://registry.yarnpkg.com/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.5.tgz#c82e032747b72811f735c2c1f0ed0c1aeb4de910" + integrity sha512-kCdgjD2J50qAqycYx0imbkA9tPtyQr1i5GwbK/EOUkpBmJGSkJe4mRJm+0F65TUSvvui1HZ5FFGFCND7l8/3WQ== + dependencies: + "@smithy/chunked-blob-reader" "^5.2.0" + "@smithy/chunked-blob-reader-native" "^4.2.1" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@smithy/hash-node@^4.2.3": version "4.2.3" resolved "https://registry.yarnpkg.com/@smithy/hash-node/-/hash-node-4.2.3.tgz#c85711fca84e022f05c71b921f98cb6a0f48e5ca" @@ -2684,6 +3343,25 @@ "@smithy/util-utf8" "^4.2.0" tslib "^2.6.2" +"@smithy/hash-node@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@smithy/hash-node/-/hash-node-4.2.4.tgz#45bd19999625166825eb29aafb007819de031894" + integrity sha512-kKU0gVhx/ppVMntvUOZE7WRMFW86HuaxLwvqileBEjL7PoILI8/djoILw3gPQloGVE6O0oOzqafxeNi2KbnUJw== + dependencies: + "@smithy/types" "^4.8.1" + "@smithy/util-buffer-from" "^4.2.0" + "@smithy/util-utf8" "^4.2.0" + tslib "^2.6.2" + +"@smithy/hash-stream-node@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@smithy/hash-stream-node/-/hash-stream-node-4.2.4.tgz#553fa9a8fe567b0018cf99be3dafb920bc241a7f" + integrity sha512-amuh2IJiyRfO5MV0X/YFlZMD6banjvjAwKdeJiYGUbId608x+oSNwv3vlyW2Gt6AGAgl3EYAuyYLGRX/xU8npQ== + dependencies: + "@smithy/types" "^4.8.1" + "@smithy/util-utf8" "^4.2.0" + tslib "^2.6.2" + "@smithy/invalid-dependency@^4.2.3": version "4.2.3" resolved "https://registry.yarnpkg.com/@smithy/invalid-dependency/-/invalid-dependency-4.2.3.tgz#4f126ddde90fe3d69d522fc37256ee853246c1ec" @@ -2692,6 +3370,14 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@smithy/invalid-dependency@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@smithy/invalid-dependency/-/invalid-dependency-4.2.4.tgz#ff957d711b72f432803fdee1e247f0dd4c98251d" + integrity sha512-z6aDLGiHzsMhbS2MjetlIWopWz//K+mCoPXjW6aLr0mypF+Y7qdEh5TyJ20Onf9FbWHiWl4eC+rITdizpnXqOw== + dependencies: + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@smithy/is-array-buffer@^2.2.0": version "2.2.0" resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz#f84f0d9f9a36601a9ca9381688bd1b726fd39111" @@ -2715,6 +3401,15 @@ "@smithy/util-utf8" "^4.2.0" tslib "^2.6.2" +"@smithy/md5-js@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@smithy/md5-js/-/md5-js-4.2.4.tgz#e012464383ffde0bd423d38ef9b5caf720ee90eb" + integrity sha512-h7kzNWZuMe5bPnZwKxhVbY1gan5+TZ2c9JcVTHCygB14buVGOZxLl+oGfpY2p2Xm48SFqEWdghpvbBdmaz3ncQ== + dependencies: + "@smithy/types" "^4.8.1" + "@smithy/util-utf8" "^4.2.0" + tslib "^2.6.2" + "@smithy/middleware-content-length@^4.2.3": version "4.2.3" resolved "https://registry.yarnpkg.com/@smithy/middleware-content-length/-/middleware-content-length-4.2.3.tgz#b7d1d79ae674dad17e35e3518db4b1f0adc08964" @@ -2724,6 +3419,15 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@smithy/middleware-content-length@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@smithy/middleware-content-length/-/middleware-content-length-4.2.4.tgz#8b625cb264c13c54440ecae59a3e6b1996dfd7b5" + integrity sha512-hJRZuFS9UsElX4DJSJfoX4M1qXRH+VFiLMUnhsWvtOOUWRNvvOfDaUSdlNbjwv1IkpVjj/Rd/O59Jl3nhAcxow== + dependencies: + "@smithy/protocol-http" "^5.3.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@smithy/middleware-endpoint@^4.3.4", "@smithy/middleware-endpoint@^4.3.5": version "4.3.5" resolved "https://registry.yarnpkg.com/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.5.tgz#c22f82f83f0b5cc6c0866a2a87b65bc2e79af352" @@ -2738,6 +3442,20 @@ "@smithy/util-middleware" "^4.2.3" tslib "^2.6.2" +"@smithy/middleware-endpoint@^4.3.6": + version "4.3.6" + resolved "https://registry.yarnpkg.com/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.6.tgz#dce57120e72ffeb2d45f1d09d424a9bed1571a21" + integrity sha512-PXehXofGMFpDqr933rxD8RGOcZ0QBAWtuzTgYRAHAL2BnKawHDEdf/TnGpcmfPJGwonhginaaeJIKluEojiF/w== + dependencies: + "@smithy/core" "^3.17.2" + "@smithy/middleware-serde" "^4.2.4" + "@smithy/node-config-provider" "^4.3.4" + "@smithy/shared-ini-file-loader" "^4.3.4" + "@smithy/types" "^4.8.1" + "@smithy/url-parser" "^4.2.4" + "@smithy/util-middleware" "^4.2.4" + tslib "^2.6.2" + "@smithy/middleware-retry@^4.4.4", "@smithy/middleware-retry@^4.4.5": version "4.4.5" resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-4.4.5.tgz#5bdb6ba1be6a97272b79fdac99db40c5e7ab81e0" @@ -2753,6 +3471,21 @@ "@smithy/uuid" "^1.1.0" tslib "^2.6.2" +"@smithy/middleware-retry@^4.4.6": + version "4.4.6" + resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-4.4.6.tgz#b3c781b42b8f1ab22ee71358c0e81303cb00d737" + integrity sha512-OhLx131znrEDxZPAvH/OYufR9d1nB2CQADyYFN4C3V/NQS7Mg4V6uvxHC/Dr96ZQW8IlHJTJ+vAhKt6oxWRndA== + dependencies: + "@smithy/node-config-provider" "^4.3.4" + "@smithy/protocol-http" "^5.3.4" + "@smithy/service-error-classification" "^4.2.4" + "@smithy/smithy-client" "^4.9.2" + "@smithy/types" "^4.8.1" + "@smithy/util-middleware" "^4.2.4" + "@smithy/util-retry" "^4.2.4" + "@smithy/uuid" "^1.1.0" + tslib "^2.6.2" + "@smithy/middleware-serde@^4.2.3": version "4.2.3" resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-4.2.3.tgz#a827e9c4ea9e51c79cca4d6741d582026a8b53eb" @@ -2762,6 +3495,15 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@smithy/middleware-serde@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-4.2.4.tgz#43da8ac40e2bcdd30e705a6047a3a667ce44433c" + integrity sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg== + dependencies: + "@smithy/protocol-http" "^5.3.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@smithy/middleware-stack@^4.2.3": version "4.2.3" resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-4.2.3.tgz#5a315aa9d0fd4faaa248780297c8cbacc31c2eba" @@ -2770,6 +3512,14 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@smithy/middleware-stack@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-4.2.4.tgz#9c833c3c8f2ddda1e2e31c9315ffa31f0f0aa85d" + integrity sha512-Gy3TKCOnm9JwpFooldwAboazw+EFYlC+Bb+1QBsSi5xI0W5lX81j/P5+CXvD/9ZjtYKRgxq+kkqd/KOHflzvgA== + dependencies: + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@smithy/node-config-provider@^4.3.3": version "4.3.3" resolved "https://registry.yarnpkg.com/@smithy/node-config-provider/-/node-config-provider-4.3.3.tgz#44140a1e6bc666bcf16faf68c35d3dae4ba8cad5" @@ -2780,6 +3530,16 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@smithy/node-config-provider@^4.3.4": + version "4.3.4" + resolved "https://registry.yarnpkg.com/@smithy/node-config-provider/-/node-config-provider-4.3.4.tgz#9e41d45167568dbd2e1bc2c24a25cb26c3fd847f" + integrity sha512-3X3w7qzmo4XNNdPKNS4nbJcGSwiEMsNsRSunMA92S4DJLLIrH5g1AyuOA2XKM9PAPi8mIWfqC+fnfKNsI4KvHw== + dependencies: + "@smithy/property-provider" "^4.2.4" + "@smithy/shared-ini-file-loader" "^4.3.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@smithy/node-http-handler@^4.4.2", "@smithy/node-http-handler@^4.4.3": version "4.4.3" resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-4.4.3.tgz#fb2d16719cb4e8df0c189e8bde60e837df5c0c5b" @@ -2791,6 +3551,17 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@smithy/node-http-handler@^4.4.4": + version "4.4.4" + resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-4.4.4.tgz#e0ccaae333960df7e9387e9487554b98674b7720" + integrity sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA== + dependencies: + "@smithy/abort-controller" "^4.2.4" + "@smithy/protocol-http" "^5.3.4" + "@smithy/querystring-builder" "^4.2.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@smithy/property-provider@^4.2.3": version "4.2.3" resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-4.2.3.tgz#a6c82ca0aa1c57f697464bee496f3fec58660864" @@ -2799,6 +3570,14 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@smithy/property-provider@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-4.2.4.tgz#ea36ed8f1e282060aaf5cd220f2b428682d52775" + integrity sha512-g2DHo08IhxV5GdY3Cpt/jr0mkTlAD39EJKN27Jb5N8Fb5qt8KG39wVKTXiTRCmHHou7lbXR8nKVU14/aRUf86w== + dependencies: + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@smithy/protocol-http@^5.3.3": version "5.3.3" resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-5.3.3.tgz#55b35c18bdc0f6d86e78f63961e50ba4ff1c5d73" @@ -2807,6 +3586,14 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@smithy/protocol-http@^5.3.4": + version "5.3.4" + resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-5.3.4.tgz#2773de28d0b7e8b0ab83e94673fee0966fc8c68c" + integrity sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw== + dependencies: + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@smithy/querystring-builder@^4.2.3": version "4.2.3" resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-4.2.3.tgz#ca273ae8c21fce01a52632202679c0f9e2acf41a" @@ -2816,6 +3603,15 @@ "@smithy/util-uri-escape" "^4.2.0" tslib "^2.6.2" +"@smithy/querystring-builder@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-4.2.4.tgz#9f57301a895bb986cf7740edd70a91df335e6109" + integrity sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig== + dependencies: + "@smithy/types" "^4.8.1" + "@smithy/util-uri-escape" "^4.2.0" + tslib "^2.6.2" + "@smithy/querystring-parser@^4.2.3": version "4.2.3" resolved "https://registry.yarnpkg.com/@smithy/querystring-parser/-/querystring-parser-4.2.3.tgz#b6d7d5cd300b4083c62d9bd30915f782d01f503e" @@ -2824,6 +3620,14 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@smithy/querystring-parser@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@smithy/querystring-parser/-/querystring-parser-4.2.4.tgz#c0cc9b13855e9fc45a0c75ae26482eab6891a25e" + integrity sha512-aHb5cqXZocdzEkZ/CvhVjdw5l4r1aU/9iMEyoKzH4eXMowT6M0YjBpp7W/+XjkBnY8Xh0kVd55GKjnPKlCwinQ== + dependencies: + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@smithy/service-error-classification@^4.2.3": version "4.2.3" resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-4.2.3.tgz#ecb41dd514841eebb93e91012ae5e343040f6828" @@ -2831,6 +3635,13 @@ dependencies: "@smithy/types" "^4.8.0" +"@smithy/service-error-classification@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-4.2.4.tgz#acace7208270c8a9c4f2218092866b4d650d4719" + integrity sha512-fdWuhEx4+jHLGeew9/IvqVU/fxT/ot70tpRGuOLxE3HzZOyKeTQfYeV1oaBXpzi93WOk668hjMuuagJ2/Qs7ng== + dependencies: + "@smithy/types" "^4.8.1" + "@smithy/shared-ini-file-loader@^4.3.3": version "4.3.3" resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.3.3.tgz#1d5162cd3a14f57e4fde56f65aa188e8138c1248" @@ -2839,6 +3650,14 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@smithy/shared-ini-file-loader@^4.3.4": + version "4.3.4" + resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.3.4.tgz#ba0707daba05d7705ae120abdc27dbfa5b5b9049" + integrity sha512-y5ozxeQ9omVjbnJo9dtTsdXj9BEvGx2X8xvRgKnV+/7wLBuYJQL6dOa/qMY6omyHi7yjt1OA97jZLoVRYi8lxA== + dependencies: + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@smithy/signature-v4@^5.3.3": version "5.3.3" resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-5.3.3.tgz#5ff13cfaa29cb531061c2582cb599b39e040e52e" @@ -2853,6 +3672,20 @@ "@smithy/util-utf8" "^4.2.0" tslib "^2.6.2" +"@smithy/signature-v4@^5.3.4": + version "5.3.4" + resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-5.3.4.tgz#d2233c39ce0b02041a11c5cfd210f3e61982931a" + integrity sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A== + dependencies: + "@smithy/is-array-buffer" "^4.2.0" + "@smithy/protocol-http" "^5.3.4" + "@smithy/types" "^4.8.1" + "@smithy/util-hex-encoding" "^4.2.0" + "@smithy/util-middleware" "^4.2.4" + "@smithy/util-uri-escape" "^4.2.0" + "@smithy/util-utf8" "^4.2.0" + tslib "^2.6.2" + "@smithy/smithy-client@^4.9.0", "@smithy/smithy-client@^4.9.1": version "4.9.1" resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-4.9.1.tgz#a36e456e837121b2ded6f7d5f1f30b205c446e20" @@ -2866,6 +3699,19 @@ "@smithy/util-stream" "^4.5.4" tslib "^2.6.2" +"@smithy/smithy-client@^4.9.2": + version "4.9.2" + resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-4.9.2.tgz#6f9d916da362de7ac8e685112e3f68a9eba56b94" + integrity sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg== + dependencies: + "@smithy/core" "^3.17.2" + "@smithy/middleware-endpoint" "^4.3.6" + "@smithy/middleware-stack" "^4.2.4" + "@smithy/protocol-http" "^5.3.4" + "@smithy/types" "^4.8.1" + "@smithy/util-stream" "^4.5.5" + tslib "^2.6.2" + "@smithy/types@^4.5.0", "@smithy/types@^4.8.0": version "4.8.0" resolved "https://registry.yarnpkg.com/@smithy/types/-/types-4.8.0.tgz#e6f65e712478910b74747081e6046e68159f767d" @@ -2873,6 +3719,13 @@ dependencies: tslib "^2.6.2" +"@smithy/types@^4.8.1": + version "4.8.1" + resolved "https://registry.yarnpkg.com/@smithy/types/-/types-4.8.1.tgz#0ecad4e329340c8844e38a18c7608d84cc1c853c" + integrity sha512-N0Zn0OT1zc+NA+UVfkYqQzviRh5ucWwO7mBV3TmHHprMnfcJNfhlPicDkBHi0ewbh+y3evR6cNAW0Raxvb01NA== + dependencies: + tslib "^2.6.2" + "@smithy/url-parser@^4.2.3": version "4.2.3" resolved "https://registry.yarnpkg.com/@smithy/url-parser/-/url-parser-4.2.3.tgz#82508f273a3f074d47d0919f7ce08028c6575c2f" @@ -2882,6 +3735,15 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@smithy/url-parser@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@smithy/url-parser/-/url-parser-4.2.4.tgz#36336ea90529ff00de473a2c82d1487d87a588b1" + integrity sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg== + dependencies: + "@smithy/querystring-parser" "^4.2.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@smithy/util-base64@^4.3.0": version "4.3.0" resolved "https://registry.yarnpkg.com/@smithy/util-base64/-/util-base64-4.3.0.tgz#5e287b528793aa7363877c1a02cd880d2e76241d" @@ -2938,6 +3800,16 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@smithy/util-defaults-mode-browser@^4.3.5": + version "4.3.5" + resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.5.tgz#c74f357b048d20c95aa636fa79d33bcfa799e2d0" + integrity sha512-GwaGjv/QLuL/QHQaqhf/maM7+MnRFQQs7Bsl6FlaeK6lm6U7mV5AAnVabw68cIoMl5FQFyKK62u7RWRzWL25OQ== + dependencies: + "@smithy/property-provider" "^4.2.4" + "@smithy/smithy-client" "^4.9.2" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@smithy/util-defaults-mode-node@^4.2.5", "@smithy/util-defaults-mode-node@^4.2.6": version "4.2.6" resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.6.tgz#01b7ff4605f6f981972083fee22d036e5dc4be38" @@ -2951,6 +3823,19 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@smithy/util-defaults-mode-node@^4.2.7": + version "4.2.7" + resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.7.tgz#2657623ff6f326f152966bfa52a593cd3b5cd70e" + integrity sha512-6hinjVqec0WYGsqN7h9hL/ywfULmJJNXGXnNZW7jrIn/cFuC/aVlVaiDfBIJEvKcOrmN8/EgsW69eY0gXABeHw== + dependencies: + "@smithy/config-resolver" "^4.4.1" + "@smithy/credential-provider-imds" "^4.2.4" + "@smithy/node-config-provider" "^4.3.4" + "@smithy/property-provider" "^4.2.4" + "@smithy/smithy-client" "^4.9.2" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@smithy/util-endpoints@^3.2.3": version "3.2.3" resolved "https://registry.yarnpkg.com/@smithy/util-endpoints/-/util-endpoints-3.2.3.tgz#8bbb80f1ad5769d9f73992c5979eea3b74d7baa9" @@ -2960,6 +3845,15 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@smithy/util-endpoints@^3.2.4": + version "3.2.4" + resolved "https://registry.yarnpkg.com/@smithy/util-endpoints/-/util-endpoints-3.2.4.tgz#d68a4692a55b14f2060de75715bd4664b93a4353" + integrity sha512-f+nBDhgYRCmUEDKEQb6q0aCcOTXRDqH5wWaFHJxt4anB4pKHlgGoYP3xtioKXH64e37ANUkzWf6p4Mnv1M5/Vg== + dependencies: + "@smithy/node-config-provider" "^4.3.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@smithy/util-hex-encoding@^4.2.0": version "4.2.0" resolved "https://registry.yarnpkg.com/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.0.tgz#1c22ea3d1e2c3a81ff81c0a4f9c056a175068a7b" @@ -2975,6 +3869,14 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@smithy/util-middleware@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@smithy/util-middleware/-/util-middleware-4.2.4.tgz#d66d6b67c4c90be7bf0659f57000122b1a6bbf82" + integrity sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg== + dependencies: + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@smithy/util-retry@^4.2.3": version "4.2.3" resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-4.2.3.tgz#b1e5c96d96aaf971b68323ff8ba8754f914f22a0" @@ -2984,6 +3886,15 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@smithy/util-retry@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-4.2.4.tgz#1f466d3bc5b5f114994ac2298e859815f3a8deec" + integrity sha512-yQncJmj4dtv/isTXxRb4AamZHy4QFr4ew8GxS6XLWt7sCIxkPxPzINWd7WLISEFPsIan14zrKgvyAF+/yzfwoA== + dependencies: + "@smithy/service-error-classification" "^4.2.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@smithy/util-stream@^4.5.3", "@smithy/util-stream@^4.5.4": version "4.5.4" resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-4.5.4.tgz#bfc60e2714c2065b8e7e91ca921cc31c73efdbd4" @@ -2998,6 +3909,20 @@ "@smithy/util-utf8" "^4.2.0" tslib "^2.6.2" +"@smithy/util-stream@^4.5.5": + version "4.5.5" + resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-4.5.5.tgz#a3fd73775c65dd23370d021b8818914a2c44f28e" + integrity sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w== + dependencies: + "@smithy/fetch-http-handler" "^5.3.5" + "@smithy/node-http-handler" "^4.4.4" + "@smithy/types" "^4.8.1" + "@smithy/util-base64" "^4.3.0" + "@smithy/util-buffer-from" "^4.2.0" + "@smithy/util-hex-encoding" "^4.2.0" + "@smithy/util-utf8" "^4.2.0" + tslib "^2.6.2" + "@smithy/util-uri-escape@^4.2.0": version "4.2.0" resolved "https://registry.yarnpkg.com/@smithy/util-uri-escape/-/util-uri-escape-4.2.0.tgz#096a4cec537d108ac24a68a9c60bee73fc7e3a9e" @@ -3030,6 +3955,15 @@ "@smithy/types" "^4.8.0" tslib "^2.6.2" +"@smithy/util-waiter@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@smithy/util-waiter/-/util-waiter-4.2.4.tgz#a28b7835aacd82ae2d10da5af5bf21b3c21b34ac" + integrity sha512-roKXtXIC6fopFvVOju8VYHtguc/jAcMlK8IlDOHsrQn0ayMkHynjm/D2DCMRf7MJFXzjHhlzg2edr3QPEakchQ== + dependencies: + "@smithy/abort-controller" "^4.2.4" + "@smithy/types" "^4.8.1" + tslib "^2.6.2" + "@smithy/uuid@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@smithy/uuid/-/uuid-1.1.0.tgz#9fd09d3f91375eab94f478858123387df1cda987" From c83dc5b2fc5cf0018862b5664669a78978669ef9 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 20:11:09 -0500 Subject: [PATCH 10/25] Move to archive after 90d --- terraform/modules/assets/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/modules/assets/main.tf b/terraform/modules/assets/main.tf index 32c70a8a..4dce3dfe 100644 --- a/terraform/modules/assets/main.tf +++ b/terraform/modules/assets/main.tf @@ -53,6 +53,6 @@ resource "aws_s3_bucket_intelligent_tiering_configuration" "tiering" { tiering { access_tier = "ARCHIVE_ACCESS" - days = 1 + days = 90 } } From 44889713aba3036c483df8896562361351465bdc Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 20:17:53 -0500 Subject: [PATCH 11/25] Update ref --- terraform/modules/assets/main.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/modules/assets/main.tf b/terraform/modules/assets/main.tf index 4dce3dfe..e96e1c8b 100644 --- a/terraform/modules/assets/main.tf +++ b/terraform/modules/assets/main.tf @@ -6,7 +6,7 @@ locals { } module "buckets" { - source = "git::https://github.com/acm-uiuc/terraform-modules.git//multiregion-s3?ref=976e9eb8f8a29746b02d9e85d78d2952e3548bf9" + source = "git::https://github.com/acm-uiuc/terraform-modules.git//multiregion-s3?ref=v2.0.0" Region1 = var.PrimaryRegion Region2 = var.SecondaryRegion BucketPrefix = local.asset_bucket_prefix From f1dea796e4f23a336c56c7be81ecd3b87d5f1db0 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 21:20:12 -0500 Subject: [PATCH 12/25] Setup S3 bucket CORS --- terraform/envs/prod/main.tf | 5 +++-- terraform/envs/qa/main.tf | 5 +++-- terraform/modules/assets/main.tf | 13 +++++++++++++ terraform/modules/assets/variables.tf | 5 +++++ 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/terraform/envs/prod/main.tf b/terraform/envs/prod/main.tf index 4d3402e6..a680ecc0 100644 --- a/terraform/envs/prod/main.tf +++ b/terraform/envs/prod/main.tf @@ -128,8 +128,9 @@ module "frontend" { } module "assets" { - source = "../../modules/assets" - ProjectId = var.ProjectId + source = "../../modules/assets" + ProjectId = var.ProjectId + BucketAllowedCorsOrigins = ["https://${var.CorePublicDomain}"] } resource "aws_lambda_event_source_mapping" "queue_consumer" { diff --git a/terraform/envs/qa/main.tf b/terraform/envs/qa/main.tf index f9ad43cc..61007996 100644 --- a/terraform/envs/qa/main.tf +++ b/terraform/envs/qa/main.tf @@ -131,8 +131,9 @@ module "frontend" { } module "assets" { - source = "../../modules/assets" - ProjectId = var.ProjectId + source = "../../modules/assets" + ProjectId = var.ProjectId + BucketAllowedCorsOrigins = ["https://${var.CorePublicDomain}", "http://localhost:5173"] } // Multi-Region Failover: US-West-2 diff --git a/terraform/modules/assets/main.tf b/terraform/modules/assets/main.tf index e96e1c8b..4ae34295 100644 --- a/terraform/modules/assets/main.tf +++ b/terraform/modules/assets/main.tf @@ -56,3 +56,16 @@ resource "aws_s3_bucket_intelligent_tiering_configuration" "tiering" { days = 90 } } + +resource "aws_s3_bucket_cors_configuration" "ui_uploads" { + for_each = module.buckets.buckets_info + bucket = each.value.id + region = each.key + cors_rule { + allowed_headers = ["*"] + allowed_methods = ["GET", "PUT"] + allowed_origins = var.BucketAllowedCorsOrigins + expose_headers = ["ETag"] + max_age_seconds = 3000 + } +} diff --git a/terraform/modules/assets/variables.tf b/terraform/modules/assets/variables.tf index d7a93e1f..5b26e5e7 100644 --- a/terraform/modules/assets/variables.tf +++ b/terraform/modules/assets/variables.tf @@ -12,3 +12,8 @@ variable "ProjectId" { type = string description = "Prefix before each resource" } + +variable "BucketAllowedCorsOrigins" { + type = list(string) + description = "List of URLs that bucket can be read/written from." +} From 1e9dfe251caa68bd16c487c4386dd0a0c3961bd9 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 22:00:35 -0500 Subject: [PATCH 13/25] Add room requests attachment functionality --- src/api/functions/s3.ts | 37 +- src/api/routes/roomRequests.ts | 159 +++++++- src/common/types/roomRequest.ts | 8 +- src/ui/App.tsx | 1 + src/ui/package.json | 5 +- .../roomRequest/ViewRoomRequest.page.tsx | 255 +++++++++++- src/ui/util/s3.ts | 69 ++++ yarn.lock | 384 ++++++++++++++---- 8 files changed, 811 insertions(+), 107 deletions(-) create mode 100644 src/ui/util/s3.ts diff --git a/src/api/functions/s3.ts b/src/api/functions/s3.ts index 99dff8e9..79449971 100644 --- a/src/api/functions/s3.ts +++ b/src/api/functions/s3.ts @@ -1,4 +1,8 @@ -import { PutObjectCommand, type S3Client } from "@aws-sdk/client-s3"; +import { + GetObjectCommand, + PutObjectCommand, + type S3Client, +} from "@aws-sdk/client-s3"; import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; import { InternalServerError } from "common/errors/index.js"; @@ -8,7 +12,7 @@ export type CreatePresignedPutInputs = { key: string; length: number; mimeType: string; - md5hash: string; // Must be a base64-encoded MD5 hash + md5hash?: string; // Must be a base64-encoded MD5 hash urlExpiresIn?: number; }; @@ -39,3 +43,32 @@ export async function createPresignedPut({ }); } } + +export type CreatePresignedGetInputs = { + s3client: S3Client; + bucketName: string; + key: string; + urlExpiresIn?: number; +}; + +export async function createPresignedGet({ + s3client, + bucketName, + key, + urlExpiresIn, +}: CreatePresignedGetInputs) { + const command = new GetObjectCommand({ + Bucket: bucketName, + Key: key, + }); + + const expiresIn = urlExpiresIn || 900; + + try { + return await getSignedUrl(s3client, command, { expiresIn }); + } catch (err) { + throw new InternalServerError({ + message: "Could not create S3 download presigned url.", + }); + } +} diff --git a/src/api/routes/roomRequests.ts b/src/api/routes/roomRequests.ts index 26e3233f..d7f5a187 100644 --- a/src/api/routes/roomRequests.ts +++ b/src/api/routes/roomRequests.ts @@ -12,8 +12,10 @@ import { DatabaseFetchError, DatabaseInsertError, InternalServerError, + NotFoundError, } from "common/errors/index.js"; import { + GetItemCommand, QueryCommand, TransactWriteItemsCommand, } from "@aws-sdk/client-dynamodb"; @@ -30,10 +32,9 @@ import { generateProjectionParams, getAllUserEmails, getDefaultFilteringQuerystring, - nonEmptyCommaSeparatedStringSchema, } from "common/utils.js"; import { ROOM_RESERVATION_RETENTION_DAYS } from "common/constants.js"; -import { createPresignedPut } from "api/functions/s3.js"; +import { createPresignedGet, createPresignedPut } from "api/functions/s3.js"; import { S3Client } from "@aws-sdk/client-s3"; const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => { @@ -82,7 +83,7 @@ const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => { const requestId = request.params.requestId; const semesterId = request.params.semesterId; const attachmentS3key = request.body.attachmentInfo - ? `roomRequests/${requestId}/${request.body.status}/${request.body.attachmentInfo.filename}` + ? `roomRequests/${requestId}/${request.body.status}/${request.id}/${request.body.attachmentInfo.filename}` : undefined; const getReservationData = new QueryCommand({ TableName: genericConfig.RoomRequestsStatusTableName, @@ -98,8 +99,7 @@ const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => { }); let uploadUrl: string | undefined = undefined; if (request.body.attachmentInfo) { - const { md5hash, fileSizeBytes, contentType } = - request.body.attachmentInfo; + const { fileSizeBytes, contentType } = request.body.attachmentInfo; request.log.info( request.body.attachmentInfo, `Creating presigned URL to store attachment`, @@ -115,7 +115,6 @@ const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => { bucketName: fastify.environmentConfig.AssetsBucketId, length: fileSizeBytes, mimeType: contentType, - md5hash, }); } const createdNotified = @@ -136,6 +135,7 @@ const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => { TableName: genericConfig.RoomRequestsStatusTableName, Item: marshall( { + ...request.body, requestId, semesterId, "createdAt#status": `${createdAt}#${request.body.status}`, @@ -144,7 +144,6 @@ const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => { Math.floor(Date.now() / 1000) + 86400 * ROOM_RESERVATION_RETENTION_DAYS, attachmentS3key, - ...request.body, }, { removeUndefinedValues: true }, ), @@ -518,11 +517,13 @@ const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => { ExpressionAttributeValues: { ":requestId": { S: requestId }, }, - ProjectionExpression: "#createdAt,#notes,#createdBy", + ProjectionExpression: + "#createdAt,#notes,#createdBy,#attachmentS3key", ExpressionAttributeNames: { "#createdBy": "createdBy", "#createdAt": "createdAt#status", "#notes": "notes", + "#attachmentS3key": "attachmentS3key", }, }), ); @@ -533,6 +534,9 @@ const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => { createdAt: unmarshalled["createdAt#status"].split("#")[0], status: unmarshalled["createdAt#status"].split("#")[1], notes: unmarshalled.notes, + attachmentFilename: unmarshalled.attachmentS3key + ? (unmarshalled.attachmentS3key as string).split("/").at(-1) + : undefined, }; }); return reply @@ -555,6 +559,145 @@ const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => { } }, ); + fastify.withTypeProvider().get( + "/:semesterId/:requestId/attachmentDownloadUrl/:createdAt/:status", + { + schema: withRoles( + [AppRoles.ROOM_REQUEST_CREATE], + withTags(["Room Requests"], { + summary: + "Get attachment download URL for a specific room request update.", + params: z.object({ + requestId: z.string().min(1).meta({ + description: "Room request ID.", + example: "6667e095-8b04-4877-b361-f636f459ba42", + }), + semesterId: z.string().min(1).meta({ + description: "Short semester slug for a given semester.", + example: "sp25", + }), + createdAt: z.iso.datetime().meta({ + description: "When the update was created", + example: "2025-10-26T22:05:22.980Z", + }), + status: z.enum(RoomRequestStatus).meta({ + description: "The status for this room request update", + example: RoomRequestStatus.APPROVED, + }), + }), + response: { + 200: { + description: + "The attachment was found and a download link was generated.", + content: { + "application/json": { + schema: z.object({ + downloadUrl: z.url(), + }), + }, + }, + }, + }, + }), + ), + onRequest: fastify.authorizeFromSchema, + }, + async (request, reply) => { + const requestId = request.params.requestId; + const semesterId = request.params.semesterId; + let command; + if (request.userRoles?.has(AppRoles.BYPASS_OBJECT_LEVEL_AUTH)) { + command = new QueryCommand({ + TableName: genericConfig.RoomRequestsTableName, + IndexName: "RequestIdIndex", + KeyConditionExpression: "requestId = :requestId", + FilterExpression: "semesterId = :semesterId", + ExpressionAttributeValues: { + ":requestId": { S: requestId }, + ":semesterId": { S: semesterId }, + }, + Limit: 1, + }); + } else { + command = new QueryCommand({ + TableName: genericConfig.RoomRequestsTableName, + KeyConditionExpression: + "semesterId = :semesterId AND #userIdRequestId = :userRequestId", + ExpressionAttributeValues: { + ":userRequestId": { S: `${request.username}#${requestId}` }, + ":semesterId": { S: semesterId }, + }, + ExpressionAttributeNames: { + "#userIdRequestId": "userId#requestId", + }, + Limit: 1, + }); + } + try { + const resp = await fastify.dynamoClient.send(command); + if (!resp.Items || resp.Count !== 1) { + throw new DatabaseFetchError({ + message: "Recieved no response.", + }); + } + // this isn't atomic, but that's fine - a little inconsistency on this isn't a problem. + try { + const statusesResponse = await fastify.dynamoClient.send( + new GetItemCommand({ + TableName: genericConfig.RoomRequestsStatusTableName, + Key: { + requestId: { S: request.params.requestId }, + "createdAt#status": { + S: `${request.params.createdAt}#${request.params.status}`, + }, + }, + ProjectionExpression: "#attachmentS3key", + ExpressionAttributeNames: { + "#attachmentS3key": "attachmentS3key", + }, + }), + ); + if (!statusesResponse.Item) { + throw new NotFoundError({ + endpointName: request.url, + }); + } + const unmarshalled = unmarshall(statusesResponse.Item) as { + attachmentS3key?: string; + }; + if (!unmarshalled.attachmentS3key) { + throw new NotFoundError({ + endpointName: request.url, + }); + } + if (!fastify.s3Client) { + fastify.s3Client = new S3Client({ + region: genericConfig.AwsRegion, + }); + } + const url = await createPresignedGet({ + s3client: fastify.s3Client, + bucketName: fastify.environmentConfig.AssetsBucketId, + key: unmarshalled.attachmentS3key, + }); + return reply.status(200).send({ downloadUrl: url }); + } catch (e) { + request.log.error(e); + throw new DatabaseFetchError({ + message: "Could not get request attachments.", + }); + } + } catch (e) { + request.log.error(e); + if (e instanceof BaseError) { + throw e; + } + throw new DatabaseFetchError({ + message: "Could not find by ID.", + }); + } + }, + ); }; export default roomRequestRoutes; diff --git a/src/common/types/roomRequest.ts b/src/common/types/roomRequest.ts index ccadb4ee..6f1d5a51 100644 --- a/src/common/types/roomRequest.ts +++ b/src/common/types/roomRequest.ts @@ -1,7 +1,7 @@ import * as z from "zod/v4"; import { AllOrganizationNameList } from "@acm-uiuc/js-shared"; import { illinoisSemesterId } from "./generic.js" -export const validMimeTypes = ['application/pdf', 'image/jpeg', 'image/heic', 'image/pdf'] +export const validMimeTypes = ['application/pdf', 'image/jpeg', 'image/heic', 'image/png'] export const maxAttachmentSizeBytes = 1e7; // 10MB export const eventThemeOptions = [ @@ -134,7 +134,6 @@ export enum RoomRequestStatus { export const roomRequestStatusAttachmentInfo = z.object({ filename: z.string().min(1).max(100), - md5hash: z.string().length(32), fileSizeBytes: z.number().min(1).max(maxAttachmentSizeBytes), contentType: z.enum(validMimeTypes) }) @@ -146,8 +145,9 @@ export const roomRequestStatusUpdateRequest = z.object({ }); export const roomRequestStatusUpdate = roomRequestStatusUpdateRequest.extend({ - createdAt: z.string().datetime(), - createdBy: z.string().email() + createdAt: z.iso.datetime(), + createdBy: z.email(), + attachmentFilename: z.optional(z.string()) }); export const roomRequestPostResponse = z.object({ diff --git a/src/ui/App.tsx b/src/ui/App.tsx index 87e799ae..5f2253e7 100644 --- a/src/ui/App.tsx +++ b/src/ui/App.tsx @@ -1,4 +1,5 @@ import "@mantine/core/styles.css"; +import "@mantine/dropzone/styles.css"; import "@mantine/notifications/styles.css"; import "@mantine/dates/styles.css"; import { MantineProvider } from "@mantine/core"; diff --git a/src/ui/package.json b/src/ui/package.json index 815aad3c..d4ed8e82 100644 --- a/src/ui/package.json +++ b/src/ui/package.json @@ -23,10 +23,12 @@ "@azure/msal-react": "^3.0.20", "@mantine/core": "^8.3.5", "@mantine/dates": "^8.3.5", + "@mantine/dropzone": "^8.3.6", "@mantine/form": "^8.3.5", "@mantine/hooks": "^8.3.5", "@mantine/notifications": "^8.3.5", "@tabler/icons-react": "^3.35.0", + "@types/spark-md5": "^3.0.5", "@ungap/with-resolvers": "^0.1.0", "axios": "^1.10.0", "dayjs": "^1.11.18", @@ -43,6 +45,7 @@ "react-pdftotext": "^1.3.0", "react-qr-reader": "^3.0.0-beta-1", "react-router-dom": "^7.9.4", + "spark-md5": "^3.0.2", "zod": "^4.1.12", "zod-validation-error": "^4.0.2" }, @@ -93,4 +96,4 @@ "resolutions": { "pdfjs-dist": "4.5.136" } -} \ No newline at end of file +} diff --git a/src/ui/pages/roomRequest/ViewRoomRequest.page.tsx b/src/ui/pages/roomRequest/ViewRoomRequest.page.tsx index 2aeaee87..4c1abb39 100644 --- a/src/ui/pages/roomRequest/ViewRoomRequest.page.tsx +++ b/src/ui/pages/roomRequest/ViewRoomRequest.page.tsx @@ -12,7 +12,17 @@ import { Badge, Button, Loader, + Group, + rem, } from "@mantine/core"; +import { Dropzone, MIME_TYPES, FileWithPath } from "@mantine/dropzone"; +import { + IconUpload, + IconX, + IconFile, + IconAlertCircle, + IconDownload, +} from "@tabler/icons-react"; import { AuthGuard } from "@ui/components/AuthGuard"; import { AppRoles } from "@common/roles"; import { useApi } from "@ui/util/api"; @@ -25,6 +35,8 @@ import { roomRequestStatusUpdateRequest, formatStatus, roomRequestDataSchema, + validMimeTypes, + maxAttachmentSizeBytes, } from "@common/types/roomRequest"; import { useParams } from "react-router-dom"; import { getStatusColor, getStatusIcon } from "./roomRequestUtils"; @@ -33,11 +45,17 @@ import { useForm } from "@mantine/form"; import { notifications } from "@mantine/notifications"; import FullScreenLoader from "@ui/components/AuthContext/LoadingScreen"; import { zod4Resolver as zodResolver } from "mantine-form-zod-resolver"; +import { + downloadFromS3PresignedUrl, + uploadToS3PresignedUrl, +} from "@ui/util/s3"; export const ViewRoomRequest: React.FC = () => { const { semesterId, requestId } = useParams(); const [data, setData] = useState(null); const [isSubmitting, setIsSubmitting] = useState(false); + const [uploadedFile, setUploadedFile] = useState(null); + const newStatusForm = useForm<{ status: RoomRequestStatus | null; notes: string; @@ -45,13 +63,69 @@ export const ViewRoomRequest: React.FC = () => { initialValues: { status: null, notes: "" }, validate: zodResolver(roomRequestStatusUpdateRequest), }); + const handleFileDrop = async (files: FileWithPath[]) => { + if (files.length === 0) { + return; + } + const file = files[0]; + if (file.size > maxAttachmentSizeBytes) { + notifications.show({ + title: "File too large", + message: `File must be less than ${maxAttachmentSizeBytes / 1e6}MB`, + color: "red", + icon: , + }); + return; + } + + if (!validMimeTypes.includes(file.type as any)) { + notifications.show({ + title: "Invalid file type", + message: "Please upload a different file and try again.", + color: "red", + icon: , + }); + return; + } + + setUploadedFile(file); + }; + + const handleFileRemove = () => { + setUploadedFile(null); + }; + + const handleDownloadAttachment = async ( + createdAt: string, + status: string, + filename?: string, + ) => { + if (!filename) { + return; + } + try { + const response = await api.get<{ downloadUrl: string }>( + `/api/v1/roomRequests/${semesterId}/${requestId}/attachmentDownloadUrl/${createdAt}/${status}`, + ); + await downloadFromS3PresignedUrl(response.data.downloadUrl, filename); + } catch (e) { + notifications.show({ + title: "Failed to download attachment", + message: "Please try again or contact support.", + color: "red", + icon: , + }); + } + }; + const handleStatusChange = async ( payload: RoomRequestStatusUpdatePostBody, - ) => { - await api.post( + ): Promise<{ uploadUrl?: string }> => { + const response = await api.post( `/api/v1/roomRequests/${semesterId}/${requestId}/status`, payload, ); + return response.data; }; const updateData = async () => { const response = await api.get( @@ -82,16 +156,69 @@ export const ViewRoomRequest: React.FC = () => { return; } setIsSubmitting(true); - await handleStatusChange( - newStatusForm.values as RoomRequestStatusUpdatePostBody, - ); - notifications.show({ - title: "Status update submitted!", - message: "The requestor has been notified.", - }); + + // Prepare payload with optional attachment info + const payload: RoomRequestStatusUpdatePostBody = { + status: newStatusForm.values.status!, + notes: newStatusForm.values.notes, + }; + + // Add attachment info if file is uploaded + if (uploadedFile) { + payload.attachmentInfo = { + filename: uploadedFile.name, + fileSizeBytes: uploadedFile.size, + contentType: uploadedFile.type, + }; + } + + const response = await handleStatusChange(payload); + + // Handle S3 file upload if uploadUrl is returned + let uploadSuccess = true; + if (uploadedFile) { + if (response.uploadUrl) { + try { + await uploadToS3PresignedUrl( + response.uploadUrl, + uploadedFile, + uploadedFile.type, + ); + } catch (uploadError) { + uploadSuccess = false; + notifications.show({ + color: "red", + title: "File upload failed", + message: + "The status was updated but the file could not be uploaded.", + icon: , + }); + } + } else { + uploadSuccess = false; + notifications.show({ + color: "red", + title: "File upload failed", + message: + "No upload URL was provided. The status was updated but the file could not be uploaded.", + icon: , + }); + } + } + + // Show success notification if upload succeeded or no file was attached + if (uploadSuccess) { + notifications.show({ + title: "Status update submitted!", + message: "The requestor has been notified.", + }); + } + + // Always reload data and clear form since status update succeeded updateData(); setIsSubmitting(false); newStatusForm.reset(); + handleFileRemove(); } catch (e) { notifications.show({ color: "red", @@ -184,10 +311,104 @@ export const ViewRoomRequest: React.FC = () => { placeholder="Provide any requisite details needed to use the room." {...newStatusForm.getInputProps("notes")} /> + + + Attachment (Optional) + + + Upload a PDF or image file (max{" "} + {maxAttachmentSizeBytes / 1e6}MB) + + + {!uploadedFile ? ( + + + + + + + + + + + + +
+ + Drag file here or click to select + + + Attach a file ( + {validMimeTypes + .map((x) => x.split("/")[1].toUpperCase()) + .join(", ")} + ) + +
+
+
+ ) : ( + + + + +
+ + {uploadedFile.name} + + + {(uploadedFile.size / 1024).toFixed(2)} KB + +
+
+ +
+
+ )} + + )} {x.createdAt && ( { + const headers: HeadersInit = { + "Content-Type": contentType, + }; + + const response = await fetch(uploadUrl, { + method: "PUT", + headers, + body: file, + }); + + if (!response.ok) { + throw new Error( + `Failed to upload file to S3: ${response.status} ${response.statusText}`, + ); + } +} + +/** + * Download a file from S3 using a presigned URL and trigger browser download + * @param downloadUrl - The presigned URL from the server + * @param filename - The name to use when saving the file + * @throws Error if the download fails + */ +export async function downloadFromS3PresignedUrl( + downloadUrl: string, + filename: string, +): Promise { + const response = await fetch(downloadUrl, { + method: "GET", + }); + + if (!response.ok) { + throw new Error( + `Failed to download file from S3: ${response.status} ${response.statusText}`, + ); + } + + // Get the blob from the response + const blob = await response.blob(); + + // Create a temporary URL for the blob + const blobUrl = window.URL.createObjectURL(blob); + + // Create a temporary anchor element and trigger download + const anchor = document.createElement("a"); + anchor.href = blobUrl; + anchor.download = filename; + anchor.style.display = "none"; + + // Append to body, click, and clean up + document.body.appendChild(anchor); + anchor.click(); + document.body.removeChild(anchor); + + // Revoke the blob URL to free up memory + window.URL.revokeObjectURL(blobUrl); +} diff --git a/yarn.lock b/yarn.lock index 8330b2a1..10b75619 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2497,6 +2497,13 @@ dependencies: clsx "^2.1.1" +"@mantine/dropzone@^8.3.6": + version "8.3.6" + resolved "https://registry.yarnpkg.com/@mantine/dropzone/-/dropzone-8.3.6.tgz#9a36ca9f9a181330d39a71870ea8df7e012abacf" + integrity sha512-zgHEoO4z4hjDMMuVapwGoMahkp3lZvaht1bYc0e1hUMVN3FQNRWBrmczL9a3CG21a1cbUdwr8cbHIxOBFVAR+Q== + dependencies: + react-dropzone "14.3.8" + "@mantine/form@^8.3.5": version "8.3.5" resolved "https://registry.yarnpkg.com/@mantine/form/-/form-8.3.5.tgz#5dc1ab443894dddc886ff9e21fafc6fb16485feb" @@ -2523,6 +2530,21 @@ resolved "https://registry.yarnpkg.com/@mantine/store/-/store-8.3.5.tgz#46da04f11dea4777543296895980901ebd80e154" integrity sha512-qN4fFsDMy86IV9oh1gZlDTv41RAsO0grjx90FGyT5QCv7NTgcavwxB74GBkhp45W8xn+Ms/awKy+6NxnmLmW1w== +"@mapbox/node-pre-gyp@^1.0.0": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" + integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ== + dependencies: + detect-libc "^2.0.0" + https-proxy-agent "^5.0.0" + make-dir "^3.1.0" + node-fetch "^2.6.7" + nopt "^5.0.0" + npmlog "^5.0.1" + rimraf "^3.0.2" + semver "^7.3.5" + tar "^6.1.11" + "@mdx-js/react@^3.0.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-3.1.0.tgz#c4522e335b3897b9a845db1dbdd2f966ae8fb0ed" @@ -2552,72 +2574,6 @@ resolved "https://registry.yarnpkg.com/@middy/util/-/util-6.4.5.tgz#624f6b5c3be2800a572c7524cd2bd8ccc5535da5" integrity sha512-170ml5Q8QIHjric+ynwmDoa1sbcaMkUJumpUNxubqzStN7rEebVHnzXLQBwvSAhp7Fhcja7q2JLo0yjHpyPO4Q== -"@napi-rs/canvas-android-arm64@0.1.80": - version "0.1.80" - resolved "https://registry.yarnpkg.com/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.80.tgz#2779ca5c8aaeb46c85eb72d29f1eb34efd46fb45" - integrity sha512-sk7xhN/MoXeuExlggf91pNziBxLPVUqF2CAVnB57KLG/pz7+U5TKG8eXdc3pm0d7Od0WreB6ZKLj37sX9muGOQ== - -"@napi-rs/canvas-darwin-arm64@0.1.80": - version "0.1.80" - resolved "https://registry.yarnpkg.com/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.80.tgz#638eaa2d0a2a373c7d15748743182718dcd95c4b" - integrity sha512-O64APRTXRUiAz0P8gErkfEr3lipLJgM6pjATwavZ22ebhjYl/SUbpgM0xcWPQBNMP1n29afAC/Us5PX1vg+JNQ== - -"@napi-rs/canvas-darwin-x64@0.1.80": - version "0.1.80" - resolved "https://registry.yarnpkg.com/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.80.tgz#bd6bc048dbd4b02b9620d9d07117ed93e6970978" - integrity sha512-FqqSU7qFce0Cp3pwnTjVkKjjOtxMqRe6lmINxpIZYaZNnVI0H5FtsaraZJ36SiTHNjZlUB69/HhxNDT1Aaa9vA== - -"@napi-rs/canvas-linux-arm-gnueabihf@0.1.80": - version "0.1.80" - resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.80.tgz#ce6bfbeb19d9234c42df5c384e5989aa7d734789" - integrity sha512-eyWz0ddBDQc7/JbAtY4OtZ5SpK8tR4JsCYEZjCE3dI8pqoWUC8oMwYSBGCYfsx2w47cQgQCgMVRVTFiiO38hHQ== - -"@napi-rs/canvas-linux-arm64-gnu@0.1.80": - version "0.1.80" - resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.80.tgz#3b7a7832fef763826fa5fb740d5757204e52607d" - integrity sha512-qwA63t8A86bnxhuA/GwOkK3jvb+XTQaTiVML0vAWoHyoZYTjNs7BzoOONDgTnNtr8/yHrq64XXzUoLqDzU+Uuw== - -"@napi-rs/canvas-linux-arm64-musl@0.1.80": - version "0.1.80" - resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.80.tgz#d8ccd91f31d70760628623cd575134ada17690a3" - integrity sha512-1XbCOz/ymhj24lFaIXtWnwv/6eFHXDrjP0jYkc6iHQ9q8oXKzUX1Lc6bu+wuGiLhGh2GS/2JlfORC5ZcXimRcg== - -"@napi-rs/canvas-linux-riscv64-gnu@0.1.80": - version "0.1.80" - resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.80.tgz#927a3b859a0e3c691beaf52a19bc4736c4ffc9b8" - integrity sha512-XTzR125w5ZMs0lJcxRlS1K3P5RaZ9RmUsPtd1uGt+EfDyYMu4c6SEROYsxyatbbu/2+lPe7MPHOO/0a0x7L/gw== - -"@napi-rs/canvas-linux-x64-gnu@0.1.80": - version "0.1.80" - resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.80.tgz#25c0416bcedd6fadc15295e9afa8d9697232050c" - integrity sha512-BeXAmhKg1kX3UCrJsYbdQd3hIMDH/K6HnP/pG2LuITaXhXBiNdh//TVVVVCBbJzVQaV5gK/4ZOCMrQW9mvuTqA== - -"@napi-rs/canvas-linux-x64-musl@0.1.80": - version "0.1.80" - resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.80.tgz#de85d644e09120a60996bbe165cc2efee804551b" - integrity sha512-x0XvZWdHbkgdgucJsRxprX/4o4sEed7qo9rCQA9ugiS9qE2QvP0RIiEugtZhfLH3cyI+jIRFJHV4Fuz+1BHHMg== - -"@napi-rs/canvas-win32-x64-msvc@0.1.80": - version "0.1.80" - resolved "https://registry.yarnpkg.com/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.80.tgz#6bb95885d9377912d71f1372fc1916fb54d6ef0a" - integrity sha512-Z8jPsM6df5V8B1HrCHB05+bDiCxjE9QA//3YrkKIdVDEwn5RKaqOxCJDRJkl48cJbylcrJbW4HxZbTte8juuPg== - -"@napi-rs/canvas@^0.1.65": - version "0.1.80" - resolved "https://registry.yarnpkg.com/@napi-rs/canvas/-/canvas-0.1.80.tgz#53615bea56fd94e07331ab13caa7a39efc4914ab" - integrity sha512-DxuT1ClnIPts1kQx8FBmkk4BQDTfI5kIzywAaMjQSXfNnra5UFU9PwurXrl+Je3bJ6BGsp/zmshVVFbCmyI+ww== - optionalDependencies: - "@napi-rs/canvas-android-arm64" "0.1.80" - "@napi-rs/canvas-darwin-arm64" "0.1.80" - "@napi-rs/canvas-darwin-x64" "0.1.80" - "@napi-rs/canvas-linux-arm-gnueabihf" "0.1.80" - "@napi-rs/canvas-linux-arm64-gnu" "0.1.80" - "@napi-rs/canvas-linux-arm64-musl" "0.1.80" - "@napi-rs/canvas-linux-riscv64-gnu" "0.1.80" - "@napi-rs/canvas-linux-x64-gnu" "0.1.80" - "@napi-rs/canvas-linux-x64-musl" "0.1.80" - "@napi-rs/canvas-win32-x64-msvc" "0.1.80" - "@napi-rs/wasm-runtime@^0.2.11": version "0.2.11" resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.11.tgz#192c1610e1625048089ab4e35bc0649ce478500e" @@ -4570,6 +4526,11 @@ resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz#5fd3592ff10c1e9695d377020c033116cc2889f2" integrity sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ== +"@types/spark-md5@^3.0.5": + version "3.0.5" + resolved "https://registry.yarnpkg.com/@types/spark-md5/-/spark-md5-3.0.5.tgz#eddec8639217e518c26e9e221ff56bf5f5f5c900" + integrity sha512-lWf05dnD42DLVKQJZrDHtWFidcLrHuip01CtnC2/S6AMhX4t9ZlEUj4iuRlAnts0PQk7KESOqKxeGE/b6sIPGg== + "@types/superagent@^8.1.0": version "8.1.9" resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-8.1.9.tgz#28bfe4658e469838ed0bf66d898354bcab21f49f" @@ -5081,6 +5042,11 @@ resolved "https://registry.yarnpkg.com/@zxing/text-encoding/-/text-encoding-0.9.0.tgz#fb50ffabc6c7c66a0c96b4c03e3d9be74864b70b" integrity sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA== +abbrev@1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + abstract-logging@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/abstract-logging/-/abstract-logging-2.0.1.tgz#6b0c371df212db7129b57d2e7fcf282b8bf1c839" @@ -5101,6 +5067,13 @@ acorn@^8.14.0, acorn@^8.15.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + agent-base@^7.1.0, agent-base@^7.1.2: version "7.1.3" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1" @@ -5187,6 +5160,19 @@ anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" +"aproba@^1.0.3 || ^2.0.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.1.0.tgz#75500a190313d95c64e871e7e4284c6ac219f0b1" + integrity sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew== + +are-we-there-yet@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" + integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== + dependencies: + delegates "^1.0.0" + readable-stream "^3.6.0" + argon2@^0.44.0: version "0.44.0" resolved "https://registry.yarnpkg.com/argon2/-/argon2-0.44.0.tgz#65a5ba662bba66af41407aa0457decf4af101742" @@ -5371,6 +5357,11 @@ atomic-sleep@^1.0.0: resolved "https://registry.yarnpkg.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz#eb85b77a601fc932cfe432c5acd364a9e2c9075b" integrity sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ== +attr-accept@^2.2.4: + version "2.2.5" + resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-2.2.5.tgz#d7061d958e6d4f97bf8665c68b75851a0713ab5e" + integrity sha512-0bDNnY/u6pPwHDMoF0FieU354oBi0a8rD9FcsLwzcGWbc8KS8KPIi7y+s13OlVY+gMWc/9xEMUgNE6Qm8ZllYQ== + available-typed-arrays@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" @@ -5617,6 +5608,15 @@ caniuse-lite@^1.0.30001726: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001726.tgz#a15bd87d5a4bf01f6b6f70ae7c97fdfd28b5ae47" integrity sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw== +canvas@^2.11.2: + version "2.11.2" + resolved "https://registry.yarnpkg.com/canvas/-/canvas-2.11.2.tgz#553d87b1e0228c7ac0fc72887c3adbac4abbd860" + integrity sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw== + dependencies: + "@mapbox/node-pre-gyp" "^1.0.0" + nan "^2.17.0" + simple-get "^3.0.3" + chai@^5.1.1, chai@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/chai/-/chai-5.2.0.tgz#1358ee106763624114addf84ab02697e411c9c05" @@ -5678,6 +5678,11 @@ chokidar@^3.5.2, chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" +chownr@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" + integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== + cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" @@ -5752,6 +5757,11 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +color-support@^1.1.2: + version "1.1.3" + resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" + integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== + colord@^2.9.3: version "2.9.3" resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" @@ -5830,6 +5840,11 @@ confusing-browser-globals@^1.0.10: resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81" integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA== +console-control-strings@^1.0.0, console-control-strings@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== + content-disposition@^0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" @@ -6010,6 +6025,13 @@ decimal.js@^10.5.0: resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.5.0.tgz#0f371c7cf6c4898ce0afb09836db73cd82010f22" integrity sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw== +decompress-response@^4.2.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986" + integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw== + dependencies: + mimic-response "^2.0.0" + deep-eql@^5.0.1: version "5.0.2" resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341" @@ -6077,6 +6099,11 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== + denque@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1" @@ -6092,6 +6119,11 @@ dequal@^2.0.2, dequal@^2.0.3: resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== +detect-libc@^2.0.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" + integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== + detect-node-es@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493" @@ -7140,6 +7172,13 @@ file-entry-cache@^8.0.0: dependencies: flat-cache "^4.0.0" +file-selector@^2.1.0: + version "2.1.2" + resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-2.1.2.tgz#fe7c7ee9e550952dfbc863d73b14dc740d7de8b4" + integrity sha512-QgXo+mXTe8ljeqUFaX3QVHc5osSItJ/Km+xpocx0aSqWGMSCf6qYs/VnzZgS864Pjn5iceMRFigeAV7AfTlaig== + dependencies: + tslib "^2.7.0" + fill-range@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" @@ -7266,6 +7305,13 @@ fs-extra@^10.0.1: jsonfile "^6.0.1" universalify "^2.0.0" +fs-minipass@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" + integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== + dependencies: + minipass "^3.0.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -7308,6 +7354,21 @@ functions-have-names@^1.2.3: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== +gauge@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" + integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== + dependencies: + aproba "^1.0.3 || ^2.0.0" + color-support "^1.1.2" + console-control-strings "^1.0.0" + has-unicode "^2.0.1" + object-assign "^4.1.1" + signal-exit "^3.0.0" + string-width "^4.2.3" + strip-ansi "^6.0.1" + wide-align "^1.1.2" + gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -7544,6 +7605,11 @@ has-tostringtag@^1.0.2: dependencies: has-symbols "^1.0.3" +has-unicode@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== + hasown@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" @@ -7620,6 +7686,14 @@ http-proxy-agent@^7.0.2: agent-base "^7.1.0" debug "^4.3.4" +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + https-proxy-agent@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" @@ -8581,6 +8655,13 @@ make-cancellable-promise@^2.0.0: resolved "https://registry.yarnpkg.com/make-cancellable-promise/-/make-cancellable-promise-2.0.0.tgz#d582b3ea435205e31653dead33a10bea0696c2fa" integrity sha512-3SEQqTpV9oqVsIWqAcmDuaNeo7yBO3tqPtqGRcKkEo0lrzD3wqbKG9mkxO65KoOgXqj+zH2phJ2LiAsdzlogSw== +make-dir@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" + integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== + dependencies: + semver "^6.0.0" + make-dir@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" @@ -8695,6 +8776,11 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +mimic-response@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43" + integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== + min-indent@^1.0.0, min-indent@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" @@ -8726,11 +8812,31 @@ minimist@^1.1.0, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== +minipass@^3.0.0: + version "3.3.6" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" + integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== + dependencies: + yallist "^4.0.0" + +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + "minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== +minizlib@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" + integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== + dependencies: + minipass "^3.0.0" + yallist "^4.0.0" + mkdirp@^0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" @@ -8738,6 +8844,11 @@ mkdirp@^0.5.1: dependencies: minimist "^1.2.6" +mkdirp@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + mnemonist@0.38.3: version "0.38.3" resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.38.3.tgz#35ec79c1c1f4357cfda2fe264659c2775ccd7d9d" @@ -8811,6 +8922,11 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== +nan@^2.17.0: + version "2.23.0" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.23.0.tgz#24aa4ddffcc37613a2d2935b97683c1ec96093c6" + integrity sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ== + nanoid@^3.3.11: version "3.3.11" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" @@ -8866,6 +8982,13 @@ node-cache@^5.1.2: dependencies: clone "2.x" +node-fetch@^2.6.7: + version "2.7.0" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" + integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== + dependencies: + whatwg-url "^5.0.0" + node-forge@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" @@ -8907,11 +9030,28 @@ nodemon@^3.1.10: touch "^3.1.0" undefsafe "^2.0.5" +nopt@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" + integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== + dependencies: + abbrev "1" + normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +npmlog@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" + integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== + dependencies: + are-we-there-yet "^2.0.0" + console-control-strings "^1.1.0" + gauge "^3.0.0" + set-blocking "^2.0.0" + number-allocator@^1.0.9: version "1.0.14" resolved "https://registry.yarnpkg.com/number-allocator/-/number-allocator-1.0.14.tgz#1f2e32855498a7740dcc8c78bed54592d930ee4d" @@ -9239,6 +9379,11 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== +path2d@^0.2.1: + version "0.2.2" + resolved "https://registry.yarnpkg.com/path2d/-/path2d-0.2.2.tgz#cc85d61ed7827e7863a2ee36713d4b5315a3d85d" + integrity sha512-+vnG6S4dYcYxZd+CZxzXCNKdELYZSKfohrk98yajCo1PtRoDgCTrrwOvK1GT0UoAdVszagDVllQc0U1vaX4NUQ== + pathe@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716" @@ -9249,12 +9394,13 @@ pathval@^2.0.0: resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.1.tgz#8855c5a2899af072d6ac05d11e46045ad0dc605d" integrity sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ== -pdfjs-dist@4.8.69, pdfjs-dist@5.4.296, pdfjs-dist@^4.6.82, pdfjs-dist@^4.8.69, pdfjs-dist@^5.4.296: - version "4.10.38" - resolved "https://registry.yarnpkg.com/pdfjs-dist/-/pdfjs-dist-4.10.38.tgz#3ee698003790dc266cc8b55c0e662ccb9ae18f53" - integrity sha512-/Y3fcFrXEAsMjJXeL9J8+ZG9U01LbuWaYypvDW2ycW1jL269L3js3DVBjDJ0Up9Np1uqDXsDrRihHANhZOlwdQ== +pdfjs-dist@4.5.136, pdfjs-dist@4.8.69, pdfjs-dist@5.4.296, pdfjs-dist@^4.6.82, pdfjs-dist@^5.4.296: + version "4.5.136" + resolved "https://registry.yarnpkg.com/pdfjs-dist/-/pdfjs-dist-4.5.136.tgz#4c6f67252d4e23212f43b36537a142171ebfb1ca" + integrity sha512-V1BALcAN/FmxBEShLxoP73PlQZAZtzlaNfRbRhJrKvXzjLC5VaIlBAQUJuWP8iaYUmIdmdLHmt3E2TBglxOm3w== optionalDependencies: - "@napi-rs/canvas" "^0.1.65" + canvas "^2.11.2" + path2d "^0.2.1" picocolors@1.1.1, picocolors@^1.1.1: version "1.1.1" @@ -9603,6 +9749,15 @@ react-docgen@^8.0.0: dependencies: scheduler "^0.27.0" +react-dropzone@14.3.8: + version "14.3.8" + resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-14.3.8.tgz#a7eab118f8a452fe3f8b162d64454e81ba830582" + integrity sha512-sBgODnq+lcA4P296DY4wacOZz3JFpD99fp+hb//iBO2HHnyeZU3FwWyXJ6salNpqQdsZrgMrotuko/BdJMV8Ug== + dependencies: + attr-accept "^2.2.4" + file-selector "^2.1.0" + prop-types "^15.8.1" + react-is@^16.13.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" @@ -9923,6 +10078,13 @@ rimraf@2.6.3: dependencies: glob "^7.1.3" +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + rollup@^2.67.2: version "2.79.2" resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.2.tgz#f150e4a5db4b121a21a747d762f701e5e9f49090" @@ -10091,12 +10253,12 @@ semver@^5.5.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: +semver@^6.0.0, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.2, semver@^7.6.3, semver@^7.7.1: +semver@^7.3.5, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.2, semver@^7.6.3, semver@^7.7.1: version "7.7.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== @@ -10221,7 +10383,7 @@ siginfo@^2.0.0: resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== -signal-exit@^3.0.2: +signal-exit@^3.0.0, signal-exit@^3.0.2: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -10231,6 +10393,20 @@ signal-exit@^4.0.1: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== +simple-concat@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" + integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== + +simple-get@^3.0.3: + version "3.1.1" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.1.tgz#cc7ba77cfbe761036fbfce3d021af25fc5584d55" + integrity sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA== + dependencies: + decompress-response "^4.2.0" + once "^1.3.1" + simple-concat "^1.0.0" + simple-update-notifier@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz#d70b92bdab7d6d90dfd73931195a30b6e3d7cebb" @@ -10304,6 +10480,11 @@ source-map@^0.6.1, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +spark-md5@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/spark-md5/-/spark-md5-3.0.2.tgz#7952c4a30784347abcee73268e473b9c0167e3fc" + integrity sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw== + split2@^3.1.0: version "3.2.2" resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" @@ -10405,6 +10586,15 @@ stream-shift@^1.0.0, stream-shift@^1.0.2: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" @@ -10414,15 +10604,6 @@ string-width@^3.0.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -10797,6 +10978,18 @@ table@^6.9.0: string-width "^4.2.3" strip-ansi "^6.0.1" +tar@^6.1.11: + version "6.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" + integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== + dependencies: + chownr "^2.0.0" + fs-minipass "^2.0.0" + minipass "^5.0.0" + minizlib "^2.1.1" + mkdirp "^1.0.3" + yallist "^4.0.0" + test-exclude@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-7.0.1.tgz#20b3ba4906ac20994e275bbcafd68d510264c2a2" @@ -10931,6 +11124,11 @@ tr46@^5.1.0: dependencies: punycode "^2.3.1" +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + tree-kill@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" @@ -11358,6 +11556,11 @@ warning@^4.0.0: dependencies: loose-envify "^1.0.0" +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + webidl-conversions@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" @@ -11388,6 +11591,14 @@ whatwg-url@^14.0.0, whatwg-url@^14.1.1: tr46 "^5.1.0" webidl-conversions "^7.0.0" +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + which-boxed-primitive@^1.0.2, which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz#d76ec27df7fa165f18d5808374a5fe23c29b176e" @@ -11468,6 +11679,13 @@ why-is-node-running@^2.3.0: siginfo "^2.0.0" stackback "0.0.2" +wide-align@^1.1.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" + integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== + dependencies: + string-width "^1.0.2 || 2 || 3 || 4" + word-wrap@^1.2.5, word-wrap@~1.2.3: version "1.2.5" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" From fde201aca937035b409949a7d82382459851eab4 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 22:01:22 -0500 Subject: [PATCH 14/25] Remove spark-md5 --- src/ui/package.json | 4 +- yarn.lock | 356 +++++++++++--------------------------------- 2 files changed, 84 insertions(+), 276 deletions(-) diff --git a/src/ui/package.json b/src/ui/package.json index d4ed8e82..6c18eb2e 100644 --- a/src/ui/package.json +++ b/src/ui/package.json @@ -28,7 +28,6 @@ "@mantine/hooks": "^8.3.5", "@mantine/notifications": "^8.3.5", "@tabler/icons-react": "^3.35.0", - "@types/spark-md5": "^3.0.5", "@ungap/with-resolvers": "^0.1.0", "axios": "^1.10.0", "dayjs": "^1.11.18", @@ -45,7 +44,6 @@ "react-pdftotext": "^1.3.0", "react-qr-reader": "^3.0.0-beta-1", "react-router-dom": "^7.9.4", - "spark-md5": "^3.0.2", "zod": "^4.1.12", "zod-validation-error": "^4.0.2" }, @@ -96,4 +94,4 @@ "resolutions": { "pdfjs-dist": "4.5.136" } -} +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 10b75619..bb464be8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2530,21 +2530,6 @@ resolved "https://registry.yarnpkg.com/@mantine/store/-/store-8.3.5.tgz#46da04f11dea4777543296895980901ebd80e154" integrity sha512-qN4fFsDMy86IV9oh1gZlDTv41RAsO0grjx90FGyT5QCv7NTgcavwxB74GBkhp45W8xn+Ms/awKy+6NxnmLmW1w== -"@mapbox/node-pre-gyp@^1.0.0": - version "1.0.11" - resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa" - integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ== - dependencies: - detect-libc "^2.0.0" - https-proxy-agent "^5.0.0" - make-dir "^3.1.0" - node-fetch "^2.6.7" - nopt "^5.0.0" - npmlog "^5.0.1" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.11" - "@mdx-js/react@^3.0.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-3.1.0.tgz#c4522e335b3897b9a845db1dbdd2f966ae8fb0ed" @@ -2574,6 +2559,72 @@ resolved "https://registry.yarnpkg.com/@middy/util/-/util-6.4.5.tgz#624f6b5c3be2800a572c7524cd2bd8ccc5535da5" integrity sha512-170ml5Q8QIHjric+ynwmDoa1sbcaMkUJumpUNxubqzStN7rEebVHnzXLQBwvSAhp7Fhcja7q2JLo0yjHpyPO4Q== +"@napi-rs/canvas-android-arm64@0.1.81": + version "0.1.81" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.81.tgz#ad1f3a433178b22e39fb3491e924107aeb457675" + integrity sha512-78Lz+AUi+MsWupyZjXwpwQrp1QCwncPvRZrdvrROcZ9Gq9grP7LfQZiGdR8LKyHIq3OR18mDP+JESGT15V1nXw== + +"@napi-rs/canvas-darwin-arm64@0.1.81": + version "0.1.81" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.81.tgz#4b669ba98fb3d396f3439d62228ab7527c44f086" + integrity sha512-omejuKgHWKDGoh8rsgsyhm/whwxMaryTQjJTd9zD7hiB9/rzcEEJLHnzXWR5ysy4/tTjHaQotE6k2t8eodTLnA== + +"@napi-rs/canvas-darwin-x64@0.1.81": + version "0.1.81" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.81.tgz#c5bb17f2223a9163731f979f4703a417cc92eae9" + integrity sha512-EYfk+co6BElq5DXNH9PBLYDYwc4QsvIVbyrsVHsxVpn4p6Y3/s8MChgC69AGqj3vzZBQ1qx2CRCMtg5cub+XuQ== + +"@napi-rs/canvas-linux-arm-gnueabihf@0.1.81": + version "0.1.81" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.81.tgz#5cbd11e47a9ef005e660d701425c57b7acdecce0" + integrity sha512-teh6Q74CyAcH31yLNQGR9MtXSFxlZa5CI6vvNUISI14gWIJWrhOwUAOly+KRe1aztWR0FWTVSPxM4p5y+06aow== + +"@napi-rs/canvas-linux-arm64-gnu@0.1.81": + version "0.1.81" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.81.tgz#b9a8a9f24fdcd59156d7d62ce35808cbfc7f0099" + integrity sha512-AGEopHFYRzJOjxY+2G1RmHPRnuWvO3Qdhq7sIazlSjxb3Z6dZHg7OB/4ZimXaimPjDACm9qWa6t5bn9bhXvkcw== + +"@napi-rs/canvas-linux-arm64-musl@0.1.81": + version "0.1.81" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.81.tgz#48aa694e2319616b8cfdfaa035deb339b034de68" + integrity sha512-Bj3m1cl4GIhsigkdwOxii4g4Ump3/QhNpx85IgAlCCYXpaly6mcsWpuDYEabfIGWOWhDUNBOndaQUPfWK1czOQ== + +"@napi-rs/canvas-linux-riscv64-gnu@0.1.81": + version "0.1.81" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.81.tgz#eb757d56ae613c6bcc6e2413c5713463d1788d2d" + integrity sha512-yg/5NkHykVdwPlD3XObwCa/EswkOwLHswJcI9rHrac+znHsmCSj5AMX/RTU9Z9F6lZTwL60JM2Esit33XhAMiw== + +"@napi-rs/canvas-linux-x64-gnu@0.1.81": + version "0.1.81" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.81.tgz#e9384ccc5a8dc74fa4b4365ccf40b6cfd9ec180d" + integrity sha512-tPfMpSEBuV5dJSKexO/UZxpOqnYTaNbG8aKa1ek8QsWu+4SJ/foWkaxscra/RUv85vepx6WWDjzBNbNJsTnO0w== + +"@napi-rs/canvas-linux-x64-musl@0.1.81": + version "0.1.81" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.81.tgz#07c004f01dc430201f1725e585fe8ee871532450" + integrity sha512-1L0xnYgzqn8Baef+inPvY4dKqdmw3KCBoe0NEDgezuBZN7MA5xElwifoG8609uNdrMtJ9J6QZarsslLRVqri7g== + +"@napi-rs/canvas-win32-x64-msvc@0.1.81": + version "0.1.81" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.81.tgz#e76784880f26d48f4863d73c9891c53f161ffe6e" + integrity sha512-57ryVbhm/z7RE9/UVcS7mrLPdlayLesy+9U0Uf6epCoeSGrs99tfieCcgZWFbIgmByQ1AZnNtFI2N6huqDLlWQ== + +"@napi-rs/canvas@^0.1.65": + version "0.1.81" + resolved "https://registry.yarnpkg.com/@napi-rs/canvas/-/canvas-0.1.81.tgz#4cb1556171a64480e52d2a3766aa5413bfc4a9b1" + integrity sha512-ReCjd5SYI/UKx/olaQLC4GtN6wUQGjlgHXs1lvUvWGXfBMR3Fxnik3cL+OxKN5ithNdoU0/GlCrdKcQDFh2XKQ== + optionalDependencies: + "@napi-rs/canvas-android-arm64" "0.1.81" + "@napi-rs/canvas-darwin-arm64" "0.1.81" + "@napi-rs/canvas-darwin-x64" "0.1.81" + "@napi-rs/canvas-linux-arm-gnueabihf" "0.1.81" + "@napi-rs/canvas-linux-arm64-gnu" "0.1.81" + "@napi-rs/canvas-linux-arm64-musl" "0.1.81" + "@napi-rs/canvas-linux-riscv64-gnu" "0.1.81" + "@napi-rs/canvas-linux-x64-gnu" "0.1.81" + "@napi-rs/canvas-linux-x64-musl" "0.1.81" + "@napi-rs/canvas-win32-x64-msvc" "0.1.81" + "@napi-rs/wasm-runtime@^0.2.11": version "0.2.11" resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.11.tgz#192c1610e1625048089ab4e35bc0649ce478500e" @@ -4526,11 +4577,6 @@ resolved "https://registry.yarnpkg.com/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.5.tgz#5fd3592ff10c1e9695d377020c033116cc2889f2" integrity sha512-mQkU2jY8jJEF7YHjHvsQO8+3ughTL1mcnn96igfhONmR+fUPSKIkefQYpSe8bsly2Ep7oQbn/6VG5/9/0qcArQ== -"@types/spark-md5@^3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@types/spark-md5/-/spark-md5-3.0.5.tgz#eddec8639217e518c26e9e221ff56bf5f5f5c900" - integrity sha512-lWf05dnD42DLVKQJZrDHtWFidcLrHuip01CtnC2/S6AMhX4t9ZlEUj4iuRlAnts0PQk7KESOqKxeGE/b6sIPGg== - "@types/superagent@^8.1.0": version "8.1.9" resolved "https://registry.yarnpkg.com/@types/superagent/-/superagent-8.1.9.tgz#28bfe4658e469838ed0bf66d898354bcab21f49f" @@ -5042,11 +5088,6 @@ resolved "https://registry.yarnpkg.com/@zxing/text-encoding/-/text-encoding-0.9.0.tgz#fb50ffabc6c7c66a0c96b4c03e3d9be74864b70b" integrity sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA== -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - abstract-logging@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/abstract-logging/-/abstract-logging-2.0.1.tgz#6b0c371df212db7129b57d2e7fcf282b8bf1c839" @@ -5067,13 +5108,6 @@ acorn@^8.14.0, acorn@^8.15.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.15.0.tgz#a360898bc415edaac46c8241f6383975b930b816" integrity sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg== -agent-base@6: - version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" - integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== - dependencies: - debug "4" - agent-base@^7.1.0, agent-base@^7.1.2: version "7.1.3" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.3.tgz#29435eb821bc4194633a5b89e5bc4703bafc25a1" @@ -5160,19 +5194,6 @@ anymatch@~3.1.2: normalize-path "^3.0.0" picomatch "^2.0.4" -"aproba@^1.0.3 || ^2.0.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.1.0.tgz#75500a190313d95c64e871e7e4284c6ac219f0b1" - integrity sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew== - -are-we-there-yet@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c" - integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw== - dependencies: - delegates "^1.0.0" - readable-stream "^3.6.0" - argon2@^0.44.0: version "0.44.0" resolved "https://registry.yarnpkg.com/argon2/-/argon2-0.44.0.tgz#65a5ba662bba66af41407aa0457decf4af101742" @@ -5608,15 +5629,6 @@ caniuse-lite@^1.0.30001726: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001726.tgz#a15bd87d5a4bf01f6b6f70ae7c97fdfd28b5ae47" integrity sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw== -canvas@^2.11.2: - version "2.11.2" - resolved "https://registry.yarnpkg.com/canvas/-/canvas-2.11.2.tgz#553d87b1e0228c7ac0fc72887c3adbac4abbd860" - integrity sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw== - dependencies: - "@mapbox/node-pre-gyp" "^1.0.0" - nan "^2.17.0" - simple-get "^3.0.3" - chai@^5.1.1, chai@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/chai/-/chai-5.2.0.tgz#1358ee106763624114addf84ab02697e411c9c05" @@ -5678,11 +5690,6 @@ chokidar@^3.5.2, chokidar@^3.5.3: optionalDependencies: fsevents "~2.3.2" -chownr@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" - integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== - cli-cursor@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" @@ -5757,11 +5764,6 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -color-support@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" - integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== - colord@^2.9.3: version "2.9.3" resolved "https://registry.yarnpkg.com/colord/-/colord-2.9.3.tgz#4f8ce919de456f1d5c1c368c307fe20f3e59fb43" @@ -5840,11 +5842,6 @@ confusing-browser-globals@^1.0.10: resolved "https://registry.yarnpkg.com/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz#ae40e9b57cdd3915408a2805ebd3a5585608dc81" integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA== -console-control-strings@^1.0.0, console-control-strings@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== - content-disposition@^0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" @@ -6025,13 +6022,6 @@ decimal.js@^10.5.0: resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.5.0.tgz#0f371c7cf6c4898ce0afb09836db73cd82010f22" integrity sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw== -decompress-response@^4.2.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-4.2.1.tgz#414023cc7a302da25ce2ec82d0d5238ccafd8986" - integrity sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw== - dependencies: - mimic-response "^2.0.0" - deep-eql@^5.0.1: version "5.0.2" resolved "https://registry.yarnpkg.com/deep-eql/-/deep-eql-5.0.2.tgz#4b756d8d770a9257300825d52a2c2cff99c3a341" @@ -6099,11 +6089,6 @@ delayed-stream@~1.0.0: resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== - denque@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/denque/-/denque-2.1.0.tgz#e93e1a6569fb5e66f16a3c2a2964617d349d6ab1" @@ -6119,11 +6104,6 @@ dequal@^2.0.2, dequal@^2.0.3: resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== -detect-libc@^2.0.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.1.2.tgz#689c5dcdc1900ef5583a4cb9f6d7b473742074ad" - integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ== - detect-node-es@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/detect-node-es/-/detect-node-es-1.1.0.tgz#163acdf643330caa0b4cd7c21e7ee7755d6fa493" @@ -7305,13 +7285,6 @@ fs-extra@^10.0.1: jsonfile "^6.0.1" universalify "^2.0.0" -fs-minipass@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" - integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== - dependencies: - minipass "^3.0.0" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -7354,21 +7327,6 @@ functions-have-names@^1.2.3: resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== -gauge@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395" - integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q== - dependencies: - aproba "^1.0.3 || ^2.0.0" - color-support "^1.1.2" - console-control-strings "^1.0.0" - has-unicode "^2.0.1" - object-assign "^4.1.1" - signal-exit "^3.0.0" - string-width "^4.2.3" - strip-ansi "^6.0.1" - wide-align "^1.1.2" - gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -7605,11 +7563,6 @@ has-tostringtag@^1.0.2: dependencies: has-symbols "^1.0.3" -has-unicode@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== - hasown@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" @@ -7686,14 +7639,6 @@ http-proxy-agent@^7.0.2: agent-base "^7.1.0" debug "^4.3.4" -https-proxy-agent@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" - integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== - dependencies: - agent-base "6" - debug "4" - https-proxy-agent@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz#da8dfeac7da130b05c2ba4b59c9b6cd66611a6b9" @@ -8655,13 +8600,6 @@ make-cancellable-promise@^2.0.0: resolved "https://registry.yarnpkg.com/make-cancellable-promise/-/make-cancellable-promise-2.0.0.tgz#d582b3ea435205e31653dead33a10bea0696c2fa" integrity sha512-3SEQqTpV9oqVsIWqAcmDuaNeo7yBO3tqPtqGRcKkEo0lrzD3wqbKG9mkxO65KoOgXqj+zH2phJ2LiAsdzlogSw== -make-dir@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== - dependencies: - semver "^6.0.0" - make-dir@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-4.0.0.tgz#c3c2307a771277cd9638305f915c29ae741b614e" @@ -8776,11 +8714,6 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -mimic-response@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-2.1.0.tgz#d13763d35f613d09ec37ebb30bac0469c0ee8f43" - integrity sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA== - min-indent@^1.0.0, min-indent@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" @@ -8812,31 +8745,11 @@ minimist@^1.1.0, minimist@^1.2.0, minimist@^1.2.5, minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== -minipass@^3.0.0: - version "3.3.6" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" - integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== - dependencies: - yallist "^4.0.0" - -minipass@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" - integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== - "minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: version "7.1.2" resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== -minizlib@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" - integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== - dependencies: - minipass "^3.0.0" - yallist "^4.0.0" - mkdirp@^0.5.1: version "0.5.6" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" @@ -8844,11 +8757,6 @@ mkdirp@^0.5.1: dependencies: minimist "^1.2.6" -mkdirp@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - mnemonist@0.38.3: version "0.38.3" resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.38.3.tgz#35ec79c1c1f4357cfda2fe264659c2775ccd7d9d" @@ -8922,11 +8830,6 @@ mute-stream@0.0.8: resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== -nan@^2.17.0: - version "2.23.0" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.23.0.tgz#24aa4ddffcc37613a2d2935b97683c1ec96093c6" - integrity sha512-1UxuyYGdoQHcGg87Lkqm3FzefucTa0NAiOcuRsDmysep3c1LVCRK2krrUDafMWtjSG04htvAmvg96+SDknOmgQ== - nanoid@^3.3.11: version "3.3.11" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" @@ -8982,13 +8885,6 @@ node-cache@^5.1.2: dependencies: clone "2.x" -node-fetch@^2.6.7: - version "2.7.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d" - integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== - dependencies: - whatwg-url "^5.0.0" - node-forge@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" @@ -9030,28 +8926,11 @@ nodemon@^3.1.10: touch "^3.1.0" undefsafe "^2.0.5" -nopt@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" - integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== - dependencies: - abbrev "1" - normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== -npmlog@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0" - integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw== - dependencies: - are-we-there-yet "^2.0.0" - console-control-strings "^1.1.0" - gauge "^3.0.0" - set-blocking "^2.0.0" - number-allocator@^1.0.9: version "1.0.14" resolved "https://registry.yarnpkg.com/number-allocator/-/number-allocator-1.0.14.tgz#1f2e32855498a7740dcc8c78bed54592d930ee4d" @@ -9379,11 +9258,6 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -path2d@^0.2.1: - version "0.2.2" - resolved "https://registry.yarnpkg.com/path2d/-/path2d-0.2.2.tgz#cc85d61ed7827e7863a2ee36713d4b5315a3d85d" - integrity sha512-+vnG6S4dYcYxZd+CZxzXCNKdELYZSKfohrk98yajCo1PtRoDgCTrrwOvK1GT0UoAdVszagDVllQc0U1vaX4NUQ== - pathe@^2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/pathe/-/pathe-2.0.3.tgz#3ecbec55421685b70a9da872b2cff3e1cbed1716" @@ -9394,13 +9268,12 @@ pathval@^2.0.0: resolved "https://registry.yarnpkg.com/pathval/-/pathval-2.0.1.tgz#8855c5a2899af072d6ac05d11e46045ad0dc605d" integrity sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ== -pdfjs-dist@4.5.136, pdfjs-dist@4.8.69, pdfjs-dist@5.4.296, pdfjs-dist@^4.6.82, pdfjs-dist@^5.4.296: - version "4.5.136" - resolved "https://registry.yarnpkg.com/pdfjs-dist/-/pdfjs-dist-4.5.136.tgz#4c6f67252d4e23212f43b36537a142171ebfb1ca" - integrity sha512-V1BALcAN/FmxBEShLxoP73PlQZAZtzlaNfRbRhJrKvXzjLC5VaIlBAQUJuWP8iaYUmIdmdLHmt3E2TBglxOm3w== +pdfjs-dist@4.8.69, pdfjs-dist@5.4.296, pdfjs-dist@^4.6.82, pdfjs-dist@^4.8.69, pdfjs-dist@^5.4.296: + version "4.10.38" + resolved "https://registry.yarnpkg.com/pdfjs-dist/-/pdfjs-dist-4.10.38.tgz#3ee698003790dc266cc8b55c0e662ccb9ae18f53" + integrity sha512-/Y3fcFrXEAsMjJXeL9J8+ZG9U01LbuWaYypvDW2ycW1jL269L3js3DVBjDJ0Up9Np1uqDXsDrRihHANhZOlwdQ== optionalDependencies: - canvas "^2.11.2" - path2d "^0.2.1" + "@napi-rs/canvas" "^0.1.65" picocolors@1.1.1, picocolors@^1.1.1: version "1.1.1" @@ -10078,13 +9951,6 @@ rimraf@2.6.3: dependencies: glob "^7.1.3" -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - rollup@^2.67.2: version "2.79.2" resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.2.tgz#f150e4a5db4b121a21a747d762f701e5e9f49090" @@ -10253,12 +10119,12 @@ semver@^5.5.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.2.tgz#48d55db737c3287cd4835e17fa13feace1c41ef8" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== -semver@^6.0.0, semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: +semver@^6.1.2, semver@^6.3.0, semver@^6.3.1: version "6.3.1" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.5, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.2, semver@^7.6.3, semver@^7.7.1: +semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.6.2, semver@^7.6.3, semver@^7.7.1: version "7.7.3" resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.3.tgz#4b5f4143d007633a8dc671cd0a6ef9147b8bb946" integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q== @@ -10383,7 +10249,7 @@ siginfo@^2.0.0: resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30" integrity sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g== -signal-exit@^3.0.0, signal-exit@^3.0.2: +signal-exit@^3.0.2: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== @@ -10393,20 +10259,6 @@ signal-exit@^4.0.1: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== -simple-concat@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.1.tgz#f46976082ba35c2263f1c8ab5edfe26c41c9552f" - integrity sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q== - -simple-get@^3.0.3: - version "3.1.1" - resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.1.1.tgz#cc7ba77cfbe761036fbfce3d021af25fc5584d55" - integrity sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA== - dependencies: - decompress-response "^4.2.0" - once "^1.3.1" - simple-concat "^1.0.0" - simple-update-notifier@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz#d70b92bdab7d6d90dfd73931195a30b6e3d7cebb" @@ -10480,11 +10332,6 @@ source-map@^0.6.1, source-map@~0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -spark-md5@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/spark-md5/-/spark-md5-3.0.2.tgz#7952c4a30784347abcee73268e473b9c0167e3fc" - integrity sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw== - split2@^3.1.0: version "3.2.2" resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f" @@ -10586,15 +10433,6 @@ stream-shift@^1.0.0, stream-shift@^1.0.2: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" @@ -10604,6 +10442,15 @@ string-width@^3.0.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -10978,18 +10825,6 @@ table@^6.9.0: string-width "^4.2.3" strip-ansi "^6.0.1" -tar@^6.1.11: - version "6.2.1" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" - integrity sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A== - dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^5.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" - test-exclude@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-7.0.1.tgz#20b3ba4906ac20994e275bbcafd68d510264c2a2" @@ -11124,11 +10959,6 @@ tr46@^5.1.0: dependencies: punycode "^2.3.1" -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== - tree-kill@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" @@ -11556,11 +11386,6 @@ warning@^4.0.0: dependencies: loose-envify "^1.0.0" -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== - webidl-conversions@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" @@ -11591,14 +11416,6 @@ whatwg-url@^14.0.0, whatwg-url@^14.1.1: tr46 "^5.1.0" webidl-conversions "^7.0.0" -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - which-boxed-primitive@^1.0.2, which-boxed-primitive@^1.1.0, which-boxed-primitive@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz#d76ec27df7fa165f18d5808374a5fe23c29b176e" @@ -11679,13 +11496,6 @@ why-is-node-running@^2.3.0: siginfo "^2.0.0" stackback "0.0.2" -wide-align@^1.1.2: - version "1.1.5" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" - integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== - dependencies: - string-width "^1.0.2 || 2 || 3 || 4" - word-wrap@^1.2.5, word-wrap@~1.2.3: version "1.2.5" resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" From 888ad9204b18e1a3b9b1de45d68fef1734970991 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 22:21:41 -0500 Subject: [PATCH 15/25] Add unit tests --- src/ui/util/s3.test.ts | 212 ++++++++++++++++++++++++++++++++ tests/unit/roomRequests.test.ts | 122 ++++++++++++++++++ tests/unit/s3.test.ts | 187 ++++++++++++++++++++++++++++ 3 files changed, 521 insertions(+) create mode 100644 src/ui/util/s3.test.ts create mode 100644 tests/unit/s3.test.ts diff --git a/src/ui/util/s3.test.ts b/src/ui/util/s3.test.ts new file mode 100644 index 00000000..24b6efed --- /dev/null +++ b/src/ui/util/s3.test.ts @@ -0,0 +1,212 @@ +import { describe, expect, test, vi, beforeEach, afterEach } from "vitest"; +import { uploadToS3PresignedUrl, downloadFromS3PresignedUrl } from "./s3"; + +// Mock global fetch +global.fetch = vi.fn(); + +describe("S3 Utility Functions", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + afterEach(() => { + vi.restoreAllMocks(); + }); + + describe("uploadToS3PresignedUrl", () => { + test("successfully uploads a file to S3", async () => { + const mockFile = new File(["test content"], "test.pdf", { + type: "application/pdf", + }); + const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=xyz"; + const mockContentType = "application/pdf"; + + vi.mocked(fetch).mockResolvedValueOnce({ + ok: true, + status: 200, + statusText: "OK", + } as Response); + + await uploadToS3PresignedUrl(mockUrl, mockFile, mockContentType); + + expect(fetch).toHaveBeenCalledWith(mockUrl, { + method: "PUT", + headers: { + "Content-Type": mockContentType, + }, + body: mockFile, + }); + }); + + test("throws error when upload fails with non-ok response", async () => { + const mockFile = new File(["test content"], "test.pdf", { + type: "application/pdf", + }); + const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=xyz"; + const mockContentType = "application/pdf"; + + vi.mocked(fetch).mockResolvedValueOnce({ + ok: false, + status: 403, + statusText: "Forbidden", + } as Response); + + await expect( + uploadToS3PresignedUrl(mockUrl, mockFile, mockContentType), + ).rejects.toThrow("Failed to upload file to S3: 403 Forbidden"); + }); + + test("throws error when upload fails with network error", async () => { + const mockFile = new File(["test content"], "test.pdf", { + type: "application/pdf", + }); + const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=xyz"; + const mockContentType = "application/pdf"; + + vi.mocked(fetch).mockRejectedValueOnce(new Error("Network error")); + + await expect( + uploadToS3PresignedUrl(mockUrl, mockFile, mockContentType), + ).rejects.toThrow("Network error"); + }); + + test("uses correct content type header", async () => { + const mockFile = new File(["image data"], "image.png", { + type: "image/png", + }); + const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=abc"; + const mockContentType = "image/png"; + + vi.mocked(fetch).mockResolvedValueOnce({ + ok: true, + status: 200, + statusText: "OK", + } as Response); + + await uploadToS3PresignedUrl(mockUrl, mockFile, mockContentType); + + expect(fetch).toHaveBeenCalledWith(mockUrl, { + method: "PUT", + headers: { + "Content-Type": "image/png", + }, + body: mockFile, + }); + }); + }); + + describe("downloadFromS3PresignedUrl", () => { + test("successfully downloads a file and triggers browser download", async () => { + const mockBlob = new Blob(["file content"], { type: "application/pdf" }); + const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=xyz"; + const mockFilename = "downloaded-file.pdf"; + + // Mock fetch response + vi.mocked(fetch).mockResolvedValueOnce({ + ok: true, + status: 200, + statusText: "OK", + blob: vi.fn().mockResolvedValue(mockBlob), + } as unknown as Response); + + // Mock DOM APIs + const mockAnchor = { + href: "", + download: "", + style: { display: "" }, + click: vi.fn(), + }; + const createElementSpy = vi + .spyOn(document, "createElement") + .mockReturnValue(mockAnchor as unknown as HTMLElement); + const appendChildSpy = vi + .spyOn(document.body, "appendChild") + .mockImplementation(() => mockAnchor as unknown as Node); + const removeChildSpy = vi + .spyOn(document.body, "removeChild") + .mockImplementation(() => mockAnchor as unknown as Node); + + const createObjectURLSpy = vi + .spyOn(window.URL, "createObjectURL") + .mockReturnValue("blob:mock-url"); + const revokeObjectURLSpy = vi.spyOn(window.URL, "revokeObjectURL"); + + await downloadFromS3PresignedUrl(mockUrl, mockFilename); + + // Verify fetch was called correctly + expect(fetch).toHaveBeenCalledWith(mockUrl, { method: "GET" }); + + // Verify DOM manipulation + expect(createElementSpy).toHaveBeenCalledWith("a"); + expect(mockAnchor.href).toBe("blob:mock-url"); + expect(mockAnchor.download).toBe(mockFilename); + expect(mockAnchor.style.display).toBe("none"); + expect(appendChildSpy).toHaveBeenCalledWith(mockAnchor); + expect(mockAnchor.click).toHaveBeenCalled(); + expect(removeChildSpy).toHaveBeenCalledWith(mockAnchor); + expect(revokeObjectURLSpy).toHaveBeenCalledWith("blob:mock-url"); + }); + + test("throws error when download fails with non-ok response", async () => { + const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=xyz"; + const mockFilename = "file.pdf"; + + vi.mocked(fetch).mockResolvedValueOnce({ + ok: false, + status: 404, + statusText: "Not Found", + } as Response); + + await expect( + downloadFromS3PresignedUrl(mockUrl, mockFilename), + ).rejects.toThrow("Failed to download file from S3: 404 Not Found"); + }); + + test("throws error when download fails with network error", async () => { + const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=xyz"; + const mockFilename = "file.pdf"; + + vi.mocked(fetch).mockRejectedValueOnce(new Error("Network error")); + + await expect( + downloadFromS3PresignedUrl(mockUrl, mockFilename), + ).rejects.toThrow("Network error"); + }); + + test("handles different file types correctly", async () => { + const mockBlob = new Blob(["image data"], { type: "image/jpeg" }); + const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=abc"; + const mockFilename = "photo.jpg"; + + vi.mocked(fetch).mockResolvedValueOnce({ + ok: true, + status: 200, + statusText: "OK", + blob: vi.fn().mockResolvedValue(mockBlob), + } as unknown as Response); + + const mockAnchor = { + href: "", + download: "", + style: { display: "" }, + click: vi.fn(), + }; + vi.spyOn(document, "createElement").mockReturnValue( + mockAnchor as unknown as HTMLElement, + ); + vi.spyOn(document.body, "appendChild").mockImplementation( + () => mockAnchor as unknown as Node, + ); + vi.spyOn(document.body, "removeChild").mockImplementation( + () => mockAnchor as unknown as Node, + ); + vi.spyOn(window.URL, "createObjectURL").mockReturnValue("blob:mock-url"); + vi.spyOn(window.URL, "revokeObjectURL"); + + await downloadFromS3PresignedUrl(mockUrl, mockFilename); + + expect(mockAnchor.download).toBe("photo.jpg"); + expect(fetch).toHaveBeenCalledWith(mockUrl, { method: "GET" }); + }); + }); +}); diff --git a/tests/unit/roomRequests.test.ts b/tests/unit/roomRequests.test.ts index 2d3f8cc4..9b7e6675 100644 --- a/tests/unit/roomRequests.test.ts +++ b/tests/unit/roomRequests.test.ts @@ -17,6 +17,12 @@ import { SQSClient, SendMessageCommand } from "@aws-sdk/client-sqs"; import { AvailableSQSFunctions } from "../../src/common/types/sqsMessage.js"; import { RoomRequestStatus } from "../../src/common/types/roomRequest.js"; +// Mock the S3 functions module +vi.mock("../../src/api/functions/s3.js", () => ({ + createPresignedPut: vi.fn(), + createPresignedGet: vi.fn(), +})); + const ddbMock = mockClient(DynamoDBClient); const sqsMock = mockClient(SQSClient); @@ -542,4 +548,120 @@ describe("Test Room Request Creation", async () => { ); expect(body.payload.to).toEqual(["originalUser"]); }); + + test("Returns uploadUrl when attachment info is provided", async () => { + const testJwt = createJwt(); + const mockPresignedUrl = + "https://s3.amazonaws.com/bucket/key?signature=abc123"; + + // Import the mocked module + const s3Functions = await import("../../src/api/functions/s3.js"); + vi.mocked(s3Functions.createPresignedPut).mockResolvedValue( + mockPresignedUrl, + ); + + ddbMock.on(QueryCommand).resolves({ + Count: 1, + Items: [marshall({ createdBy: "originalUser" })], + }); + + ddbMock.on(TransactWriteItemsCommand).resolves({}); + sqsMock.on(SendMessageCommand).resolves({ MessageId: "mock-sqs-id" }); + + const statusBodyWithAttachment = { + status: RoomRequestStatus.APPROVED, + notes: "Request approved with attachment.", + attachmentInfo: { + filename: "approval-letter.pdf", + fileSizeBytes: 102400, + contentType: "application/pdf", + }, + }; + + await app.ready(); + const response = await supertest(app.server) + .post(makeUrl()) + .set("authorization", `Bearer ${testJwt}`) + .send(statusBodyWithAttachment); + + expect(response.statusCode).toBe(201); + expect(response.body).toHaveProperty("uploadUrl"); + expect(response.body.uploadUrl).toBe(mockPresignedUrl); + expect(s3Functions.createPresignedPut).toHaveBeenCalledOnce(); + }); + + test("Does not return uploadUrl when no attachment info is provided", async () => { + const testJwt = createJwt(); + + ddbMock.on(QueryCommand).resolves({ + Count: 1, + Items: [marshall({ createdBy: "originalUser" })], + }); + + ddbMock.on(TransactWriteItemsCommand).resolves({}); + sqsMock.on(SendMessageCommand).resolves({ MessageId: "mock-sqs-id" }); + + await app.ready(); + const response = await supertest(app.server) + .post(makeUrl()) + .set("authorization", `Bearer ${testJwt}`) + .send(statusBody); + + expect(response.statusCode).toBe(201); + expect(response.body).toEqual({}); + }); + + test("Validates attachment info schema", async () => { + const testJwt = createJwt(); + + ddbMock.on(QueryCommand).resolves({ + Count: 1, + Items: [marshall({ createdBy: "originalUser" })], + }); + + const invalidAttachmentBody = { + status: RoomRequestStatus.APPROVED, + notes: "Request approved.", + attachmentInfo: { + filename: "test.pdf", + fileSizeBytes: 999999999999, // exceeds max size + contentType: "application/pdf", + }, + }; + + await app.ready(); + const response = await supertest(app.server) + .post(makeUrl()) + .set("authorization", `Bearer ${testJwt}`) + .send(invalidAttachmentBody); + + expect(response.statusCode).toBe(400); + }); + + test("Validates attachment content type", async () => { + const testJwt = createJwt(); + + ddbMock.on(QueryCommand).resolves({ + Count: 1, + Items: [marshall({ createdBy: "originalUser" })], + }); + + const invalidContentTypeBody = { + status: RoomRequestStatus.APPROVED, + notes: "Request approved.", + attachmentInfo: { + filename: "malicious.exe", + fileSizeBytes: 1024, + contentType: "application/x-msdownload", // invalid type + }, + }; + + await app.ready(); + const response = await supertest(app.server) + .post(makeUrl()) + .set("authorization", `Bearer ${testJwt}`) + .send(invalidContentTypeBody); + + expect(response.statusCode).toBe(400); + }); }); diff --git a/tests/unit/s3.test.ts b/tests/unit/s3.test.ts new file mode 100644 index 00000000..d3efb81a --- /dev/null +++ b/tests/unit/s3.test.ts @@ -0,0 +1,187 @@ +import { describe, expect, test, vi, beforeEach } from "vitest"; +import { + S3Client, + PutObjectCommand, + GetObjectCommand, +} from "@aws-sdk/client-s3"; +import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; +import { + createPresignedPut, + createPresignedGet, +} from "../../src/api/functions/s3.js"; +import { InternalServerError } from "../../src/common/errors/index.js"; + +// Mock the getSignedUrl function from AWS SDK +// Note: We use vi.mock here instead of aws-sdk-client-mock because +// getSignedUrl is a standalone function, not an S3Client command +vi.mock("@aws-sdk/s3-request-presigner", () => ({ + getSignedUrl: vi.fn(), +})); + +describe("S3 Presigned URL Functions", () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + describe("createPresignedPut", () => { + test("creates a presigned PUT URL successfully", async () => { + const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=xyz"; + const mockS3Client = new S3Client({ region: "us-east-1" }); + + vi.mocked(getSignedUrl).mockResolvedValueOnce(mockUrl); + + const result = await createPresignedPut({ + s3client: mockS3Client, + bucketName: "test-bucket", + key: "test-key", + length: 1024, + mimeType: "application/pdf", + }); + + expect(result).toBe(mockUrl); + expect(getSignedUrl).toHaveBeenCalledWith( + mockS3Client, + expect.any(PutObjectCommand), + { expiresIn: 900 }, + ); + expect(getSignedUrl).toHaveBeenCalledTimes(1); + }); + + test("creates a presigned PUT URL with custom expiration", async () => { + const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=abc"; + const mockS3Client = new S3Client({ region: "us-east-1" }); + + vi.mocked(getSignedUrl).mockResolvedValueOnce(mockUrl); + + const result = await createPresignedPut({ + s3client: mockS3Client, + bucketName: "test-bucket", + key: "test-key", + length: 2048, + mimeType: "image/png", + urlExpiresIn: 3600, + }); + + expect(result).toBe(mockUrl); + expect(getSignedUrl).toHaveBeenCalledWith( + mockS3Client, + expect.any(PutObjectCommand), + { expiresIn: 3600 }, + ); + }); + + test("creates a presigned PUT URL with MD5 hash", async () => { + const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=def"; + const mockS3Client = new S3Client({ region: "us-east-1" }); + + vi.mocked(getSignedUrl).mockResolvedValueOnce(mockUrl); + + const result = await createPresignedPut({ + s3client: mockS3Client, + bucketName: "test-bucket", + key: "test-key", + length: 512, + mimeType: "application/pdf", + md5hash: "base64-encoded-hash", + }); + + expect(result).toBe(mockUrl); + expect(getSignedUrl).toHaveBeenCalled(); + }); + + test("throws InternalServerError when URL generation fails", async () => { + const mockS3Client = new S3Client({ region: "us-east-1" }); + + vi.mocked(getSignedUrl).mockRejectedValueOnce(new Error("AWS Error")); + + await expect( + createPresignedPut({ + s3client: mockS3Client, + bucketName: "test-bucket", + key: "test-key", + length: 1024, + mimeType: "application/pdf", + }), + ).rejects.toThrow(InternalServerError); + + vi.mocked(getSignedUrl).mockRejectedValueOnce(new Error("AWS Error")); + + await expect( + createPresignedPut({ + s3client: mockS3Client, + bucketName: "test-bucket", + key: "test-key", + length: 1024, + mimeType: "application/pdf", + }), + ).rejects.toThrow("Could not create S3 upload presigned url."); + }); + }); + + describe("createPresignedGet", () => { + test("creates a presigned GET URL successfully", async () => { + const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=ghi"; + const mockS3Client = new S3Client({ region: "us-east-1" }); + + vi.mocked(getSignedUrl).mockResolvedValueOnce(mockUrl); + + const result = await createPresignedGet({ + s3client: mockS3Client, + bucketName: "test-bucket", + key: "test-key", + }); + + expect(result).toBe(mockUrl); + expect(getSignedUrl).toHaveBeenCalledWith( + mockS3Client, + expect.any(GetObjectCommand), + { expiresIn: 900 }, + ); + }); + + test("creates a presigned GET URL with custom expiration", async () => { + const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=jkl"; + const mockS3Client = new S3Client({ region: "us-east-1" }); + + vi.mocked(getSignedUrl).mockResolvedValueOnce(mockUrl); + + const result = await createPresignedGet({ + s3client: mockS3Client, + bucketName: "test-bucket", + key: "test-key", + urlExpiresIn: 1800, + }); + + expect(result).toBe(mockUrl); + expect(getSignedUrl).toHaveBeenCalledWith( + mockS3Client, + expect.any(GetObjectCommand), + { expiresIn: 1800 }, + ); + }); + + test("throws InternalServerError when URL generation fails", async () => { + const mockS3Client = new S3Client({ region: "us-east-1" }); + + vi.mocked(getSignedUrl).mockRejectedValueOnce(new Error("AWS Error")); + + await expect( + createPresignedGet({ + s3client: mockS3Client, + bucketName: "test-bucket", + key: "test-key", + }), + ).rejects.toThrow(InternalServerError); + + vi.mocked(getSignedUrl).mockRejectedValueOnce(new Error("AWS Error")); + + await expect( + createPresignedGet({ + s3client: mockS3Client, + bucketName: "test-bucket", + key: "test-key", + }), + ).rejects.toThrow("Could not create S3 download presigned url."); + }); + }); +}); From c3f63b58a4ac0d9990737c3d63b553b8435704c7 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 22:35:21 -0500 Subject: [PATCH 16/25] Setup IAM policies for S3 --- terraform/modules/assets/main.tf | 37 ++++++++++++++++++++++++++ terraform/modules/lambdas/main.tf | 13 +++++++++ terraform/modules/lambdas/variables.tf | 4 +++ 3 files changed, 54 insertions(+) diff --git a/terraform/modules/assets/main.tf b/terraform/modules/assets/main.tf index 4ae34295..a47fc8af 100644 --- a/terraform/modules/assets/main.tf +++ b/terraform/modules/assets/main.tf @@ -69,3 +69,40 @@ resource "aws_s3_bucket_cors_configuration" "ui_uploads" { max_age_seconds = 3000 } } + +data "aws_iam_policy_document" "bucket_access" { + statement { + sid = "AllowPutGetObjects" + effect = "Allow" + actions = [ + "s3:PutObject", + "s3:GetObject", + "s3:DeleteObject" + ] + resources = [ + for bucket_info in module.buckets.buckets_info : "${bucket_info.arn}/*" + ] + } + + statement { + sid = "AllowListBucket" + effect = "Allow" + actions = [ + "s3:ListBucket" + ] + resources = [ + for bucket_info in module.buckets.buckets_info : bucket_info.arn + ] + } +} + +resource "aws_iam_policy" "bucket_access" { + name = "${var.ProjectId}-bucket-access" + description = "Policy to allow operations on ${local.asset_bucket_prefix} buckets" + policy = data.aws_iam_policy_document.bucket_access.json +} + +output "access_policy_arn" { + description = "ARN of the IAM policy for bucket access" + value = aws_iam_policy.bucket_access.arn +} diff --git a/terraform/modules/lambdas/main.tf b/terraform/modules/lambdas/main.tf index 29956c66..4e293795 100644 --- a/terraform/modules/lambdas/main.tf +++ b/terraform/modules/lambdas/main.tf @@ -319,17 +319,30 @@ resource "aws_iam_role_policy_attachment" "api_attach" { policy_arn = each.value } +resource "aws_iam_role_policy_attachment" "api_attach" { + for_each = var.AdditionalIamPolicies + role = aws_iam_role.api_role.name + policy_arn = each.value +} + resource "aws_iam_role_policy_attachment" "entra_attach" { for_each = local.entra_policies role = aws_iam_role.entra_role.name policy_arn = each.value } + resource "aws_iam_role_policy_attachment" "sqs_attach_shared" { for_each = local.sqs_policies role = aws_iam_role.sqs_consumer_role.name policy_arn = each.value } +resource "aws_iam_role_policy_attachment" "sqs_attach_shared" { + for_each = var.AdditionalIamPolicies + role = aws_iam_role.sqs_consumer_role.name + policy_arn = each.value +} + resource "aws_lambda_function" "api_lambda" { region = var.region depends_on = [aws_cloudwatch_log_group.api_logs] diff --git a/terraform/modules/lambdas/variables.tf b/terraform/modules/lambdas/variables.tf index b06ed848..b283bd69 100644 --- a/terraform/modules/lambdas/variables.tf +++ b/terraform/modules/lambdas/variables.tf @@ -37,3 +37,7 @@ variable "region" { type = string description = "AWS region to deploy non-global resources to" } + +variable "AdditionalIamPolicies" { + type = set(string) +} From 9d7ff31487d30a4ce263afdc2ce48646f660ea9c Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 22:36:09 -0500 Subject: [PATCH 17/25] Fix wiring --- terraform/envs/prod/main.tf | 2 ++ terraform/envs/qa/main.tf | 2 ++ 2 files changed, 4 insertions(+) diff --git a/terraform/envs/prod/main.tf b/terraform/envs/prod/main.tf index a680ecc0..8852a908 100644 --- a/terraform/envs/prod/main.tf +++ b/terraform/envs/prod/main.tf @@ -104,6 +104,7 @@ module "lambdas" { PreviousOriginVerifyKeyExpiresAt = module.origin_verify.previous_invalid_time LogRetentionDays = var.LogRetentionDays EmailDomain = var.EmailDomain + AdditionalIamPolicies = [module.assets.access_policy_arn] } module "frontend" { @@ -155,6 +156,7 @@ module "lambdas_usw2" { PreviousOriginVerifyKeyExpiresAt = module.origin_verify.previous_invalid_time LogRetentionDays = var.LogRetentionDays EmailDomain = var.EmailDomain + AdditionalIamPolicies = [module.assets.access_policy_arn] } module "sqs_queues_usw2" { diff --git a/terraform/envs/qa/main.tf b/terraform/envs/qa/main.tf index 61007996..8912d515 100644 --- a/terraform/envs/qa/main.tf +++ b/terraform/envs/qa/main.tf @@ -107,6 +107,7 @@ module "lambdas" { PreviousOriginVerifyKeyExpiresAt = module.origin_verify.previous_invalid_time LogRetentionDays = var.LogRetentionDays EmailDomain = var.EmailDomain + AdditionalIamPolicies = [module.assets.access_policy_arn] } module "frontend" { @@ -148,6 +149,7 @@ module "lambdas_usw2" { PreviousOriginVerifyKeyExpiresAt = module.origin_verify.previous_invalid_time LogRetentionDays = var.LogRetentionDays EmailDomain = var.EmailDomain + AdditionalIamPolicies = [module.assets.access_policy_arn] } module "sqs_queues_usw2" { From 604a826cc8a0369c03c0accbd88923b81c8636b1 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 22:45:55 -0500 Subject: [PATCH 18/25] if request to get the file, and it doesn't exist, delete the file attribute from the entry --- src/api/routes/roomRequests.ts | 36 +++++++++++++++++++++++++++++++- terraform/modules/assets/main.tf | 3 ++- 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/api/routes/roomRequests.ts b/src/api/routes/roomRequests.ts index d7f5a187..3db8f9f7 100644 --- a/src/api/routes/roomRequests.ts +++ b/src/api/routes/roomRequests.ts @@ -18,6 +18,7 @@ import { GetItemCommand, QueryCommand, TransactWriteItemsCommand, + UpdateItemCommand, } from "@aws-sdk/client-dynamodb"; import { genericConfig, notificationRecipients } from "common/config.js"; import { marshall, unmarshall } from "@aws-sdk/util-dynamodb"; @@ -35,7 +36,7 @@ import { } from "common/utils.js"; import { ROOM_RESERVATION_RETENTION_DAYS } from "common/constants.js"; import { createPresignedGet, createPresignedPut } from "api/functions/s3.js"; -import { S3Client } from "@aws-sdk/client-s3"; +import { HeadObjectCommand, NotFound, S3Client } from "@aws-sdk/client-s3"; const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => { await fastify.register(rateLimiter, { @@ -675,6 +676,39 @@ const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => { region: genericConfig.AwsRegion, }); } + try { + await fastify.s3Client.send( + new HeadObjectCommand({ + Bucket: fastify.environmentConfig.AssetsBucketId, + Key: unmarshalled.attachmentS3key, + }), + ); + } catch (error) { + if (error instanceof NotFound) { + // Key doesn't exist in S3, delete the attribute from DynamoDB + await fastify.dynamoClient.send( + new UpdateItemCommand({ + TableName: genericConfig.RoomRequestsStatusTableName, + Key: { + requestId: { S: request.params.requestId }, + "createdAt#status": { + S: `${request.params.createdAt}#${request.params.status}`, + }, + }, + UpdateExpression: "REMOVE #attachmentS3key", + ExpressionAttributeNames: { + "#attachmentS3key": "attachmentS3key", + }, + }), + ); + + throw new NotFoundError({ + endpointName: request.url, + }); + } else { + throw error; + } + } const url = await createPresignedGet({ s3client: fastify.s3Client, bucketName: fastify.environmentConfig.AssetsBucketId, diff --git a/terraform/modules/assets/main.tf b/terraform/modules/assets/main.tf index a47fc8af..93ec5368 100644 --- a/terraform/modules/assets/main.tf +++ b/terraform/modules/assets/main.tf @@ -77,7 +77,8 @@ data "aws_iam_policy_document" "bucket_access" { actions = [ "s3:PutObject", "s3:GetObject", - "s3:DeleteObject" + "s3:DeleteObject", + "s3:HeadObject" ] resources = [ for bucket_info in module.buckets.buckets_info : "${bucket_info.arn}/*" From 04496771117c98e2322391406b7159f5d1af2ad7 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 22:48:14 -0500 Subject: [PATCH 19/25] fix --- terraform/modules/lambdas/main.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/terraform/modules/lambdas/main.tf b/terraform/modules/lambdas/main.tf index 4e293795..0b694d77 100644 --- a/terraform/modules/lambdas/main.tf +++ b/terraform/modules/lambdas/main.tf @@ -319,7 +319,7 @@ resource "aws_iam_role_policy_attachment" "api_attach" { policy_arn = each.value } -resource "aws_iam_role_policy_attachment" "api_attach" { +resource "aws_iam_role_policy_attachment" "api_attach_addl" { for_each = var.AdditionalIamPolicies role = aws_iam_role.api_role.name policy_arn = each.value @@ -337,7 +337,7 @@ resource "aws_iam_role_policy_attachment" "sqs_attach_shared" { policy_arn = each.value } -resource "aws_iam_role_policy_attachment" "sqs_attach_shared" { +resource "aws_iam_role_policy_attachment" "sqs_attach_addl" { for_each = var.AdditionalIamPolicies role = aws_iam_role.sqs_consumer_role.name policy_arn = each.value From d2789d9460616e5d98ab6baa88deedc81ee4fc02 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 22:54:54 -0500 Subject: [PATCH 20/25] Fix --- .../roomRequest/ViewRoomRequest.page.tsx | 23 +- src/ui/util/s3.test.ts | 212 ------------------ tests/unit/s3.test.ts | 12 +- 3 files changed, 26 insertions(+), 221 deletions(-) delete mode 100644 src/ui/util/s3.test.ts diff --git a/src/ui/pages/roomRequest/ViewRoomRequest.page.tsx b/src/ui/pages/roomRequest/ViewRoomRequest.page.tsx index 4c1abb39..d0f20917 100644 --- a/src/ui/pages/roomRequest/ViewRoomRequest.page.tsx +++ b/src/ui/pages/roomRequest/ViewRoomRequest.page.tsx @@ -55,6 +55,9 @@ export const ViewRoomRequest: React.FC = () => { const [data, setData] = useState(null); const [isSubmitting, setIsSubmitting] = useState(false); const [uploadedFile, setUploadedFile] = useState(null); + const [downloadingAttachment, setDownloadingAttachment] = useState< + string | null + >(null); const newStatusForm = useForm<{ status: RoomRequestStatus | null; @@ -103,6 +106,8 @@ export const ViewRoomRequest: React.FC = () => { if (!filename) { return; } + const attachmentKey = `${createdAt}#${status}`; + setDownloadingAttachment(attachmentKey); try { const response = await api.get<{ downloadUrl: string }>( `/api/v1/roomRequests/${semesterId}/${requestId}/attachmentDownloadUrl/${createdAt}/${status}`, @@ -115,6 +120,8 @@ export const ViewRoomRequest: React.FC = () => { color: "red", icon: , }); + } finally { + setDownloadingAttachment(null); } }; @@ -457,7 +464,14 @@ export const ViewRoomRequest: React.FC = () => { )} {x.createdAt && ( diff --git a/src/ui/util/s3.test.ts b/src/ui/util/s3.test.ts deleted file mode 100644 index 24b6efed..00000000 --- a/src/ui/util/s3.test.ts +++ /dev/null @@ -1,212 +0,0 @@ -import { describe, expect, test, vi, beforeEach, afterEach } from "vitest"; -import { uploadToS3PresignedUrl, downloadFromS3PresignedUrl } from "./s3"; - -// Mock global fetch -global.fetch = vi.fn(); - -describe("S3 Utility Functions", () => { - beforeEach(() => { - vi.clearAllMocks(); - }); - - afterEach(() => { - vi.restoreAllMocks(); - }); - - describe("uploadToS3PresignedUrl", () => { - test("successfully uploads a file to S3", async () => { - const mockFile = new File(["test content"], "test.pdf", { - type: "application/pdf", - }); - const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=xyz"; - const mockContentType = "application/pdf"; - - vi.mocked(fetch).mockResolvedValueOnce({ - ok: true, - status: 200, - statusText: "OK", - } as Response); - - await uploadToS3PresignedUrl(mockUrl, mockFile, mockContentType); - - expect(fetch).toHaveBeenCalledWith(mockUrl, { - method: "PUT", - headers: { - "Content-Type": mockContentType, - }, - body: mockFile, - }); - }); - - test("throws error when upload fails with non-ok response", async () => { - const mockFile = new File(["test content"], "test.pdf", { - type: "application/pdf", - }); - const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=xyz"; - const mockContentType = "application/pdf"; - - vi.mocked(fetch).mockResolvedValueOnce({ - ok: false, - status: 403, - statusText: "Forbidden", - } as Response); - - await expect( - uploadToS3PresignedUrl(mockUrl, mockFile, mockContentType), - ).rejects.toThrow("Failed to upload file to S3: 403 Forbidden"); - }); - - test("throws error when upload fails with network error", async () => { - const mockFile = new File(["test content"], "test.pdf", { - type: "application/pdf", - }); - const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=xyz"; - const mockContentType = "application/pdf"; - - vi.mocked(fetch).mockRejectedValueOnce(new Error("Network error")); - - await expect( - uploadToS3PresignedUrl(mockUrl, mockFile, mockContentType), - ).rejects.toThrow("Network error"); - }); - - test("uses correct content type header", async () => { - const mockFile = new File(["image data"], "image.png", { - type: "image/png", - }); - const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=abc"; - const mockContentType = "image/png"; - - vi.mocked(fetch).mockResolvedValueOnce({ - ok: true, - status: 200, - statusText: "OK", - } as Response); - - await uploadToS3PresignedUrl(mockUrl, mockFile, mockContentType); - - expect(fetch).toHaveBeenCalledWith(mockUrl, { - method: "PUT", - headers: { - "Content-Type": "image/png", - }, - body: mockFile, - }); - }); - }); - - describe("downloadFromS3PresignedUrl", () => { - test("successfully downloads a file and triggers browser download", async () => { - const mockBlob = new Blob(["file content"], { type: "application/pdf" }); - const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=xyz"; - const mockFilename = "downloaded-file.pdf"; - - // Mock fetch response - vi.mocked(fetch).mockResolvedValueOnce({ - ok: true, - status: 200, - statusText: "OK", - blob: vi.fn().mockResolvedValue(mockBlob), - } as unknown as Response); - - // Mock DOM APIs - const mockAnchor = { - href: "", - download: "", - style: { display: "" }, - click: vi.fn(), - }; - const createElementSpy = vi - .spyOn(document, "createElement") - .mockReturnValue(mockAnchor as unknown as HTMLElement); - const appendChildSpy = vi - .spyOn(document.body, "appendChild") - .mockImplementation(() => mockAnchor as unknown as Node); - const removeChildSpy = vi - .spyOn(document.body, "removeChild") - .mockImplementation(() => mockAnchor as unknown as Node); - - const createObjectURLSpy = vi - .spyOn(window.URL, "createObjectURL") - .mockReturnValue("blob:mock-url"); - const revokeObjectURLSpy = vi.spyOn(window.URL, "revokeObjectURL"); - - await downloadFromS3PresignedUrl(mockUrl, mockFilename); - - // Verify fetch was called correctly - expect(fetch).toHaveBeenCalledWith(mockUrl, { method: "GET" }); - - // Verify DOM manipulation - expect(createElementSpy).toHaveBeenCalledWith("a"); - expect(mockAnchor.href).toBe("blob:mock-url"); - expect(mockAnchor.download).toBe(mockFilename); - expect(mockAnchor.style.display).toBe("none"); - expect(appendChildSpy).toHaveBeenCalledWith(mockAnchor); - expect(mockAnchor.click).toHaveBeenCalled(); - expect(removeChildSpy).toHaveBeenCalledWith(mockAnchor); - expect(revokeObjectURLSpy).toHaveBeenCalledWith("blob:mock-url"); - }); - - test("throws error when download fails with non-ok response", async () => { - const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=xyz"; - const mockFilename = "file.pdf"; - - vi.mocked(fetch).mockResolvedValueOnce({ - ok: false, - status: 404, - statusText: "Not Found", - } as Response); - - await expect( - downloadFromS3PresignedUrl(mockUrl, mockFilename), - ).rejects.toThrow("Failed to download file from S3: 404 Not Found"); - }); - - test("throws error when download fails with network error", async () => { - const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=xyz"; - const mockFilename = "file.pdf"; - - vi.mocked(fetch).mockRejectedValueOnce(new Error("Network error")); - - await expect( - downloadFromS3PresignedUrl(mockUrl, mockFilename), - ).rejects.toThrow("Network error"); - }); - - test("handles different file types correctly", async () => { - const mockBlob = new Blob(["image data"], { type: "image/jpeg" }); - const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=abc"; - const mockFilename = "photo.jpg"; - - vi.mocked(fetch).mockResolvedValueOnce({ - ok: true, - status: 200, - statusText: "OK", - blob: vi.fn().mockResolvedValue(mockBlob), - } as unknown as Response); - - const mockAnchor = { - href: "", - download: "", - style: { display: "" }, - click: vi.fn(), - }; - vi.spyOn(document, "createElement").mockReturnValue( - mockAnchor as unknown as HTMLElement, - ); - vi.spyOn(document.body, "appendChild").mockImplementation( - () => mockAnchor as unknown as Node, - ); - vi.spyOn(document.body, "removeChild").mockImplementation( - () => mockAnchor as unknown as Node, - ); - vi.spyOn(window.URL, "createObjectURL").mockReturnValue("blob:mock-url"); - vi.spyOn(window.URL, "revokeObjectURL"); - - await downloadFromS3PresignedUrl(mockUrl, mockFilename); - - expect(mockAnchor.download).toBe("photo.jpg"); - expect(fetch).toHaveBeenCalledWith(mockUrl, { method: "GET" }); - }); - }); -}); diff --git a/tests/unit/s3.test.ts b/tests/unit/s3.test.ts index d3efb81a..70d6fb6b 100644 --- a/tests/unit/s3.test.ts +++ b/tests/unit/s3.test.ts @@ -15,7 +15,9 @@ import { InternalServerError } from "../../src/common/errors/index.js"; // Note: We use vi.mock here instead of aws-sdk-client-mock because // getSignedUrl is a standalone function, not an S3Client command vi.mock("@aws-sdk/s3-request-presigner", () => ({ - getSignedUrl: vi.fn(), + getSignedUrl: vi + .fn() + .mockResolvedValue("https://s3.amazonaws.com/bucket/key?signature=xyz"), })); describe("S3 Presigned URL Functions", () => { @@ -28,7 +30,7 @@ describe("S3 Presigned URL Functions", () => { const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=xyz"; const mockS3Client = new S3Client({ region: "us-east-1" }); - vi.mocked(getSignedUrl).mockResolvedValueOnce(mockUrl); + vi.mocked(getSignedUrl); const result = await createPresignedPut({ s3client: mockS3Client, @@ -51,8 +53,6 @@ describe("S3 Presigned URL Functions", () => { const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=abc"; const mockS3Client = new S3Client({ region: "us-east-1" }); - vi.mocked(getSignedUrl).mockResolvedValueOnce(mockUrl); - const result = await createPresignedPut({ s3client: mockS3Client, bucketName: "test-bucket", @@ -71,11 +71,9 @@ describe("S3 Presigned URL Functions", () => { }); test("creates a presigned PUT URL with MD5 hash", async () => { - const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=def"; + const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=xyz"; const mockS3Client = new S3Client({ region: "us-east-1" }); - vi.mocked(getSignedUrl).mockResolvedValueOnce(mockUrl); - const result = await createPresignedPut({ s3client: mockS3Client, bucketName: "test-bucket", From c32847f72415db826aaefb20705b409d81c27571 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 22:58:46 -0500 Subject: [PATCH 21/25] fix --- tests/unit/s3.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/s3.test.ts b/tests/unit/s3.test.ts index 70d6fb6b..e99cfb18 100644 --- a/tests/unit/s3.test.ts +++ b/tests/unit/s3.test.ts @@ -50,7 +50,7 @@ describe("S3 Presigned URL Functions", () => { }); test("creates a presigned PUT URL with custom expiration", async () => { - const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=abc"; + const mockUrl = "https://s3.amazonaws.com/bucket/key?signature=xyz"; const mockS3Client = new S3Client({ region: "us-east-1" }); const result = await createPresignedPut({ From feaab0b72e9d0c86ffa25f8a3b5cb0ee9da920e8 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 23:09:04 -0500 Subject: [PATCH 22/25] Update --- terraform/envs/prod/main.tf | 4 ++-- terraform/envs/qa/main.tf | 4 ++-- terraform/modules/lambdas/variables.tf | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/terraform/envs/prod/main.tf b/terraform/envs/prod/main.tf index 8852a908..35351753 100644 --- a/terraform/envs/prod/main.tf +++ b/terraform/envs/prod/main.tf @@ -104,7 +104,7 @@ module "lambdas" { PreviousOriginVerifyKeyExpiresAt = module.origin_verify.previous_invalid_time LogRetentionDays = var.LogRetentionDays EmailDomain = var.EmailDomain - AdditionalIamPolicies = [module.assets.access_policy_arn] + AdditionalIamPolicies = { assets : module.assets.access_policy_arn } } module "frontend" { @@ -156,7 +156,7 @@ module "lambdas_usw2" { PreviousOriginVerifyKeyExpiresAt = module.origin_verify.previous_invalid_time LogRetentionDays = var.LogRetentionDays EmailDomain = var.EmailDomain - AdditionalIamPolicies = [module.assets.access_policy_arn] + AdditionalIamPolicies = { assets : module.assets.access_policy_arn } } module "sqs_queues_usw2" { diff --git a/terraform/envs/qa/main.tf b/terraform/envs/qa/main.tf index 8912d515..4a3c3c8e 100644 --- a/terraform/envs/qa/main.tf +++ b/terraform/envs/qa/main.tf @@ -107,7 +107,7 @@ module "lambdas" { PreviousOriginVerifyKeyExpiresAt = module.origin_verify.previous_invalid_time LogRetentionDays = var.LogRetentionDays EmailDomain = var.EmailDomain - AdditionalIamPolicies = [module.assets.access_policy_arn] + AdditionalIamPolicies = { assets : module.assets.access_policy_arn } } module "frontend" { @@ -149,7 +149,7 @@ module "lambdas_usw2" { PreviousOriginVerifyKeyExpiresAt = module.origin_verify.previous_invalid_time LogRetentionDays = var.LogRetentionDays EmailDomain = var.EmailDomain - AdditionalIamPolicies = [module.assets.access_policy_arn] + AdditionalIamPolicies = { assets : module.assets.access_policy_arn } } module "sqs_queues_usw2" { diff --git a/terraform/modules/lambdas/variables.tf b/terraform/modules/lambdas/variables.tf index b283bd69..1a6a98fd 100644 --- a/terraform/modules/lambdas/variables.tf +++ b/terraform/modules/lambdas/variables.tf @@ -39,5 +39,5 @@ variable "region" { } variable "AdditionalIamPolicies" { - type = set(string) + type = map(string) } From 6b64c35a8a33f9b24e644f40213472c4cf99726a Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 23:21:17 -0500 Subject: [PATCH 23/25] Fix catch --- src/api/routes/roomRequests.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/api/routes/roomRequests.ts b/src/api/routes/roomRequests.ts index 3db8f9f7..9e0139a3 100644 --- a/src/api/routes/roomRequests.ts +++ b/src/api/routes/roomRequests.ts @@ -716,6 +716,9 @@ const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => { }); return reply.status(200).send({ downloadUrl: url }); } catch (e) { + if (e instanceof NotFoundError) { + throw e; + } request.log.error(e); throw new DatabaseFetchError({ message: "Could not get request attachments.", From 850736928f32bfa97c7b4d62435cbf7909d0b157 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 23:33:39 -0500 Subject: [PATCH 24/25] Fix --- src/api/routes/roomRequests.ts | 133 ++++++++++++++++----------------- 1 file changed, 64 insertions(+), 69 deletions(-) diff --git a/src/api/routes/roomRequests.ts b/src/api/routes/roomRequests.ts index 9e0139a3..5d8ea850 100644 --- a/src/api/routes/roomRequests.ts +++ b/src/api/routes/roomRequests.ts @@ -1,4 +1,4 @@ -import { FastifyPluginAsync } from "fastify"; +import { FastifyPluginAsync, type FastifyRequest } from "fastify"; import rateLimiter from "api/plugins/rateLimiter.js"; import { formatStatus, @@ -19,6 +19,7 @@ import { QueryCommand, TransactWriteItemsCommand, UpdateItemCommand, + type QueryCommandOutput, } from "@aws-sdk/client-dynamodb"; import { genericConfig, notificationRecipients } from "common/config.js"; import { marshall, unmarshall } from "@aws-sdk/util-dynamodb"; @@ -38,6 +39,51 @@ import { ROOM_RESERVATION_RETENTION_DAYS } from "common/constants.js"; import { createPresignedGet, createPresignedPut } from "api/functions/s3.js"; import { HeadObjectCommand, NotFound, S3Client } from "@aws-sdk/client-s3"; +async function verifyRoomRequestAccess( + fastify: any, + request: FastifyRequest, + requestId: string, + semesterId: string, +): Promise { + let command: QueryCommand; + if (request.userRoles?.has(AppRoles.BYPASS_OBJECT_LEVEL_AUTH)) { + command = new QueryCommand({ + TableName: genericConfig.RoomRequestsTableName, + IndexName: "RequestIdIndex", + KeyConditionExpression: "requestId = :requestId", + FilterExpression: "semesterId = :semesterId", + ExpressionAttributeValues: { + ":requestId": { S: requestId }, + ":semesterId": { S: semesterId }, + }, + Limit: 1, + }); + } else { + command = new QueryCommand({ + TableName: genericConfig.RoomRequestsTableName, + KeyConditionExpression: + "semesterId = :semesterId AND #userIdRequestId = :userRequestId", + ExpressionAttributeValues: { + ":userRequestId": { S: `${request.username}#${requestId}` }, + ":semesterId": { S: semesterId }, + }, + ExpressionAttributeNames: { + "#userIdRequestId": "userId#requestId", + }, + Limit: 1, + }); + } + + const resp = await fastify.dynamoClient.send(command); + if (!resp.Items || resp.Count !== 1) { + throw new DatabaseFetchError({ + message: "Recieved no database item.", + }); + } + + return resp; +} + const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => { await fastify.register(rateLimiter, { limit: 20, @@ -474,41 +520,13 @@ const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => { async (request, reply) => { const requestId = request.params.requestId; const semesterId = request.params.semesterId; - let command; - if (request.userRoles?.has(AppRoles.BYPASS_OBJECT_LEVEL_AUTH)) { - command = new QueryCommand({ - TableName: genericConfig.RoomRequestsTableName, - IndexName: "RequestIdIndex", - KeyConditionExpression: "requestId = :requestId", - FilterExpression: "semesterId = :semesterId", - ExpressionAttributeValues: { - ":requestId": { S: requestId }, - ":semesterId": { S: semesterId }, - }, - Limit: 1, - }); - } else { - command = new QueryCommand({ - TableName: genericConfig.RoomRequestsTableName, - KeyConditionExpression: - "semesterId = :semesterId AND #userIdRequestId = :userRequestId", - ExpressionAttributeValues: { - ":userRequestId": { S: `${request.username}#${requestId}` }, - ":semesterId": { S: semesterId }, - }, - ExpressionAttributeNames: { - "#userIdRequestId": "userId#requestId", - }, - Limit: 1, - }); - } try { - const resp = await fastify.dynamoClient.send(command); - if (!resp.Items || resp.Count !== 1) { - throw new DatabaseFetchError({ - message: "Recieved no response.", - }); - } + const resp = await verifyRoomRequestAccess( + fastify, + request, + requestId, + semesterId, + ); // this isn't atomic, but that's fine - a little inconsistency on this isn't a problem. try { const statusesResponse = await fastify.dynamoClient.send( @@ -540,6 +558,11 @@ const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => { : undefined, }; }); + if (!resp.Items || resp.Count !== 1) { + throw new DatabaseFetchError({ + message: "Recieved no database item.", + }); + } return reply .status(200) .send({ data: unmarshall(resp.Items[0]), updates }); @@ -606,41 +629,13 @@ const roomRequestRoutes: FastifyPluginAsync = async (fastify, _options) => { async (request, reply) => { const requestId = request.params.requestId; const semesterId = request.params.semesterId; - let command; - if (request.userRoles?.has(AppRoles.BYPASS_OBJECT_LEVEL_AUTH)) { - command = new QueryCommand({ - TableName: genericConfig.RoomRequestsTableName, - IndexName: "RequestIdIndex", - KeyConditionExpression: "requestId = :requestId", - FilterExpression: "semesterId = :semesterId", - ExpressionAttributeValues: { - ":requestId": { S: requestId }, - ":semesterId": { S: semesterId }, - }, - Limit: 1, - }); - } else { - command = new QueryCommand({ - TableName: genericConfig.RoomRequestsTableName, - KeyConditionExpression: - "semesterId = :semesterId AND #userIdRequestId = :userRequestId", - ExpressionAttributeValues: { - ":userRequestId": { S: `${request.username}#${requestId}` }, - ":semesterId": { S: semesterId }, - }, - ExpressionAttributeNames: { - "#userIdRequestId": "userId#requestId", - }, - Limit: 1, - }); - } try { - const resp = await fastify.dynamoClient.send(command); - if (!resp.Items || resp.Count !== 1) { - throw new DatabaseFetchError({ - message: "Recieved no response.", - }); - } + const resp = await verifyRoomRequestAccess( + fastify, + request, + requestId, + semesterId, + ); // this isn't atomic, but that's fine - a little inconsistency on this isn't a problem. try { const statusesResponse = await fastify.dynamoClient.send( From 6399b67d48a7dee6df1c31bdcaad0e825bc77675 Mon Sep 17 00:00:00 2001 From: Dev Singh Date: Sat, 1 Nov 2025 23:34:11 -0500 Subject: [PATCH 25/25] Cleanup --- src/api/routes/membership.ts | 14 ++------------ src/api/routes/mobileWallet.ts | 5 ----- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/src/api/routes/membership.ts b/src/api/routes/membership.ts index 4930efc5..f6975975 100644 --- a/src/api/routes/membership.ts +++ b/src/api/routes/membership.ts @@ -1,7 +1,6 @@ import { checkExternalMembership, checkPaidMembershipFromTable, - setPaidMembershipInTable, MEMBER_CACHE_SECONDS, getExternalMemberList, patchExternalMemberList, @@ -13,18 +12,9 @@ import { InternalServerError, ValidationError, } from "common/errors/index.js"; -import { getEntraIdToken } from "api/functions/entraId.js"; -import { genericConfig, roleArns } from "common/config.js"; -import { getRoleCredentials } from "api/functions/sts.js"; -import { SecretsManagerClient } from "@aws-sdk/client-secrets-manager"; -import { - BatchWriteItemCommand, - DynamoDBClient, - QueryCommand, - ScanCommand, -} from "@aws-sdk/client-dynamodb"; +import { genericConfig } from "common/config.js"; +import { ScanCommand } from "@aws-sdk/client-dynamodb"; import rateLimiter from "api/plugins/rateLimiter.js"; -import { createCheckoutSession } from "api/functions/stripe.js"; import { getSecretValue } from "api/plugins/auth.js"; import stripe, { Stripe } from "stripe"; import { AvailableSQSFunctions, SQSPayload } from "common/types/sqsMessage.js"; diff --git a/src/api/routes/mobileWallet.ts b/src/api/routes/mobileWallet.ts index af654059..323b1224 100644 --- a/src/api/routes/mobileWallet.ts +++ b/src/api/routes/mobileWallet.ts @@ -2,7 +2,6 @@ import { FastifyPluginAsync } from "fastify"; import { InternalServerError, UnauthenticatedError, - ValidationError, } from "../../common/errors/index.js"; import * as z from "zod/v4"; import { checkPaidMembershipFromTable } from "../functions/membership.js"; @@ -16,10 +15,6 @@ import rateLimiter from "api/plugins/rateLimiter.js"; import { FastifyZodOpenApiTypeProvider } from "fastify-zod-openapi"; import { withTags } from "api/components/index.js"; -const queuedResponseJsonSchema = z.object({ - queueId: z.string().uuid(), -}); - const mobileWalletRoute: FastifyPluginAsync = async (fastify, _options) => { fastify.register(rateLimiter, { limit: 5,