diff --git a/package.json b/package.json index e06b516..c66036a 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,9 @@ "./src" ], "dependencies": { + "@aws-sdk/client-dynamodb": "^3.47.0", + "@aws-sdk/lib-dynamodb": "^3.47.0", "@ginger.io/jay-z": "0.0.14", - "aws-sdk": "^2.853.0", "aws-xray-sdk": "^3.2.0", "fast-json-stable-stringify": "^2.1.0", "js-yaml": "^3.13.1", diff --git a/src/main/dynamo/Beyonce.ts b/src/main/dynamo/Beyonce.ts index ac9283c..96e50d0 100644 --- a/src/main/dynamo/Beyonce.ts +++ b/src/main/dynamo/Beyonce.ts @@ -1,5 +1,6 @@ +import { DynamoDBClient } from "@aws-sdk/client-dynamodb" +import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb" import { JayZ } from "@ginger.io/jay-z" -import { DynamoDB } from "aws-sdk" import { captureAWSClient } from "aws-xray-sdk" import { UpdateItemExpressionBuilder } from "./expressions/UpdateItemExpressionBuilder" import { groupModelsByType } from "./groupModelsByType" @@ -41,12 +42,12 @@ export interface ScanOptions { * does auto mapping between JSON <=> DynamoDB Items */ export class Beyonce { - private client: DynamoDB.DocumentClient + private client: DynamoDBDocumentClient private jayz?: JayZ private consistentReads: boolean - constructor(private table: Table, dynamo: DynamoDB, options: Options = {}) { - this.client = new DynamoDB.DocumentClient({ service: dynamo }) + constructor(private table: Table, dynamo: DynamoDBClient, options: Options = {}) { + this.client = DynamoDBDocumentClient.from(dynamo, { marshallOptions: { removeUndefinedValues: true } }) if (options.xRayTracingEnabled) { // hack per: https://github.com/aws/aws-xray-sdk-node/issues/23#issuecomment-509745488 captureAWSClient((this.client as any).service) diff --git a/src/main/dynamo/QueryBuilder.ts b/src/main/dynamo/QueryBuilder.ts index e18c835..e81625e 100644 --- a/src/main/dynamo/QueryBuilder.ts +++ b/src/main/dynamo/QueryBuilder.ts @@ -1,6 +1,5 @@ +import { DynamoDBDocumentClient, QueryCommand, QueryCommandInput } from "@aws-sdk/lib-dynamodb" import { JayZ } from "@ginger.io/jay-z" -import { DynamoDB } from "aws-sdk" -import { DocumentClient } from "aws-sdk/clients/dynamodb" import { ready } from "libsodium-wrappers" import { QueryExpressionBuilder } from "./expressions/QueryExpressionBuilder" import { groupModelsByType } from "./groupModelsByType" @@ -12,7 +11,7 @@ import { Table } from "./Table" import { GroupedModels, TaggedModel } from "./types" interface TableQueryConfig { - db: DynamoDB.DocumentClient + db: DynamoDBDocumentClient table: Table key: PartitionKey | PartitionKeyAndSortKeyPrefix jayz?: JayZ @@ -20,7 +19,7 @@ interface TableQueryConfig { } interface GSIQueryConfig { - db: DynamoDB.DocumentClient + db: DynamoDBDocumentClient table: Table gsiName: string gsiKey: PartitionKey @@ -44,14 +43,14 @@ export class QueryBuilder extends QueryExpressionBuilder< async exec(): Promise> { const query = this.createQueryInput({ lastEvaluatedKey: undefined }) - const iterator = pagedIterator( + const iterator = pagedIterator( { lastEvaluatedKey: undefined }, ({ lastEvaluatedKey, pageSize }) => ({ ...query, ExclusiveStartKey: lastEvaluatedKey, Limit: pageSize }), - (query) => this.config.db.query(query).promise(), + (query) => this.config.db.send(new QueryCommand(query)), this.config.jayz ) @@ -61,14 +60,14 @@ export class QueryBuilder extends QueryExpressionBuilder< async *iterator(options: IteratorOptions = {}): PaginatedIteratorResults { const iteratorOptions = toInternalIteratorOptions(options) const query = this.createQueryInput(iteratorOptions) - const iterator = pagedIterator( + const iterator = pagedIterator( iteratorOptions, ({ lastEvaluatedKey, pageSize }) => ({ ...query, ExclusiveStartKey: lastEvaluatedKey, Limit: pageSize }), - (query) => this.config.db.query(query).promise(), + (query) => this.config.db.send(new QueryCommand(query)), this.config.jayz ) @@ -88,7 +87,7 @@ export class QueryBuilder extends QueryExpressionBuilder< } } - private createQueryInput(options: InternalIteratorOptions): DynamoDB.DocumentClient.QueryInput { + private createQueryInput(options: InternalIteratorOptions): QueryCommandInput { if (isTableQuery(this.config)) { const { table, consistentRead } = this.config const keyCondition = this.buildKeyConditionForTable(this.config) diff --git a/src/main/dynamo/ScanBuilder.ts b/src/main/dynamo/ScanBuilder.ts index 711266f..b5cf1a3 100644 --- a/src/main/dynamo/ScanBuilder.ts +++ b/src/main/dynamo/ScanBuilder.ts @@ -1,6 +1,5 @@ +import { DynamoDBDocumentClient, ScanCommand, ScanCommandInput } from "@aws-sdk/lib-dynamodb" import { JayZ } from "@ginger.io/jay-z" -import { DynamoDB } from "aws-sdk" -import { DocumentClient } from "aws-sdk/clients/dynamodb" import { ready } from "libsodium-wrappers" import { QueryExpressionBuilder } from "./expressions/QueryExpressionBuilder" import { groupModelsByType } from "./groupModelsByType" @@ -11,7 +10,7 @@ import { Table } from "./Table" import { GroupedModels, TaggedModel } from "./types" interface ScanConfig { - db: DynamoDB.DocumentClient + db: DynamoDBDocumentClient table: Table jayz?: JayZ consistentRead?: boolean @@ -33,14 +32,14 @@ export class ScanBuilder extends QueryExpressionBuilder> { const scanInput = this.createScanInput() - const iterator = pagedIterator( + const iterator = pagedIterator( { lastEvaluatedKey: undefined }, ({ lastEvaluatedKey, pageSize }) => ({ ...scanInput, ExclusiveStartKey: lastEvaluatedKey, Limit: pageSize }), - (input) => this.config.db.scan(input).promise(), + (input) => this.config.db.send(new ScanCommand(input)), this.config.jayz ) @@ -50,14 +49,14 @@ export class ScanBuilder extends QueryExpressionBuilder { const iteratorOptions = toInternalIteratorOptions(options) const scanInput = this.createScanInput(iteratorOptions) - const iterator = pagedIterator( + const iterator = pagedIterator( iteratorOptions, ({ lastEvaluatedKey, pageSize }) => ({ ...scanInput, ExclusiveStartKey: lastEvaluatedKey, Limit: pageSize }), - (input) => this.config.db.scan(input).promise(), + (input) => this.config.db.send(new ScanCommand(input)), this.config.jayz ) diff --git a/src/main/dynamo/Table.ts b/src/main/dynamo/Table.ts index 02ba1ad..3c887d6 100644 --- a/src/main/dynamo/Table.ts +++ b/src/main/dynamo/Table.ts @@ -1,4 +1,4 @@ -import { DynamoDB } from "aws-sdk" +import { AttributeDefinition, BillingMode, CreateTableCommandInput } from "@aws-sdk/client-dynamodb" import { GSI, GSIBuilder } from "./GSI" import { Model, PartitionKeyBuilder } from "./Model" import { Partition } from "./Partition" @@ -53,7 +53,7 @@ export class Table { return this.modelTags } - asCreateTableInput(billingMode: DynamoDB.Types.BillingMode): DynamoDB.Types.CreateTableInput { + asCreateTableInput(billingMode: BillingMode): CreateTableCommandInput { const attributeSet = new Set([ this.partitionKeyName, this.sortKeyName, @@ -61,7 +61,7 @@ export class Table { ...this.gsis.flatMap((_) => [_.partitionKeyName, _.sortKeyName]) ]) - const attributeDefinitions: DynamoDB.Types.AttributeDefinitions = Array.from(attributeSet).map((attr) => ({ + const attributeDefinitions: AttributeDefinition[] = Array.from(attributeSet).map((attr) => ({ AttributeName: attr, AttributeType: "S" })) diff --git a/src/main/dynamo/UnprocessedKeyCollector.ts b/src/main/dynamo/UnprocessedKeyCollector.ts index 6a04926..c618421 100644 --- a/src/main/dynamo/UnprocessedKeyCollector.ts +++ b/src/main/dynamo/UnprocessedKeyCollector.ts @@ -1,4 +1,4 @@ -import { DynamoDB } from "aws-sdk" +import { NativeAttributeValue } from "@aws-sdk/util-dynamodb" import { PartitionAndSortKey } from "./keys" import { Table } from "./Table" import { TaggedModel } from "./types" @@ -10,6 +10,7 @@ import { TaggedModel } from "./types" * This makes it easy for the caller to re-submit the operation (e.g. batchGet) by directly * passing the keys returned by Beyonce */ + export class UnprocessedKeyCollector> { private dynamoKeyToBeyonceKey: { [key: string]: T } = {} private unprocessedKeys: T[] = [] @@ -18,7 +19,7 @@ export class UnprocessedKeyCollector> inputKeys.forEach((key) => (this.dynamoKeyToBeyonceKey[`${key.partitionKey}-${key.sortKey}`] = key)) } - add(unprocessedKey: DynamoDB.DocumentClient.Key): void { + add(unprocessedKey: { [key: string]: NativeAttributeValue }): void { const { partitionKeyName, sortKeyName } = this.table const beyonceKey = this.dynamoKeyToBeyonceKey[`${unprocessedKey[partitionKeyName]}-${unprocessedKey[sortKeyName]}`] if (beyonceKey) { diff --git a/src/main/dynamo/expressions/DynamoDBExpression.ts b/src/main/dynamo/expressions/DynamoDBExpression.ts index d8984d7..a755264 100644 --- a/src/main/dynamo/expressions/DynamoDBExpression.ts +++ b/src/main/dynamo/expressions/DynamoDBExpression.ts @@ -1,7 +1,7 @@ -import { DynamoDB } from "aws-sdk" +import { NativeAttributeValue } from "@aws-sdk/util-dynamodb" -export type DynamoDBExpression = { - expression: DynamoDB.DocumentClient.ConditionExpression - attributeNames: DynamoDB.DocumentClient.ExpressionAttributeNameMap - attributeValues: DynamoDB.DocumentClient.ExpressionAttributeValueMap +export interface DynamoDBExpression { + expression: string + attributeNames: { [key: string]: string } + attributeValues: { [key: string]: NativeAttributeValue } } diff --git a/src/main/dynamo/iterators/pagedIterator.ts b/src/main/dynamo/iterators/pagedIterator.ts index fdb31d1..841bd0b 100644 --- a/src/main/dynamo/iterators/pagedIterator.ts +++ b/src/main/dynamo/iterators/pagedIterator.ts @@ -1,5 +1,5 @@ +import { NativeAttributeValue } from "@aws-sdk/util-dynamodb" import { JayZ } from "@ginger.io/jay-z" -import { DynamoDB } from "aws-sdk" import { DocumentClient } from "aws-sdk/clients/dynamodb" import { CompositeError } from "../../CompositeError" import { groupModelsByType } from "../groupModelsByType" @@ -15,7 +15,7 @@ export type RawDynamoDBPage = { export type PageResults = { items: T[] errors: Error[] - lastEvaluatedKey?: DynamoDB.DocumentClient.Key + lastEvaluatedKey?: { [key: string]: NativeAttributeValue } } export async function groupAllPages( @@ -47,7 +47,7 @@ export async function* pagedIterator( const items: U[] = [] const errors: Error[] = [] try { - const response: DynamoDB.DocumentClient.QueryOutput = await executeOperation(pendingOperation) + const response = await executeOperation(pendingOperation) if (response.LastEvaluatedKey !== undefined) { lastEvaluatedKey = response.LastEvaluatedKey @@ -72,13 +72,13 @@ export async function* pagedIterator( if (item) { items.push(item) } else if (error) { - errors.push(error) + errors.push(error as Error) } }) yield { items, lastEvaluatedKey, errors } } catch (error) { - errors.push(error) + errors.push(error as Error) yield { items: [], lastEvaluatedKey, errors } } } diff --git a/src/main/dynamo/operations/BaseParams.ts b/src/main/dynamo/operations/BaseParams.ts index 79068ce..2a564df 100644 --- a/src/main/dynamo/operations/BaseParams.ts +++ b/src/main/dynamo/operations/BaseParams.ts @@ -1,7 +1,7 @@ -import { DynamoDB } from "aws-sdk" +import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb" import { Table } from "../Table" export interface BaseParams { table: Table - client: DynamoDB.DocumentClient + client: DynamoDBDocumentClient } diff --git a/src/main/dynamo/operations/batchGetItems.ts b/src/main/dynamo/operations/batchGetItems.ts index 8a8ad11..d925983 100644 --- a/src/main/dynamo/operations/batchGetItems.ts +++ b/src/main/dynamo/operations/batchGetItems.ts @@ -1,4 +1,5 @@ -import { DynamoDB } from "aws-sdk" +import { BatchGetCommand, DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb" +import { NativeAttributeValue } from "@aws-sdk/util-dynamodb" import { PartitionAndSortKey } from "../keys" import { Table } from "../Table" import { TaggedModel } from "../types" @@ -7,13 +8,13 @@ import { BaseParams } from "./BaseParams" export interface BatchGetItemsParams> extends BaseParams { table: Table - client: DynamoDB.DocumentClient + client: DynamoDBDocumentClient keys: T[] consistentRead?: boolean } export interface BatchGetItemsResult> { - items: DynamoDB.DocumentClient.AttributeMap[] + items: { [key: string]: NativeAttributeValue }[] unprocessedKeys: T[] } @@ -23,8 +24,8 @@ export async function batchGetItems>( ): Promise> { const { table, client, consistentRead } = params const { tableName } = table - const results = await client - .batchGet({ + const results = await client.send( + new BatchGetCommand({ RequestItems: { [tableName]: { ConsistentRead: consistentRead, @@ -32,11 +33,11 @@ export async function batchGetItems>( } } }) - .promise() + ) const unprocessedKeys = new UnprocessedKeyCollector(table, params.keys) if (results.UnprocessedKeys && results.UnprocessedKeys[tableName]) { - results.UnprocessedKeys[tableName].Keys.forEach((key) => unprocessedKeys.add(key)) + results.UnprocessedKeys[tableName].Keys?.forEach((key) => unprocessedKeys.add(key)) } if (results.Responses && results.Responses[tableName]) { diff --git a/src/main/dynamo/operations/batchWriteItems.ts b/src/main/dynamo/operations/batchWriteItems.ts index 3df6022..86aae19 100644 --- a/src/main/dynamo/operations/batchWriteItems.ts +++ b/src/main/dynamo/operations/batchWriteItems.ts @@ -1,4 +1,4 @@ -import { DynamoDB } from "aws-sdk" +import { BatchWriteCommand, BatchWriteCommandInput } from "@aws-sdk/lib-dynamodb" import { PartitionAndSortKey } from "../keys" import { TaggedModel } from "../types" import { UnprocessedKeyCollector } from "../UnprocessedKeyCollector" @@ -20,14 +20,14 @@ export async function batchWriteItems( ): Promise> { const { table, client, putItems = [], deleteItems = [] } = params const { tableName, partitionKeyName, sortKeyName } = table - const requests: DynamoDB.DocumentClient.WriteRequest[] = [] + const requests: BatchWriteCommandInput["RequestItems"] = { [tableName]: [] } putItems.forEach((item) => { - requests.push({ PutRequest: { Item: item } }) + requests[tableName].push({ PutRequest: { Item: item } }) }) deleteItems.forEach((key) => { - requests.push({ + requests[tableName].push({ DeleteRequest: { Key: { [partitionKeyName]: key.partitionKey, @@ -37,7 +37,7 @@ export async function batchWriteItems( }) }) - const results = await client.batchWrite({ RequestItems: { [tableName]: requests } }).promise() + const results = await client.send(new BatchWriteCommand({ RequestItems: requests })) const unprocessedPuts: T[] = [] const unprocessedDeleteKeys = new UnprocessedKeyCollector(table, deleteItems) @@ -45,7 +45,7 @@ export async function batchWriteItems( results.UnprocessedItems[tableName].forEach(({ PutRequest, DeleteRequest }) => { if (PutRequest) { unprocessedPuts.push(PutRequest.Item as T) - } else if (DeleteRequest) { + } else if (DeleteRequest && DeleteRequest.Key) { unprocessedDeleteKeys.add(DeleteRequest.Key) } }) diff --git a/src/main/dynamo/operations/deleteItem.ts b/src/main/dynamo/operations/deleteItem.ts index 83a83df..37dea3c 100644 --- a/src/main/dynamo/operations/deleteItem.ts +++ b/src/main/dynamo/operations/deleteItem.ts @@ -1,3 +1,4 @@ +import { DeleteCommand } from "@aws-sdk/lib-dynamodb" import { PartitionAndSortKey } from "../keys" import { TaggedModel } from "../types" import { BaseParams } from "./BaseParams" @@ -8,13 +9,13 @@ export interface DeleteItemParams extends BaseParams { export async function deleteItem(params: DeleteItemParams): Promise { const { table, client, key } = params - await client - .delete({ + await client.send( + new DeleteCommand({ TableName: table.tableName, Key: { [table.partitionKeyName]: key.partitionKey, [table.sortKeyName]: key.sortKey } }) - .promise() + ) } diff --git a/src/main/dynamo/operations/getItem.ts b/src/main/dynamo/operations/getItem.ts index 50f0205..c454708 100644 --- a/src/main/dynamo/operations/getItem.ts +++ b/src/main/dynamo/operations/getItem.ts @@ -1,3 +1,4 @@ +import { GetCommand } from "@aws-sdk/lib-dynamodb" import { AttributeMap } from "aws-sdk/clients/dynamodb" import { PartitionAndSortKey } from "../keys" import { TaggedModel } from "../types" @@ -14,8 +15,8 @@ export interface GetItemResult { export async function getItem(params: GetItemParams): Promise { const { table, client, key, consistentRead } = params - const results = await client - .get({ + const results = await client.send( + new GetCommand({ TableName: table.tableName, ConsistentRead: consistentRead, Key: { @@ -23,7 +24,7 @@ export async function getItem(params: GetItemParams): [table.sortKeyName]: key.sortKey } }) - .promise() + ) return { item: results.Item } } diff --git a/src/main/dynamo/operations/putItem.ts b/src/main/dynamo/operations/putItem.ts index 0f17ba8..fe5ab11 100644 --- a/src/main/dynamo/operations/putItem.ts +++ b/src/main/dynamo/operations/putItem.ts @@ -1,3 +1,4 @@ +import { PutCommand } from "@aws-sdk/lib-dynamodb" import { TaggedModel } from "../types" import { MaybeEncryptedItem } from "../util" import { BaseParams } from "./BaseParams" @@ -8,10 +9,10 @@ export interface PutItemParams extends BaseParams { export async function putItem(params: PutItemParams): Promise { const { table, client, item } = params - await client - .put({ + await client.send( + new PutCommand({ TableName: table.tableName, Item: item }) - .promise() + ) } diff --git a/src/main/dynamo/operations/transactWriteItems.ts b/src/main/dynamo/operations/transactWriteItems.ts index 20c09b4..539ae0c 100644 --- a/src/main/dynamo/operations/transactWriteItems.ts +++ b/src/main/dynamo/operations/transactWriteItems.ts @@ -1,4 +1,4 @@ -import { DynamoDB } from "aws-sdk" +import { TransactWriteCommand, TransactWriteCommandInput } from "@aws-sdk/lib-dynamodb" import { v4 as generateUUID } from "uuid" import { PartitionAndSortKey } from "../keys" import { TaggedModel } from "../types" @@ -13,7 +13,7 @@ export interface TransactWriteItemParams extends BasePara export async function transactWriteItems(params: TransactWriteItemParams): Promise { const { table, client, clientRequestToken = generateUUID(), putItems = [], deleteItems = [] } = params - const requests: DynamoDB.DocumentClient.TransactWriteItem[] = [] + const requests: TransactWriteCommandInput["TransactItems"] = [] putItems.forEach((item) => { requests.push({ Put: { @@ -35,30 +35,5 @@ export async function transactWriteItems(params: Transact }) ) - const response = client.transactWrite({ TransactItems: requests, ClientRequestToken: clientRequestToken }) - - // If a transaction is cancelled (i.e. fails), the AWS sdk sticks the reasons in the response - // body, but not the exception. So when errors occur, we extract the reasons (if present) - // See: https://github.com/aws/aws-sdk-js/issues/2464#issuecomment-503524701 - let transactionCancellationReasons: any[] | undefined - response.on("extractError", ({ error, httpResponse }) => { - try { - if (error) { - const { CancellationReasons } = JSON.parse(httpResponse.body.toString()) - if (CancellationReasons) { - transactionCancellationReasons = CancellationReasons - } - } - } catch (e) {} // no op - }) - - try { - await response.promise() - } catch (e) { - if (transactionCancellationReasons) { - throw new Error(`${e.message}. Cancellation Reasons: ${JSON.stringify(transactionCancellationReasons)}`) - } else { - throw e - } - } + await client.send(new TransactWriteCommand({ TransactItems: requests, ClientRequestToken: clientRequestToken })) } diff --git a/src/main/dynamo/operations/updateItem.ts b/src/main/dynamo/operations/updateItem.ts index 04c870d..e45b8cd 100644 --- a/src/main/dynamo/operations/updateItem.ts +++ b/src/main/dynamo/operations/updateItem.ts @@ -1,3 +1,4 @@ +import { UpdateCommand } from "@aws-sdk/lib-dynamodb" import { DynamoDBExpression } from "../expressions/DynamoDBExpression" import { PartitionAndSortKey } from "../keys" import { TaggedModel } from "../types" @@ -17,8 +18,8 @@ export async function updateItem(params: UpdateItemParams const { table, client, key, keyConditionExpression, updateExpression } = params const { expression, attributeNames, attributeValues } = updateExpression const hasValues = Object.keys(attributeValues).length > 0 - const result = await client - .update({ + const result = await client.send( + new UpdateCommand({ TableName: table.tableName, ConditionExpression: keyConditionExpression, Key: { @@ -30,7 +31,7 @@ export async function updateItem(params: UpdateItemParams ExpressionAttributeValues: hasValues ? attributeValues : undefined, ReturnValues: "ALL_NEW" // return item after update applied }) - .promise() + ) if (result.Attributes !== undefined) { return { item: result.Attributes as T } diff --git a/src/main/globalTypes.d.ts b/src/main/globalTypes.d.ts new file mode 100644 index 0000000..48afdfa --- /dev/null +++ b/src/main/globalTypes.d.ts @@ -0,0 +1,9 @@ +/** The AWS V3 SDK wants to work in both the browser and node envs. + * So, it relies on DOM type definitions (File + Blob) that don't exist in node + * These fype declarations get us around that + */ +declare global { + interface Blob {} + interface File {} +} +export {} diff --git a/src/test/codegen/generateCode.test.ts b/src/test/codegen/generateCode.test.ts index 60198b5..717f2b3 100644 --- a/src/test/codegen/generateCode.test.ts +++ b/src/test/codegen/generateCode.test.ts @@ -7,7 +7,7 @@ tables: models: Author: id: string - name: string + name: string partitions: Authors: partitionKeyPrefix: Author @@ -54,7 +54,7 @@ tables: Author: id: string name: string - + Book: id: string authorId: string @@ -131,7 +131,7 @@ tables: Author: partitionKey: [$id] sortKey: [Author, $userId] - + Music: models: Musician: @@ -206,7 +206,7 @@ tables: Author: id: string name: string - + Book: id: string name: string @@ -214,7 +214,7 @@ tables: partitions: Authors: partitionKeyPrefix: Author - models: + models: Author: partitionKey: [$id] sortKey: [Author, $id] diff --git a/src/test/dynamo/Beyonce.test.ts b/src/test/dynamo/Beyonce.test.ts index 5f85c11..c61c3ed 100644 --- a/src/test/dynamo/Beyonce.test.ts +++ b/src/test/dynamo/Beyonce.test.ts @@ -1,5 +1,5 @@ +import { DynamoDBClient } from "@aws-sdk/client-dynamodb" import { JayZ } from "@ginger.io/jay-z" -import { DynamoDB } from "aws-sdk" import crypto from "crypto" import { Beyonce } from "../../main/dynamo/Beyonce" import { @@ -42,10 +42,10 @@ describe("Beyonce", () => { musician.name = "Gary Moore" }) - expect(updated).toEqual({ ...musician, name: "Gary Moore" }) + expect(updated).toDeepEqual({ ...musician, name: "Gary Moore" }) const reRead = await db.get(MusicianModel.key({ id: musician.id })) - expect(reRead).toEqual({ ...musician, name: "Gary Moore" }) + expect(reRead).toDeepEqual({ ...musician, name: "Gary Moore" }) }) it("should update a nested item attribute", async () => { @@ -57,7 +57,7 @@ describe("Beyonce", () => { musician.details.description = "scottish blues dude" }) - expect(updated).toEqual({ + expect(updated).toDeepEqual({ ...musician, details: { description: "scottish blues dude" } }) @@ -73,7 +73,7 @@ describe("Beyonce", () => { delete musician.details.description }) - expect(updated).toEqual({ + expect(updated).toDeepEqual({ ...musician, details: {} }) @@ -83,27 +83,32 @@ describe("Beyonce", () => { await setup() const [musician, _, __] = aMusicianWithTwoSongs() - const mockGet = jest.fn(() => ({ - promise: () => - Promise.resolve({ - Item: musician - }) - })) + const mockSend = jest.fn(() => + Promise.resolve({ + Item: musician + }) + ) - const db = new Beyonce(table, new DynamoDB({ region: "us-west-2" })) - ;(db as any).client.get = mockGet + const db = new Beyonce(table, new DynamoDBClient({ region: "us-west-2" })) + ;(db as any).client.send = mockSend await db.get(MusicianModel.key({ id: musician.id }), { consistentRead: true }) - expect(mockGet).toHaveBeenCalledWith({ - TableName: table.tableName, - Key: { - pk: "musician-1", - sk: "musician-1" - }, - ConsistentRead: true - }) + expect(mockSend.mock.calls).toMatchObject([ + [ + { + input: { + TableName: table.tableName, + Key: { + pk: "musician-1", + sk: "musician-1" + }, + ConsistentRead: true + } + } + ] + ]) }) it("should put and delete an item using pk + sk", async () => { @@ -147,34 +152,39 @@ describe("Beyonce", () => { await setup() const [musician, _, __] = aMusicianWithTwoSongs() - const mockGet = jest.fn(() => ({ - promise: () => - Promise.resolve({ - Item: musician - }) - })) + const mockSend = jest.fn(() => + Promise.resolve({ + Item: musician + }) + ) - const db = new Beyonce(table, new DynamoDB({ region: "us-west-2" })) - ;(db as any).client.batchGet = mockGet + const db = new Beyonce(table, new DynamoDBClient({ region: "us-west-2" })) + ;(db as any).client.send = mockSend await db.batchGet({ keys: [MusicianModel.key({ id: musician.id })], consistentRead: true }) - expect(mockGet).toHaveBeenCalledWith({ - RequestItems: { - [table.tableName]: { - ConsistentRead: true, - Keys: [ - { - pk: "musician-1", - sk: "musician-1" + expect(mockSend.mock.calls).toMatchObject([ + [ + { + input: { + RequestItems: { + [table.tableName]: { + ConsistentRead: true, + Keys: [ + { + pk: "musician-1", + sk: "musician-1" + } + ] + } } - ] + } } - } - }) + ] + ]) }) it("should return empty arrays when no items found during batchGet", async () => { @@ -285,7 +295,7 @@ async function testPutAndRetrieveItem(jayZ?: JayZ) { await db.put(musician) const result = await db.get(MusicianModel.key({ id: musician.id })) - expect(result).toEqual(musician) + expect(result).toDeepEqual(musician) } async function testPutAndRetrieveForItemWithEmptyFields(jayZ?: JayZ) { @@ -296,8 +306,8 @@ async function testPutAndRetrieveForItemWithEmptyFields(jayZ?: JayZ) { await db.batchWrite({ putItems: [musician, song1, song2] }) const result = await db.get(MusicianModel.key({ id: musician.id })) - expect(await db.get(SongModel.key({ musicianId: musician.id, id: song1.id }))).toEqual(song1) - expect(await db.get(SongModel.key({ musicianId: musician.id, id: song2.id }))).toEqual(song2) + expect(await db.get(SongModel.key({ musicianId: musician.id, id: song1.id }))).toDeepEqual(song1) + expect(await db.get(SongModel.key({ musicianId: musician.id, id: song2.id }))).toDeepEqual(song2) } async function testPutAndRetrieveItemWithUndefinedField(jayZ?: JayZ) { @@ -313,7 +323,7 @@ async function testPutAndRetrieveItemWithUndefinedField(jayZ?: JayZ) { await db.put(musician) const result = await db.get(MusicianModel.key({ id: musician.id })) - expect(result).toEqual(musician) + expect(result).toDeepEqual(musician) } async function testPutAndDeleteItem(jayZ?: JayZ) { @@ -323,9 +333,9 @@ async function testPutAndDeleteItem(jayZ?: JayZ) { const key = MusicianModel.key({ id: musician.id }) - expect(await db.get(key)).toEqual(musician) + expect(await db.get(key)).toDeepEqual(musician) await db.delete(key) - expect(await db.get(key)).toEqual(undefined) + expect(await db.get(key)).toDeepEqual(undefined) } async function testPutAndDeleteItemInTransaction(jayZ?: JayZ) { @@ -338,9 +348,9 @@ async function testPutAndDeleteItemInTransaction(jayZ?: JayZ) { deleteItems: [SongModel.key({ musicianId: song1.musicianId, id: song1.id })] }) - expect(await db.get(SongModel.key({ musicianId: song2.musicianId, id: song2.id }))).toEqual(song2) + expect(await db.get(SongModel.key({ musicianId: song2.musicianId, id: song2.id }))).toDeepEqual(song2) - expect(await db.get(SongModel.key({ musicianId: song1.musicianId, id: song1.id }))).toEqual(undefined) + expect(await db.get(SongModel.key({ musicianId: song1.musicianId, id: song1.id }))).toDeepEqual(undefined) } async function testPutAndRetrieveCompoundPartitionKey(jayZ?: JayZ) { @@ -363,7 +373,7 @@ async function testPutAndRetrieveCompoundPartitionKey(jayZ?: JayZ) { const result = await db.get(PersonModel.key({ first: "Bob", last: "Smith", sortKey: "sortKey-123" })) - expect(result).toEqual(model) + expect(result).toDeepEqual(model) } async function testPutAndRetrieveCompoundSortKey(jayZ?: JayZ) { @@ -388,7 +398,7 @@ async function testPutAndRetrieveCompoundSortKey(jayZ?: JayZ) { await db.put(model) const result = await db.get(LineItemModel.key({ id: "l1", orderId: "o1", timestamp: "456" })) - expect(result).toEqual(model) + expect(result).toDeepEqual(model) } async function testBatchGet(jayZ?: JayZ) { @@ -406,12 +416,12 @@ async function testBatchGet(jayZ?: JayZ) { }) sortById(results.items.song) - expect(results.items).toEqual({ + expect(results.items).toDeepEqual({ musician: [musician], song: [song1, song2] }) - expect(results.unprocessedKeys).toEqual([]) + expect(results.unprocessedKeys).toDeepEqual([]) } async function testBatchGetWithDuplicateKeys(jayZ?: JayZ) { @@ -430,12 +440,12 @@ async function testBatchGetWithDuplicateKeys(jayZ?: JayZ) { }) sortById(results.items.song) - expect(results.items).toEqual({ + expect(results.items).toDeepEqual({ musician: [musician], song: [song1, song2] }) - expect(results.unprocessedKeys).toEqual([]) + expect(results.unprocessedKeys).toDeepEqual([]) } async function testChunkedBatchGet(jayZ?: JayZ) { @@ -465,8 +475,8 @@ async function testChunkedBatchGet(jayZ?: JayZ) { keys: songs.map(({ id, musicianId }) => SongModel.key({ id, musicianId })) }) - expect(sortById(results.items.song)).toEqual(songs) - expect(results.unprocessedKeys).toEqual([]) + expect(sortById(results.items.song)).toDeepEqual(songs) + expect(results.unprocessedKeys).toDeepEqual([]) } async function testBatchGetWithUnprocessedKeys() { @@ -496,7 +506,7 @@ async function testBatchGetWithUnprocessedKeys() { // And then if we do 1 more batchGet, we should get all of them const results2 = await db.batchGet({ keys: results1.unprocessedKeys }) const retrievedSongs = [...results1.items.song, ...results2.items.song] - expect(retrievedSongs.length).toEqual(50) + expect(retrievedSongs.length).toDeepEqual(50) } async function testEmptyBatchGet(jayZ?: JayZ) { @@ -510,7 +520,7 @@ async function testEmptyBatchGet(jayZ?: JayZ) { ] }) - expect(results.items).toEqual({ + expect(results.items).toDeepEqual({ musician: [], song: [] }) @@ -523,7 +533,7 @@ async function testGSIByModel(jayZ?: JayZ) { const result = await db.queryGSI(byModelAndIdGSI.name, byModelAndIdGSI.key(ModelType.Song)).exec() - expect(result).toEqual({ musician: [], song: [song1, song2] }) + expect(result).toDeepEqual({ musician: [], song: [song1, song2] }) } async function testInvertedIndexGSI(jayZ?: JayZ) { @@ -551,14 +561,14 @@ async function testInvertedIndexGSI(jayZ?: JayZ) { musicianId: santana.id, id: "1", title: "A song where Slash and Santana play together", - mp3: Buffer.from("fake-data", "utf8") + mp3: Buffer.from([1, 2, 3]) }) const slashesSong = SongModel.create({ musicianId: slash.id, id: "1", // note the same id as above title: "A song where Slash and Santana play together", - mp3: Buffer.from("fake-data", "utf8") + mp3: Buffer.from([1, 2, 3]) }) await db.batchWriteWithTransaction({ @@ -568,22 +578,20 @@ async function testInvertedIndexGSI(jayZ?: JayZ) { // Now when we query our inverted index, pk and sk are reversed, // so song id: 1 => [santanasSong, slashesSong] const { song: songs } = await db.queryGSI(invertedIndexGSI.name, invertedIndexGSI.key(`${ModelType.Song}-1`)).exec() - - expect(songs).toEqual([santanasSong, slashesSong]) + expect(songs).toDeepEqual([santanasSong, slashesSong]) } async function testBatchWriteWithTransaction(jayZ?: JayZ) { const db = await setup(jayZ) const [musician, song1, song2] = aMusicianWithTwoSongs() await db.batchWriteWithTransaction({ putItems: [musician, song1, song2] }) - const results = await db.query(MusicianPartition.key({ id: musician.id })).exec() - sortById(results.song) - expect(results).toEqual({ - musician: [musician], - song: [song1, song2] - }) + + expect(results.musician).toDeepEqual([musician]) + expect(results.song.length).toDeepEqual(2) + expect(song1.mp3.equals(results.song[0].mp3)).toDeepEqual(true) + expect(song2.mp3.equals(results.song[1].mp3)).toDeepEqual(true) } async function testBatchWrite(jayZ?: JayZ) { @@ -598,10 +606,8 @@ async function testBatchWrite(jayZ?: JayZ) { const results = await db.query(MusicianPartition.key({ id: musician.id })).exec() sortById(results.song) - expect(results).toEqual({ - musician: [musician], - song: [song1] - }) + expect(results.musician).toDeepEqual([musician]) + expect(song1.mp3.equals(results.song[0].mp3)).toDeepEqual(true) } function sortById(items: T[]): T[] { diff --git a/src/test/dynamo/expressions/QueryExpressionBuilder.test.ts b/src/test/dynamo/expressions/QueryExpressionBuilder.test.ts index 12c81b2..dcaac3f 100644 --- a/src/test/dynamo/expressions/QueryExpressionBuilder.test.ts +++ b/src/test/dynamo/expressions/QueryExpressionBuilder.test.ts @@ -1,10 +1,11 @@ import { QueryExpressionBuilder, Operator } from "../../../main/dynamo/expressions/QueryExpressionBuilder" import { Musician } from "../models" +import "../util" describe("ExpressionBuilder basic clauses", () => { it("doing nothing should yield blank expression", () => { const result = exp().build() - expect(result).toEqual({ + expect(result).toDeepEqual({ expression: "", attributeNames: {}, attributeValues: {} @@ -14,7 +15,7 @@ describe("ExpressionBuilder basic clauses", () => { it("should support where clauses", () => { const result = exp().where("name", "=", "Bob Marley").build() - expect(result).toEqual({ + expect(result).toDeepEqual({ expression: "#name = :v1", attributeNames: { "#name": "name" }, attributeValues: { ":v1": "Bob Marley" } @@ -26,7 +27,7 @@ describe("ExpressionBuilder basic clauses", () => { operators.forEach((operator) => { const result = exp().where("name", operator, "Bob Marley").build() - expect(result).toEqual({ + expect(result).toDeepEqual({ expression: `#name ${operator} :v1`, attributeNames: { "#name": "name" }, attributeValues: { ":v1": "Bob Marley" } @@ -37,7 +38,7 @@ describe("ExpressionBuilder basic clauses", () => { it("should support or clauses", () => { const result = exp().where("name", "=", "Bob Marley").or("id", "<>", "123").build() - expect(result).toEqual({ + expect(result).toDeepEqual({ expression: "#name = :v1 OR #id <> :v2", attributeNames: { @@ -55,7 +56,7 @@ describe("ExpressionBuilder basic clauses", () => { it("should support using the same attribute name multiple times", () => { const result = exp().where("name", "=", "Bob Marley").or("name", "=", "Flea").build() - expect(result).toEqual({ + expect(result).toDeepEqual({ expression: "#name = :v1 OR #name = :v2", attributeNames: { @@ -72,7 +73,7 @@ describe("ExpressionBuilder basic clauses", () => { it("should support and clauses", () => { const result = exp().where("name", "=", "Bob Marley").and("id", "<>", "123").build() - expect(result).toEqual({ + expect(result).toDeepEqual({ expression: "#name = :v1 AND #id <> :v2", attributeNames: { @@ -92,7 +93,7 @@ describe("ExpressionBuilder attribute_exists and attribute_not_exists clauses", it("should support attributeExists clauses", () => { const result = exp().attributeExists("name").build() - expect(result).toEqual({ + expect(result).toDeepEqual({ expression: "attribute_exists(#name)", attributeNames: { "#name": "name" }, attributeValues: {} @@ -102,7 +103,7 @@ describe("ExpressionBuilder attribute_exists and attribute_not_exists clauses", it("should support attributeNotExists clauses", () => { const result = exp().attributeNotExists("name").build() - expect(result).toEqual({ + expect(result).toDeepEqual({ expression: "attribute_not_exists(#name)", attributeNames: { "#name": "name" }, attributeValues: {} @@ -123,7 +124,7 @@ describe("ExpressionBuilder and/or attribute_(not)_exists clauses", () => { it("should support orAttributeExists clauses", () => { const result = exp().where("name", "=", "Bob Marley").orAttributeExists("id").build() - expect(result).toEqual({ + expect(result).toDeepEqual({ expression: "#name = :v1 OR attribute_exists(#id)", attributeNames, attributeValues @@ -133,7 +134,7 @@ describe("ExpressionBuilder and/or attribute_(not)_exists clauses", () => { it("should support andAttributeExists clauses", () => { const result = exp().where("name", "=", "Bob Marley").andAttributeExists("id").build() - expect(result).toEqual({ + expect(result).toDeepEqual({ expression: "#name = :v1 AND attribute_exists(#id)", attributeNames, attributeValues @@ -143,7 +144,7 @@ describe("ExpressionBuilder and/or attribute_(not)_exists clauses", () => { it("should support orAttributeNotExists clauses", () => { const result = exp().where("name", "=", "Bob Marley").orAttributeNotExists("id").build() - expect(result).toEqual({ + expect(result).toDeepEqual({ expression: "#name = :v1 OR attribute_not_exists(#id)", attributeNames, attributeValues @@ -153,7 +154,7 @@ describe("ExpressionBuilder and/or attribute_(not)_exists clauses", () => { it("should support andAttributeNotExists clauses", () => { const result = exp().where("name", "=", "Bob Marley").andAttributeNotExists("id").build() - expect(result).toEqual({ + expect(result).toDeepEqual({ expression: "#name = :v1 AND attribute_not_exists(#id)", attributeNames, attributeValues diff --git a/src/test/dynamo/models.ts b/src/test/dynamo/models.ts index 6627810..ba4ef2d 100644 --- a/src/test/dynamo/models.ts +++ b/src/test/dynamo/models.ts @@ -84,7 +84,7 @@ export function aSong(partial?: Partial): Song { musicianId: "1", id: "1", title: "No Woman, No Cry", - mp3: Buffer.from("fake-data", "utf8"), + mp3: Buffer.from([1, 2, 3]), ...partial }) } diff --git a/src/test/dynamo/query.test.ts b/src/test/dynamo/query.test.ts index fbcc43a..20f298b 100644 --- a/src/test/dynamo/query.test.ts +++ b/src/test/dynamo/query.test.ts @@ -148,14 +148,14 @@ describe("Beyonce.query with JayZ", () => { async function testEmptyQuery(jayZ?: JayZ) { const db = await setup(jayZ) const result = await db.query(MusicianPartition.key({ id: "foo-1" })).exec() - expect(result).toEqual({ musician: [], song: [] }) + expect(result).toDeepEqual({ musician: [], song: [] }) } async function testQueryWithPaginatedResults(jayZ?: JayZ) { const db = await setup(jayZ) const songs = await create25Songs(db) const results = await db.query(MusicianPartition.key({ id: "1" })).exec() - expect(results.song.length).toEqual(songs.length) + expect(results.song.length).toDeepEqual(songs.length) } async function testQueryWithSortedPaginatedResults(jayZ?: JayZ) { @@ -173,7 +173,7 @@ async function testQueryWithSortedPaginatedResults(jayZ?: JayZ) { .iterator() .next() - expect(results.value.items.song).toEqual([song5, song4, song3, song2, song1]) + expect(results.value.items.song).toDeepEqual([song5, song4, song3, song2, song1]) } async function testQueryWithFilter(jayZ?: JayZ) { @@ -186,7 +186,7 @@ async function testQueryWithFilter(jayZ?: JayZ) { .where("model", "=", ModelType.Song) .exec() - expect(result).toEqual({ musician: [], song: [song1, song2] }) + expect(result).toDeepEqual({ musician: [], song: [song1, song2] }) } async function testPaginatedQueryWithFilter(jayZ?: JayZ) { @@ -212,8 +212,8 @@ async function testPaginatedQueryWithFilter(jayZ?: JayZ) { songsProcessed.push(...items.song) } - expect(musiciansProcessed).toEqual([musician]) - expect(songsProcessed.length).toEqual(0) + expect(musiciansProcessed).toDeepEqual([musician]) + expect(songsProcessed.length).toDeepEqual(0) } async function testQueryForSingleTypeOfModel(jayZ?: JayZ) { @@ -223,7 +223,7 @@ async function testQueryForSingleTypeOfModel(jayZ?: JayZ) { const result = await db.query(SongModel.partitionKey({ musicianId: musician.id })).exec() - expect(result).toEqual({ song: [song1, song2] }) + expect(result).toDeepEqual({ song: [song1, song2] }) } async function testQueryWithCombinedAttributeFilters(jayZ?: JayZ) { @@ -240,7 +240,7 @@ async function testQueryWithCombinedAttributeFilters(jayZ?: JayZ) { .orAttributeNotExists("mp3") .exec() - expect(result).toEqual({ + expect(result).toDeepEqual({ musician: [musician], song: [song1, song2] }) @@ -254,7 +254,7 @@ async function testQueryWithLimit(jayZ?: JayZ) { const { value: response1 } = await db.query(key).iterator({ pageSize: 1 }).next() - expect(response1.items).toEqual({ musician: [musician], song: [] }) + expect(response1.items).toDeepEqual({ musician: [musician], song: [] }) const { value: response2 } = await db .query(key) @@ -264,7 +264,7 @@ async function testQueryWithLimit(jayZ?: JayZ) { }) .next() - expect(response2.items).toEqual({ musician: [], song: [song1] }) + expect(response2.items).toDeepEqual({ musician: [], song: [song1] }) const { value: response3 } = await db .query(key) @@ -274,8 +274,8 @@ async function testQueryWithLimit(jayZ?: JayZ) { }) .next() - expect(response3.items).toEqual({ musician: [], song: [song2] }) - expect(response3.cursor).toEqual(undefined) + expect(response3.items).toDeepEqual({ musician: [], song: [song2] }) + expect(response3.cursor).toDeepEqual(undefined) } async function testPaginatedQueryReturnUndefinedCursor(jayZ?: JayZ) { @@ -288,7 +288,7 @@ async function testPaginatedQueryReturnUndefinedCursor(jayZ?: JayZ) { .iterator({ pageSize: 3 }) .next() - expect(response1.items).toEqual({ + expect(response1.items).toDeepEqual({ musician: [musician], song: [song1, song2] }) @@ -301,7 +301,7 @@ async function testPaginatedQueryReturnUndefinedCursor(jayZ?: JayZ) { }) .next() - expect(response2.items).toEqual({ musician: [], song: [] }) + expect(response2.items).toDeepEqual({ musician: [], song: [] }) expect(response2.cursor).toBeUndefined() } @@ -316,7 +316,7 @@ async function testQueryWithReverseAndLimit(jayZ?: JayZ) { .iterator({ pageSize: 1 }) .next() - expect(value.items).toEqual({ musician: [], song: [song2] }) + expect(value.items).toDeepEqual({ musician: [], song: [song2] }) } async function testPutAndRetrieveMultipleItems(jayZ?: JayZ) { @@ -325,7 +325,7 @@ async function testPutAndRetrieveMultipleItems(jayZ?: JayZ) { await db.batchWriteWithTransaction({ putItems: [musician, song1, song2] }) const result = await db.query(MusicianPartition.key({ id: musician.id })).exec() - expect(result).toEqual({ + expect(result).toDeepEqual({ musician: [musician], song: [song1, song2] }) diff --git a/src/test/dynamo/scan.test.ts b/src/test/dynamo/scan.test.ts index a12fb4b..9a99f4e 100644 --- a/src/test/dynamo/scan.test.ts +++ b/src/test/dynamo/scan.test.ts @@ -111,8 +111,8 @@ describe("Beyonce.scan with JayZ", () => { } } - expect(songs.length).toEqual(25) - expect(errors).toEqual([new Error("wrong secret key for the given ciphertext")]) + expect(songs.length).toDeepEqual(25) + expect(errors).toDeepEqual([new Error("wrong secret key for the given ciphertext")]) }) it("should return undefined cursor when there are no more records to scan", async () => { @@ -124,14 +124,14 @@ describe("Beyonce.scan with JayZ", () => { async function testEmptyScan(jayZ?: JayZ) { const db = await setup(jayZ) const result = await db.scan().exec() - expect(result).toEqual({ musician: [], song: [] }) + expect(result).toDeepEqual({ musician: [], song: [] }) } async function testScanWithPaginatedResults(jayZ?: JayZ) { const db = await setup(jayZ) const songs = await create25Songs(db) const results = await db.scan().exec() - expect(results.song.length).toEqual(songs.length) + expect(results.song.length).toDeepEqual(songs.length) } async function testScanWithFilteredPaginatedResults(jayZ?: JayZ) { @@ -156,8 +156,8 @@ async function testScanWithFilteredPaginatedResults(jayZ?: JayZ) { songsProcessed.push(...items.song) } - expect(musiciansProcessed).toEqual([musician]) - expect(songsProcessed.length).toEqual(0) + expect(musiciansProcessed).toDeepEqual([musician]) + expect(songsProcessed.length).toDeepEqual(0) } async function testParallelScan(jayZ?: JayZ) { @@ -182,7 +182,7 @@ async function testParallelScan(jayZ?: JayZ) { results.push(...items.song) } - expect(results.length).toEqual(songs.length) + expect(results.length).toDeepEqual(songs.length) } async function testScanWithFilter(jayZ?: JayZ) { @@ -192,7 +192,7 @@ async function testScanWithFilter(jayZ?: JayZ) { const result = await db.scan().where("model", "=", ModelType.Song).exec() - expect(result).toEqual({ musician: [], song: [song1, song2] }) + expect(result).toDeepEqual({ musician: [], song: [song1, song2] }) } async function testScanWithCombinedAttributeFilters(jayZ?: JayZ) { @@ -209,7 +209,7 @@ async function testScanWithCombinedAttributeFilters(jayZ?: JayZ) { .orAttributeNotExists("mp3") .exec() - expect(result).toEqual({ musician: [musician], song: [song1, song2] }) + expect(result).toDeepEqual({ musician: [musician], song: [song1, song2] }) } async function testScanWithLimit(jayZ?: JayZ) { @@ -219,7 +219,7 @@ async function testScanWithLimit(jayZ?: JayZ) { const { value: response1 } = await db.scan().iterator({ pageSize: 1 }).next() - expect(response1.items).toEqual({ musician: [musician], song: [] }) + expect(response1.items).toDeepEqual({ musician: [musician], song: [] }) const { value: response2 } = await db .scan() @@ -229,7 +229,7 @@ async function testScanWithLimit(jayZ?: JayZ) { }) .next() - expect(response2.items).toEqual({ musician: [], song: [song1] }) + expect(response2.items).toDeepEqual({ musician: [], song: [song1] }) } async function testScanWithPaginatedResultsReturnUndefinedCursor(jayZ?: JayZ) { @@ -239,7 +239,7 @@ async function testScanWithPaginatedResultsReturnUndefinedCursor(jayZ?: JayZ) { const { value: response1 } = await db.scan().iterator({ pageSize: 3 }).next() - expect(response1.items).toEqual({ + expect(response1.items).toDeepEqual({ musician: [musician], song: [song1, song2] }) @@ -252,6 +252,6 @@ async function testScanWithPaginatedResultsReturnUndefinedCursor(jayZ?: JayZ) { }) .next() - expect(response2.items).toEqual({ musician: [], song: [] }) + expect(response2.items).toDeepEqual({ musician: [], song: [] }) expect(response2.cursor).toBeUndefined() } diff --git a/src/test/dynamo/util.ts b/src/test/dynamo/util.ts index 84eb54c..ed6b1d8 100644 --- a/src/test/dynamo/util.ts +++ b/src/test/dynamo/util.ts @@ -1,10 +1,61 @@ +import { CreateTableCommand, DeleteTableCommand, DynamoDBClient, ListTablesCommand } from "@aws-sdk/client-dynamodb" import { DataKey, DataKeyProvider, FixedDataKeyProvider, JayZ } from "@ginger.io/jay-z" -import { DynamoDB } from "aws-sdk" import crypto from "crypto" import { crypto_kdf_KEYBYTES, from_base64, randombytes_buf, ready, to_base64 } from "libsodium-wrappers" import { Beyonce } from "../../main/dynamo/Beyonce" import { Song, SongModel, table } from "./models" +/** Jest's toEqual(...) matcher doesn't know how to compare a Uint8Array and a Node Buffer. + * This can happen when the input object is a Buffer, and the result object is a Uint8Array because + * the DynamoDB client deserializes binary fields as Uint8Arrays + * + * So this custom toDeepEqual(...) matcher makes that work (+ does a deep equality check of other types too) + */ + +declare global { + namespace jest { + interface Matchers { + toDeepEqual(expected: any): R + } + } +} + +expect.extend({ + toDeepEqual(received: any, expected: any) { + let index = 0 + let pass = true + const valuesToCheck = [[received, expected]] + + while (index < valuesToCheck.length) { + const [value1, value2] = valuesToCheck[index] + + if (value1 === undefined || value1 === null || value2 === undefined || value2 === null) { + pass = pass && value1 === value2 + } else if (value1.equals && typeof value1.equals === "function") { + pass = pass && value1.equals(value2) + } else if (value2.equals && typeof value2.equals === "function") { + pass = pass && value2.equals(value1) + } else if (typeof value1 !== "object") { + pass = pass && this.equals(value1, value2) + } else { + for (const key in value1) { + valuesToCheck.push([value1[key], value2[key]]) + } + } + + index++ + } + + return { + pass, + message: () => + [`Expected: ${this.utils.printExpected(expected)}`, `Received: ${this.utils.printExpected(received)}`].join( + "\n" + ) + } + } +}) + export const port = 8000 const isRunningOnCI = process.env.CI_BUILD_ID !== undefined // When running in the CI env, we run Dynamo in a Docker container. And the host must match the service name defined in codeship-services.yml @@ -17,24 +68,24 @@ export async function setup(jayz?: JayZ): Promise { // DynamoDB Local runs as an external http server, so we need to clear // the table from previous test runs - const { TableNames: tables } = await client.listTables().promise() + const { TableNames: tables } = await client.send(new ListTablesCommand({})) if (tables !== undefined && tables.indexOf(tableName) !== -1) { - await client.deleteTable({ TableName: tableName }).promise() + await client.send(new DeleteTableCommand({ TableName: tableName })) } - await client.createTable(table.asCreateTableInput("PAY_PER_REQUEST")).promise() + await client.send(new CreateTableCommand(table.asCreateTableInput("PAY_PER_REQUEST"))) return createBeyonce(client, jayz) } -export function createDynamoDB(): DynamoDB { - return new DynamoDB({ +export function createDynamoDB(): DynamoDBClient { + return new DynamoDBClient({ endpoint, region: "us-west-2" // silly, but still need to specify region for LocalDynamo }) } -export function createBeyonce(db: DynamoDB, jayz?: JayZ): Beyonce { +export function createBeyonce(db: DynamoDBClient, jayz?: JayZ): Beyonce { return new Beyonce(table, db, { jayz }) } diff --git a/tsconfig.json b/tsconfig.json index 626bb81..2412f25 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es6", + "target": "es2017", "module": "commonjs", "strict": true, "outDir": "./dist", diff --git a/yarn.lock b/yarn.lock index aaeffb4..6ade936 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,672 @@ # yarn lockfile v1 +"@aws-crypto/ie11-detection@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/ie11-detection/-/ie11-detection-2.0.0.tgz#bb6c2facf8f03457e949dcf0921477397ffa4c6e" + integrity sha512-pkVXf/dq6PITJ0jzYZ69VhL8VFOFoPZLZqtU/12SGnzYuJOOGNfF41q9GxdI1yqC8R13Rq3jOLKDFpUJFT5eTA== + dependencies: + tslib "^1.11.1" + +"@aws-crypto/sha256-browser@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-browser/-/sha256-browser-2.0.0.tgz#741c9024df55ec59b51e5b1f5d806a4852699fb5" + integrity sha512-rYXOQ8BFOaqMEHJrLHul/25ckWH6GTJtdLSajhlqGMx0PmSueAuvboCuZCTqEKlxR8CQOwRarxYMZZSYlhRA1A== + dependencies: + "@aws-crypto/ie11-detection" "^2.0.0" + "@aws-crypto/sha256-js" "^2.0.0" + "@aws-crypto/supports-web-crypto" "^2.0.0" + "@aws-crypto/util" "^2.0.0" + "@aws-sdk/types" "^3.1.0" + "@aws-sdk/util-locate-window" "^3.0.0" + "@aws-sdk/util-utf8-browser" "^3.0.0" + tslib "^1.11.1" + +"@aws-crypto/sha256-js@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-2.0.0.tgz#f1f936039bdebd0b9e2dd834d65afdc2aac4efcb" + integrity sha512-VZY+mCY4Nmrs5WGfitmNqXzaE873fcIZDu54cbaDaaamsaTOP1DBImV9F4pICc3EHjQXujyE8jig+PFCaew9ig== + dependencies: + "@aws-crypto/util" "^2.0.0" + "@aws-sdk/types" "^3.1.0" + tslib "^1.11.1" + +"@aws-crypto/sha256-js@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-2.0.1.tgz#79e1e6cf61f652ef2089c08d471c722ecf1626a9" + integrity sha512-mbHTBSPBvg6o/mN/c18Z/zifM05eJrapj5ggoOIeHIWckvkv5VgGi7r/wYpt+QAO2ySKXLNvH2d8L7bne4xrMQ== + dependencies: + "@aws-crypto/util" "^2.0.1" + "@aws-sdk/types" "^3.1.0" + tslib "^1.11.1" + +"@aws-crypto/supports-web-crypto@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/supports-web-crypto/-/supports-web-crypto-2.0.0.tgz#fd6cde30b88f77d5a4f57b2c37c560d918014f9e" + integrity sha512-Ge7WQ3E0OC7FHYprsZV3h0QIcpdyJLvIeg+uTuHqRYm8D6qCFJoiC+edSzSyFiHtZf+NOQDJ1q46qxjtzIY2nA== + dependencies: + tslib "^1.11.1" + +"@aws-crypto/util@^2.0.0", "@aws-crypto/util@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-2.0.1.tgz#976cf619cf85084ca85ec5eb947a6ac6b8b5c98c" + integrity sha512-JJmFFwvbm08lULw4Nm5QOLg8+lAQeC8aCXK5xrtxntYzYXCGfHwUJ4Is3770Q7HmICsXthGQ+ZsDL7C2uH3yBQ== + dependencies: + "@aws-sdk/types" "^3.1.0" + "@aws-sdk/util-utf8-browser" "^3.0.0" + tslib "^1.11.1" + +"@aws-sdk/abort-controller@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/abort-controller/-/abort-controller-3.47.0.tgz#0d11693da6824ba8597ee979abb2be9ec385cba7" + integrity sha512-6sxt11dVaJT8CzfVsGCV3h2R0LO12fvXsvCZsMsPGtivb4ZgoFK+PO3hs+9xuA3zjMUC7mb6LE2RM8EXKBDjDw== + dependencies: + "@aws-sdk/types" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/client-dynamodb@^3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-dynamodb/-/client-dynamodb-3.47.0.tgz#29d5552931e1168aeae75aeee4720baa90882c06" + integrity sha512-WqmHSbeuAA2UMv5j5oD1AQiTFOdx0H4nggpZLE0ijMwTCEWlzNPYBGYvz34sAwgcrUoFD+pH++ch55jOAiAtoA== + dependencies: + "@aws-crypto/sha256-browser" "2.0.0" + "@aws-crypto/sha256-js" "2.0.0" + "@aws-sdk/client-sts" "3.47.0" + "@aws-sdk/config-resolver" "3.47.0" + "@aws-sdk/credential-provider-node" "3.47.0" + "@aws-sdk/fetch-http-handler" "3.47.0" + "@aws-sdk/hash-node" "3.47.0" + "@aws-sdk/invalid-dependency" "3.47.0" + "@aws-sdk/middleware-content-length" "3.47.0" + "@aws-sdk/middleware-endpoint-discovery" "3.47.0" + "@aws-sdk/middleware-host-header" "3.47.0" + "@aws-sdk/middleware-logger" "3.47.0" + "@aws-sdk/middleware-retry" "3.47.0" + "@aws-sdk/middleware-serde" "3.47.0" + "@aws-sdk/middleware-signing" "3.47.0" + "@aws-sdk/middleware-stack" "3.47.0" + "@aws-sdk/middleware-user-agent" "3.47.0" + "@aws-sdk/node-config-provider" "3.47.0" + "@aws-sdk/node-http-handler" "3.47.0" + "@aws-sdk/protocol-http" "3.47.0" + "@aws-sdk/smithy-client" "3.47.0" + "@aws-sdk/types" "3.47.0" + "@aws-sdk/url-parser" "3.47.0" + "@aws-sdk/util-base64-browser" "3.47.0" + "@aws-sdk/util-base64-node" "3.47.0" + "@aws-sdk/util-body-length-browser" "3.47.0" + "@aws-sdk/util-body-length-node" "3.47.0" + "@aws-sdk/util-defaults-mode-browser" "3.47.0" + "@aws-sdk/util-defaults-mode-node" "3.47.0" + "@aws-sdk/util-user-agent-browser" "3.47.0" + "@aws-sdk/util-user-agent-node" "3.47.0" + "@aws-sdk/util-utf8-browser" "3.47.0" + "@aws-sdk/util-utf8-node" "3.47.0" + "@aws-sdk/util-waiter" "3.47.0" + tslib "^2.3.0" + uuid "^8.3.2" + +"@aws-sdk/client-sso@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.47.0.tgz#6ec2858bebba4b24bb7a3611bf0223513e35ae6b" + integrity sha512-akkyVuElsSiCCUSGIIZjIhSaPg6hjebffjtcfn1yNHTrZchKw02htUpl4BJUpZE2patFABIDhaW4UK3xPtklAQ== + dependencies: + "@aws-crypto/sha256-browser" "2.0.0" + "@aws-crypto/sha256-js" "2.0.0" + "@aws-sdk/config-resolver" "3.47.0" + "@aws-sdk/fetch-http-handler" "3.47.0" + "@aws-sdk/hash-node" "3.47.0" + "@aws-sdk/invalid-dependency" "3.47.0" + "@aws-sdk/middleware-content-length" "3.47.0" + "@aws-sdk/middleware-host-header" "3.47.0" + "@aws-sdk/middleware-logger" "3.47.0" + "@aws-sdk/middleware-retry" "3.47.0" + "@aws-sdk/middleware-serde" "3.47.0" + "@aws-sdk/middleware-stack" "3.47.0" + "@aws-sdk/middleware-user-agent" "3.47.0" + "@aws-sdk/node-config-provider" "3.47.0" + "@aws-sdk/node-http-handler" "3.47.0" + "@aws-sdk/protocol-http" "3.47.0" + "@aws-sdk/smithy-client" "3.47.0" + "@aws-sdk/types" "3.47.0" + "@aws-sdk/url-parser" "3.47.0" + "@aws-sdk/util-base64-browser" "3.47.0" + "@aws-sdk/util-base64-node" "3.47.0" + "@aws-sdk/util-body-length-browser" "3.47.0" + "@aws-sdk/util-body-length-node" "3.47.0" + "@aws-sdk/util-defaults-mode-browser" "3.47.0" + "@aws-sdk/util-defaults-mode-node" "3.47.0" + "@aws-sdk/util-user-agent-browser" "3.47.0" + "@aws-sdk/util-user-agent-node" "3.47.0" + "@aws-sdk/util-utf8-browser" "3.47.0" + "@aws-sdk/util-utf8-node" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/client-sts@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.47.0.tgz#3e6095d58a61630d1b2d1b12c2d428546f6e9727" + integrity sha512-GVBeDm8XS2nSz2XS8cDJuudb3E4OWk9CCMzftjJBdFNacRx76irSBnerCGgHG1wwoaUD90lUCDbdY/IwVlS4Pg== + dependencies: + "@aws-crypto/sha256-browser" "2.0.0" + "@aws-crypto/sha256-js" "2.0.0" + "@aws-sdk/config-resolver" "3.47.0" + "@aws-sdk/credential-provider-node" "3.47.0" + "@aws-sdk/fetch-http-handler" "3.47.0" + "@aws-sdk/hash-node" "3.47.0" + "@aws-sdk/invalid-dependency" "3.47.0" + "@aws-sdk/middleware-content-length" "3.47.0" + "@aws-sdk/middleware-host-header" "3.47.0" + "@aws-sdk/middleware-logger" "3.47.0" + "@aws-sdk/middleware-retry" "3.47.0" + "@aws-sdk/middleware-sdk-sts" "3.47.0" + "@aws-sdk/middleware-serde" "3.47.0" + "@aws-sdk/middleware-signing" "3.47.0" + "@aws-sdk/middleware-stack" "3.47.0" + "@aws-sdk/middleware-user-agent" "3.47.0" + "@aws-sdk/node-config-provider" "3.47.0" + "@aws-sdk/node-http-handler" "3.47.0" + "@aws-sdk/protocol-http" "3.47.0" + "@aws-sdk/smithy-client" "3.47.0" + "@aws-sdk/types" "3.47.0" + "@aws-sdk/url-parser" "3.47.0" + "@aws-sdk/util-base64-browser" "3.47.0" + "@aws-sdk/util-base64-node" "3.47.0" + "@aws-sdk/util-body-length-browser" "3.47.0" + "@aws-sdk/util-body-length-node" "3.47.0" + "@aws-sdk/util-defaults-mode-browser" "3.47.0" + "@aws-sdk/util-defaults-mode-node" "3.47.0" + "@aws-sdk/util-user-agent-browser" "3.47.0" + "@aws-sdk/util-user-agent-node" "3.47.0" + "@aws-sdk/util-utf8-browser" "3.47.0" + "@aws-sdk/util-utf8-node" "3.47.0" + entities "2.2.0" + fast-xml-parser "3.19.0" + tslib "^2.3.0" + +"@aws-sdk/config-resolver@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/config-resolver/-/config-resolver-3.47.0.tgz#08f000793b19e91c7a7fa6d4ef96657d209d23fc" + integrity sha512-D3YV/hIVaUOHDVpLCwZGOyjSdQpxOVKnRPWT++kR6W0r5WC9F4tEtVCYwMnFRTVhOH87VvcMG/dkT5J4gTAgtQ== + dependencies: + "@aws-sdk/signature-v4" "3.47.0" + "@aws-sdk/types" "3.47.0" + "@aws-sdk/util-config-provider" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/credential-provider-env@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.47.0.tgz#27bc668f9076b35112f8bbb0e218836bd5e972b4" + integrity sha512-x5FctbVUkr//KbjDm8UFFZ7caEl0O1E3vDOxezzZ4yUX4EraKRuYKO1dZIAGNBbNzSBv5simpqVxIXNuGyK9zw== + dependencies: + "@aws-sdk/property-provider" "3.47.0" + "@aws-sdk/types" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/credential-provider-imds@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.47.0.tgz#77c606a2205f3b4c32835dd6b529d5a0632ac548" + integrity sha512-GKfl8O/5Ywnn6/0KfsXopXKrGF31MWCBivISAbubN08X5Up7sQoJPAaDZ5xsi389yZ7+fdTCLKwOyrxobIsGLA== + dependencies: + "@aws-sdk/node-config-provider" "3.47.0" + "@aws-sdk/property-provider" "3.47.0" + "@aws-sdk/types" "3.47.0" + "@aws-sdk/url-parser" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/credential-provider-ini@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.47.0.tgz#93141495412c8d60dfcca55da88d2e4e786695e4" + integrity sha512-h0VWqSdpDYjOMVJRmBXcVFW1+znXMGPmp2fXIg/1dgNkgbdstknFEwUXbgzmrVmE33Wc2UNpQYmnn3lvLUo85Q== + dependencies: + "@aws-sdk/credential-provider-env" "3.47.0" + "@aws-sdk/credential-provider-imds" "3.47.0" + "@aws-sdk/credential-provider-sso" "3.47.0" + "@aws-sdk/credential-provider-web-identity" "3.47.0" + "@aws-sdk/property-provider" "3.47.0" + "@aws-sdk/shared-ini-file-loader" "3.47.0" + "@aws-sdk/types" "3.47.0" + "@aws-sdk/util-credentials" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/credential-provider-node@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.47.0.tgz#c7baa6b06e6f75710458326798bc3f52d133d9d1" + integrity sha512-38T8CK7aUI7Uca3Wu686c6OAaLCfvmIPteiTyRQDr+GA9ElJo5d6bONc2ICibLzV7OGqgP/a7wPONnGPEe3VzA== + dependencies: + "@aws-sdk/credential-provider-env" "3.47.0" + "@aws-sdk/credential-provider-imds" "3.47.0" + "@aws-sdk/credential-provider-ini" "3.47.0" + "@aws-sdk/credential-provider-process" "3.47.0" + "@aws-sdk/credential-provider-sso" "3.47.0" + "@aws-sdk/credential-provider-web-identity" "3.47.0" + "@aws-sdk/property-provider" "3.47.0" + "@aws-sdk/shared-ini-file-loader" "3.47.0" + "@aws-sdk/types" "3.47.0" + "@aws-sdk/util-credentials" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/credential-provider-process@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.47.0.tgz#f7a9dfd45152922468d4908d3bc7508d1d3cdead" + integrity sha512-uk/u9tCzsgrYx9V6GtGlp6xkbblyF0auofxKIEyr2xIFQAtfa9GhCAP1F9bMbH9LcdF3pYhGI5rT3FCBuBbdmg== + dependencies: + "@aws-sdk/property-provider" "3.47.0" + "@aws-sdk/shared-ini-file-loader" "3.47.0" + "@aws-sdk/types" "3.47.0" + "@aws-sdk/util-credentials" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/credential-provider-sso@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.47.0.tgz#fc29c5f5b5cf3be4bf94503e9ec574558fb71d1b" + integrity sha512-isM2AKsgz/8mWP4mAAZZ0h4dMx2cNXu7mwNVl0XICV0JQlMA2CYcC9UfQ34NtCsZUY+gjhU2A001Ai9yJDispg== + dependencies: + "@aws-sdk/client-sso" "3.47.0" + "@aws-sdk/property-provider" "3.47.0" + "@aws-sdk/shared-ini-file-loader" "3.47.0" + "@aws-sdk/types" "3.47.0" + "@aws-sdk/util-credentials" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/credential-provider-web-identity@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.47.0.tgz#eb89137c545d4472f2ad2eae3ac7b1a466a03fae" + integrity sha512-Tz17aDOuQv/lIRHuc/cbCS902QCpGakcy4MBxDPj1g5ccozrJC7IniS7OB3X4ghberggxx/4raWjNToNqtfobg== + dependencies: + "@aws-sdk/property-provider" "3.47.0" + "@aws-sdk/types" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/endpoint-cache@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/endpoint-cache/-/endpoint-cache-3.47.0.tgz#0585b36c8e41d90274c748b341c2ad454f7ca1f6" + integrity sha512-nTO4eKTP218dYKYlrVrkwoQW4Sy++KWxj/v0kgJcHUdogK2u6WcqTRy5E6jainA5HWr5h7AbjmLfHaf2ENFvVg== + dependencies: + mnemonist "0.38.3" + tslib "^2.3.0" + +"@aws-sdk/fetch-http-handler@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.47.0.tgz#a11aa9c95a8901a96dd0f2956708d757a13581a0" + integrity sha512-FSQ5qQkHmCNAgjO2E89vV4QAN66EnHK8sTh4eH55UU0+9/h85g0uMTLMovoEN5Jk+h6AmPCbeq9i+HcPJTmWEQ== + dependencies: + "@aws-sdk/protocol-http" "3.47.0" + "@aws-sdk/querystring-builder" "3.47.0" + "@aws-sdk/types" "3.47.0" + "@aws-sdk/util-base64-browser" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/hash-node@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/hash-node/-/hash-node-3.47.0.tgz#171bb22270dba034d98e8394b5fb46debcdddfb0" + integrity sha512-OqLS/WweCBJz4BwO+EPF1yDeDo8YXXavY/vXElX6reb9+xew9TqmHoFSlFSR8GXkPU7SO+YnlOtmikpMz6fExQ== + dependencies: + "@aws-sdk/types" "3.47.0" + "@aws-sdk/util-buffer-from" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/invalid-dependency@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/invalid-dependency/-/invalid-dependency-3.47.0.tgz#6bd35243526fd7932b3340019c6b87b0f11723f9" + integrity sha512-D2n0RA0o8WyFqPuwbVks177KasNK0bcJn+Fp6GzopSwSXQctULidm7S9pDS9fQW9TZW8xREeHhEyRgmstKc+PQ== + dependencies: + "@aws-sdk/types" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/is-array-buffer@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/is-array-buffer/-/is-array-buffer-3.47.0.tgz#b8f0c0f769956e2505f4e1b9fc4440c727b52022" + integrity sha512-vm3rjUo9EYjLiog3OxGu+f0CdFjTooO2mg5bGb13Xv/2jpg6Z573Skms8nPEaF+ULJWJvobdK+yGw8r4w22cLA== + dependencies: + tslib "^2.3.0" + +"@aws-sdk/lib-dynamodb@^3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/lib-dynamodb/-/lib-dynamodb-3.47.0.tgz#810d74549813985c81e8e17723195241c5812771" + integrity sha512-rQZIDmkzOAjftoMWO2HueCUVseiOA6LjHdY/E6SgdxiOF36cS60P+WhnIjm31E82dcwARcI9FQeIDhMhj/nSnA== + dependencies: + "@aws-sdk/util-dynamodb" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/middleware-content-length@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-content-length/-/middleware-content-length-3.47.0.tgz#d093f3a17ec0fdf243d01c6aa8842f598c9e59a9" + integrity sha512-xLz7BOYpb4rDsxOzyo5v7zPPI1F6vP+S19zpGcBWCg9csIOrbwSTrtwU+yOAfq7ZG+GSVxWnvMEsyqm362VF8Q== + dependencies: + "@aws-sdk/protocol-http" "3.47.0" + "@aws-sdk/types" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/middleware-endpoint-discovery@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.47.0.tgz#1a53496115be32d7b2dd2c174239bdd07accc926" + integrity sha512-uddIRtKBHKbr76wwW4PGNKYruTu22vpvwLoesAhWSj5mR8bL8IMtdWwrA/BgjIiJWV8bRLrITaHpIglzrB7ceg== + dependencies: + "@aws-sdk/config-resolver" "3.47.0" + "@aws-sdk/endpoint-cache" "3.47.0" + "@aws-sdk/protocol-http" "3.47.0" + "@aws-sdk/types" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/middleware-host-header@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.47.0.tgz#b2491dc114db203dd96321054cb1a8f2d368b0cc" + integrity sha512-jkCoH7wHTWo5UduB46e4A71Uj5EKSYf/44Sxf+/PGyOaGW+SbP9nkjdjyWKB5p84WmvhayZLed/qUJgJpTrpGA== + dependencies: + "@aws-sdk/protocol-http" "3.47.0" + "@aws-sdk/types" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/middleware-logger@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.47.0.tgz#1fa43b4b5f21a9d4de9a96a067c0ea3fc2569bb8" + integrity sha512-cK1q+43n2jh/j7jTuFIez7u5k56i2YnjP3DRlh12PfiXiA9V39mfdIu59XHERtE+wJlAyHUq1lYix83CMXOWfQ== + dependencies: + "@aws-sdk/types" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/middleware-retry@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-retry/-/middleware-retry-3.47.0.tgz#779cfcbd050b0fc802d13ddb307b01c1eddf1ed4" + integrity sha512-AHIxtUFNWSLNZjpgR0Jfx+6X78qPJjmyrfv8S5MVW1uURZK14aepV+0JyGBkjFPJVu0yQzcIlvIgKO20e3zQwQ== + dependencies: + "@aws-sdk/protocol-http" "3.47.0" + "@aws-sdk/service-error-classification" "3.47.0" + "@aws-sdk/types" "3.47.0" + tslib "^2.3.0" + uuid "^8.3.2" + +"@aws-sdk/middleware-sdk-sts@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.47.0.tgz#621257aba9db686ffc16212cb0ee1b3c5af1d57a" + integrity sha512-DbXxMeGmnxjOt6fk2UHuQQmuRILnHr5mj6e3xwiYmkg7ClM2fmP3vy94Q98RgDtpEwlyb6yHCONiWP4iXExoug== + dependencies: + "@aws-sdk/middleware-signing" "3.47.0" + "@aws-sdk/property-provider" "3.47.0" + "@aws-sdk/protocol-http" "3.47.0" + "@aws-sdk/signature-v4" "3.47.0" + "@aws-sdk/types" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/middleware-serde@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-serde/-/middleware-serde-3.47.0.tgz#fc84aa32fc60a694e4dbe173ccd92081bdfa45bf" + integrity sha512-MYJqW9xoq//FHa6A6drZ48Wswy8vuFrnbTsKK45AsIKs8kdscYnlWC8s7ndmYrMoT4235TRi8QgcjLC8WMIu9Q== + dependencies: + "@aws-sdk/types" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/middleware-signing@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-signing/-/middleware-signing-3.47.0.tgz#d1672d0913ad9aa0c2b33146e22a9f07b865aaa4" + integrity sha512-oDQ93PiP/90Kl7b3AcHLxsHtWNSxTSdYbJRu4mLb31jKobd2GmLc+tz7L8DpKRyv+fkbrf0Lxh/zLAwaaZdNfg== + dependencies: + "@aws-sdk/property-provider" "3.47.0" + "@aws-sdk/protocol-http" "3.47.0" + "@aws-sdk/signature-v4" "3.47.0" + "@aws-sdk/types" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/middleware-stack@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-stack/-/middleware-stack-3.47.0.tgz#590b68f3d62e5a04c843616789c57d4d5a0628bd" + integrity sha512-F2iwZMXERLTddIovCa7uQmrKXTu3O/Rbym/xKC51J1hnELoNudzIuNIdUQsnSfSIJBl0pB5najN1O2IHBcO/oQ== + dependencies: + tslib "^2.3.0" + +"@aws-sdk/middleware-user-agent@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.47.0.tgz#9eed6f5f0de43506506bebab8a573d1c78eac0a2" + integrity sha512-L0uYhbzXDXSYkvtSzLhpSqv/Hg0Wlwf0PPdYHqPmNJFrN+rigjxvu32e10lZj8JCsqX/tRlPULQdrn1mOvHeMg== + dependencies: + "@aws-sdk/protocol-http" "3.47.0" + "@aws-sdk/types" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/node-config-provider@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/node-config-provider/-/node-config-provider-3.47.0.tgz#6294495b47a554a5e8c6f12b54c1c4c1afda5f1a" + integrity sha512-YLv2CmM8CfedhtrqMhSoEtJenJlWWGCBOvhewXhEPMa+P/PKZ9HxsKdOTC/+lpuWhnD700fG6kFnn2R0kSQE4g== + dependencies: + "@aws-sdk/property-provider" "3.47.0" + "@aws-sdk/shared-ini-file-loader" "3.47.0" + "@aws-sdk/types" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/node-http-handler@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/node-http-handler/-/node-http-handler-3.47.0.tgz#242281a97da25f2465efe4d4c856d6d87fa8752b" + integrity sha512-wZAU3BLLn/mmWR8bYIBdx+gcdwjO1KNNe7C6yXUwvFgClBjCxqR6C32k8CJ3eGiKulGgkBmX8DKGXIdqv0W7kw== + dependencies: + "@aws-sdk/abort-controller" "3.47.0" + "@aws-sdk/protocol-http" "3.47.0" + "@aws-sdk/querystring-builder" "3.47.0" + "@aws-sdk/types" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/property-provider@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/property-provider/-/property-provider-3.47.0.tgz#de9fe96a582c6f6a7402ae8779a3f698a8e785a8" + integrity sha512-S59dASvUxqepS9jTxoN9YrP1CTioYcbNLdg2VwFNglXNRekOP2sxyvtGxDE3oVc3ZgzEyq8+OWsReONf8Tdy4g== + dependencies: + "@aws-sdk/types" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/protocol-http@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/protocol-http/-/protocol-http-3.47.0.tgz#97f730909c1b4a35705d7e2377a4188e821d66c7" + integrity sha512-Oz9iTfuMmpGVB8AGqJ4A1S8OmcAQlM4/f0QLHLp1Kcjnu7H3jysk3B7qWLgqxO7DwKEX4XU8AXohwQv1aXgI8Q== + dependencies: + "@aws-sdk/types" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/querystring-builder@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/querystring-builder/-/querystring-builder-3.47.0.tgz#82c77755e37266130560b0b93c144db9220e3ebd" + integrity sha512-Ou5ipsOZgsMkSnA61Y5xRoOaxHX9vuqBlWL6iAppSonFanj73qrmymUY+AGUznDiUAxCWcvxdnPUIYDm5grwyg== + dependencies: + "@aws-sdk/types" "3.47.0" + "@aws-sdk/util-uri-escape" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/querystring-parser@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/querystring-parser/-/querystring-parser-3.47.0.tgz#c8b7f27f0ce75363b91ec13eabd393f24883b4d2" + integrity sha512-UQlLg7KDHQAQwS4lILE9wht+m3azXrNjWDAHeQqsG8mqCjvSCu5L9t3BBI+EO4dPb9CKa61fjtuzslxvpZdZ3w== + dependencies: + "@aws-sdk/types" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/service-error-classification@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/service-error-classification/-/service-error-classification-3.47.0.tgz#0fbcd467861371072fb09e2a7c7de6a003f432cc" + integrity sha512-15SEeOb+In/hEiSfEWYQvjuA5NeoWlh1iOt8aX4eQLqqIIr5DWyLsremTeWtNN3rIbJzU7yVHg5cv2xn3MJ8Wg== + +"@aws-sdk/shared-ini-file-loader@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.47.0.tgz#8ec659e09362e3fd021dad1bfb952e4796dc88e2" + integrity sha512-yPl190HEyTNawkaOnGkG4zgY+dlXDvSx/RRMxsYoBycaU7V4dfYlXkVZDFe0hqnxw/s/aN7qKfzvEvRkrd9kcg== + dependencies: + tslib "^2.3.0" + +"@aws-sdk/signature-v4@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4/-/signature-v4-3.47.0.tgz#28f3515ef2525a1cbb73f222cfb6e04c9d0d0cb2" + integrity sha512-b1JDXaBRNQ9niMz7Hj6XZ2OfDNT8+a+3fP+BxmFlaFPV++Huo1ClpimzFS8KjRBBrFltTOPPJnEfS+M4cBsnEQ== + dependencies: + "@aws-sdk/is-array-buffer" "3.47.0" + "@aws-sdk/types" "3.47.0" + "@aws-sdk/util-hex-encoding" "3.47.0" + "@aws-sdk/util-uri-escape" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/smithy-client@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/smithy-client/-/smithy-client-3.47.0.tgz#b572eb014f449c3dae09781d24922ab30f4876e0" + integrity sha512-rq1H//VJKopXgRJgso+BdFBD4hrssbFky1BuvXu7orIi8Wp7oS2LogKctqclX7THrXCNT6mzHaxvU6xEOWYUXg== + dependencies: + "@aws-sdk/middleware-stack" "3.47.0" + "@aws-sdk/types" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/types@3.47.0", "@aws-sdk/types@^3.1.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.47.0.tgz#58afc08084a0c929687a9c242edc3794abb22e95" + integrity sha512-ljxyrASkxCsgPXW/jRGGokNtjOql4RbzEl23HEliDmmETlKOrUKVDa2iqhnz5nvqVTc1MgOQv/dr9YBO1LHHIQ== + +"@aws-sdk/url-parser@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/url-parser/-/url-parser-3.47.0.tgz#e3ba9a22bc54317e67949195d6e21c119e44e29a" + integrity sha512-BGfyYZgPvcJ+fW5+i29fy9IwG/2R3LYnWyZ85AFbE++8YcMueJhD7Sychh3mUINViCzjUTVC971m56ee9O9QLA== + dependencies: + "@aws-sdk/querystring-parser" "3.47.0" + "@aws-sdk/types" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/util-base64-browser@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-base64-browser/-/util-base64-browser-3.47.0.tgz#a6356ccf65cc7694bbb6959b925cae5248adc130" + integrity sha512-mG6mCdWWzxdDNKmF4YAn4LH7DBdPfTH/eN8ZrkEWamx9goaO1odQz7p86bxMFe5qMHSPRMgGpCuQoJurg7E4cg== + dependencies: + tslib "^2.3.0" + +"@aws-sdk/util-base64-node@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-base64-node/-/util-base64-node-3.47.0.tgz#0e5ef98e827a376b429d166802dcde41b80a13db" + integrity sha512-r2ym8kSeLR4m18TFM8M3IThkj3i0DvETF/kxPdfa2fHKL7Lq7bfUDJjzr0LmFhdy7iEEcjeLO1hyBklyCke1nQ== + dependencies: + "@aws-sdk/util-buffer-from" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/util-body-length-browser@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.47.0.tgz#4fee151afa6520a4a65a2b8be17b53ef678f4b5e" + integrity sha512-1hHX3uXrl/XKYx2dEULDhtBeofQLHQhllUSbtxj/t8HBZtNhwTSXgb0jbZhPvUFCnzL5ag4znYzEyukLLxgwwQ== + dependencies: + tslib "^2.3.0" + +"@aws-sdk/util-body-length-node@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-body-length-node/-/util-body-length-node-3.47.0.tgz#51839111713c77a05dbaeb6861a80e0138dd5be5" + integrity sha512-PGh5179ZEDS9kcUy1M0i5QiNMeVsCseXh152OT6rU/3yb0h9rozefED/DYEnW/UC0eQNDyj0mgEpT9R86e4S2w== + dependencies: + tslib "^2.3.0" + +"@aws-sdk/util-buffer-from@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-buffer-from/-/util-buffer-from-3.47.0.tgz#b5d837bae4f77fd94e5d546b1d12d61d58d56b03" + integrity sha512-pANJWhIZ32RuQVwtqf2rqllZngZYW0dgOiDwCMCDjBOuhlrqCVs2cwOvDJp7SS5TUg6dt6powFC7UKRRjFMe1g== + dependencies: + "@aws-sdk/is-array-buffer" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/util-config-provider@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-config-provider/-/util-config-provider-3.47.0.tgz#f7d4cb9976b4c901cfc4af03286fc020e232de6d" + integrity sha512-93JmYEtExWBlFM18yt7CuUCBf7WQGAjDEMuhy2sCmhgu+lRwicSCLkjEUFPUTxOv2QbU3HJV2CSKzpAjFAWrSA== + dependencies: + tslib "^2.3.0" + +"@aws-sdk/util-credentials@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-credentials/-/util-credentials-3.47.0.tgz#ef0f81582dde9f67f3175556843244def9ee6e62" + integrity sha512-0I4Azt1C+xWORep3Qq/B6ZYoIL+fPCgqxYL7k3amW5yjkS4T/r0Md6mG41pb9CEHkbIYtQhzfhcUjqb1hNgIvg== + dependencies: + "@aws-sdk/shared-ini-file-loader" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/util-defaults-mode-browser@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.47.0.tgz#967374906c62b897aeca793b05fb59c4083a934e" + integrity sha512-W5ZYzxU23h6F/2vf6H0BJOzV0UVaCzi9l4sN/00m0FfoGMylwSVeJ0dKMwhMAq5o8sdCSRfzHdvAsXj5TjtghQ== + dependencies: + "@aws-sdk/property-provider" "3.47.0" + "@aws-sdk/types" "3.47.0" + bowser "^2.11.0" + tslib "^2.3.0" + +"@aws-sdk/util-defaults-mode-node@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.47.0.tgz#bdb4b91a3609e88840d4209daaa740c9b2c793f3" + integrity sha512-WSTXyAp51FaP0IGf2ZKS1iF7IZ+ct0q8qSBDp12frTIdJO2RZDTQftTq+RrOSj20LXnZi5rf0ICUOFJjomWg4w== + dependencies: + "@aws-sdk/config-resolver" "3.47.0" + "@aws-sdk/credential-provider-imds" "3.47.0" + "@aws-sdk/node-config-provider" "3.47.0" + "@aws-sdk/property-provider" "3.47.0" + "@aws-sdk/types" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/util-dynamodb@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-dynamodb/-/util-dynamodb-3.47.0.tgz#1ac3933f699a73ee10b4d38e0d077aa4a36fc340" + integrity sha512-HfRjXpbZfpcJT/8qQRRroZyCo2qPeGDFX4l84RXXGC6fC5zAFrftzDhJmr8peK7mcs2eNa+MftmZuGv6PEj4bA== + dependencies: + tslib "^2.3.0" + +"@aws-sdk/util-hex-encoding@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.47.0.tgz#f6a9445516965caf1a77a2b185483fc978b6afa9" + integrity sha512-94pkobzbyfasUTUOQSWOixo71ohEPGw2FHnTw/vQ28wQYVYJE8NaV2Z4MyeQlsxSvsthsE4D5u5i1uo+WKFzSQ== + dependencies: + tslib "^2.3.0" + +"@aws-sdk/util-locate-window@^3.0.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-locate-window/-/util-locate-window-3.47.0.tgz#0d5a9fbc84199597fb2d0d1cf8502f2bcfcf057f" + integrity sha512-ptZQQNDG4++Za8EEVs43rmKPnjnIvOnX0QvLQ5cc4Opu28CdYJL89tTt3rq5o+DgQhC+E5rYuLLdqTekYXXxJg== + dependencies: + tslib "^2.3.0" + +"@aws-sdk/util-uri-escape@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-uri-escape/-/util-uri-escape-3.47.0.tgz#1b3ea5939b89179e2c61a9775a2776ad528b94dd" + integrity sha512-4qxKb98t395h7dQWlD0iUMZpTH1JEPWdcNUCZtbVLwXy5lKzJOl4MPMwObdMhruMa9rgMEKwk6btaSzPK12KAw== + dependencies: + tslib "^2.3.0" + +"@aws-sdk/util-user-agent-browser@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.47.0.tgz#e5df5749db066046e649a016f42e48f26b9a6333" + integrity sha512-T0MHvvdt98aDGjSnW1wZU0rTtsA/6zr8735ZHTF6ObEH8ZQ28RPTtD0eWO5pUWfReU8yQxDXhBhJK41/lOOtSA== + dependencies: + "@aws-sdk/types" "3.47.0" + bowser "^2.11.0" + tslib "^2.3.0" + +"@aws-sdk/util-user-agent-node@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.47.0.tgz#df0161fa59c0598356b143a5bea4bcd61dec80d4" + integrity sha512-aGft3RuO8vQyTFMR5tn4WMtjsVMA9WiPx9WCloheieXmlO7gtez9qr51GFYteBQq9lfdiY9PPj4uaOG21efSIg== + dependencies: + "@aws-sdk/node-config-provider" "3.47.0" + "@aws-sdk/types" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/util-utf8-browser@3.47.0", "@aws-sdk/util-utf8-browser@^3.0.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.47.0.tgz#6ac71d3107d909a2ba0aedc176799bd60d6853e8" + integrity sha512-qOYj00VqTVyUVb9gndS9yGHB/tRuK7EPGFvnhRh4VEkwVymH8ywyoFntRhWS/hSrrcQp0W35iS+fJPqdQ1nGWg== + dependencies: + tslib "^2.3.0" + +"@aws-sdk/util-utf8-node@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-utf8-node/-/util-utf8-node-3.47.0.tgz#0c9228bc734e71c02df926d92d9521ac6dd04afe" + integrity sha512-zbcF4zYPta/5tsogtRQ99uPyEB2WGaOyybRaS4cGPhtLiRdA/1wcwmld8ctEaCCf4m4wr2Vu6U9v3SnY92V55w== + dependencies: + "@aws-sdk/util-buffer-from" "3.47.0" + tslib "^2.3.0" + +"@aws-sdk/util-waiter@3.47.0": + version "3.47.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-waiter/-/util-waiter-3.47.0.tgz#2637a58c7a8b92d9ce2fd12d0c0cbc2c5d02cfaa" + integrity sha512-ED8Q7v8Z23NimTcPTK+VN2+NcTvVNLpm5+FzqCiXShZ6tM088e0fzwhyIVTejgbc0mvJE7QfEbR9ZSbr3a1zcw== + dependencies: + "@aws-sdk/abort-controller" "3.47.0" + "@aws-sdk/types" "3.47.0" + tslib "^2.3.0" + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.8.3": version "7.8.3" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e" @@ -954,6 +1620,11 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" +bowser@^2.11.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f" + integrity sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -1355,6 +2026,11 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" +entities@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -1510,6 +2186,11 @@ fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" +fast-xml-parser@3.19.0: + version "3.19.0" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-3.19.0.tgz#cb637ec3f3999f51406dd8ff0e6fc4d83e520d01" + integrity sha512-4pXwmBplsCPv8FOY1WRakF970TjNGnGnfbOnLqjlYvMiF1SR3yOHyxMR/YCXpPTOspNF5gwudqktIP4VsWkvBg== + fb-watchman@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" @@ -2652,6 +3333,13 @@ mkdirp@^0.5.0, mkdirp@~0.5.0: dependencies: minimist "^1.2.5" +mnemonist@0.38.3: + version "0.38.3" + resolved "https://registry.yarnpkg.com/mnemonist/-/mnemonist-0.38.3.tgz#35ec79c1c1f4357cfda2fe264659c2775ccd7d9d" + integrity sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw== + dependencies: + obliterator "^1.6.1" + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -2764,6 +3452,11 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" +obliterator@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/obliterator/-/obliterator-1.6.1.tgz#dea03e8ab821f6c4d96a299e17aef6a3af994ef3" + integrity sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig== + once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -3525,6 +4218,16 @@ ts-node@^9.1.1: source-map-support "^0.5.17" yn "3.1.1" +tslib@^1.11.1: + version "1.14.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" + integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== + +tslib@^2.3.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"