diff --git a/infrastructure/terraform/components/api/ddb_table_suppliers.tf b/infrastructure/terraform/components/api/ddb_table_suppliers.tf new file mode 100644 index 00000000..24a79fb6 --- /dev/null +++ b/infrastructure/terraform/components/api/ddb_table_suppliers.tf @@ -0,0 +1,34 @@ +resource "aws_dynamodb_table" "suppliers" { + name = "${local.csi}-suppliers" + billing_mode = "PAY_PER_REQUEST" + + hash_key = "id" + range_key = "apimId" + + ttl { + attribute_name = "ttl" + enabled = false + } + + global_secondary_index { + name = "supplier-apim-index" + hash_key = "apimId" + projection_type = "ALL" + } + + attribute { + name = "id" + type = "S" + } + + attribute { + name = "apimId" + type = "S" + } + + point_in_time_recovery { + enabled = true + } + + tags = var.default_tags +} diff --git a/infrastructure/terraform/components/api/module_authorizer_lambda.tf b/infrastructure/terraform/components/api/module_authorizer_lambda.tf index 9d56c950..6ff2c2c4 100644 --- a/infrastructure/terraform/components/api/module_authorizer_lambda.tf +++ b/infrastructure/terraform/components/api/module_authorizer_lambda.tf @@ -11,6 +11,10 @@ module "authorizer_lambda" { log_retention_in_days = var.log_retention_in_days kms_key_arn = module.kms.key_arn + iam_policy_document = { + body = data.aws_iam_policy_document.authorizer_lambda.json + } + function_name = "authorizer" description = "Authorizer for Suppliers API" @@ -30,4 +34,40 @@ module "authorizer_lambda" { send_to_firehose = true log_destination_arn = local.destination_arn log_subscription_role_arn = local.acct.log_subscription_role_arn + + lambda_env_vars = { + CLOUDWATCH_NAMESPACE = "/aws/api-gateway/supplier/alarms", + CLIENT_CERTIFICATE_EXPIRATION_ALERT_DAYS = 14, + APIM_APPLICATION_ID_HEADER = "apim-application-id", + SUPPLIERS_TABLE_NAME = aws_dynamodb_table.suppliers.name + } +} + +data "aws_iam_policy_document" "authorizer_lambda" { + statement { + sid = "AllowPutMetricData" + effect = "Allow" + + actions = [ + "cloudwatch:PutMetricData" + ] + + resources = [ + "*" + ] + } + + statement { + sid = "AllowDynamoDBAccess" + effect = "Allow" + + actions = [ + "dynamodb:Query" + ] + + resources = [ + aws_dynamodb_table.suppliers.arn, + "${aws_dynamodb_table.suppliers.arn}/index/supplier-apim-index" + ] + } } diff --git a/infrastructure/terraform/components/api/resources/spec.tmpl.json b/infrastructure/terraform/components/api/resources/spec.tmpl.json index 4514619b..713d49a3 100644 --- a/infrastructure/terraform/components/api/resources/spec.tmpl.json +++ b/infrastructure/terraform/components/api/resources/spec.tmpl.json @@ -3,13 +3,13 @@ "securitySchemes": { "LambdaAuthorizer": { "in": "header", - "name": "NHSD-Supplier-ID", + "name": "apim-application-id", "type": "apiKey", "x-amazon-apigateway-authorizer": { "authorizerCredentials": "${APIG_EXECUTION_ROLE_ARN}", "authorizerResultTtlInSeconds": 0, "authorizerUri": "arn:aws:apigateway:${AWS_REGION}:lambda:path/2015-03-31/functions/${AUTHORIZER_LAMBDA_ARN}/invocations", - "identitySource": "method.request.header.NHSD-Supplier-ID", + "identitySource": "method.request.header.apim-application-id", "type": "request" }, "x-amazon-apigateway-authtype": "custom" diff --git a/internal/datastore/src/__test__/db.ts b/internal/datastore/src/__test__/db.ts index 85f89193..16951106 100644 --- a/internal/datastore/src/__test__/db.ts +++ b/internal/datastore/src/__test__/db.ts @@ -31,6 +31,7 @@ export async function setupDynamoDBContainer() { endpoint, lettersTableName: 'letters', miTableName: 'management-info', + suppliersTableName: 'suppliers', lettersTtlHours: 1, miTtlHours: 1 }; @@ -94,6 +95,29 @@ const createMITableCommand = new CreateTableCommand({ ] }); +const createSupplierTableCommand = new CreateTableCommand({ + TableName: 'suppliers', + BillingMode: 'PAY_PER_REQUEST', + KeySchema: [ + { AttributeName: 'id', KeyType: 'HASH' } // Partition key + ], + GlobalSecondaryIndexes: [ + { + IndexName: 'supplier-apim-index', + KeySchema: [ + { AttributeName: 'apimId', KeyType: 'HASH' } // Partition key for GSI + ], + Projection: { + ProjectionType: 'ALL' + } + } + ], + AttributeDefinitions: [ + { AttributeName: 'id', AttributeType: 'S' }, + { AttributeName: 'apimId', AttributeType: 'S' } + ] + }); + export async function createTables(context: DBContext) { const { ddbClient } = context; @@ -102,6 +126,7 @@ export async function createTables(context: DBContext) { await ddbClient.send(updateTimeToLiveCommand); await ddbClient.send(createMITableCommand); + await ddbClient.send(createSupplierTableCommand); } @@ -115,4 +140,8 @@ export async function deleteTables(context: DBContext) { await ddbClient.send(new DeleteTableCommand({ TableName: 'management-info' })); + + await ddbClient.send(new DeleteTableCommand({ + TableName: 'suppliers' + })); } diff --git a/internal/datastore/src/__test__/supplier-repository.test.ts b/internal/datastore/src/__test__/supplier-repository.test.ts new file mode 100644 index 00000000..c7456574 --- /dev/null +++ b/internal/datastore/src/__test__/supplier-repository.test.ts @@ -0,0 +1,127 @@ +import { Logger } from "pino"; +import { createTables, DBContext, deleteTables, setupDynamoDBContainer } from "./db"; +import { createTestLogger, LogStream } from "./logs"; +import { SupplierRepository } from "../supplier-repository"; +import { Supplier } from "../types"; +import { v4 as uuidv4 } from 'uuid'; + +function createSupplier(status: 'ENABLED' | 'DISABLED', apimId = uuidv4()): Omit { + return { + id: uuidv4(), + name: 'Supplier One', + apimId, + status + } +} + +// Database tests can take longer, especially with setup and teardown +jest.setTimeout(30000); + +describe('SupplierRepository', () => { + let db: DBContext; + let supplierRepository: SupplierRepository; + let logStream: LogStream; + let logger: Logger; + + beforeAll(async () => { + db = await setupDynamoDBContainer(); + }); + + beforeEach(async () => { + await createTables(db); + ( + { logStream, logger } = createTestLogger() + ); + + supplierRepository = new SupplierRepository(db.docClient, logger, db.config); + }); + + afterEach(async () => { + await deleteTables(db); + jest.useRealTimers(); + }); + + afterAll(async () => { + await db.container.stop(); + }); + + test('creates an enabled supplier with provided values and timestamps', async () => { + jest.useFakeTimers(); + // Month is zero-indexed in JS Date + jest.setSystemTime(new Date(2020, 1, 1)); + + const supplier = createSupplier('ENABLED'); + + const persistedSupplier = await supplierRepository.putSupplier(supplier); + + expect(persistedSupplier).toEqual(expect.objectContaining({ + ...supplier, + updatedAt: '2020-02-01T00:00:00.000Z', + })); + }); + + test('fetches a supplier by its ID', async () => { + const supplier = createSupplier('DISABLED') + await supplierRepository.putSupplier(supplier); + + const fetched = await supplierRepository.getSupplierById(supplier.id); + + expect(fetched).toEqual(expect.objectContaining({ + ...supplier + })); + }); + + test('throws an error fetching a supplier that does not exist', async () => { + await expect(supplierRepository.getSupplierById('non-existent-id')) + .rejects.toThrow('Supplier with id non-existent-id not found'); + }); + + test('overwrites an existing supplier entry', async () => { + const supplier = createSupplier('DISABLED'); + + const original = await supplierRepository.putSupplier(supplier); + expect(original.status).toBe('DISABLED'); + + supplier.status = 'ENABLED'; + const updated = await supplierRepository.putSupplier(supplier); + expect(updated.status).toBe('ENABLED'); + }); + + test('rethrows errors from DynamoDB when creating a letter', async () => { + const misconfiguredRepository = new SupplierRepository(db.docClient, logger, { + ...db.config, + suppliersTableName: 'nonexistent-table' + }); + await expect(misconfiguredRepository.putSupplier(createSupplier('ENABLED'))) + .rejects.toThrow('Cannot do operations on a non-existent table'); + }); + + test('fetches a supplier by apimId', async () => { + const supplier = createSupplier('ENABLED'); + + await supplierRepository.putSupplier(supplier); + + const fetched = await supplierRepository.getSupplierByApimId(supplier.apimId); + expect(fetched).toEqual(expect.objectContaining({ + ...supplier + })); + }); + + test('throws an error fetching a supplier by apimId that does not exist', async () => { + await expect(supplierRepository.getSupplierByApimId('non-existent-apim-id')) + .rejects.toThrow('Supplier with apimId non-existent-apim-id not found'); + }); + + test('throws an error fetching a supplier by apimId when multiple exist', async () => { + const apimId = 'duplicate-apim-id'; + const supplier1 = createSupplier('ENABLED', apimId); + const supplier2 = createSupplier('DISABLED', apimId); + + await supplierRepository.putSupplier(supplier1); + await supplierRepository.putSupplier(supplier2); + + await expect(supplierRepository.getSupplierByApimId(apimId)) + .rejects.toThrow(`Multiple suppliers found with apimId ${apimId}`); + }); + +}); diff --git a/internal/datastore/src/config.ts b/internal/datastore/src/config.ts index 92081caf..b9a4f6ec 100644 --- a/internal/datastore/src/config.ts +++ b/internal/datastore/src/config.ts @@ -3,6 +3,7 @@ export type DatastoreConfig = { endpoint?: string, lettersTableName: string, miTableName: string, + suppliersTableName: string, lettersTtlHours: number, miTtlHours: number } diff --git a/internal/datastore/src/index.ts b/internal/datastore/src/index.ts index 20f80e92..31f2b754 100644 --- a/internal/datastore/src/index.ts +++ b/internal/datastore/src/index.ts @@ -1,4 +1,5 @@ export * from './types'; export * from './mi-repository'; export * from './letter-repository'; +export * from './supplier-repository'; export * from './types'; diff --git a/internal/datastore/src/supplier-repository.ts b/internal/datastore/src/supplier-repository.ts new file mode 100644 index 00000000..14a82765 --- /dev/null +++ b/internal/datastore/src/supplier-repository.ts @@ -0,0 +1,71 @@ +import { + DynamoDBDocumentClient, + GetCommand, + PutCommand, + QueryCommand +} from '@aws-sdk/lib-dynamodb'; +import { Supplier, SupplierSchema } from './types'; +import { Logger } from 'pino'; + +export type SupplierRepositoryConfig = { + suppliersTableName: string +}; + +export class SupplierRepository { + constructor(readonly ddbClient: DynamoDBDocumentClient, + readonly log: Logger, + readonly config: SupplierRepositoryConfig) { + } + + async putSupplier(supplier: Omit): Promise { + + const now = new Date().toISOString(); + const supplierDb = { + ...supplier, + updatedAt: now + }; + + await this.ddbClient.send(new PutCommand({ + TableName: this.config.suppliersTableName, + Item: supplierDb, + })); + + return SupplierSchema.parse(supplierDb); + } + + async getSupplierById(supplierId: string): Promise { + const result = await this.ddbClient.send(new GetCommand({ + TableName: this.config.suppliersTableName, + Key: { + id: supplierId + } + })); + + if (!result.Item) { + throw new Error(`Supplier with id ${supplierId} not found`); + } + + return SupplierSchema.parse(result.Item); + } + + async getSupplierByApimId(apimId: string): Promise { + const result = await this.ddbClient.send(new QueryCommand({ + TableName: this.config.suppliersTableName, + IndexName: 'supplier-apim-index', + KeyConditionExpression: 'apimId = :apimId', + ExpressionAttributeValues: { + ':apimId': apimId + }, + })); + + if(result.Count && result.Count > 1) { + throw new Error(`Multiple suppliers found with apimId ${apimId}`); + } + + if(result.Count === 0 || !result.Items) { + throw new Error(`Supplier with apimId ${apimId} not found`); + } + + return SupplierSchema.parse(result.Items[0]); + } +}; diff --git a/internal/datastore/src/types.ts b/internal/datastore/src/types.ts index 2f2aa810..c77f96cd 100644 --- a/internal/datastore/src/types.ts +++ b/internal/datastore/src/types.ts @@ -1,13 +1,14 @@ import { z } from 'zod'; import { idRef } from '@internal/helpers'; -export const SupplerStatus = z.enum(['ENABLED', 'DISABLED']); +export const SupplierStatus = z.enum(['ENABLED', 'DISABLED']); export const SupplierSchema = z.object({ id: z.string(), name: z.string(), apimId: z.string(), - status: SupplerStatus + status: SupplierStatus, + updatedAt: z.string(), }).describe('Supplier'); export type Supplier = z.infer; diff --git a/lambdas/api-handler/src/config/deps.ts b/lambdas/api-handler/src/config/deps.ts index 1942bc6c..e704150f 100644 --- a/lambdas/api-handler/src/config/deps.ts +++ b/lambdas/api-handler/src/config/deps.ts @@ -19,25 +19,21 @@ function createDocumentClient(): DynamoDBDocumentClient { } function createLetterRepository(documentClient: DynamoDBDocumentClient, log: pino.Logger, envVars: EnvVars): LetterRepository { - const ddbClient = new DynamoDBClient({}); - const docClient = DynamoDBDocumentClient.from(ddbClient); const config = { lettersTableName: envVars.LETTERS_TABLE_NAME, lettersTtlHours: envVars.LETTER_TTL_HOURS }; - return new LetterRepository(docClient, log, config); + return new LetterRepository(documentClient, log, config); } function createMIRepository(documentClient: DynamoDBDocumentClient, log: pino.Logger, envVars: EnvVars): MIRepository { - const ddbClient = new DynamoDBClient({}); - const docClient = DynamoDBDocumentClient.from(ddbClient); const config = { miTableName: envVars.MI_TABLE_NAME, miTtlHours: envVars.MI_TTL_HOURS }; - return new MIRepository(docClient, log, config); + return new MIRepository(documentClient, log, config); } export function createDependenciesContainer(): Deps { diff --git a/lambdas/api-handler/src/handlers/__tests__/utils/test-utils.ts b/lambdas/api-handler/src/handlers/__tests__/utils/test-utils.ts index 4a3a9171..dd348694 100644 --- a/lambdas/api-handler/src/handlers/__tests__/utils/test-utils.ts +++ b/lambdas/api-handler/src/handlers/__tests__/utils/test-utils.ts @@ -18,7 +18,7 @@ export function makeApiGwEvent( requestContext: { accountId: '123456789012', apiId: 'api-id', - authorizer: {}, + authorizer: null, protocol: 'HTTP/1.1', httpMethod: 'GET', identity: {} as any, diff --git a/lambdas/api-handler/src/handlers/get-letter-data.ts b/lambdas/api-handler/src/handlers/get-letter-data.ts index 547c8e17..62b1b39f 100644 --- a/lambdas/api-handler/src/handlers/get-letter-data.ts +++ b/lambdas/api-handler/src/handlers/get-letter-data.ts @@ -1,5 +1,6 @@ import { APIGatewayProxyHandler } from "aws-lambda"; -import { assertNotEmpty, validateCommonHeaders } from "../utils/validation"; +import { assertNotEmpty } from "../utils/validation"; +import { extractCommonIds } from '../utils/commonIds'; import { ApiErrorDetail } from '../contracts/errors'; import { mapErrorToResponse } from "../mappers/error-mapper"; import { ValidationError } from "../errors"; @@ -11,10 +12,10 @@ export function createGetLetterDataHandler(deps: Deps): APIGatewayProxyHandler { return async (event) => { - const commonHeadersResult = validateCommonHeaders(event.headers, deps); + const commonIds = extractCommonIds(event.headers, event.requestContext, deps); - if (!commonHeadersResult.ok) { - return mapErrorToResponse(commonHeadersResult.error, commonHeadersResult.correlationId, deps.logger); + if (!commonIds.ok) { + return mapErrorToResponse(commonIds.error, commonIds.correlationId, deps.logger); } try { @@ -24,13 +25,13 @@ export function createGetLetterDataHandler(deps: Deps): APIGatewayProxyHandler { return { statusCode: 303, headers: { - 'Location': await getLetterDataUrl(commonHeadersResult.value.supplierId, letterId, deps) + 'Location': await getLetterDataUrl(commonIds.value.supplierId, letterId, deps) }, body: '' }; } catch (error) { - return mapErrorToResponse(error, commonHeadersResult.value.correlationId, deps.logger); + return mapErrorToResponse(error, commonIds.value.correlationId, deps.logger); } } }; diff --git a/lambdas/api-handler/src/handlers/get-letter.ts b/lambdas/api-handler/src/handlers/get-letter.ts index 5c428dce..15e5a884 100644 --- a/lambdas/api-handler/src/handlers/get-letter.ts +++ b/lambdas/api-handler/src/handlers/get-letter.ts @@ -1,5 +1,6 @@ import { APIGatewayProxyHandler } from "aws-lambda"; -import { assertNotEmpty, validateCommonHeaders } from "../utils/validation"; +import { assertNotEmpty } from "../utils/validation"; +import { extractCommonIds } from '../utils/commonIds'; import { ValidationError } from "../errors"; import { ApiErrorDetail } from "../contracts/errors"; import { getLetterById } from "../services/letter-operations"; @@ -12,22 +13,22 @@ export function createGetLetterHandler(deps: Deps): APIGatewayProxyHandler { return async (event) => { - const commonHeadersResult = validateCommonHeaders(event.headers, deps); + const commonIds = extractCommonIds(event.headers, event.requestContext, deps); - if (!commonHeadersResult.ok) { - return mapErrorToResponse(commonHeadersResult.error, commonHeadersResult.correlationId, deps.logger); + if (!commonIds.ok) { + return mapErrorToResponse(commonIds.error, commonIds.correlationId, deps.logger); } try { const letterId = assertNotEmpty(event.pathParameters?.id, new ValidationError(ApiErrorDetail.InvalidRequestMissingLetterIdPathParameter)); - const letter = await getLetterById(commonHeadersResult.value.supplierId, letterId, deps.letterRepo); + const letter = await getLetterById(commonIds.value.supplierId, letterId, deps.letterRepo); const response = mapToGetLetterResponse(letter); deps.logger.info({ description: 'Letter successfully fetched by id', - supplierId: commonHeadersResult.value.supplierId, + supplierId: commonIds.value.supplierId, letterId }); @@ -37,7 +38,7 @@ export function createGetLetterHandler(deps: Deps): APIGatewayProxyHandler { }; } catch (error) { - return mapErrorToResponse(error, commonHeadersResult.value.correlationId, deps.logger); + return mapErrorToResponse(error, commonIds.value.correlationId, deps.logger); } } } diff --git a/lambdas/api-handler/src/handlers/get-letters.ts b/lambdas/api-handler/src/handlers/get-letters.ts index 0d42ea20..25c1e3ce 100644 --- a/lambdas/api-handler/src/handlers/get-letters.ts +++ b/lambdas/api-handler/src/handlers/get-letters.ts @@ -1,6 +1,6 @@ import { APIGatewayProxyEventQueryStringParameters, APIGatewayProxyHandler } from 'aws-lambda'; import { getLettersForSupplier } from '../services/letter-operations'; -import { validateCommonHeaders } from '../utils/validation'; +import { extractCommonIds } from '../utils/commonIds'; import { ApiErrorDetail } from '../contracts/errors'; import { mapErrorToResponse } from '../mappers/error-mapper'; import { ValidationError } from '../errors'; @@ -16,10 +16,10 @@ export function createGetLettersHandler(deps: Deps): APIGatewayProxyHandler { return async (event) => { - const commonHeadersResult = validateCommonHeaders(event.headers, deps); + const commonIds = extractCommonIds(event.headers, event.requestContext, deps); - if (!commonHeadersResult.ok) { - return mapErrorToResponse(commonHeadersResult.error, commonHeadersResult.correlationId, deps.logger); + if (!commonIds.ok) { + return mapErrorToResponse(commonIds.error, commonIds.correlationId, deps.logger); } try { @@ -28,7 +28,7 @@ export function createGetLettersHandler(deps: Deps): APIGatewayProxyHandler { const limitNumber = getLimitOrDefault(event.queryStringParameters, maxLimit, deps.logger); const letters = await getLettersForSupplier( - commonHeadersResult.value.supplierId, + commonIds.value.supplierId, status, limitNumber, deps.letterRepo, @@ -38,7 +38,7 @@ export function createGetLettersHandler(deps: Deps): APIGatewayProxyHandler { deps.logger.info({ description: 'Pending letters successfully fetched', - supplierId: commonHeadersResult.value.supplierId, + supplierId: commonIds.value.supplierId, limitNumber, status, lettersCount: letters.length @@ -50,7 +50,7 @@ export function createGetLettersHandler(deps: Deps): APIGatewayProxyHandler { }; } catch (error) { - return mapErrorToResponse(error, commonHeadersResult.value.correlationId, deps.logger); + return mapErrorToResponse(error, commonIds.value.correlationId, deps.logger); } } }; diff --git a/lambdas/api-handler/src/handlers/patch-letter.ts b/lambdas/api-handler/src/handlers/patch-letter.ts index e8442fae..2ff1d911 100644 --- a/lambdas/api-handler/src/handlers/patch-letter.ts +++ b/lambdas/api-handler/src/handlers/patch-letter.ts @@ -4,7 +4,8 @@ import { PatchLetterRequest, PatchLetterRequestSchema } from '../contracts/lette import { ApiErrorDetail } from '../contracts/errors'; import { ValidationError } from '../errors'; import { mapErrorToResponse } from '../mappers/error-mapper'; -import { assertNotEmpty, validateCommonHeaders } from '../utils/validation'; +import { assertNotEmpty } from '../utils/validation'; +import { extractCommonIds } from '../utils/commonIds'; import { mapToLetterDto } from '../mappers/letter-mapper'; import type { Deps } from "../config/deps"; @@ -13,10 +14,10 @@ export function createPatchLetterHandler(deps: Deps): APIGatewayProxyHandler { return async (event) => { - const commonHeadersResult = validateCommonHeaders(event.headers, deps); + const commonIds = extractCommonIds(event.headers, event.requestContext, deps); - if (!commonHeadersResult.ok) { - return mapErrorToResponse(commonHeadersResult.error, commonHeadersResult.correlationId, deps.logger); + if (!commonIds.ok) { + return mapErrorToResponse(commonIds.error, commonIds.correlationId, deps.logger); } try { @@ -35,7 +36,7 @@ export function createPatchLetterHandler(deps: Deps): APIGatewayProxyHandler { else throw error; } - const updatedLetter = await patchLetterStatus(mapToLetterDto(patchLetterRequest, commonHeadersResult.value.supplierId), letterId, deps.letterRepo); + const updatedLetter = await patchLetterStatus(mapToLetterDto(patchLetterRequest, commonIds.value.supplierId), letterId, deps.letterRepo); return { statusCode: 200, @@ -43,7 +44,7 @@ export function createPatchLetterHandler(deps: Deps): APIGatewayProxyHandler { }; } catch (error) { - return mapErrorToResponse(error, commonHeadersResult.value.correlationId, deps.logger); + return mapErrorToResponse(error, commonIds.value.correlationId, deps.logger); } }; }; diff --git a/lambdas/api-handler/src/handlers/post-mi.ts b/lambdas/api-handler/src/handlers/post-mi.ts index 526f849b..03229a13 100644 --- a/lambdas/api-handler/src/handlers/post-mi.ts +++ b/lambdas/api-handler/src/handlers/post-mi.ts @@ -3,7 +3,8 @@ import { postMI as postMIOperation } from '../services/mi-operations'; import { ApiErrorDetail } from "../contracts/errors"; import { ValidationError } from "../errors"; import { mapErrorToResponse } from "../mappers/error-mapper"; -import { assertNotEmpty, validateCommonHeaders, validateIso8601Timestamp } from "../utils/validation"; +import { assertNotEmpty, validateIso8601Timestamp } from "../utils/validation"; +import { extractCommonIds } from '../utils/commonIds'; import { PostMIRequest, PostMIRequestSchema } from "../contracts/mi"; import { mapToMI } from "../mappers/mi-mapper"; import { Deps } from "../config/deps"; @@ -12,10 +13,10 @@ export function createPostMIHandler(deps: Deps): APIGatewayProxyHandler { return async (event) => { - const commonHeadersResult = validateCommonHeaders(event.headers, deps); + const commonIds = extractCommonIds(event.headers, event.requestContext, deps); - if (!commonHeadersResult.ok) { - return mapErrorToResponse(commonHeadersResult.error, commonHeadersResult.correlationId, deps.logger); + if (!commonIds.ok) { + return mapErrorToResponse(commonIds.error, commonIds.correlationId, deps.logger); } try { @@ -33,7 +34,7 @@ export function createPostMIHandler(deps: Deps): APIGatewayProxyHandler { } validateIso8601Timestamp(postMIRequest.data.attributes.timestamp); - const result = await postMIOperation(mapToMI(postMIRequest, commonHeadersResult.value.supplierId), deps.miRepo); + const result = await postMIOperation(mapToMI(postMIRequest, commonIds.value.supplierId), deps.miRepo); return { statusCode: 201, @@ -41,7 +42,7 @@ export function createPostMIHandler(deps: Deps): APIGatewayProxyHandler { }; } catch (error) { - return mapErrorToResponse(error, commonHeadersResult.value.correlationId, deps.logger); + return mapErrorToResponse(error, commonIds.value.correlationId, deps.logger); } } }; diff --git a/lambdas/api-handler/src/utils/__tests__/commonIds.test.ts b/lambdas/api-handler/src/utils/__tests__/commonIds.test.ts new file mode 100644 index 00000000..2aa10c5b --- /dev/null +++ b/lambdas/api-handler/src/utils/__tests__/commonIds.test.ts @@ -0,0 +1,98 @@ +import { APIGatewayProxyEvent } from 'aws-lambda'; +import { extractCommonIds } from '../commonIds'; + +const mockDeps = { + env: { + APIM_CORRELATION_HEADER: 'x-correlation-id', + SUPPLIER_ID_HEADER: 'x-supplier-id', + } +} as any; + +const mockContext = {} as APIGatewayProxyEvent['requestContext']; + +describe('extractCommonIds', () => { + it('returns error if headers are missing', () => { + expect(extractCommonIds({}, mockContext, mockDeps)).toEqual({ + ok: false, + error: expect.any(Error) + }); + }); + + it('returns error if correlation id is missing', () => { + const headers = { 'x-supplier-id': 'SUP123', 'x-request-id': 'REQ123' }; + expect(extractCommonIds(headers, mockContext, mockDeps)).toEqual({ + ok: false, + error: expect.any(Error) + }); + }); + + it('returns error if request id is missing', () => { + const headers = { 'x-correlation-id': 'CORR123', 'x-supplier-id': 'SUP123' }; + expect(extractCommonIds(headers, mockContext, mockDeps)).toEqual({ + ok: false, + error: expect.any(Error), + correlationId: 'CORR123' + }); + }); + + it('returns error if supplier id is missing', () => { + const headers = { 'x-correlation-id': 'CORR123', 'x-request-id': 'REQ123' }; + expect(extractCommonIds(headers, mockContext, mockDeps)).toEqual({ + ok: false, + error: expect.any(Error), + correlationId: 'CORR123' + }); + }); + + it('returns ok and ids if all present', () => { + const headers = { + 'x-correlation-id': 'CORR123', + 'x-request-id': 'REQ123', + 'x-supplier-id': 'SUP123' + }; + expect(extractCommonIds(headers, mockContext, mockDeps)).toEqual({ + ok: true, + value: { + correlationId: 'CORR123', + supplierId: 'SUP123' + } + }); + }); + + it('handles mixed case header names', () => { + const headers = { + 'X-Correlation-Id': 'CORR123', + 'X-Request-Id': 'REQ123', + 'X-Supplier-Id': 'SUP123' + }; + expect(extractCommonIds(headers, mockContext, mockDeps)).toEqual({ + ok: true, + value: { + correlationId: 'CORR123', + supplierId: 'SUP123' + } + }); + }); + + it('uses the supplier id from the authorizer if present', () => { + const headers = { 'x-correlation-id': 'CORR123', 'x-supplier-id': 'SUP123', 'x-request-id': 'REQ123' }; + const context = { 'authorizer': {'principalId': 'SUP456'}} as unknown as APIGatewayProxyEvent['requestContext']; + expect(extractCommonIds(headers, context, mockDeps)).toEqual({ + ok: true, + value: { + correlationId: 'CORR123', + supplierId: 'SUP456' + } + }); + }); + + it('refuses to use the supplier id from the header if authorizer is present', () => { + const headers = { 'x-correlation-id': 'CORR123', 'x-supplier-id': 'SUP123', 'x-request-id': 'REQ123' }; + const context = { 'authorizer': {}} as unknown as APIGatewayProxyEvent['requestContext']; + expect(extractCommonIds(headers, context, mockDeps)).toEqual({ + ok: false, + error: expect.any(Error), + correlationId: 'CORR123' + }); + }); +}); diff --git a/lambdas/api-handler/src/utils/commonIds.ts b/lambdas/api-handler/src/utils/commonIds.ts new file mode 100644 index 00000000..47293a1f --- /dev/null +++ b/lambdas/api-handler/src/utils/commonIds.ts @@ -0,0 +1,45 @@ +import { APIGatewayProxyEvent, APIGatewayProxyEventHeaders } from 'aws-lambda'; +import { Deps } from '../config/deps'; +import { lowerCaseKeys } from './validation'; + + +export function extractCommonIds(headers: APIGatewayProxyEventHeaders, context: APIGatewayProxyEvent['requestContext'], deps: Deps +): { ok: true; value: { correlationId: string; supplierId: string; }; } | { ok: false; error: Error; correlationId?: string; } { + + if (!headers || Object.keys(headers).length === 0) { + return { ok: false, error: new Error('The request headers are empty') }; + } + + const lowerCasedHeaders = lowerCaseKeys(headers); + + const correlationId = lowerCasedHeaders[deps.env.APIM_CORRELATION_HEADER]; + if (!correlationId) { + return { ok: false, error: new Error("The request headers don't contain the APIM correlation id") }; + } + + const requestId = lowerCasedHeaders['x-request-id']; + if (!requestId) { + return { + ok: false, + error: new Error("The request headers don't contain the x-request-id"), + correlationId + }; + } + + // In normal API usage, we expect the authorizer to provide the supplier ID. When the lambda is invoked directly, for instance + // in the AWS console, then fall back to using the header. + + const supplierId = context.authorizer? + context.authorizer.principalId: + lowerCasedHeaders[deps.env.SUPPLIER_ID_HEADER]; + + if (!supplierId) { + return { + ok: false, + error: new Error('The supplier ID is missing from the request'), + correlationId + }; + } + + return { ok: true, value: { correlationId, supplierId } }; +} diff --git a/lambdas/api-handler/src/utils/validation.ts b/lambdas/api-handler/src/utils/validation.ts index f86b4fff..c898760d 100644 --- a/lambdas/api-handler/src/utils/validation.ts +++ b/lambdas/api-handler/src/utils/validation.ts @@ -1,7 +1,5 @@ -import { APIGatewayProxyEventHeaders } from 'aws-lambda'; import { ValidationError } from '../errors'; import { ApiErrorDetail } from '../contracts/errors'; -import { Deps } from '../config/deps'; export function assertNotEmpty( value: T | null | undefined, @@ -26,41 +24,6 @@ export function lowerCaseKeys(obj: Record): Record { return Object.fromEntries(Object.entries(obj).map(([k, v]) => [k.toLowerCase(), v])); } -export function validateCommonHeaders(headers: APIGatewayProxyEventHeaders, deps: Deps -): { ok: true; value: {correlationId: string, supplierId: string } } | { ok: false; error: Error; correlationId?: string } { - - if (!headers || Object.keys(headers).length === 0) { - return { ok: false, error: new Error('The request headers are empty') }; - } - - const lowerCasedHeaders = lowerCaseKeys(headers); - - const correlationId = lowerCasedHeaders[deps.env.APIM_CORRELATION_HEADER]; - if (!correlationId) { - return { ok: false, error: new Error("The request headers don't contain the APIM correlation id") }; - } - - const requestId = lowerCasedHeaders['x-request-id']; - if (!requestId) { - return { - ok: false, - error: new Error("The request headers don't contain the x-request-id"), - correlationId - }; - } - - const supplierId = lowerCasedHeaders[deps.env.SUPPLIER_ID_HEADER]; - if (!supplierId) { - return { - ok: false, - error: new Error('The supplier ID is missing from the request'), - correlationId - }; - } - - return { ok: true, value: { correlationId, supplierId } }; -} - export function validateIso8601Timestamp(timestamp: string) { function normalisePrecision([_, mainPart, fractionalPart='.000']: string[]) : string { diff --git a/lambdas/authorizer/src/__tests__/index.test.ts b/lambdas/authorizer/src/__tests__/index.test.ts index 71bde62d..120d1dec 100644 --- a/lambdas/authorizer/src/__tests__/index.test.ts +++ b/lambdas/authorizer/src/__tests__/index.test.ts @@ -1,5 +1,21 @@ -import { APIGatewayRequestAuthorizerEvent, Callback, Context } from 'aws-lambda'; -import { handler } from '../index'; +import { APIGatewayEventClientCertificate, APIGatewayRequestAuthorizerEvent, Callback, Context } from 'aws-lambda'; +import { Deps } from '../deps'; +import pino from 'pino'; +import { EnvVars } from '../env'; +import { createAuthorizerHandler } from '../authorizer'; + +const mockedDeps: jest.Mocked = { + logger: { info: jest.fn(), error: jest.fn() } as unknown as pino.Logger, + env: { + CLOUDWATCH_NAMESPACE: 'cloudwatch-namespace', + CLIENT_CERTIFICATE_EXPIRATION_ALERT_DAYS: 14, + APIM_APPLICATION_ID_HEADER: 'apim-application-id' + } as unknown as EnvVars, + supplierRepo: { + getSupplierByApimId: jest.fn(), + } as any, + } as Deps; + describe('Authorizer Lambda Function', () => { let mockEvent: APIGatewayRequestAuthorizerEvent; @@ -11,93 +27,183 @@ describe('Authorizer Lambda Function', () => { type: 'REQUEST', methodArn: 'arn:aws:execute-api:region:account-id:api-id/stage/GET/resource', headers: {}, - pathParameters: {} + pathParameters: {}, + requestContext: {identity: {clientCert: null}}, } as APIGatewayRequestAuthorizerEvent; mockContext = {} as Context; mockCallback = jest.fn(); }); - it('Should allow access when headers match', () => { - mockEvent.headers = { headerauth1: 'headervalue1' }; - - handler(mockEvent, mockContext, mockCallback); - - expect(mockCallback).toHaveBeenCalledWith(null, expect.objectContaining({ - policyDocument: expect.objectContaining({ - Statement: expect.arrayContaining([ - expect.objectContaining({ - Effect: 'Allow', - }), - ]), - }), - })); + describe('Certificate expiry check', () => { + + beforeEach(() => { + jest.useFakeTimers({ doNotFake: ['nextTick'] }) + .setSystemTime(new Date('2025-11-03T14:19:00Z')); + }); + + afterEach(() => { + jest.useRealTimers(); + }) + + it('Should not log CloudWatch metric when certificate is null', async () => { + mockEvent.requestContext.identity.clientCert = null; + + const handler = createAuthorizerHandler(mockedDeps); + handler(mockEvent, mockContext, mockCallback); + await new Promise(process.nextTick); + + const mockedInfo = mockedDeps.logger.info as jest.Mock; + expect(mockedInfo.mock.calls).not.toContainEqual( + expect.stringContaining('CloudWatchMetrics')); + }); + + it('Should log CloudWatch metric when the certificate expiry threshold is reached', async () => { + mockEvent.requestContext.identity.clientCert = buildCertWithExpiry('2025-11-17T14:19:00Z'); + + const handler = createAuthorizerHandler(mockedDeps); + handler(mockEvent, mockContext, mockCallback); + await new Promise(process.nextTick); + + const mockedInfo = mockedDeps.logger.info as jest.Mock; + expect(mockedInfo.mock.calls.map(call => call[0])).toContain(JSON.stringify( + {_aws: {Timestamp: 1762179540000, + CloudWatchMetrics: [{ + Namespace: 'cloudwatch-namespace', + Dimensions: ['SUBJECT_DN', 'NOT_AFTER'], + Metrics: [{Name: 'apim-client-certificate-near-expiry', Unit: 'Count', Value: 1}] + }]}, + SUBJECT_DN: 'CN=test-subject', + NOT_AFTER: '2025-11-17T14:19:00Z', + 'apim-client-certificate-near-expiry': 1})); + }); + + it('Should not log CloudWatch metric when the certificate expiry threshold is not yet reached', async () => { + mockEvent.requestContext.identity.clientCert = buildCertWithExpiry('2025-11-18T14:19:00Z'); + + const handler = createAuthorizerHandler(mockedDeps); + handler(mockEvent, mockContext, mockCallback); + await new Promise(process.nextTick); + + const mockedInfo = mockedDeps.logger.info as jest.Mock; + expect(mockedInfo.mock.calls).not.toContainEqual( + expect.stringContaining('CloudWatchMetrics')); + }); }); - it('Should deny access when headers do not match', () => { - mockEvent.headers = { headerauth1: 'wrongValue' }; - - handler(mockEvent, mockContext, mockCallback); - - expect(mockCallback).toHaveBeenCalledWith(null, expect.objectContaining({ - policyDocument: expect.objectContaining({ - Statement: expect.arrayContaining([ - expect.objectContaining({ - Effect: 'Deny', - }), - ]), - }), - })); + function buildCertWithExpiry(expiry: string): APIGatewayEventClientCertificate { + + return { + subjectDN: 'CN=test-subject', + validity: { + notAfter: expiry, + } as APIGatewayEventClientCertificate['validity'], + } as APIGatewayEventClientCertificate; + } + + describe('Supplier ID lookup', () => { + + it('Should deny the request when no headers are present', async () => { + mockEvent.headers = null; + + const handler = createAuthorizerHandler(mockedDeps); + handler(mockEvent, mockContext, mockCallback); + await new Promise(process.nextTick); + + expect(mockCallback).toHaveBeenCalledWith(null, expect.objectContaining({ + policyDocument: expect.objectContaining({ + Statement: [ + expect.objectContaining({ + Effect: 'Deny', + }), + ], + }), + })); + }); + + it('Should deny the request when the APIM application ID header is absent', async () => { + mockEvent.headers = {'x-apim-correlation-id': 'correlation-id'}; + + const handler = createAuthorizerHandler(mockedDeps); + handler(mockEvent, mockContext, mockCallback); + await new Promise(process.nextTick); + + expect(mockCallback).toHaveBeenCalledWith(null, expect.objectContaining({ + policyDocument: expect.objectContaining({ + Statement: [ + expect.objectContaining({ + Effect: 'Deny', + }), + ], + }), + })); + }); + + it('Should deny the request when no supplier ID is found', async () => { + mockEvent.headers = { 'apim-application-id': 'unknown-apim-id' }; + (mockedDeps.supplierRepo.getSupplierByApimId as jest.Mock).mockRejectedValue(new Error('Supplier not found')); + + const handler = createAuthorizerHandler(mockedDeps); + handler(mockEvent, mockContext, mockCallback); + await new Promise(process.nextTick); + + expect(mockCallback).toHaveBeenCalledWith(null, expect.objectContaining({ + policyDocument: expect.objectContaining({ + Statement: [ + expect.objectContaining({ + Effect: 'Deny', + }), + ], + }), + })); + }); + + it('Should allow the request when the supplier ID is found', async () => { + mockEvent.headers = { 'apim-application-id': 'valid-apim-id' }; + (mockedDeps.supplierRepo.getSupplierByApimId as jest.Mock).mockResolvedValue({ + id: 'supplier-123', + apimApplicationId: 'valid-apim-id', + name: 'Test Supplier', + status: 'ENABLED' + }); + + const handler = createAuthorizerHandler(mockedDeps); + handler(mockEvent, mockContext, mockCallback); + await new Promise(process.nextTick); + + expect(mockCallback).toHaveBeenCalledWith(null, expect.objectContaining({ + policyDocument: expect.objectContaining({ + Statement: [ + expect.objectContaining({ + Effect: 'Allow', + }), + ], + }), + principalId: 'supplier-123', + })); + }); }); - it('Should handle null headers gracefully', () => { - mockEvent.headers = null; + it('Should deny the request the supplier is disabled', async () => { + mockEvent.headers = { 'apim-application-id': 'unknown-apim-id' }; + (mockedDeps.supplierRepo.getSupplierByApimId as jest.Mock).mockResolvedValue({ + id: 'supplier-123', + apimApplicationId: 'valid-apim-id', + name: 'Test Supplier', + status: 'DISABLED' + }); + const handler = createAuthorizerHandler(mockedDeps); handler(mockEvent, mockContext, mockCallback); + await new Promise(process.nextTick); expect(mockCallback).toHaveBeenCalledWith(null, expect.objectContaining({ policyDocument: expect.objectContaining({ - Statement: expect.arrayContaining([ + Statement: [ expect.objectContaining({ Effect: 'Deny', }), - ]), - }), - })); - }); - - it('Should handle defined headers correctly', () => { - mockEvent.headers = { headerauth1: 'headervalue1' }; - - handler(mockEvent, mockContext, mockCallback); - - expect(mockCallback).toHaveBeenCalledWith(null, expect.objectContaining({ - policyDocument: expect.objectContaining({ - Statement: expect.arrayContaining([ - expect.objectContaining({ - Effect: 'Allow', - }), - ]), - }), - })); - }); - - it('Should handle additional headers correctly', () => { - mockEvent.headers = { - headerauth1: 'headervalue1' , - otherheader1: 'headervalue2', - otherheader2: 'headervalue3' - }; - - handler(mockEvent, mockContext, mockCallback); - - expect(mockCallback).toHaveBeenCalledWith(null, expect.objectContaining({ - policyDocument: expect.objectContaining({ - Statement: expect.arrayContaining([ - expect.objectContaining({ - Effect: 'Allow', - }), - ]), + ], }), })); }); diff --git a/lambdas/authorizer/src/authorizer.ts b/lambdas/authorizer/src/authorizer.ts new file mode 100644 index 00000000..ec43fdd0 --- /dev/null +++ b/lambdas/authorizer/src/authorizer.ts @@ -0,0 +1,137 @@ +// A simple request-based authorizer example to demonstrate how to use request +// parameters to allow or deny a request. In this example, a request is +// authorized if the client-supplied HeaderAuth1 header and stage variable of StageVar1 +// both match specified values of 'headerValue1' and 'stageValue1', respectively. +// +// Example curl request (replace and as appropriate): +// +// curl -H "HeaderAuth1: headerValue1" \ +// "//your-resource" +// + +// See https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html for the original JS documentation + +import {APIGatewayAuthorizerResult, APIGatewayEventClientCertificate, APIGatewayRequestAuthorizerEvent, APIGatewayRequestAuthorizerEventHeaders, APIGatewayRequestAuthorizerHandler, + Callback, Context } from 'aws-lambda'; +import { Deps } from './deps'; +import { Supplier } from '@internal/datastore'; + +export function createAuthorizerHandler(deps: Deps): APIGatewayRequestAuthorizerHandler { + + return ( + event: APIGatewayRequestAuthorizerEvent, + context: Context, + callback: Callback + ): void => { + deps.logger.info(event, 'Received event'); + + + checkCertificateExpiry(event.requestContext.identity.clientCert, deps); + + getSupplier(event.headers, deps) + .then((supplier: Supplier) => { + deps.logger.info('Allow event'); + callback(null, generateAllow(event.methodArn, supplier.id)); + }) + .catch((error) => { + deps.logger.info(error, 'Deny event'); + callback(null, generateDeny(event.methodArn)); + }); + }; +} + +async function getSupplier(headers: APIGatewayRequestAuthorizerEventHeaders | null, deps: Deps): Promise { + const apimId = Object.entries(headers || {}) + .find(([headerName, _]) => headerName.toLowerCase() === deps.env.APIM_APPLICATION_ID_HEADER)?.[1] as string; + + if(!apimId) { + throw new Error('No APIM application ID found in header'); + } + const supplier = await deps.supplierRepo.getSupplierByApimId(apimId); + if (supplier.status === 'DISABLED') { + throw new Error(`Supplier ${supplier.id} is disabled`); + } + return supplier; +} + + + // Helper function to generate an IAM policy + function generatePolicy( + principalId: string, + effect: 'Allow' | 'Deny', + resource: string + ): APIGatewayAuthorizerResult { + // Required output: + const authResponse: APIGatewayAuthorizerResult = { + principalId, + policyDocument: { + Version: '2012-10-17', + Statement: [ + { + Action: 'execute-api:Invoke', + Effect: effect, + Resource: resource, + }, + ], + }, + }; + return authResponse; + } + +function generateAllow(resource: string, supplierId: string): APIGatewayAuthorizerResult { + return generatePolicy(supplierId, 'Allow', resource); +} + +function generateDeny(resource: string): APIGatewayAuthorizerResult { + return generatePolicy('invalid-user', 'Deny', resource); +} + +function getCertificateExpiryInDays(certificate: APIGatewayEventClientCertificate): number { + const now = new Date().getTime(); + const expiry = new Date(certificate.validity.notAfter).getTime(); + return (expiry - now) / (1000 * 60 * 60 * 24); +} + +async function checkCertificateExpiry(certificate: APIGatewayEventClientCertificate | null, deps: Deps): Promise { + deps.logger.info({ + description: 'Client certificate details', + issuerDN: certificate?.issuerDN, + subjectDN: certificate?.subjectDN, + validity: certificate?.validity, + }); + + if (!certificate) { + // In a real production environment, we won't have got this far if there wasn't a cert + return; + } + + const expiry = getCertificateExpiryInDays(certificate); + + if (expiry <= deps.env.CLIENT_CERTIFICATE_EXPIRATION_ALERT_DAYS) { + deps.logger.info(JSON.stringify(buildCloudWatchMetric(deps.env.CLOUDWATCH_NAMESPACE, certificate))); + } + + function buildCloudWatchMetric(namespace: string, certificate: APIGatewayEventClientCertificate) { + return { + _aws: { + Timestamp: new Date().valueOf(), + CloudWatchMetrics: [ + { + Namespace: namespace, + Dimensions: ['SUBJECT_DN', 'NOT_AFTER'], + Metrics: [ + { + Name: 'apim-client-certificate-near-expiry', + Unit: 'Count', + Value: 1, + }, + ], + }, + ], + }, + 'SUBJECT_DN': certificate.subjectDN, + 'NOT_AFTER': certificate.validity.notAfter, + 'apim-client-certificate-near-expiry': 1, + }; + } +}; diff --git a/lambdas/authorizer/src/deps.ts b/lambdas/authorizer/src/deps.ts new file mode 100644 index 00000000..218fa649 --- /dev/null +++ b/lambdas/authorizer/src/deps.ts @@ -0,0 +1,34 @@ +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; +import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb"; +import pino from 'pino'; +import { envVars, EnvVars } from "./env"; +import { SupplierRepository } from '@internal/datastore'; + +export type Deps = { + supplierRepo: SupplierRepository; + logger: pino.Logger; + env: EnvVars; +}; + +function createDocumentClient(): DynamoDBDocumentClient { + const ddbClient = new DynamoDBClient({}); + return DynamoDBDocumentClient.from(ddbClient); +} + +function createSupplierRepository(documentClient: DynamoDBDocumentClient, log: pino.Logger, envVars: EnvVars): SupplierRepository { + const config = { + suppliersTableName: envVars.SUPPLIERS_TABLE_NAME + }; + + return new SupplierRepository(documentClient, log, config); +} + +export function createDependenciesContainer(): Deps { + const log = pino(); + + return { + supplierRepo: createSupplierRepository(createDocumentClient(), log, envVars), + logger: log, + env: envVars + }; +} diff --git a/lambdas/authorizer/src/env.ts b/lambdas/authorizer/src/env.ts new file mode 100644 index 00000000..5e181324 --- /dev/null +++ b/lambdas/authorizer/src/env.ts @@ -0,0 +1,12 @@ +import {z} from 'zod'; + +const EnvVarsSchema = z.object({ + SUPPLIERS_TABLE_NAME: z.string(), + CLOUDWATCH_NAMESPACE: z.string(), + APIM_APPLICATION_ID_HEADER: z.string(), + CLIENT_CERTIFICATE_EXPIRATION_ALERT_DAYS: z.coerce.number().int() +}); + +export type EnvVars = z.infer; + +export const envVars = EnvVarsSchema.parse(process.env); diff --git a/lambdas/authorizer/src/index.ts b/lambdas/authorizer/src/index.ts index 9797d5d2..f65d0f3d 100644 --- a/lambdas/authorizer/src/index.ts +++ b/lambdas/authorizer/src/index.ts @@ -1,74 +1,6 @@ -// A simple request-based authorizer example to demonstrate how to use request -// parameters to allow or deny a request. In this example, a request is -// authorized if the client-supplied HeaderAuth1 header and stage variable of StageVar1 -// both match specified values of 'headerValue1' and 'stageValue1', respectively. -// -// Example curl request (replace and as appropriate): -// -// curl -H "HeaderAuth1: headerValue1" \ -// "//your-resource" -// +import { createAuthorizerHandler } from "./authorizer"; +import { createDependenciesContainer } from "./deps"; -// See https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-use-lambda-authorizer.html for the original JS documentation +const container = createDependenciesContainer(); -import { APIGatewayAuthorizerResult, APIGatewayRequestAuthorizerEvent, Callback, Context } from 'aws-lambda'; -import pino from 'pino'; - -export const handler = ( - event: APIGatewayRequestAuthorizerEvent, - context: Context, - callback: Callback, - log = pino() -): void => { - log.info(event, 'Received event'); - - const headers = event.headers || {}; - - // Perform authorization to return the Allow policy for correct parameters and - // the 'Unauthorized' error, otherwise. - if ( - headers['headerauth1'] === 'headervalue1' - ) { - log.info('Allow event'); - callback(null, generateAllow('me', event.methodArn)); - } else { - log.info('Deny event'); - callback(null, generateDeny('me', event.methodArn)); - } -}; - -// Helper function to generate an IAM policy -function generatePolicy( - principalId: string, - effect: 'Allow' | 'Deny', - resource: string -): APIGatewayAuthorizerResult { - // Required output: - const authResponse: APIGatewayAuthorizerResult = { - principalId, - policyDocument: { - Version: '2012-10-17', - Statement: [ - { - Action: 'execute-api:Invoke', - Effect: effect, - Resource: resource, - }, - ], - }, - context: { - stringKey: 'stringval', - numberKey: 123, - booleanKey: true, - }, - }; - return authResponse; -} - -function generateAllow(principalId: string, resource: string): APIGatewayAuthorizerResult { - return generatePolicy(principalId, 'Allow', resource); -} - -function generateDeny(principalId: string, resource: string): APIGatewayAuthorizerResult { - return generatePolicy(principalId, 'Deny', resource); -} +export const handler = createAuthorizerHandler(container); diff --git a/package-lock.json b/package-lock.json index 0dd021ca..4e6cc26a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,7 @@ ], "dependencies": { "@aws-sdk/client-api-gateway": "^3.906.0", + "@aws-sdk/client-s3": "^3.925.0", "@playwright/test": "^1.55.1", "ajv": "^8.17.1", "js-yaml": "^4.1.0", @@ -3060,6 +3061,8 @@ }, "node_modules/@aws-crypto/crc32": { "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", + "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/util": "^5.2.0", @@ -3072,6 +3075,8 @@ }, "node_modules/@aws-crypto/crc32c": { "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", + "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/util": "^5.2.0", @@ -3752,65 +3757,67 @@ } }, "node_modules/@aws-sdk/client-s3": { - "version": "3.896.0", + "version": "3.925.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.925.0.tgz", + "integrity": "sha512-imAul+6pyJYH4cbxPz1OiFXxrKKTRqVzlT2e0M6zbPHmUcJsF5E+b+4qvHQChU8wFGtIWJHH/JChF2ibfTnXdA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.896.0", - "@aws-sdk/credential-provider-node": "3.896.0", - "@aws-sdk/middleware-bucket-endpoint": "3.893.0", - "@aws-sdk/middleware-expect-continue": "3.893.0", - "@aws-sdk/middleware-flexible-checksums": "3.896.0", - "@aws-sdk/middleware-host-header": "3.893.0", - "@aws-sdk/middleware-location-constraint": "3.893.0", - "@aws-sdk/middleware-logger": "3.893.0", - "@aws-sdk/middleware-recursion-detection": "3.893.0", - "@aws-sdk/middleware-sdk-s3": "3.896.0", - "@aws-sdk/middleware-ssec": "3.893.0", - "@aws-sdk/middleware-user-agent": "3.896.0", - "@aws-sdk/region-config-resolver": "3.893.0", - "@aws-sdk/signature-v4-multi-region": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@aws-sdk/util-endpoints": "3.895.0", - "@aws-sdk/util-user-agent-browser": "3.893.0", - "@aws-sdk/util-user-agent-node": "3.896.0", - "@aws-sdk/xml-builder": "3.894.0", - "@smithy/config-resolver": "^4.2.2", - "@smithy/core": "^3.12.0", - "@smithy/eventstream-serde-browser": "^4.1.1", - "@smithy/eventstream-serde-config-resolver": "^4.2.1", - "@smithy/eventstream-serde-node": "^4.1.1", - "@smithy/fetch-http-handler": "^5.2.1", - "@smithy/hash-blob-browser": "^4.1.1", - "@smithy/hash-node": "^4.1.1", - "@smithy/hash-stream-node": "^4.1.1", - "@smithy/invalid-dependency": "^4.1.1", - "@smithy/md5-js": "^4.1.1", - "@smithy/middleware-content-length": "^4.1.1", - "@smithy/middleware-endpoint": "^4.2.4", - "@smithy/middleware-retry": "^4.3.0", - "@smithy/middleware-serde": "^4.1.1", - "@smithy/middleware-stack": "^4.1.1", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/node-http-handler": "^4.2.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/smithy-client": "^4.6.4", - "@smithy/types": "^4.5.0", - "@smithy/url-parser": "^4.1.1", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-body-length-browser": "^4.1.0", - "@smithy/util-body-length-node": "^4.1.0", - "@smithy/util-defaults-mode-browser": "^4.1.4", - "@smithy/util-defaults-mode-node": "^4.1.4", - "@smithy/util-endpoints": "^3.1.2", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-retry": "^4.1.2", - "@smithy/util-stream": "^4.3.2", - "@smithy/util-utf8": "^4.1.0", - "@smithy/util-waiter": "^4.1.1", - "@smithy/uuid": "^1.0.0", + "@aws-sdk/core": "3.922.0", + "@aws-sdk/credential-provider-node": "3.925.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.925.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.2", + "@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.8", + "@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" }, "engines": { @@ -3818,46 +3825,48 @@ } }, "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/client-sso": { - "version": "3.896.0", + "version": "3.925.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.925.0.tgz", + "integrity": "sha512-ixC9CyXe/mBo1X+bzOxIIzsdBYzM+klWoHUYzwnPMrXhpDrMjj8D24R/FPqrDnhoYYXiyS4BApRLpeymsFJq2Q==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.896.0", - "@aws-sdk/middleware-host-header": "3.893.0", - "@aws-sdk/middleware-logger": "3.893.0", - "@aws-sdk/middleware-recursion-detection": "3.893.0", - "@aws-sdk/middleware-user-agent": "3.896.0", - "@aws-sdk/region-config-resolver": "3.893.0", - "@aws-sdk/types": "3.893.0", - "@aws-sdk/util-endpoints": "3.895.0", - "@aws-sdk/util-user-agent-browser": "3.893.0", - "@aws-sdk/util-user-agent-node": "3.896.0", - "@smithy/config-resolver": "^4.2.2", - "@smithy/core": "^3.12.0", - "@smithy/fetch-http-handler": "^5.2.1", - "@smithy/hash-node": "^4.1.1", - "@smithy/invalid-dependency": "^4.1.1", - "@smithy/middleware-content-length": "^4.1.1", - "@smithy/middleware-endpoint": "^4.2.4", - "@smithy/middleware-retry": "^4.3.0", - "@smithy/middleware-serde": "^4.1.1", - "@smithy/middleware-stack": "^4.1.1", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/node-http-handler": "^4.2.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/smithy-client": "^4.6.4", - "@smithy/types": "^4.5.0", - "@smithy/url-parser": "^4.1.1", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-body-length-browser": "^4.1.0", - "@smithy/util-body-length-node": "^4.1.0", - "@smithy/util-defaults-mode-browser": "^4.1.4", - "@smithy/util-defaults-mode-node": "^4.1.4", - "@smithy/util-endpoints": "^3.1.2", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-retry": "^4.1.2", - "@smithy/util-utf8": "^4.1.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.925.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.2", + "@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.8", + "@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" }, "engines": { @@ -3865,21 +3874,23 @@ } }, "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/core": { - "version": "3.896.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.893.0", - "@aws-sdk/xml-builder": "3.894.0", - "@smithy/core": "^3.12.0", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/signature-v4": "^5.2.1", - "@smithy/smithy-client": "^4.6.4", - "@smithy/types": "^4.5.0", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-utf8": "^4.1.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.922.0.tgz", + "integrity": "sha512-EvfP4cqJfpO3L2v5vkIlTkMesPtRwWlMfsaW6Tpfm7iYfBOuTi6jx60pMDMTyJNVfh6cGmXwh/kj1jQdR+w99Q==", + "license": "Apache-2.0", + "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" }, "engines": { @@ -3887,13 +3898,15 @@ } }, "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-env": { - "version": "3.896.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.922.0.tgz", + "integrity": "sha512-WikGQpKkROJSK3D3E7odPjZ8tU7WJp5/TgGdRuZw3izsHUeH48xMv6IznafpRTmvHcjAbDQj4U3CJZNAzOK/OQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@smithy/property-provider": "^4.1.1", - "@smithy/types": "^4.5.0", + "@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" }, "engines": { @@ -3901,18 +3914,20 @@ } }, "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-http": { - "version": "3.896.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@smithy/fetch-http-handler": "^5.2.1", - "@smithy/node-http-handler": "^4.2.1", - "@smithy/property-provider": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/smithy-client": "^4.6.4", - "@smithy/types": "^4.5.0", - "@smithy/util-stream": "^4.3.2", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.922.0.tgz", + "integrity": "sha512-i72DgHMK7ydAEqdzU0Duqh60Q8W59EZmRJ73y0Y5oFmNOqnYsAI+UXyOoCsubp+Dkr6+yOwAn1gPt1XGE9Aowg==", + "license": "Apache-2.0", + "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" }, "engines": { @@ -3920,21 +3935,23 @@ } }, "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.896.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.896.0", - "@aws-sdk/credential-provider-env": "3.896.0", - "@aws-sdk/credential-provider-http": "3.896.0", - "@aws-sdk/credential-provider-process": "3.896.0", - "@aws-sdk/credential-provider-sso": "3.896.0", - "@aws-sdk/credential-provider-web-identity": "3.896.0", - "@aws-sdk/nested-clients": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@smithy/credential-provider-imds": "^4.1.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", + "version": "3.925.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.925.0.tgz", + "integrity": "sha512-TOs/UkKWwXrSPolRTChpDUQjczw6KqbbanF0EzjUm3sp/AS1ThOQCKuTTdaOBZXkCIJdvRmZjF3adccE3rAoXg==", + "license": "Apache-2.0", + "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.925.0", + "@aws-sdk/credential-provider-web-identity": "3.925.0", + "@aws-sdk/nested-clients": "3.925.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" }, "engines": { @@ -3942,20 +3959,22 @@ } }, "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-node": { - "version": "3.896.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.896.0", - "@aws-sdk/credential-provider-http": "3.896.0", - "@aws-sdk/credential-provider-ini": "3.896.0", - "@aws-sdk/credential-provider-process": "3.896.0", - "@aws-sdk/credential-provider-sso": "3.896.0", - "@aws-sdk/credential-provider-web-identity": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@smithy/credential-provider-imds": "^4.1.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", + "version": "3.925.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.925.0.tgz", + "integrity": "sha512-+T9mnnTY73MLkVxsk5RtzE4fv7GnMhR7iXhL/yTusf1zLfA09uxlA9VCz6tWxm5rHcO4ZN0x4hnqqDhM+DB5KQ==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/credential-provider-env": "3.922.0", + "@aws-sdk/credential-provider-http": "3.922.0", + "@aws-sdk/credential-provider-ini": "3.925.0", + "@aws-sdk/credential-provider-process": "3.922.0", + "@aws-sdk/credential-provider-sso": "3.925.0", + "@aws-sdk/credential-provider-web-identity": "3.925.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" }, "engines": { @@ -3963,14 +3982,16 @@ } }, "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-process": { - "version": "3.896.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.922.0.tgz", + "integrity": "sha512-1DZOYezT6okslpvMW7oA2q+y17CJd4fxjNFH0jtThfswdh9CtG62+wxenqO+NExttq0UMaKisrkZiVrYQBTShw==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", + "@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" }, "engines": { @@ -3978,16 +3999,18 @@ } }, "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.896.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-sso": "3.896.0", - "@aws-sdk/core": "3.896.0", - "@aws-sdk/token-providers": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", + "version": "3.925.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.925.0.tgz", + "integrity": "sha512-aZlUC6LRsOMDvIu0ifF62mTjL3KGzclWu5XBBN8eLDAYTdhqMxv3HyrqWoiHnGZnZGaVU+II+qsVoeBnGOwHow==", + "license": "Apache-2.0", + "dependencies": { + "@aws-sdk/client-sso": "3.925.0", + "@aws-sdk/core": "3.922.0", + "@aws-sdk/token-providers": "3.925.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" }, "engines": { @@ -3995,15 +4018,17 @@ } }, "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.896.0", + "version": "3.925.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.925.0.tgz", + "integrity": "sha512-dR34s8Sfd1wJBzIuvRFO2FCnLmYD8iwPWrdXWI2ZypFt1EQR8jeQ20mnS+UOCoR5Z0tY6wJqEgTXKl4KuZ+DUg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.896.0", - "@aws-sdk/nested-clients": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", + "@aws-sdk/core": "3.922.0", + "@aws-sdk/nested-clients": "3.925.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" }, "engines": { @@ -4011,12 +4036,14 @@ } }, "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-host-header": { - "version": "3.893.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.922.0.tgz", + "integrity": "sha512-HPquFgBnq/KqKRVkiuCt97PmWbKtxQ5iUNLEc6FIviqOoZTmaYG3EDsIbuFBz9C4RHJU4FKLmHL2bL3FEId6AA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.893.0", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", + "@aws-sdk/types": "3.922.0", + "@smithy/protocol-http": "^5.3.4", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -4024,11 +4051,13 @@ } }, "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-logger": { - "version": "3.893.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.922.0.tgz", + "integrity": "sha512-AkvYO6b80FBm5/kk2E636zNNcNgjztNNUxpqVx+huyGn9ZqGTzS4kLqW2hO6CBe5APzVtPCtiQsXL24nzuOlAg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.893.0", - "@smithy/types": "^4.5.0", + "@aws-sdk/types": "3.922.0", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -4036,13 +4065,15 @@ } }, "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.893.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.922.0.tgz", + "integrity": "sha512-TtSCEDonV/9R0VhVlCpxZbp/9sxQvTTRKzIf8LxW3uXpby6Wl8IxEciBJlxmSkoqxh542WRcko7NYODlvL/gDA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.893.0", - "@aws/lambda-invoke-store": "^0.0.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", + "@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" }, "engines": { @@ -4050,15 +4081,17 @@ } }, "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.896.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.922.0.tgz", + "integrity": "sha512-N4Qx/9KP3oVQBJOrSghhz8iZFtUC2NNeSZt88hpPhbqAEAtuX8aD8OzVcpnAtrwWqy82Yd2YTxlkqMGkgqnBsQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@aws-sdk/util-endpoints": "3.895.0", - "@smithy/core": "^3.12.0", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", + "@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" }, "engines": { @@ -4066,46 +4099,48 @@ } }, "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/nested-clients": { - "version": "3.896.0", + "version": "3.925.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.925.0.tgz", + "integrity": "sha512-Fc8QhH+1YzGQb5aWQUX6gRnKSzUZ9p3p/muqXIgYBL8RSd5O6hSPhDTyrOWE247zFlOjVlAlEnoTMJKarH0cIA==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.896.0", - "@aws-sdk/middleware-host-header": "3.893.0", - "@aws-sdk/middleware-logger": "3.893.0", - "@aws-sdk/middleware-recursion-detection": "3.893.0", - "@aws-sdk/middleware-user-agent": "3.896.0", - "@aws-sdk/region-config-resolver": "3.893.0", - "@aws-sdk/types": "3.893.0", - "@aws-sdk/util-endpoints": "3.895.0", - "@aws-sdk/util-user-agent-browser": "3.893.0", - "@aws-sdk/util-user-agent-node": "3.896.0", - "@smithy/config-resolver": "^4.2.2", - "@smithy/core": "^3.12.0", - "@smithy/fetch-http-handler": "^5.2.1", - "@smithy/hash-node": "^4.1.1", - "@smithy/invalid-dependency": "^4.1.1", - "@smithy/middleware-content-length": "^4.1.1", - "@smithy/middleware-endpoint": "^4.2.4", - "@smithy/middleware-retry": "^4.3.0", - "@smithy/middleware-serde": "^4.1.1", - "@smithy/middleware-stack": "^4.1.1", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/node-http-handler": "^4.2.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/smithy-client": "^4.6.4", - "@smithy/types": "^4.5.0", - "@smithy/url-parser": "^4.1.1", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-body-length-browser": "^4.1.0", - "@smithy/util-body-length-node": "^4.1.0", - "@smithy/util-defaults-mode-browser": "^4.1.4", - "@smithy/util-defaults-mode-node": "^4.1.4", - "@smithy/util-endpoints": "^3.1.2", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-retry": "^4.1.2", - "@smithy/util-utf8": "^4.1.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.925.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.2", + "@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.8", + "@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" }, "engines": { @@ -4113,14 +4148,15 @@ } }, "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/region-config-resolver": { - "version": "3.893.0", + "version": "3.925.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.925.0.tgz", + "integrity": "sha512-FOthcdF9oDb1pfQBRCfWPZhJZT5wqpvdAS5aJzB1WDZ+6EuaAhLzLH/fW1slDunIqq1PSQGG3uSnVglVVOvPHQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.893.0", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/types": "^4.5.0", - "@smithy/util-config-provider": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", + "@aws-sdk/types": "3.922.0", + "@smithy/config-resolver": "^4.4.2", + "@smithy/node-config-provider": "^4.3.4", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -4128,15 +4164,17 @@ } }, "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/token-providers": { - "version": "3.896.0", + "version": "3.925.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.925.0.tgz", + "integrity": "sha512-F4Oibka1W5YYDeL+rGt/Hg3NLjOzrJdmuZOE0OFQt/U6dnJwYmYi2gFqduvZnZcD1agNm37mh7/GUq1zvKS6ig==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.896.0", - "@aws-sdk/nested-clients": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@smithy/property-provider": "^4.1.1", - "@smithy/shared-ini-file-loader": "^4.2.0", - "@smithy/types": "^4.5.0", + "@aws-sdk/core": "3.922.0", + "@aws-sdk/nested-clients": "3.925.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" }, "engines": { @@ -4144,10 +4182,12 @@ } }, "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/types": { - "version": "3.893.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.922.0.tgz", + "integrity": "sha512-eLA6XjVobAUAMivvM7DBL79mnHyrm+32TkXNWZua5mnxF+6kQCfblKKJvxMZLGosO53/Ex46ogim8IY5Nbqv2w==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -4155,13 +4195,15 @@ } }, "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-endpoints": { - "version": "3.895.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.922.0.tgz", + "integrity": "sha512-4ZdQCSuNMY8HMlR1YN4MRDdXuKd+uQTeKIr5/pIM+g3TjInZoj8imvXudjcrFGA63UF3t92YVTkBq88mg58RXQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.893.0", - "@smithy/types": "^4.5.0", - "@smithy/url-parser": "^4.1.1", - "@smithy/util-endpoints": "^3.1.2", + "@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" }, "engines": { @@ -4169,23 +4211,27 @@ } }, "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.893.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.922.0.tgz", + "integrity": "sha512-qOJAERZ3Plj1st7M4Q5henl5FRpE30uLm6L9edZqZXGR6c7ry9jzexWamWVpQ4H4xVAVmiO9dIEBAfbq4mduOA==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.893.0", - "@smithy/types": "^4.5.0", + "@aws-sdk/types": "3.922.0", + "@smithy/types": "^4.8.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.896.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.922.0.tgz", + "integrity": "sha512-NrPe/Rsr5kcGunkog0eBV+bY0inkRELsD2SacC4lQZvZiXf8VJ2Y7j+Yq1tB+h+FPLsdt3v9wItIvDf/laAm0Q==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-user-agent": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/types": "^4.5.0", + "@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" }, "engines": { @@ -4201,10 +4247,12 @@ } }, "node_modules/@aws-sdk/client-s3/node_modules/@aws-sdk/xml-builder": { - "version": "3.894.0", + "version": "3.921.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.921.0.tgz", + "integrity": "sha512-LVHg0jgjyicKKvpNIEMXIMr1EBViESxcPkqfOlT+X1FkmUMTNZEEVF18tOJg4m4hV5vxtkWcqtr4IEeWa1C41Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.8.1", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" }, @@ -4212,8 +4260,19 @@ "node": ">=18.0.0" } }, + "node_modules/@aws-sdk/client-s3/node_modules/@aws/lambda-invoke-store": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.1.1.tgz", + "integrity": "sha512-RcLam17LdlbSOSp9VxmUu1eI6Mwxp+OwhD2QhiSNmNCzoDb0EeUXTD2n/WbcnrAYMGlmf05th6QYq23VqvJqpA==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@aws-sdk/client-s3/node_modules/fast-xml-parser": { "version": "5.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", "funding": [ { "type": "github", @@ -4230,6 +4289,8 @@ }, "node_modules/@aws-sdk/client-s3/node_modules/strnum": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", + "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", "funding": [ { "type": "github", @@ -4488,15 +4549,17 @@ } }, "node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.893.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.922.0.tgz", + "integrity": "sha512-Dpr2YeOaLFqt3q1hocwBesynE3x8/dXZqXZRuzSX/9/VQcwYBFChHAm4mTAl4zuvArtDbLrwzWSxmOWYZGtq5w==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.893.0", + "@aws-sdk/types": "3.922.0", "@aws-sdk/util-arn-parser": "3.893.0", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "@smithy/util-config-provider": "^4.1.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" }, "engines": { @@ -4504,10 +4567,12 @@ } }, "node_modules/@aws-sdk/middleware-bucket-endpoint/node_modules/@aws-sdk/types": { - "version": "3.893.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.922.0.tgz", + "integrity": "sha512-eLA6XjVobAUAMivvM7DBL79mnHyrm+32TkXNWZua5mnxF+6kQCfblKKJvxMZLGosO53/Ex46ogim8IY5Nbqv2w==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -4530,12 +4595,14 @@ } }, "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.893.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.922.0.tgz", + "integrity": "sha512-xmnLWMtmHJHJBupSWMUEW1gyxuRIeQ1Ov2xa8Tqq77fPr4Ft2AluEwiDMaZIMHoAvpxWKEEt9Si59Li7GIA+bQ==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.893.0", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", + "@aws-sdk/types": "3.922.0", + "@smithy/protocol-http": "^5.3.4", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -4543,10 +4610,12 @@ } }, "node_modules/@aws-sdk/middleware-expect-continue/node_modules/@aws-sdk/types": { - "version": "3.893.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.922.0.tgz", + "integrity": "sha512-eLA6XjVobAUAMivvM7DBL79mnHyrm+32TkXNWZua5mnxF+6kQCfblKKJvxMZLGosO53/Ex46ogim8IY5Nbqv2w==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -4554,21 +4623,23 @@ } }, "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.896.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.922.0.tgz", + "integrity": "sha512-G363np7YcJhf+gBucskdv8cOTbs2TRwocEzRupuqDIooGDlLBlfJrvwehdgtWR8l53yjJR3zcHvGrVPTe2h8Nw==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", - "@aws-sdk/core": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@smithy/is-array-buffer": "^4.1.0", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/protocol-http": "^5.2.1", - "@smithy/types": "^4.5.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-stream": "^4.3.2", - "@smithy/util-utf8": "^4.1.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" }, "engines": { @@ -4576,21 +4647,23 @@ } }, "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@aws-sdk/core": { - "version": "3.896.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.893.0", - "@aws-sdk/xml-builder": "3.894.0", - "@smithy/core": "^3.12.0", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/signature-v4": "^5.2.1", - "@smithy/smithy-client": "^4.6.4", - "@smithy/types": "^4.5.0", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-utf8": "^4.1.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.922.0.tgz", + "integrity": "sha512-EvfP4cqJfpO3L2v5vkIlTkMesPtRwWlMfsaW6Tpfm7iYfBOuTi6jx60pMDMTyJNVfh6cGmXwh/kj1jQdR+w99Q==", + "license": "Apache-2.0", + "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" }, "engines": { @@ -4598,10 +4671,12 @@ } }, "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@aws-sdk/types": { - "version": "3.893.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.922.0.tgz", + "integrity": "sha512-eLA6XjVobAUAMivvM7DBL79mnHyrm+32TkXNWZua5mnxF+6kQCfblKKJvxMZLGosO53/Ex46ogim8IY5Nbqv2w==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -4609,10 +4684,12 @@ } }, "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/@aws-sdk/xml-builder": { - "version": "3.894.0", + "version": "3.921.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.921.0.tgz", + "integrity": "sha512-LVHg0jgjyicKKvpNIEMXIMr1EBViESxcPkqfOlT+X1FkmUMTNZEEVF18tOJg4m4hV5vxtkWcqtr4IEeWa1C41Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.8.1", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" }, @@ -4622,6 +4699,8 @@ }, "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/fast-xml-parser": { "version": "5.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", "funding": [ { "type": "github", @@ -4638,6 +4717,8 @@ }, "node_modules/@aws-sdk/middleware-flexible-checksums/node_modules/strnum": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", + "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", "funding": [ { "type": "github", @@ -4660,11 +4741,13 @@ } }, "node_modules/@aws-sdk/middleware-location-constraint": { - "version": "3.893.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.922.0.tgz", + "integrity": "sha512-T4iqd7WQ2DDjCH/0s50mnhdoX+IJns83ZE+3zj9IDlpU0N2aq8R91IG890qTfYkUEdP9yRm0xir/CNed+v6Dew==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.893.0", - "@smithy/types": "^4.5.0", + "@aws-sdk/types": "3.922.0", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -4672,10 +4755,12 @@ } }, "node_modules/@aws-sdk/middleware-location-constraint/node_modules/@aws-sdk/types": { - "version": "3.893.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.922.0.tgz", + "integrity": "sha512-eLA6XjVobAUAMivvM7DBL79mnHyrm+32TkXNWZua5mnxF+6kQCfblKKJvxMZLGosO53/Ex46ogim8IY5Nbqv2w==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -4732,22 +4817,24 @@ } }, "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.896.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.922.0.tgz", + "integrity": "sha512-ygg8lME1oFAbsH42ed2wtGqfHLoT5irgx6VC4X98j79fV1qXEwwwbqMsAiMQ/HJehpjqAFRVsHox3MHLN48Z5A==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/core": "3.896.0", - "@aws-sdk/types": "3.893.0", + "@aws-sdk/core": "3.922.0", + "@aws-sdk/types": "3.922.0", "@aws-sdk/util-arn-parser": "3.893.0", - "@smithy/core": "^3.12.0", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/protocol-http": "^5.2.1", - "@smithy/signature-v4": "^5.2.1", - "@smithy/smithy-client": "^4.6.4", - "@smithy/types": "^4.5.0", - "@smithy/util-config-provider": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-stream": "^4.3.2", - "@smithy/util-utf8": "^4.1.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" }, "engines": { @@ -4755,21 +4842,23 @@ } }, "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@aws-sdk/core": { - "version": "3.896.0", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.893.0", - "@aws-sdk/xml-builder": "3.894.0", - "@smithy/core": "^3.12.0", - "@smithy/node-config-provider": "^4.2.2", - "@smithy/property-provider": "^4.1.1", - "@smithy/protocol-http": "^5.2.1", - "@smithy/signature-v4": "^5.2.1", - "@smithy/smithy-client": "^4.6.4", - "@smithy/types": "^4.5.0", - "@smithy/util-base64": "^4.1.0", - "@smithy/util-middleware": "^4.1.1", - "@smithy/util-utf8": "^4.1.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.922.0.tgz", + "integrity": "sha512-EvfP4cqJfpO3L2v5vkIlTkMesPtRwWlMfsaW6Tpfm7iYfBOuTi6jx60pMDMTyJNVfh6cGmXwh/kj1jQdR+w99Q==", + "license": "Apache-2.0", + "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" }, "engines": { @@ -4777,10 +4866,12 @@ } }, "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@aws-sdk/types": { - "version": "3.893.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.922.0.tgz", + "integrity": "sha512-eLA6XjVobAUAMivvM7DBL79mnHyrm+32TkXNWZua5mnxF+6kQCfblKKJvxMZLGosO53/Ex46ogim8IY5Nbqv2w==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -4788,10 +4879,12 @@ } }, "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/@aws-sdk/xml-builder": { - "version": "3.894.0", + "version": "3.921.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.921.0.tgz", + "integrity": "sha512-LVHg0jgjyicKKvpNIEMXIMr1EBViESxcPkqfOlT+X1FkmUMTNZEEVF18tOJg4m4hV5vxtkWcqtr4IEeWa1C41Q==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.8.1", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" }, @@ -4801,6 +4894,8 @@ }, "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/fast-xml-parser": { "version": "5.2.5", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", + "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", "funding": [ { "type": "github", @@ -4817,6 +4912,8 @@ }, "node_modules/@aws-sdk/middleware-sdk-s3/node_modules/strnum": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", + "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", "funding": [ { "type": "github", @@ -4826,11 +4923,13 @@ "license": "MIT" }, "node_modules/@aws-sdk/middleware-ssec": { - "version": "3.893.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.922.0.tgz", + "integrity": "sha512-eHvSJZTSRJO+/tjjGD6ocnPc8q9o3m26+qbwQTu/4V6yOJQ1q+xkDZNqwJQphL+CodYaQ7uljp8g1Ji/AN3D9w==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/types": "3.893.0", - "@smithy/types": "^4.5.0", + "@aws-sdk/types": "3.922.0", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -4838,10 +4937,12 @@ } }, "node_modules/@aws-sdk/middleware-ssec/node_modules/@aws-sdk/types": { - "version": "3.893.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.922.0.tgz", + "integrity": "sha512-eLA6XjVobAUAMivvM7DBL79mnHyrm+32TkXNWZua5mnxF+6kQCfblKKJvxMZLGosO53/Ex46ogim8IY5Nbqv2w==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -5061,14 +5162,16 @@ "license": "MIT" }, "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.896.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.922.0.tgz", + "integrity": "sha512-mmsgEEL5pE+A7gFYiJMDBCLVciaXq4EFI5iAP7bPpnHvOplnNOYxVy2IreKMllGvrfjVyLnwxzZYlo5zZ65FWg==", "license": "Apache-2.0", "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.896.0", - "@aws-sdk/types": "3.893.0", - "@smithy/protocol-http": "^5.2.1", - "@smithy/signature-v4": "^5.2.1", - "@smithy/types": "^4.5.0", + "@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" }, "engines": { @@ -5076,10 +5179,12 @@ } }, "node_modules/@aws-sdk/signature-v4-multi-region/node_modules/@aws-sdk/types": { - "version": "3.893.0", + "version": "3.922.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.922.0.tgz", + "integrity": "sha512-eLA6XjVobAUAMivvM7DBL79mnHyrm+32TkXNWZua5mnxF+6kQCfblKKJvxMZLGosO53/Ex46ogim8IY5Nbqv2w==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -9097,10 +9202,12 @@ } }, "node_modules/@smithy/abort-controller": { - "version": "4.2.0", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.4.tgz", + "integrity": "sha512-Z4DUr/AkgyFf1bOThW2HwzREagee0sB5ycl+hDiSZOfRLW8ZgrOjDi6g8mHH19yyU5E2A/64W3z6SMIf5XiUSQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -9108,7 +9215,9 @@ } }, "node_modules/@smithy/chunked-blob-reader": { - "version": "5.1.0", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.2.0.tgz", + "integrity": "sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -9118,10 +9227,12 @@ } }, "node_modules/@smithy/chunked-blob-reader-native": { - "version": "4.1.0", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.2.1.tgz", + "integrity": "sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/util-base64": "^4.1.0", + "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" }, "engines": { @@ -9129,13 +9240,16 @@ } }, "node_modules/@smithy/config-resolver": { - "version": "4.3.0", + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.2.tgz", + "integrity": "sha512-4Jys0ni2tB2VZzgslbEgszZyMdTkPOFGA8g+So/NjR8oy6Qwaq4eSwsrRI+NMtb0Dq4kqCzGUu/nGUx7OM/xfw==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.0", - "@smithy/types": "^4.6.0", + "@smithy/node-config-provider": "^4.3.4", + "@smithy/types": "^4.8.1", "@smithy/util-config-provider": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", + "@smithy/util-endpoints": "^3.2.4", + "@smithy/util-middleware": "^4.2.4", "tslib": "^2.6.2" }, "engines": { @@ -9143,16 +9257,18 @@ } }, "node_modules/@smithy/core": { - "version": "3.15.0", + "version": "3.17.2", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.17.2.tgz", + "integrity": "sha512-n3g4Nl1Te+qGPDbNFAYf+smkRVB+JhFsGy9uJXXZQEufoP4u0r+WLh6KvTDolCswaagysDc/afS1yvb2jnj1gQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/middleware-serde": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", + "@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.0", - "@smithy/util-stream": "^4.5.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" @@ -9162,13 +9278,15 @@ } }, "node_modules/@smithy/credential-provider-imds": { - "version": "4.2.0", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.4.tgz", + "integrity": "sha512-YVNMjhdz2pVto5bRdux7GMs0x1m0Afz3OcQy/4Yf9DH4fWOtroGH7uLvs7ZmDyoBJzLdegtIPpXrpJOZWvUXdw==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.0", - "@smithy/property-provider": "^4.2.0", - "@smithy/types": "^4.6.0", - "@smithy/url-parser": "^4.2.0", + "@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" }, "engines": { @@ -9176,12 +9294,14 @@ } }, "node_modules/@smithy/eventstream-codec": { - "version": "4.1.1", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.4.tgz", + "integrity": "sha512-aV8blR9RBDKrOlZVgjOdmOibTC2sBXNiT7WA558b4MPdsLTV6sbyc1WIE9QiIuYMJjYtnPLciefoqSW8Gi+MZQ==", "license": "Apache-2.0", "dependencies": { "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^4.5.0", - "@smithy/util-hex-encoding": "^4.1.0", + "@smithy/types": "^4.8.1", + "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -9189,11 +9309,13 @@ } }, "node_modules/@smithy/eventstream-serde-browser": { - "version": "4.1.1", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.4.tgz", + "integrity": "sha512-d5T7ZS3J/r8P/PDjgmCcutmNxnSRvPH1U6iHeXjzI50sMr78GLmFcrczLw33Ap92oEKqa4CLrkAPeSSOqvGdUA==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^4.1.1", - "@smithy/types": "^4.5.0", + "@smithy/eventstream-serde-universal": "^4.2.4", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -9201,10 +9323,12 @@ } }, "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "4.2.1", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.4.tgz", + "integrity": "sha512-lxfDT0UuSc1HqltOGsTEAlZ6H29gpfDSdEPTapD5G63RbnYToZ+ezjzdonCCH90j5tRRCw3aLXVbiZaBW3VRVg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -9212,11 +9336,13 @@ } }, "node_modules/@smithy/eventstream-serde-node": { - "version": "4.1.1", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.4.tgz", + "integrity": "sha512-TPhiGByWnYyzcpU/K3pO5V7QgtXYpE0NaJPEZBCa1Y5jlw5SjqzMSbFiLb+ZkJhqoQc0ImGyVINqnq1ze0ZRcQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-serde-universal": "^4.1.1", - "@smithy/types": "^4.5.0", + "@smithy/eventstream-serde-universal": "^4.2.4", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -9224,11 +9350,13 @@ } }, "node_modules/@smithy/eventstream-serde-universal": { - "version": "4.1.1", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.4.tgz", + "integrity": "sha512-GNI/IXaY/XBB1SkGBFmbW033uWA0tj085eCxYih0eccUe/PFR7+UBQv9HNDk2fD9TJu7UVsCWsH99TkpEPSOzQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/eventstream-codec": "^4.1.1", - "@smithy/types": "^4.5.0", + "@smithy/eventstream-codec": "^4.2.4", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -9236,12 +9364,14 @@ } }, "node_modules/@smithy/fetch-http-handler": { - "version": "5.3.1", + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.5.tgz", + "integrity": "sha512-mg83SM3FLI8Sa2ooTJbsh5MFfyMTyNRwxqpKHmE0ICRIa66Aodv80DMsTQI02xBLVJ0hckwqTRr5IGAbbWuFLQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.0", - "@smithy/querystring-builder": "^4.2.0", - "@smithy/types": "^4.6.0", + "@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" }, @@ -9250,12 +9380,14 @@ } }, "node_modules/@smithy/hash-blob-browser": { - "version": "4.1.1", + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.2.5.tgz", + "integrity": "sha512-kCdgjD2J50qAqycYx0imbkA9tPtyQr1i5GwbK/EOUkpBmJGSkJe4mRJm+0F65TUSvvui1HZ5FFGFCND7l8/3WQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/chunked-blob-reader": "^5.1.0", - "@smithy/chunked-blob-reader-native": "^4.1.0", - "@smithy/types": "^4.5.0", + "@smithy/chunked-blob-reader": "^5.2.0", + "@smithy/chunked-blob-reader-native": "^4.2.1", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -9263,10 +9395,12 @@ } }, "node_modules/@smithy/hash-node": { - "version": "4.2.0", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.4.tgz", + "integrity": "sha512-kKU0gVhx/ppVMntvUOZE7WRMFW86HuaxLwvqileBEjL7PoILI8/djoILw3gPQloGVE6O0oOzqafxeNi2KbnUJw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.8.1", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" @@ -9276,11 +9410,13 @@ } }, "node_modules/@smithy/hash-stream-node": { - "version": "4.1.1", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.2.4.tgz", + "integrity": "sha512-amuh2IJiyRfO5MV0X/YFlZMD6banjvjAwKdeJiYGUbId608x+oSNwv3vlyW2Gt6AGAgl3EYAuyYLGRX/xU8npQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", - "@smithy/util-utf8": "^4.1.0", + "@smithy/types": "^4.8.1", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -9288,10 +9424,12 @@ } }, "node_modules/@smithy/invalid-dependency": { - "version": "4.2.0", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.4.tgz", + "integrity": "sha512-z6aDLGiHzsMhbS2MjetlIWopWz//K+mCoPXjW6aLr0mypF+Y7qdEh5TyJ20Onf9FbWHiWl4eC+rITdizpnXqOw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -9309,11 +9447,13 @@ } }, "node_modules/@smithy/md5-js": { - "version": "4.1.1", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.2.4.tgz", + "integrity": "sha512-h7kzNWZuMe5bPnZwKxhVbY1gan5+TZ2c9JcVTHCygB14buVGOZxLl+oGfpY2p2Xm48SFqEWdghpvbBdmaz3ncQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.5.0", - "@smithy/util-utf8": "^4.1.0", + "@smithy/types": "^4.8.1", + "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" }, "engines": { @@ -9321,11 +9461,13 @@ } }, "node_modules/@smithy/middleware-content-length": { - "version": "4.2.0", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.4.tgz", + "integrity": "sha512-hJRZuFS9UsElX4DJSJfoX4M1qXRH+VFiLMUnhsWvtOOUWRNvvOfDaUSdlNbjwv1IkpVjj/Rd/O59Jl3nhAcxow==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", + "@smithy/protocol-http": "^5.3.4", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -9333,16 +9475,18 @@ } }, "node_modules/@smithy/middleware-endpoint": { - "version": "4.3.1", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.15.0", - "@smithy/middleware-serde": "^4.2.0", - "@smithy/node-config-provider": "^4.3.0", - "@smithy/shared-ini-file-loader": "^4.3.0", - "@smithy/types": "^4.6.0", - "@smithy/url-parser": "^4.2.0", - "@smithy/util-middleware": "^4.2.0", + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.3.6.tgz", + "integrity": "sha512-PXehXofGMFpDqr933rxD8RGOcZ0QBAWtuzTgYRAHAL2BnKawHDEdf/TnGpcmfPJGwonhginaaeJIKluEojiF/w==", + "license": "Apache-2.0", + "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" }, "engines": { @@ -9350,16 +9494,18 @@ } }, "node_modules/@smithy/middleware-retry": { - "version": "4.4.1", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.3.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/service-error-classification": "^4.2.0", - "@smithy/smithy-client": "^4.7.1", - "@smithy/types": "^4.6.0", - "@smithy/util-middleware": "^4.2.0", - "@smithy/util-retry": "^4.2.0", + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.6.tgz", + "integrity": "sha512-OhLx131znrEDxZPAvH/OYufR9d1nB2CQADyYFN4C3V/NQS7Mg4V6uvxHC/Dr96ZQW8IlHJTJ+vAhKt6oxWRndA==", + "license": "Apache-2.0", + "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" }, @@ -9368,11 +9514,13 @@ } }, "node_modules/@smithy/middleware-serde": { - "version": "4.2.0", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.4.tgz", + "integrity": "sha512-jUr3x2CDhV15TOX2/Uoz4gfgeqLrRoTQbYAuhLS7lcVKNev7FeYSJ1ebEfjk+l9kbb7k7LfzIR/irgxys5ZTOg==", "license": "Apache-2.0", "dependencies": { - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", + "@smithy/protocol-http": "^5.3.4", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -9380,10 +9528,12 @@ } }, "node_modules/@smithy/middleware-stack": { - "version": "4.2.0", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.4.tgz", + "integrity": "sha512-Gy3TKCOnm9JwpFooldwAboazw+EFYlC+Bb+1QBsSi5xI0W5lX81j/P5+CXvD/9ZjtYKRgxq+kkqd/KOHflzvgA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -9391,12 +9541,14 @@ } }, "node_modules/@smithy/node-config-provider": { - "version": "4.3.0", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.4.tgz", + "integrity": "sha512-3X3w7qzmo4XNNdPKNS4nbJcGSwiEMsNsRSunMA92S4DJLLIrH5g1AyuOA2XKM9PAPi8mIWfqC+fnfKNsI4KvHw==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.2.0", - "@smithy/shared-ini-file-loader": "^4.3.0", - "@smithy/types": "^4.6.0", + "@smithy/property-provider": "^4.2.4", + "@smithy/shared-ini-file-loader": "^4.3.4", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -9404,13 +9556,15 @@ } }, "node_modules/@smithy/node-http-handler": { - "version": "4.3.0", + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.4.tgz", + "integrity": "sha512-VXHGfzCXLZeKnFp6QXjAdy+U8JF9etfpUXD1FAbzY1GzsFJiDQRQIt2CnMUvUdz3/YaHNqT3RphVWMUpXTIODA==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/querystring-builder": "^4.2.0", - "@smithy/types": "^4.6.0", + "@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" }, "engines": { @@ -9418,10 +9572,12 @@ } }, "node_modules/@smithy/property-provider": { - "version": "4.2.0", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.4.tgz", + "integrity": "sha512-g2DHo08IhxV5GdY3Cpt/jr0mkTlAD39EJKN27Jb5N8Fb5qt8KG39wVKTXiTRCmHHou7lbXR8nKVU14/aRUf86w==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -9429,10 +9585,12 @@ } }, "node_modules/@smithy/protocol-http": { - "version": "5.3.0", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.4.tgz", + "integrity": "sha512-3sfFd2MAzVt0Q/klOmjFi3oIkxczHs0avbwrfn1aBqtc23WqQSmjvk77MBw9WkEQcwbOYIX5/2z4ULj8DuxSsw==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -9440,10 +9598,12 @@ } }, "node_modules/@smithy/querystring-builder": { - "version": "4.2.0", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.4.tgz", + "integrity": "sha512-KQ1gFXXC+WsbPFnk7pzskzOpn4s+KheWgO3dzkIEmnb6NskAIGp/dGdbKisTPJdtov28qNDohQrgDUKzXZBLig==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.8.1", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" }, @@ -9452,10 +9612,12 @@ } }, "node_modules/@smithy/querystring-parser": { - "version": "4.2.0", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.4.tgz", + "integrity": "sha512-aHb5cqXZocdzEkZ/CvhVjdw5l4r1aU/9iMEyoKzH4eXMowT6M0YjBpp7W/+XjkBnY8Xh0kVd55GKjnPKlCwinQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -9463,20 +9625,24 @@ } }, "node_modules/@smithy/service-error-classification": { - "version": "4.2.0", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.4.tgz", + "integrity": "sha512-fdWuhEx4+jHLGeew9/IvqVU/fxT/ot70tpRGuOLxE3HzZOyKeTQfYeV1oaBXpzi93WOk668hjMuuagJ2/Qs7ng==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0" + "@smithy/types": "^4.8.1" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.3.0", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.3.4.tgz", + "integrity": "sha512-y5ozxeQ9omVjbnJo9dtTsdXj9BEvGx2X8xvRgKnV+/7wLBuYJQL6dOa/qMY6omyHi7yjt1OA97jZLoVRYi8lxA==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -9484,14 +9650,16 @@ } }, "node_modules/@smithy/signature-v4": { - "version": "5.3.0", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.4.tgz", + "integrity": "sha512-ScDCpasxH7w1HXHYbtk3jcivjvdA1VICyAdgvVqKhKKwxi+MTwZEqFw0minE+oZ7F07oF25xh4FGJxgqgShz0A==", "license": "Apache-2.0", "dependencies": { "@smithy/is-array-buffer": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", + "@smithy/protocol-http": "^5.3.4", + "@smithy/types": "^4.8.1", "@smithy/util-hex-encoding": "^4.2.0", - "@smithy/util-middleware": "^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" @@ -9501,15 +9669,17 @@ } }, "node_modules/@smithy/smithy-client": { - "version": "4.7.1", + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.9.2.tgz", + "integrity": "sha512-gZU4uAFcdrSi3io8U99Qs/FvVdRxPvIMToi+MFfsy/DN9UqtknJ1ais+2M9yR8e0ASQpNmFYEKeIKVcMjQg3rg==", "license": "Apache-2.0", "dependencies": { - "@smithy/core": "^3.15.0", - "@smithy/middleware-endpoint": "^4.3.1", - "@smithy/middleware-stack": "^4.2.0", - "@smithy/protocol-http": "^5.3.0", - "@smithy/types": "^4.6.0", - "@smithy/util-stream": "^4.5.0", + "@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" }, "engines": { @@ -9517,7 +9687,9 @@ } }, "node_modules/@smithy/types": { - "version": "4.6.0", + "version": "4.8.1", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.8.1.tgz", + "integrity": "sha512-N0Zn0OT1zc+NA+UVfkYqQzviRh5ucWwO7mBV3TmHHprMnfcJNfhlPicDkBHi0ewbh+y3evR6cNAW0Raxvb01NA==", "license": "Apache-2.0", "dependencies": { "tslib": "^2.6.2" @@ -9527,11 +9699,13 @@ } }, "node_modules/@smithy/url-parser": { - "version": "4.2.0", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.4.tgz", + "integrity": "sha512-w/N/Iw0/PTwJ36PDqU9PzAwVElo4qXxCC0eCTlUtIz/Z5V/2j/cViMHi0hPukSBHp4DVwvUlUhLgCzqSJ6plrg==", "license": "Apache-2.0", "dependencies": { - "@smithy/querystring-parser": "^4.2.0", - "@smithy/types": "^4.6.0", + "@smithy/querystring-parser": "^4.2.4", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -9592,12 +9766,14 @@ } }, "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.3.0", + "version": "4.3.5", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.5.tgz", + "integrity": "sha512-GwaGjv/QLuL/QHQaqhf/maM7+MnRFQQs7Bsl6FlaeK6lm6U7mV5AAnVabw68cIoMl5FQFyKK62u7RWRzWL25OQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/property-provider": "^4.2.0", - "@smithy/smithy-client": "^4.7.1", - "@smithy/types": "^4.6.0", + "@smithy/property-provider": "^4.2.4", + "@smithy/smithy-client": "^4.9.2", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -9605,15 +9781,17 @@ } }, "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.2.1", + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.8.tgz", + "integrity": "sha512-gIoTf9V/nFSIZ0TtgDNLd+Ws59AJvijmMDYrOozoMHPJaG9cMRdqNO50jZTlbM6ydzQYY8L/mQ4tKSw/TB+s6g==", "license": "Apache-2.0", "dependencies": { - "@smithy/config-resolver": "^4.3.0", - "@smithy/credential-provider-imds": "^4.2.0", - "@smithy/node-config-provider": "^4.3.0", - "@smithy/property-provider": "^4.2.0", - "@smithy/smithy-client": "^4.7.1", - "@smithy/types": "^4.6.0", + "@smithy/config-resolver": "^4.4.2", + "@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" }, "engines": { @@ -9621,11 +9799,13 @@ } }, "node_modules/@smithy/util-endpoints": { - "version": "3.2.0", + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.2.4.tgz", + "integrity": "sha512-f+nBDhgYRCmUEDKEQb6q0aCcOTXRDqH5wWaFHJxt4anB4pKHlgGoYP3xtioKXH64e37ANUkzWf6p4Mnv1M5/Vg==", "license": "Apache-2.0", "dependencies": { - "@smithy/node-config-provider": "^4.3.0", - "@smithy/types": "^4.6.0", + "@smithy/node-config-provider": "^4.3.4", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -9643,10 +9823,12 @@ } }, "node_modules/@smithy/util-middleware": { - "version": "4.2.0", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.4.tgz", + "integrity": "sha512-fKGQAPAn8sgV0plRikRVo6g6aR0KyKvgzNrPuM74RZKy/wWVzx3BMk+ZWEueyN3L5v5EDg+P582mKU+sH5OAsg==", "license": "Apache-2.0", "dependencies": { - "@smithy/types": "^4.6.0", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -9654,11 +9836,13 @@ } }, "node_modules/@smithy/util-retry": { - "version": "4.2.0", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.4.tgz", + "integrity": "sha512-yQncJmj4dtv/isTXxRb4AamZHy4QFr4ew8GxS6XLWt7sCIxkPxPzINWd7WLISEFPsIan14zrKgvyAF+/yzfwoA==", "license": "Apache-2.0", "dependencies": { - "@smithy/service-error-classification": "^4.2.0", - "@smithy/types": "^4.6.0", + "@smithy/service-error-classification": "^4.2.4", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -9666,12 +9850,14 @@ } }, "node_modules/@smithy/util-stream": { - "version": "4.5.0", + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.5.tgz", + "integrity": "sha512-7M5aVFjT+HPilPOKbOmQfCIPchZe4DSBc1wf1+NvHvSoFTiFtauZzT+onZvCj70xhXd0AEmYnZYmdJIuwxOo4w==", "license": "Apache-2.0", "dependencies": { - "@smithy/fetch-http-handler": "^5.3.1", - "@smithy/node-http-handler": "^4.3.0", - "@smithy/types": "^4.6.0", + "@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", @@ -9704,11 +9890,13 @@ } }, "node_modules/@smithy/util-waiter": { - "version": "4.1.1", + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.2.4.tgz", + "integrity": "sha512-roKXtXIC6fopFvVOju8VYHtguc/jAcMlK8IlDOHsrQn0ayMkHynjm/D2DCMRf7MJFXzjHhlzg2edr3QPEakchQ==", "license": "Apache-2.0", "dependencies": { - "@smithy/abort-controller": "^4.1.1", - "@smithy/types": "^4.5.0", + "@smithy/abort-controller": "^4.2.4", + "@smithy/types": "^4.8.1", "tslib": "^2.6.2" }, "engines": { @@ -14502,6 +14690,7 @@ }, "node_modules/escalade": { "version": "3.2.0", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -15908,6 +16097,7 @@ }, "node_modules/get-caller-file": { "version": "2.0.5", + "dev": true, "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -20424,10 +20614,6 @@ "resolved": "docs", "link": true }, - "node_modules/nhs-notify-supplier-api-data-generator": { - "resolved": "scripts/test-data", - "link": true - }, "node_modules/nhs-notify-supplier-api-handler": { "resolved": "lambdas/api-handler", "link": true @@ -21887,6 +22073,7 @@ }, "node_modules/require-directory": { "version": "2.1.1", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -24341,6 +24528,7 @@ }, "node_modules/y18n": { "version": "5.0.8", + "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -24383,6 +24571,7 @@ }, "node_modules/yargs-parser": { "version": "21.1.1", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -24488,6 +24677,7 @@ "scripts/test-data": { "name": "nhs-notify-supplier-api-data-generator", "version": "0.0.1", + "extraneous": true, "dependencies": { "@aws-sdk/client-s3": "^3.858.0", "@internal/datastore": "*", @@ -24503,890 +24693,41 @@ "typescript": "^5.8.3" } }, - "scripts/test-data/node_modules/@jest/core": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "scripts/test-data/node_modules/@jest/environment": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "scripts/utilities/letter-test-data": { + "name": "nhs-notify-supplier-api-letter-test-data-utility", + "version": "0.0.1", + "extraneous": true, "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" + "@aws-sdk/client-s3": "^3.858.0", + "@internal/datastore": "*", + "esbuild": "^0.25.11", + "pino": "^9.7.0", + "yargs": "^17.7.2" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + "devDependencies": { + "@tsconfig/node22": "^22.0.2", + "@types/jest": "^30.0.0", + "jest": "^30.2.0", + "jest-mock-extended": "^4.0.0", + "typescript": "^5.8.3" } }, - "scripts/test-data/node_modules/@jest/expect": { - "version": "29.7.0", - "dev": true, - "license": "MIT", + "scripts/utilities/supplier-data": { + "name": "nhs-notify-supplier-api-suppliers-data-utility", + "version": "0.0.1", + "extraneous": true, "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" + "@internal/datastore": "*", + "esbuild": "^0.25.11", + "pino": "^9.7.0", + "yargs": "^17.7.2" }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "scripts/test-data/node_modules/@jest/fake-timers": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "scripts/test-data/node_modules/@jest/globals": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "scripts/test-data/node_modules/@jest/reporters": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "scripts/test-data/node_modules/@jest/source-map": { - "version": "29.6.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "scripts/test-data/node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "scripts/test-data/node_modules/@jest/transform": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "scripts/test-data/node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "scripts/test-data/node_modules/@types/jest": { - "version": "29.5.14", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "scripts/test-data/node_modules/babel-jest": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "scripts/test-data/node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "scripts/test-data/node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "scripts/test-data/node_modules/babel-plugin-istanbul/node_modules/semver": { - "version": "6.3.1", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "scripts/test-data/node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "scripts/test-data/node_modules/babel-preset-jest": { - "version": "29.6.3", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "scripts/test-data/node_modules/brace-expansion": { - "version": "1.1.12", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "scripts/test-data/node_modules/camelcase": { - "version": "6.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "scripts/test-data/node_modules/ci-info": { - "version": "3.9.0", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "scripts/test-data/node_modules/cjs-module-lexer": { - "version": "1.4.3", - "dev": true, - "license": "MIT" - }, - "scripts/test-data/node_modules/cliui": { - "version": "8.0.1", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "scripts/test-data/node_modules/glob": { - "version": "7.2.3", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "scripts/test-data/node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "scripts/test-data/node_modules/jest": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "scripts/test-data/node_modules/jest-changed-files": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "scripts/test-data/node_modules/jest-circus": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "scripts/test-data/node_modules/jest-cli": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "scripts/test-data/node_modules/jest-config": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "scripts/test-data/node_modules/jest-docblock": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "scripts/test-data/node_modules/jest-each": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "scripts/test-data/node_modules/jest-environment-node": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "scripts/test-data/node_modules/jest-haste-map": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "scripts/test-data/node_modules/jest-leak-detector": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "scripts/test-data/node_modules/jest-mock": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "scripts/test-data/node_modules/jest-regex-util": { - "version": "29.6.3", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "scripts/test-data/node_modules/jest-resolve": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "scripts/test-data/node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "scripts/test-data/node_modules/jest-runner": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "scripts/test-data/node_modules/jest-runtime": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "scripts/test-data/node_modules/jest-snapshot": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "scripts/test-data/node_modules/jest-validate": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "scripts/test-data/node_modules/jest-watcher": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "scripts/test-data/node_modules/jest-worker": { - "version": "29.7.0", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "scripts/test-data/node_modules/minimatch": { - "version": "3.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "scripts/test-data/node_modules/pure-rand": { - "version": "6.1.0", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "scripts/test-data/node_modules/signal-exit": { - "version": "3.0.7", - "dev": true, - "license": "ISC" - }, - "scripts/test-data/node_modules/supports-color": { - "version": "8.1.1", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "scripts/test-data/node_modules/wrap-ansi": { - "version": "7.0.0", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "scripts/test-data/node_modules/write-file-atomic": { - "version": "4.0.2", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "scripts/test-data/node_modules/yargs": { - "version": "17.7.2", - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" + "devDependencies": { + "@tsconfig/node22": "^22.0.2", + "@types/jest": "^30.0.0", + "jest": "^30.2.0", + "jest-mock-extended": "^4.0.0", + "typescript": "^5.8.3" } }, "tests": { diff --git a/package.json b/package.json index 4a7bbd11..6e647ab3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "dependencies": { "@aws-sdk/client-api-gateway": "^3.906.0", + "@aws-sdk/client-s3": "^3.925.0", "@playwright/test": "^1.55.1", "ajv": "^8.17.1", "js-yaml": "^4.1.0", @@ -12,8 +13,8 @@ "@redocly/cli": "^1.34.5", "@tsconfig/node22": "^22.0.2", "@types/jest": "^30.0.0", - "@typescript-eslint/eslint-plugin": "^8.46.2", "@types/js-yaml": "^4.0.9", + "@typescript-eslint/eslint-plugin": "^8.46.2", "@typescript-eslint/parser": "^8.27.0", "esbuild": "^0.25.11", "eslint": "^9.27.0", diff --git a/scripts/test-data/.eslintignore b/scripts/utilities/letter-test-data/.eslintignore similarity index 100% rename from scripts/test-data/.eslintignore rename to scripts/utilities/letter-test-data/.eslintignore diff --git a/scripts/test-data/.gitignore b/scripts/utilities/letter-test-data/.gitignore similarity index 100% rename from scripts/test-data/.gitignore rename to scripts/utilities/letter-test-data/.gitignore diff --git a/scripts/test-data/README.md b/scripts/utilities/letter-test-data/README.md similarity index 100% rename from scripts/test-data/README.md rename to scripts/utilities/letter-test-data/README.md diff --git a/scripts/test-data/jest.config.ts b/scripts/utilities/letter-test-data/jest.config.ts similarity index 100% rename from scripts/test-data/jest.config.ts rename to scripts/utilities/letter-test-data/jest.config.ts diff --git a/scripts/test-data/package.json b/scripts/utilities/letter-test-data/package.json similarity index 90% rename from scripts/test-data/package.json rename to scripts/utilities/letter-test-data/package.json index c181c2cb..a5286292 100644 --- a/scripts/test-data/package.json +++ b/scripts/utilities/letter-test-data/package.json @@ -13,7 +13,7 @@ "jest-mock-extended": "^4.0.0", "typescript": "^5.8.3" }, - "name": "nhs-notify-supplier-api-data-generator", + "name": "nhs-notify-supplier-api-letter-test-data-utility", "private": true, "scripts": { "cli": "tsx ./src/cli/index.ts", diff --git a/scripts/test-data/src/__test__/helpers/create_letter_helpers.test.ts b/scripts/utilities/letter-test-data/src/__test__/helpers/create_letter_helpers.test.ts similarity index 100% rename from scripts/test-data/src/__test__/helpers/create_letter_helpers.test.ts rename to scripts/utilities/letter-test-data/src/__test__/helpers/create_letter_helpers.test.ts diff --git a/scripts/test-data/src/cli/index.ts b/scripts/utilities/letter-test-data/src/cli/index.ts similarity index 99% rename from scripts/test-data/src/cli/index.ts rename to scripts/utilities/letter-test-data/src/cli/index.ts index a584c971..915c392b 100644 --- a/scripts/test-data/src/cli/index.ts +++ b/scripts/utilities/letter-test-data/src/cli/index.ts @@ -1,5 +1,5 @@ import { hideBin } from "yargs/helpers"; -import yargs from "yargs/yargs"; +import yargs from 'yargs'; import { LetterStatusType } from "@internal/datastore/src/types"; import { randomUUID } from "crypto"; import { createLetter, createLetterDto } from "../helpers/create_letter_helpers"; diff --git a/scripts/test-data/src/helpers/create_letter_helpers.ts b/scripts/utilities/letter-test-data/src/helpers/create_letter_helpers.ts similarity index 100% rename from scripts/test-data/src/helpers/create_letter_helpers.ts rename to scripts/utilities/letter-test-data/src/helpers/create_letter_helpers.ts diff --git a/scripts/test-data/src/helpers/s3_helpers.ts b/scripts/utilities/letter-test-data/src/helpers/s3_helpers.ts similarity index 100% rename from scripts/test-data/src/helpers/s3_helpers.ts rename to scripts/utilities/letter-test-data/src/helpers/s3_helpers.ts diff --git a/scripts/test-data/src/infrastructure/letter-repo-factory.ts b/scripts/utilities/letter-test-data/src/infrastructure/letter-repo-factory.ts similarity index 90% rename from scripts/test-data/src/infrastructure/letter-repo-factory.ts rename to scripts/utilities/letter-test-data/src/infrastructure/letter-repo-factory.ts index 269642c0..e0060337 100644 --- a/scripts/test-data/src/infrastructure/letter-repo-factory.ts +++ b/scripts/utilities/letter-test-data/src/infrastructure/letter-repo-factory.ts @@ -1,6 +1,6 @@ import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb'; -import pino from 'pino'; +import { pino } from 'pino'; import { LetterRepository } from '@internal/datastore'; export function createLetterRepository(environment: string, ttlHours:number): LetterRepository { @@ -9,7 +9,7 @@ export function createLetterRepository(environment: string, ttlHours:number): Le const log = pino(); const config = { lettersTableName: `nhs-${environment}-supapi-letters`, - ttlHours: ttlHours, + lettersTtlHours: ttlHours, }; return new LetterRepository(docClient, log, config); diff --git a/scripts/test-data/test_letter.pdf b/scripts/utilities/letter-test-data/test_letter.pdf similarity index 100% rename from scripts/test-data/test_letter.pdf rename to scripts/utilities/letter-test-data/test_letter.pdf diff --git a/scripts/test-data/tsconfig.json b/scripts/utilities/letter-test-data/tsconfig.json similarity index 65% rename from scripts/test-data/tsconfig.json rename to scripts/utilities/letter-test-data/tsconfig.json index 24902365..730d18dd 100644 --- a/scripts/test-data/tsconfig.json +++ b/scripts/utilities/letter-test-data/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": {}, - "extends": "../../tsconfig.base.json", + "extends": "../../../tsconfig.base.json", "include": [ "src/**/*", "jest.config.ts" diff --git a/scripts/utilities/supplier-data/.eslintignore b/scripts/utilities/supplier-data/.eslintignore new file mode 100644 index 00000000..1521c8b7 --- /dev/null +++ b/scripts/utilities/supplier-data/.eslintignore @@ -0,0 +1 @@ +dist diff --git a/scripts/utilities/supplier-data/.gitignore b/scripts/utilities/supplier-data/.gitignore new file mode 100644 index 00000000..80323f7c --- /dev/null +++ b/scripts/utilities/supplier-data/.gitignore @@ -0,0 +1,4 @@ +coverage +node_modules +dist +.reports diff --git a/scripts/utilities/supplier-data/README.md b/scripts/utilities/supplier-data/README.md new file mode 100644 index 00000000..79a18f98 --- /dev/null +++ b/scripts/utilities/supplier-data/README.md @@ -0,0 +1,30 @@ +# Test letter generator + +Simple scripts to manipulate supplier data within the Suppliers table via the internal datastore definitions. + +## Usage + +Log in the desired AWS account and then run the command below. You may need to set the AWS_REGION envar (eu-west-2) + +Note that the AWS account ID is required in order to resolve the bucket name. + +```bash +npm run cli -- put-supplier \ + --id supplier-id \ + --name supplier \ + --apimId supplier-apim-id \ + --status ENABLED + --environment pr147 \ +``` + +```bash +npm run cli -- get-supplier-by-id \ + --id supplier-id \ + --environment main \ +``` + +```bash +npm run cli -- get-supplier-by-apim-id \ + --apimId apim-supplier-id + --environment main \ +``` diff --git a/scripts/utilities/supplier-data/jest.config.ts b/scripts/utilities/supplier-data/jest.config.ts new file mode 100644 index 00000000..f2972c27 --- /dev/null +++ b/scripts/utilities/supplier-data/jest.config.ts @@ -0,0 +1,61 @@ +import type { Config } from 'jest'; + +export const baseJestConfig: Config = { + preset: 'ts-jest', + + // Automatically clear mock calls, instances, contexts and results before every test + clearMocks: true, + + // Indicates whether the coverage information should be collected while executing the test + collectCoverage: true, + + // The directory where Jest should output its coverage files + coverageDirectory: './.reports/unit/coverage', + + // Indicates which provider should be used to instrument code for coverage + coverageProvider: 'babel', + + coverageThreshold: { + global: { + branches: 100, + functions: 100, + lines: 100, + statements: -10, + }, + }, + + coveragePathIgnorePatterns: ['/__tests__/'], + transform: { '^.+\\.ts$': 'ts-jest' }, + testPathIgnorePatterns: ['.build'], + testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'], + + // Use this configuration option to add custom reporters to Jest + reporters: [ + 'default', + [ + 'jest-html-reporter', + { + pageTitle: 'Test Report', + outputPath: './.reports/unit/test-report.html', + includeFailureMsg: true, + }, + ], + ], + + // The test environment that will be used for testing + testEnvironment: 'jsdom', +}; + +const utilsJestConfig = { + ...baseJestConfig, + + testEnvironment: 'node', + + coveragePathIgnorePatterns: [ + ...(baseJestConfig.coveragePathIgnorePatterns ?? []), + 'cli/index.ts', + 'suppliers-repo-factory.ts', + ], +}; + +export default utilsJestConfig; diff --git a/scripts/utilities/supplier-data/package.json b/scripts/utilities/supplier-data/package.json new file mode 100644 index 00000000..a74ca076 --- /dev/null +++ b/scripts/utilities/supplier-data/package.json @@ -0,0 +1,25 @@ +{ + "dependencies": { + "@internal/datastore": "*", + "esbuild": "^0.25.11", + "pino": "^9.7.0", + "yargs": "^17.7.2" + }, + "devDependencies": { + "@tsconfig/node22": "^22.0.2", + "@types/jest": "^30.0.0", + "jest": "^30.2.0", + "jest-mock-extended": "^4.0.0", + "typescript": "^5.8.3" + }, + "name": "nhs-notify-supplier-api-suppliers-data-utility", + "private": true, + "scripts": { + "cli": "tsx ./src/cli/index.ts", + "lint": "eslint .", + "lint:fix": "eslint . --fix", + "test:unit": "echo \"Supplier data utility has no unit tests\"", + "typecheck": "tsc --noEmit" + }, + "version": "0.0.1" +} diff --git a/scripts/utilities/supplier-data/src/cli/index.ts b/scripts/utilities/supplier-data/src/cli/index.ts new file mode 100644 index 00000000..4c37156a --- /dev/null +++ b/scripts/utilities/supplier-data/src/cli/index.ts @@ -0,0 +1,120 @@ +import { hideBin } from "yargs/helpers"; +import yargs from 'yargs'; +import { LetterStatusType } from "@internal/datastore/src/types"; +import { randomUUID } from "crypto"; +import { createSupplierRepository } from "../infrastructure/suppliers-repo-factory"; + + + +async function main() { + await yargs(hideBin(process.argv)) + .command( + "put-supplier", + "Create or update a supplier", + { + environment: { + type: "string", + demandOption: true, + }, + "id": { + type: "string", + demandOption: true, + }, + "name": { + type: "string", + demandOption: false, + }, + "apimId": { + type: "string", + demandOption: false, + }, + status: { + type: "string", + demandOption: true, + choices: [ + "ENABLED", + "DISABLED" + ], + }, + }, + async (argv) => { + // parse args + const id = argv.id; + const name = argv.name; + const apimId = argv.apimId; + const status = argv.status as "ENABLED" | "DISABLED"; + + const environment = argv.environment; + + const supplierRepository = createSupplierRepository(environment); + + const putResult = await supplierRepository.putSupplier({ + id, + name, + apimId, + status, + }); + + console.log(`PUT successful ${JSON.stringify(putResult)}`); + } + ) + .command( + "get-supplier-by-id", + "Get a supplier by their Supplier ID", + { + "id": { + type: "string", + demandOption: true, + }, + environment: { + type: "string", + demandOption: true, + }, + }, + async (argv) => { + + const id = argv.id; + const environment = argv.environment; + + const supplierRepository = createSupplierRepository(environment); + + const getResult = await supplierRepository.getSupplierById(id); + + console.log(`GET successful: ${JSON.stringify(getResult)}`); + }, + ) + .command( + "get-supplier-by-apim-id", + "Get a supplier by their APIM ID", + { + "apimId": { + type: "string", + demandOption: true, + }, + environment: { + type: "string", + demandOption: true, + }, + }, + async (argv) => { + + const apimId = argv.apimId; + const environment = argv.environment; + + const supplierRepository = createSupplierRepository(environment); + + const getResult = await supplierRepository.getSupplierByApimId(apimId); + + console.log(`GET successful: ${JSON.stringify(getResult)}`); + }, + ) + .demandCommand(1) + .parse(); +} + +if (require.main === module) { + main().catch((err) => { + console.error(err); + process.exitCode = 1; + }); +} diff --git a/scripts/utilities/supplier-data/src/infrastructure/suppliers-repo-factory.ts b/scripts/utilities/supplier-data/src/infrastructure/suppliers-repo-factory.ts new file mode 100644 index 00000000..3bddc616 --- /dev/null +++ b/scripts/utilities/supplier-data/src/infrastructure/suppliers-repo-factory.ts @@ -0,0 +1,15 @@ +import { DynamoDBClient } from '@aws-sdk/client-dynamodb'; +import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb'; +import { pino } from 'pino'; +import { SupplierRepository } from '@internal/datastore'; + +export function createSupplierRepository(environment: string): SupplierRepository { + const ddbClient = new DynamoDBClient({}); + const docClient = DynamoDBDocumentClient.from(ddbClient); + const log = pino(); + const config = { + suppliersTableName: `nhs-${environment}-supapi-suppliers`, + }; + + return new SupplierRepository(docClient, log, config); +} diff --git a/scripts/utilities/supplier-data/tsconfig.json b/scripts/utilities/supplier-data/tsconfig.json new file mode 100644 index 00000000..730d18dd --- /dev/null +++ b/scripts/utilities/supplier-data/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": {}, + "extends": "../../../tsconfig.base.json", + "include": [ + "src/**/*", + "jest.config.ts" + ] +} diff --git a/specification/api/components/x-nhsd-apim/target-attributes.yml b/specification/api/components/x-nhsd-apim/target-attributes.yml index 74d901d1..b3864cc7 100644 --- a/specification/api/components/x-nhsd-apim/target-attributes.yml +++ b/specification/api/components/x-nhsd-apim/target-attributes.yml @@ -1,3 +1,9 @@ - name: NHSD-Supplier-ID header: NHSD-Supplier-ID required: false +- name: developer.app.id + header: NHSD-Application-ID + required: false +- name: NHSD-Product-ID + header: NHSD-Product-ID + required: false