From cc415660bd35858aa683bd4dc6c0b25401c1cb5f Mon Sep 17 00:00:00 2001 From: Bryan Gaston Date: Wed, 17 Aug 2022 21:36:56 -0700 Subject: [PATCH 1/5] feat: upgrade dynamodb lib --- README.md | 2 +- package.json | 3 +- src/main/dom.d.ts | 10 + src/main/dynamo/Beyonce.ts | 9 +- src/main/dynamo/QueryBuilder.ts | 17 +- src/main/dynamo/ScanBuilder.ts | 13 +- src/main/dynamo/Table.ts | 6 +- src/main/dynamo/UnprocessedKeyCollector.ts | 5 +- .../dynamo/expressions/DynamoDBExpression.ts | 8 +- src/main/dynamo/iterators/pagedIterator.ts | 16 +- src/main/dynamo/iterators/types.ts | 5 +- src/main/dynamo/iterators/util.ts | 11 +- src/main/dynamo/operations/BaseParams.ts | 4 +- src/main/dynamo/operations/batchGetItems.ts | 15 +- src/main/dynamo/operations/batchWriteItems.ts | 11 +- src/main/dynamo/operations/deleteItem.ts | 6 +- src/main/dynamo/operations/getItem.ts | 10 +- src/main/dynamo/operations/putItem.ts | 8 +- .../dynamo/operations/transactWriteItems.ts | 34 +- src/main/dynamo/operations/updateItem.ts | 6 +- src/main/dynamo/types.ts | 1 + src/test/dynamo/Beyonce.test.ts | 51 +- src/test/dynamo/iterators/util.test.ts | 2 +- src/test/dynamo/util.ts | 8 +- yarn.lock | 718 ++++++++++++++++++ 25 files changed, 849 insertions(+), 130 deletions(-) create mode 100644 src/main/dom.d.ts diff --git a/README.md b/README.md index cea8916..ea6947e 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,7 @@ Now you can write partition-aware, type safe queries with abandon: ```TypeScript import { Beyonce } from "@ginger.io/beyonce" -import { DynamoDB } from "aws-sdk" +import { DynamoDB } from "@aws-sdk/client-dynamodb" import { LibraryTable } from "generated/models" const beyonce = new Beyonce(LibraryTable, dynamo) diff --git a/package.json b/package.json index 696ab38..8169b42 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,9 @@ "./src" ], "dependencies": { + "@aws-sdk/client-dynamodb": "3.113.0", + "@aws-sdk/lib-dynamodb": "3.113.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/dom.d.ts b/src/main/dom.d.ts new file mode 100644 index 0000000..ecb1558 --- /dev/null +++ b/src/main/dom.d.ts @@ -0,0 +1,10 @@ +export {} + +// @aws-sdk/client-dynamodb version 3.150.0 causes the build to fail if targeting a Nodejs environment instead of dom, +// since it expects several types that are available only in the browser. +// See https://stackoverflow.com/a/69581652/17092655. +declare global { + type ReadableStream = unknown + type Blob = unknown + type File = unknown +} diff --git a/src/main/dynamo/Beyonce.ts b/src/main/dynamo/Beyonce.ts index 0c7c3fa..639f395 100644 --- a/src/main/dynamo/Beyonce.ts +++ b/src/main/dynamo/Beyonce.ts @@ -1,5 +1,6 @@ import { JayZ } from "@ginger.io/jay-z" -import { DynamoDB } from "aws-sdk" +import { DynamoDB } from "@aws-sdk/client-dynamodb" +import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb" import { captureAWSClient } from "aws-xray-sdk" import { UpdateItemExpressionBuilder } from "./expressions/UpdateItemExpressionBuilder" import { groupModelsByType } from "./groupModelsByType" @@ -41,12 +42,14 @@ 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 }) + 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..e77a069 100644 --- a/src/main/dynamo/QueryBuilder.ts +++ b/src/main/dynamo/QueryBuilder.ts @@ -1,6 +1,5 @@ import { JayZ } from "@ginger.io/jay-z" -import { DynamoDB } from "aws-sdk" -import { DocumentClient } from "aws-sdk/clients/dynamodb" +import { DynamoDBDocumentClient, QueryCommand, QueryCommandInput } from "@aws-sdk/lib-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..4275bc6 100644 --- a/src/main/dynamo/ScanBuilder.ts +++ b/src/main/dynamo/ScanBuilder.ts @@ -1,6 +1,5 @@ import { JayZ } from "@ginger.io/jay-z" -import { DynamoDB } from "aws-sdk" -import { DocumentClient } from "aws-sdk/clients/dynamodb" +import { DynamoDBDocumentClient, ScanCommand, ScanCommandInput } from "@aws-sdk/lib-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..471ddf1 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, CreateTableInput } 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): CreateTableInput { 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..bfbb1c4 100644 --- a/src/main/dynamo/UnprocessedKeyCollector.ts +++ b/src/main/dynamo/UnprocessedKeyCollector.ts @@ -1,7 +1,6 @@ -import { DynamoDB } from "aws-sdk" import { PartitionAndSortKey } from "./keys" import { Table } from "./Table" -import { TaggedModel } from "./types" +import { Key, TaggedModel } from "./types" /** Collects a set of unprocessed DynamoDB keys returned from an operation (e.g. batchGet) and maps the "raw" * key Dynamo returns back to the Beyonce key (PartitionAndSortKey class instance) @@ -18,7 +17,7 @@ export class UnprocessedKeyCollector> inputKeys.forEach((key) => (this.dynamoKeyToBeyonceKey[`${key.partitionKey}-${key.sortKey}`] = key)) } - add(unprocessedKey: DynamoDB.DocumentClient.Key): void { + add(unprocessedKey: Key): 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..1fb5b24 100644 --- a/src/main/dynamo/expressions/DynamoDBExpression.ts +++ b/src/main/dynamo/expressions/DynamoDBExpression.ts @@ -1,7 +1,5 @@ -import { DynamoDB } from "aws-sdk" - export type DynamoDBExpression = { - expression: DynamoDB.DocumentClient.ConditionExpression - attributeNames: DynamoDB.DocumentClient.ExpressionAttributeNameMap - attributeValues: DynamoDB.DocumentClient.ExpressionAttributeValueMap + expression: string + attributeNames: Record + attributeValues: Record } diff --git a/src/main/dynamo/iterators/pagedIterator.ts b/src/main/dynamo/iterators/pagedIterator.ts index fdb31d1..0ca2dc1 100644 --- a/src/main/dynamo/iterators/pagedIterator.ts +++ b/src/main/dynamo/iterators/pagedIterator.ts @@ -1,21 +1,15 @@ import { JayZ } from "@ginger.io/jay-z" -import { DynamoDB } from "aws-sdk" -import { DocumentClient } from "aws-sdk/clients/dynamodb" +import { QueryCommandOutput } from "@aws-sdk/lib-dynamodb" import { CompositeError } from "../../CompositeError" import { groupModelsByType } from "../groupModelsByType" -import { GroupedModels, TaggedModel } from "../types" +import { GroupedModels, Key, TaggedModel } from "../types" import { decryptOrPassThroughItem } from "../util" import { InternalIteratorOptions } from "./types" -export type RawDynamoDBPage = { - LastEvaluatedKey?: DocumentClient.Key - Items?: DocumentClient.ItemList -} - export type PageResults = { items: T[] errors: Error[] - lastEvaluatedKey?: DynamoDB.DocumentClient.Key + lastEvaluatedKey?: Key } export async function groupAllPages( @@ -37,7 +31,7 @@ export async function groupAllPages( export async function* pagedIterator( options: InternalIteratorOptions, buildOperation: (opts: InternalIteratorOptions) => T, - executeOperation: (op: T) => Promise, + executeOperation: (op: T) => Promise, jayz?: JayZ ): AsyncGenerator, PageResults> { let pendingOperation: T | undefined = buildOperation(options) @@ -47,7 +41,7 @@ export async function* pagedIterator( const items: U[] = [] const errors: Error[] = [] try { - const response: DynamoDB.DocumentClient.QueryOutput = await executeOperation(pendingOperation) + const response: QueryCommandOutput = await executeOperation(pendingOperation) if (response.LastEvaluatedKey !== undefined) { lastEvaluatedKey = response.LastEvaluatedKey diff --git a/src/main/dynamo/iterators/types.ts b/src/main/dynamo/iterators/types.ts index a4979eb..5467692 100644 --- a/src/main/dynamo/iterators/types.ts +++ b/src/main/dynamo/iterators/types.ts @@ -1,5 +1,4 @@ -import { DocumentClient } from "aws-sdk/clients/dynamodb" -import { GroupedModels, TaggedModel } from "../types" +import { GroupedModels, Key, TaggedModel } from "../types" export type Cursor = string @@ -11,7 +10,7 @@ export interface IteratorOptions { /** Iterator options intended for internal use (within this library) */ export interface InternalIteratorOptions { - lastEvaluatedKey: DocumentClient.Key | undefined + lastEvaluatedKey: Key | undefined pageSize?: number } diff --git a/src/main/dynamo/iterators/util.ts b/src/main/dynamo/iterators/util.ts index 479124b..39a4363 100644 --- a/src/main/dynamo/iterators/util.ts +++ b/src/main/dynamo/iterators/util.ts @@ -1,8 +1,9 @@ -import { Cursor } from "aws-sdk/clients/cloudsearchdomain" -import { DocumentClient } from "aws-sdk/clients/dynamodb" import { base64_variants, from_base64, to_base64 } from "libsodium-wrappers" import { TextDecoder } from "util" -import { InternalIteratorOptions, IteratorOptions } from "./types" +import { InternalIteratorOptions, IteratorOptions} from "./types" +import { Key } from "main/dynamo/types" + +type Cursor = string export function toInternalIteratorOptions(options: IteratorOptions): InternalIteratorOptions { return { @@ -11,13 +12,13 @@ export function toInternalIteratorOptions(options: IteratorOptions): InternalIte } } -export function maybeSerializeCursor(key?: DocumentClient.Key): string | undefined { +export function maybeSerializeCursor(key?: Key): string | undefined { if (key) { return to_base64(JSON.stringify(key), base64_variants.ORIGINAL_NO_PADDING) } } -export function maybeDeserializeCursor(cursor?: Cursor): DocumentClient.Key | undefined { +export function maybeDeserializeCursor(cursor?: Cursor): Key | undefined { if (cursor) { const json = new TextDecoder().decode(from_base64(cursor, base64_variants.ORIGINAL_NO_PADDING)) return JSON.parse(json) 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..097b56a 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 { DynamoDBDocumentClient, BatchGetCommand } 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: Record[] unprocessedKeys: T[] } @@ -23,20 +24,18 @@ 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, Keys: mapToKeysAndRemoveDuplicate(params.keys, table) } } - }) - .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..81d661a 100644 --- a/src/main/dynamo/operations/batchWriteItems.ts +++ b/src/main/dynamo/operations/batchWriteItems.ts @@ -1,4 +1,5 @@ -import { DynamoDB } from "aws-sdk" +import { WriteRequest } from "@aws-sdk/client-dynamodb" +import { BatchWriteCommand } from "@aws-sdk/lib-dynamodb" import { PartitionAndSortKey } from "../keys" import { TaggedModel } from "../types" import { UnprocessedKeyCollector } from "../UnprocessedKeyCollector" @@ -20,7 +21,7 @@ export async function batchWriteItems( ): Promise> { const { table, client, putItems = [], deleteItems = [] } = params const { tableName, partitionKeyName, sortKeyName } = table - const requests: DynamoDB.DocumentClient.WriteRequest[] = [] + const requests: WriteRequest[] = [] putItems.forEach((item) => { requests.push({ PutRequest: { Item: item } }) @@ -29,6 +30,8 @@ export async function batchWriteItems( deleteItems.forEach((key) => { requests.push({ DeleteRequest: { + // @ts-ignore The type is incorrect for `Key`. It expects Record, but since we're using + // a DynamoDBDocumentClient, it needs to be Record instead. Key: { [partitionKeyName]: key.partitionKey, [sortKeyName]: key.sortKey @@ -37,7 +40,7 @@ export async function batchWriteItems( }) }) - const results = await client.batchWrite({ RequestItems: { [tableName]: requests } }).promise() + const results = await client.send(new BatchWriteCommand({ RequestItems: { [tableName]: requests } })) const unprocessedPuts: T[] = [] const unprocessedDeleteKeys = new UnprocessedKeyCollector(table, deleteItems) @@ -45,7 +48,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..9ee1d7c 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" @@ -9,12 +10,11 @@ export interface DeleteItemParams extends BaseParams { export async function deleteItem(params: DeleteItemParams): Promise { const { table, client, key } = params await client - .delete({ + .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..16c441f 100644 --- a/src/main/dynamo/operations/getItem.ts +++ b/src/main/dynamo/operations/getItem.ts @@ -1,4 +1,5 @@ -import { AttributeMap } from "aws-sdk/clients/dynamodb" +import { GetCommand } from "@aws-sdk/lib-dynamodb" +import { NativeAttributeValue } from "@aws-sdk/util-dynamodb" import { PartitionAndSortKey } from "../keys" import { TaggedModel } from "../types" import { BaseParams } from "./BaseParams" @@ -9,21 +10,20 @@ export interface GetItemParams extends BaseParams { } export interface GetItemResult { - item?: AttributeMap + item?: Record } export async function getItem(params: GetItemParams): Promise { const { table, client, key, consistentRead } = params const results = await client - .get({ + .send(new GetCommand({ TableName: table.tableName, ConsistentRead: consistentRead, Key: { [table.partitionKeyName]: key.partitionKey, [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..e4576c7 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" @@ -9,9 +10,8 @@ export interface PutItemParams extends BaseParams { export async function putItem(params: PutItemParams): Promise { const { table, client, item } = params await client - .put({ + .send(new PutCommand({ TableName: table.tableName, - Item: item - }) - .promise() + Item: item, + })) } diff --git a/src/main/dynamo/operations/transactWriteItems.ts b/src/main/dynamo/operations/transactWriteItems.ts index d921b44..42f8878 100644 --- a/src/main/dynamo/operations/transactWriteItems.ts +++ b/src/main/dynamo/operations/transactWriteItems.ts @@ -1,4 +1,5 @@ -import { DynamoDB } from "aws-sdk" +import { TransactWriteCommand } from "@aws-sdk/lib-dynamodb" +import { TransactWriteItem } from "@aws-sdk/client-dynamodb" import { v4 as generateUUID } from "uuid" import { PartitionAndSortKey } from "../keys" import { TaggedModel } from "../types" @@ -13,7 +14,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: TransactWriteItem[] = [] putItems.forEach(({ item, failIfNotUnique = false }) => { requests.push({ Put: { @@ -33,6 +34,8 @@ export async function transactWriteItems(params: Transact requests.push({ Delete: { TableName: table.tableName, + // @ts-ignore The type is incorrect for `Key`. It expects Record, but since we're using + // a DynamoDBDocumentClient, it needs to be Record instead. Key: { [table.partitionKeyName]: key.partitionKey, [table.sortKeyName]: key.sortKey @@ -41,30 +44,15 @@ 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 - }) + const response = client.send(new TransactWriteCommand({ TransactItems: requests, ClientRequestToken: clientRequestToken })) try { - await response.promise() + await response } catch (e) { - if (transactionCancellationReasons) { - throw new Error(`${e.message}. Cancellation Reasons: ${JSON.stringify(transactionCancellationReasons)}`) - } else { - throw e + if (e && e.CancellationReasons) { + throw new Error(`${e.message}. Cancellation Reasons: ${JSON.stringify(e.CancellationReasons)}`) } + + throw e } } diff --git a/src/main/dynamo/operations/updateItem.ts b/src/main/dynamo/operations/updateItem.ts index 04c870d..ee9ce75 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" @@ -18,7 +19,7 @@ export async function updateItem(params: UpdateItemParams const { expression, attributeNames, attributeValues } = updateExpression const hasValues = Object.keys(attributeValues).length > 0 const result = await client - .update({ + .send( new UpdateCommand({ TableName: table.tableName, ConditionExpression: keyConditionExpression, Key: { @@ -29,8 +30,7 @@ export async function updateItem(params: UpdateItemParams ExpressionAttributeNames: attributeNames, 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/dynamo/types.ts b/src/main/dynamo/types.ts index bcacf6f..b839585 100644 --- a/src/main/dynamo/types.ts +++ b/src/main/dynamo/types.ts @@ -1,6 +1,7 @@ import { Model } from "./Model" import { PartitionAndSortKey } from "./keys" +export type Key = Record export type ExtractFields = T extends Model ? U : never export type TaggedModel = Record & { model: string } diff --git a/src/test/dynamo/Beyonce.test.ts b/src/test/dynamo/Beyonce.test.ts index 64fcb77..8ef9a3d 100644 --- a/src/test/dynamo/Beyonce.test.ts +++ b/src/test/dynamo/Beyonce.test.ts @@ -1,5 +1,5 @@ import { JayZ } from "@ginger.io/jay-z" -import { DynamoDB } from "aws-sdk" +import { DynamoDB } from "@aws-sdk/client-dynamodb" import crypto from "crypto" import { Beyonce } from "../../main/dynamo/Beyonce" import { @@ -91,19 +91,23 @@ describe("Beyonce", () => { })) const db = new Beyonce(table, new DynamoDB({ region: "us-west-2" })) - ;(db as any).client.get = mockGet + ;(db as any).client.send = mockGet 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(mockGet).toHaveBeenCalledWith( + expect.objectContaining({ + input: { + TableName: table.tableName, + Key: { + pk: "musician-1", + sk: "musician-1" + }, + ConsistentRead: true + } + }) + ) }) it("should put and delete an item using pk + sk", async () => { @@ -155,26 +159,29 @@ describe("Beyonce", () => { })) const db = new Beyonce(table, new DynamoDB({ region: "us-west-2" })) - ;(db as any).client.batchGet = mockGet + ;(db as any).client.send = mockGet 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(mockGet).toHaveBeenCalledWith( + expect.objectContaining({ + 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 () => { diff --git a/src/test/dynamo/iterators/util.test.ts b/src/test/dynamo/iterators/util.test.ts index e97ad86..69d8496 100644 --- a/src/test/dynamo/iterators/util.test.ts +++ b/src/test/dynamo/iterators/util.test.ts @@ -18,7 +18,7 @@ describe("iterator utils", () => { expect(lastEvaluatedKey).toEqual({ pk: "Bob", sk: "Marley" }) }) - it("shoud not deserialize an undefined cursor", () => { + it("should not deserialize an undefined cursor", () => { expect(maybeDeserializeCursor()).toEqual(undefined) }) }) diff --git a/src/test/dynamo/util.ts b/src/test/dynamo/util.ts index 84eb54c..ce01879 100644 --- a/src/test/dynamo/util.ts +++ b/src/test/dynamo/util.ts @@ -1,5 +1,5 @@ import { DataKey, DataKeyProvider, FixedDataKeyProvider, JayZ } from "@ginger.io/jay-z" -import { DynamoDB } from "aws-sdk" +import { DynamoDB, ListTablesCommand } from "@aws-sdk/client-dynamodb" import crypto from "crypto" import { crypto_kdf_KEYBYTES, from_base64, randombytes_buf, ready, to_base64 } from "libsodium-wrappers" import { Beyonce } from "../../main/dynamo/Beyonce" @@ -17,12 +17,12 @@ 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.deleteTable({ TableName: tableName }) } - await client.createTable(table.asCreateTableInput("PAY_PER_REQUEST")).promise() + await client.createTable(table.asCreateTableInput("PAY_PER_REQUEST")) return createBeyonce(client, jayz) } diff --git a/yarn.lock b/yarn.lock index aaeffb4..7c09129 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,687 @@ # 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.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/abort-controller/-/abort-controller-3.110.0.tgz#15b493b776ec4f7236c6ad6134a6fe87e9dc5292" + integrity sha512-zok/WEVuK7Jh6V9YeA56pNZtxUASon9LTkS7vE65A4UFmNkPGNBCNgoiBcbhWfxwrZ8wtXcQk6rtUut39831mA== + dependencies: + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/client-dynamodb@3.113.0": + version "3.113.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-dynamodb/-/client-dynamodb-3.113.0.tgz#769cc63c2e8f84da63111cd8bd6d6e4fc55bb221" + integrity sha512-tp0/smnSBqoPdSbZqRaH46S9cDkOMXsbG8Qph+gV3+SL+V3nJLeGFzA+3WPmF0l0Bnv0/9Tms4ySZB0MsBSy/A== + dependencies: + "@aws-crypto/sha256-browser" "2.0.0" + "@aws-crypto/sha256-js" "2.0.0" + "@aws-sdk/client-sts" "3.112.0" + "@aws-sdk/config-resolver" "3.110.0" + "@aws-sdk/credential-provider-node" "3.112.0" + "@aws-sdk/fetch-http-handler" "3.110.0" + "@aws-sdk/hash-node" "3.110.0" + "@aws-sdk/invalid-dependency" "3.110.0" + "@aws-sdk/middleware-content-length" "3.110.0" + "@aws-sdk/middleware-endpoint-discovery" "3.110.0" + "@aws-sdk/middleware-host-header" "3.110.0" + "@aws-sdk/middleware-logger" "3.110.0" + "@aws-sdk/middleware-recursion-detection" "3.110.0" + "@aws-sdk/middleware-retry" "3.110.0" + "@aws-sdk/middleware-serde" "3.110.0" + "@aws-sdk/middleware-signing" "3.110.0" + "@aws-sdk/middleware-stack" "3.110.0" + "@aws-sdk/middleware-user-agent" "3.110.0" + "@aws-sdk/node-config-provider" "3.110.0" + "@aws-sdk/node-http-handler" "3.110.0" + "@aws-sdk/protocol-http" "3.110.0" + "@aws-sdk/smithy-client" "3.110.0" + "@aws-sdk/types" "3.110.0" + "@aws-sdk/url-parser" "3.110.0" + "@aws-sdk/util-base64-browser" "3.109.0" + "@aws-sdk/util-base64-node" "3.55.0" + "@aws-sdk/util-body-length-browser" "3.55.0" + "@aws-sdk/util-body-length-node" "3.55.0" + "@aws-sdk/util-defaults-mode-browser" "3.110.0" + "@aws-sdk/util-defaults-mode-node" "3.110.0" + "@aws-sdk/util-user-agent-browser" "3.110.0" + "@aws-sdk/util-user-agent-node" "3.110.0" + "@aws-sdk/util-utf8-browser" "3.109.0" + "@aws-sdk/util-utf8-node" "3.109.0" + "@aws-sdk/util-waiter" "3.110.0" + tslib "^2.3.1" + uuid "^8.3.2" + +"@aws-sdk/client-sso@3.112.0": + version "3.112.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.112.0.tgz#5f308d34566441b94c633018af74036530e69ac6" + integrity sha512-FwFmiapxuVQiyMdDaBvCpajnJkVWEUHBdO+7rIpzgKHkODEPou5/AwboaGRPEFYULOyYeI0HiDFzpK0G6de+7Q== + dependencies: + "@aws-crypto/sha256-browser" "2.0.0" + "@aws-crypto/sha256-js" "2.0.0" + "@aws-sdk/config-resolver" "3.110.0" + "@aws-sdk/fetch-http-handler" "3.110.0" + "@aws-sdk/hash-node" "3.110.0" + "@aws-sdk/invalid-dependency" "3.110.0" + "@aws-sdk/middleware-content-length" "3.110.0" + "@aws-sdk/middleware-host-header" "3.110.0" + "@aws-sdk/middleware-logger" "3.110.0" + "@aws-sdk/middleware-recursion-detection" "3.110.0" + "@aws-sdk/middleware-retry" "3.110.0" + "@aws-sdk/middleware-serde" "3.110.0" + "@aws-sdk/middleware-stack" "3.110.0" + "@aws-sdk/middleware-user-agent" "3.110.0" + "@aws-sdk/node-config-provider" "3.110.0" + "@aws-sdk/node-http-handler" "3.110.0" + "@aws-sdk/protocol-http" "3.110.0" + "@aws-sdk/smithy-client" "3.110.0" + "@aws-sdk/types" "3.110.0" + "@aws-sdk/url-parser" "3.110.0" + "@aws-sdk/util-base64-browser" "3.109.0" + "@aws-sdk/util-base64-node" "3.55.0" + "@aws-sdk/util-body-length-browser" "3.55.0" + "@aws-sdk/util-body-length-node" "3.55.0" + "@aws-sdk/util-defaults-mode-browser" "3.110.0" + "@aws-sdk/util-defaults-mode-node" "3.110.0" + "@aws-sdk/util-user-agent-browser" "3.110.0" + "@aws-sdk/util-user-agent-node" "3.110.0" + "@aws-sdk/util-utf8-browser" "3.109.0" + "@aws-sdk/util-utf8-node" "3.109.0" + tslib "^2.3.1" + +"@aws-sdk/client-sts@3.112.0": + version "3.112.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.112.0.tgz#97aa584562b7b9b18ca09b5a025e3f5093e07a72" + integrity sha512-hSApRO2wg3jk9VRGM6SCZO3aFP7DKVSUqs6FrvlXlj+JU88ZKObjrGE61cCzXoD89Dh+b9t8A2T6W51Nzriaxw== + dependencies: + "@aws-crypto/sha256-browser" "2.0.0" + "@aws-crypto/sha256-js" "2.0.0" + "@aws-sdk/config-resolver" "3.110.0" + "@aws-sdk/credential-provider-node" "3.112.0" + "@aws-sdk/fetch-http-handler" "3.110.0" + "@aws-sdk/hash-node" "3.110.0" + "@aws-sdk/invalid-dependency" "3.110.0" + "@aws-sdk/middleware-content-length" "3.110.0" + "@aws-sdk/middleware-host-header" "3.110.0" + "@aws-sdk/middleware-logger" "3.110.0" + "@aws-sdk/middleware-recursion-detection" "3.110.0" + "@aws-sdk/middleware-retry" "3.110.0" + "@aws-sdk/middleware-sdk-sts" "3.110.0" + "@aws-sdk/middleware-serde" "3.110.0" + "@aws-sdk/middleware-signing" "3.110.0" + "@aws-sdk/middleware-stack" "3.110.0" + "@aws-sdk/middleware-user-agent" "3.110.0" + "@aws-sdk/node-config-provider" "3.110.0" + "@aws-sdk/node-http-handler" "3.110.0" + "@aws-sdk/protocol-http" "3.110.0" + "@aws-sdk/smithy-client" "3.110.0" + "@aws-sdk/types" "3.110.0" + "@aws-sdk/url-parser" "3.110.0" + "@aws-sdk/util-base64-browser" "3.109.0" + "@aws-sdk/util-base64-node" "3.55.0" + "@aws-sdk/util-body-length-browser" "3.55.0" + "@aws-sdk/util-body-length-node" "3.55.0" + "@aws-sdk/util-defaults-mode-browser" "3.110.0" + "@aws-sdk/util-defaults-mode-node" "3.110.0" + "@aws-sdk/util-user-agent-browser" "3.110.0" + "@aws-sdk/util-user-agent-node" "3.110.0" + "@aws-sdk/util-utf8-browser" "3.109.0" + "@aws-sdk/util-utf8-node" "3.109.0" + entities "2.2.0" + fast-xml-parser "3.19.0" + tslib "^2.3.1" + +"@aws-sdk/config-resolver@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/config-resolver/-/config-resolver-3.110.0.tgz#93de506934aa06dd973e5e3dab95b629697372f9" + integrity sha512-7VvtKy4CL63BAktQ2vgsjhWDSXpkXO5YdiI56LQnHztrvSuJBBaxJ7R1p/k0b2tEUhYKUziAIW8EKE/7EGPR4g== + dependencies: + "@aws-sdk/signature-v4" "3.110.0" + "@aws-sdk/types" "3.110.0" + "@aws-sdk/util-config-provider" "3.109.0" + "@aws-sdk/util-middleware" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/credential-provider-env@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.110.0.tgz#c95552fc0a3ae857ced0e171e53082cf3c84bc74" + integrity sha512-oFU3IYk/Bl5tdsz1qigtm3I25a9cvXPqlE8VjYjxVDdLujF5zd/4HLbhP4GQWhpEwZmM1ijcSNfLcyywVevTZg== + dependencies: + "@aws-sdk/property-provider" "3.110.0" + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/credential-provider-imds@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-imds/-/credential-provider-imds-3.110.0.tgz#ba4f178ccab65c5760bce38e7f694584dad3fd74" + integrity sha512-atl+7/dAB+8fG9XI2fYyCgXKYDbOzot65VAwis+14bOEUCVp7PCJifBEZ/L8GEq564p+Fa2p1IpV0wuQXxqFUQ== + dependencies: + "@aws-sdk/node-config-provider" "3.110.0" + "@aws-sdk/property-provider" "3.110.0" + "@aws-sdk/types" "3.110.0" + "@aws-sdk/url-parser" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/credential-provider-ini@3.112.0": + version "3.112.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.112.0.tgz#46001d9e61957ddc5a253ba2c45be92474960b43" + integrity sha512-ebgZ6/jZdTGHQ3zfq/ccmS+7YmLk6yUWHDmh69VK+B1Dd+S1jFwbD9EQ+pYWCp/gEl9F620NSwb6KghRylPWEQ== + dependencies: + "@aws-sdk/credential-provider-env" "3.110.0" + "@aws-sdk/credential-provider-imds" "3.110.0" + "@aws-sdk/credential-provider-sso" "3.112.0" + "@aws-sdk/credential-provider-web-identity" "3.110.0" + "@aws-sdk/property-provider" "3.110.0" + "@aws-sdk/shared-ini-file-loader" "3.110.0" + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/credential-provider-node@3.112.0": + version "3.112.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.112.0.tgz#66a87f8d759aabca06bf3205453e1a28301ddeac" + integrity sha512-7txS7P3BAaU4cksFw/PnoVskVvO8h/TPvOl/BxFtCiUdwA6FRltLvBeMlN08fwUoqgM6z06q8areBdeDqCHOSw== + dependencies: + "@aws-sdk/credential-provider-env" "3.110.0" + "@aws-sdk/credential-provider-imds" "3.110.0" + "@aws-sdk/credential-provider-ini" "3.112.0" + "@aws-sdk/credential-provider-process" "3.110.0" + "@aws-sdk/credential-provider-sso" "3.112.0" + "@aws-sdk/credential-provider-web-identity" "3.110.0" + "@aws-sdk/property-provider" "3.110.0" + "@aws-sdk/shared-ini-file-loader" "3.110.0" + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/credential-provider-process@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.110.0.tgz#1f4543edd532beb4b690e6f3aaf74d00af3be5c4" + integrity sha512-JJcZePvRTfQHYj/+EEY13yItnZH/e8exlARFUjN0L13UrgHpOJtDQBa+YBHXo6MbTFQh+re25z2kzc+zOYSMNQ== + dependencies: + "@aws-sdk/property-provider" "3.110.0" + "@aws-sdk/shared-ini-file-loader" "3.110.0" + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/credential-provider-sso@3.112.0": + version "3.112.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.112.0.tgz#1ef5e8f331040b8e5be9b011f8074c682bf068ba" + integrity sha512-b6rOrSXbNK3fGyPvNpyF5zdktmAoNOqHCTmFSUcxRxOipyRGb5JACsbjWthIQkpWkpNCT8GFNLEg9spXPFIdLA== + dependencies: + "@aws-sdk/client-sso" "3.112.0" + "@aws-sdk/property-provider" "3.110.0" + "@aws-sdk/shared-ini-file-loader" "3.110.0" + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/credential-provider-web-identity@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.110.0.tgz#236e192826c3856e1f2b8eaa1ad126affd641082" + integrity sha512-e4e5u7v3fsUFZsMcFMhMy1NdJBQpunYcLwpYlszm3OEICwTTekQ+hVvnVRd134doHvzepE4yp9sAop0Cj+IRVQ== + dependencies: + "@aws-sdk/property-provider" "3.110.0" + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/endpoint-cache@3.55.0": + version "3.55.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/endpoint-cache/-/endpoint-cache-3.55.0.tgz#b4be2cb363af005a67c2a6f938f06fcbbbbbccf8" + integrity sha512-kxDoHFDuQwZEEUZRp+ZLOg68EXuKPzUN86DcpIZantDVcmu7MSPTbbQp9DZd8MnKVEKCP7Sop5f7zCqOPl3LXw== + dependencies: + mnemonist "0.38.3" + tslib "^2.3.1" + +"@aws-sdk/fetch-http-handler@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/fetch-http-handler/-/fetch-http-handler-3.110.0.tgz#0b6d552659b779c49ba0f99c78a57755864bf1b0" + integrity sha512-vk+K4GeCZL2J2rtvKO+T0Q7i3MDpEGZBMg5K2tj9sMcEQwty0BF0aFnP7Eu2l4/Zif2z1mWuUFM2WcZI6DVnbw== + dependencies: + "@aws-sdk/protocol-http" "3.110.0" + "@aws-sdk/querystring-builder" "3.110.0" + "@aws-sdk/types" "3.110.0" + "@aws-sdk/util-base64-browser" "3.109.0" + tslib "^2.3.1" + +"@aws-sdk/hash-node@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/hash-node/-/hash-node-3.110.0.tgz#b225bfd16596b6485c1c610e8fef8de1e40931c4" + integrity sha512-wakl+kP2O8wTGYiQ3InZy+CVfGrIpFfq9fo4zif9PZac0BbUbguUU1dkY34uZiaf+4o2/9MoDYrHU2HYeXKxWw== + dependencies: + "@aws-sdk/types" "3.110.0" + "@aws-sdk/util-buffer-from" "3.55.0" + tslib "^2.3.1" + +"@aws-sdk/invalid-dependency@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/invalid-dependency/-/invalid-dependency-3.110.0.tgz#9104dfd40e35b6737dc7ab01f4e79c76c1109c44" + integrity sha512-O8J1InmtJkoiUMbQDtxBfOzgigBp9iSVsNXQrhs2qHh3826cJOfE7NGT3u+NMw73Pk5j2cfmOh1+7k/76IqxOg== + dependencies: + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/is-array-buffer@3.55.0": + version "3.55.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/is-array-buffer/-/is-array-buffer-3.55.0.tgz#c46122c5636f01d5895e5256a587768c3425ea7a" + integrity sha512-NbiPHVYuPxdqdFd6FxzzN3H1BQn/iWA3ri3Ry7AyLeP/tGs1yzEWMwf8BN8TSMALI0GXT6Sh0GDWy3Ok5xB6DA== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/lib-dynamodb@3.113.0": + version "3.113.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/lib-dynamodb/-/lib-dynamodb-3.113.0.tgz#a77ddb714277f48108e9a2d00b97762b1ca7ee17" + integrity sha512-n7bp3k0goagZ+9f+WQ2Dx+ggyP1UbCvpCyNzuQBW4xTUolt4b6QQJSJwAC1TfPfNTA++NdCpPwTPAhzHh7BzFA== + dependencies: + "@aws-sdk/util-dynamodb" "3.113.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-content-length@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-content-length/-/middleware-content-length-3.110.0.tgz#f4dc3508952c5fae9740f172d3b76135dd4dba37" + integrity sha512-hKU+zdqfAJQg22LXMVu/z35nNIHrVAKpVKPe9+WYVdL/Z7JKUPK7QymqKGOyDuDbzW6OxyulC1zKGEX12zGmdA== + dependencies: + "@aws-sdk/protocol-http" "3.110.0" + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-endpoint-discovery@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.110.0.tgz#3e9e18b4e191bb94e3068bd526fb766b02b4d777" + integrity sha512-GUnNcuEBqD21E8EgevTcaBXJGnH5dp7eDSfWceYJP+iSscFXLzFFOf1I6H0cwvfARVMHVvlFz0tQcojCSHUZig== + dependencies: + "@aws-sdk/config-resolver" "3.110.0" + "@aws-sdk/endpoint-cache" "3.55.0" + "@aws-sdk/protocol-http" "3.110.0" + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-host-header@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.110.0.tgz#a28115e2797b86c2fb583000593b723a51313b92" + integrity sha512-/Cknn1vL2LTlclI0MX2RzmtdPlCJ5palCRXxm/mod1oHwg4oNTKRlUX3LUD+L8g7JuJ4h053Ch9KS/A0vanE5Q== + dependencies: + "@aws-sdk/protocol-http" "3.110.0" + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-logger@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.110.0.tgz#69eb0b2d0d9833f6fdbe33eb1876254e7cee53ec" + integrity sha512-+pz+a+8dfTnzLj79nHrv3aONMp/N36/erMd+7JXeR84QEosVLrFBUwKA8x5x6O3s1iBbQzRKMYEIuja9xn1BPA== + dependencies: + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-recursion-detection@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.110.0.tgz#8daa2bc9f62cbf499d9c615726cf2a51f46e70ff" + integrity sha512-Wav782zd7bcd1e6txRob76CDOdVOaUQ8HXoywiIm/uFrEEUZvhs2mgnXjVUVCMBUehdNgnL99z420aS13JeL/Q== + dependencies: + "@aws-sdk/protocol-http" "3.110.0" + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-retry@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-retry/-/middleware-retry-3.110.0.tgz#3bdbd66d06dcbddbdf684d1d81c6d5fd7746f03b" + integrity sha512-lwLAQQveCiUqymQvVYjCee6QOXw3Zqbc9yq+pxYdXbs1Cv1XMA6PeJeUU5r5KEVuSceBLyyrnl6E0R1l1om1MQ== + dependencies: + "@aws-sdk/protocol-http" "3.110.0" + "@aws-sdk/service-error-classification" "3.110.0" + "@aws-sdk/types" "3.110.0" + "@aws-sdk/util-middleware" "3.110.0" + tslib "^2.3.1" + uuid "^8.3.2" + +"@aws-sdk/middleware-sdk-sts@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-sts/-/middleware-sdk-sts-3.110.0.tgz#8c1e34b72355c5e63495927a01839f210327f0c1" + integrity sha512-EjY/YFdlr5jECde6qIrTIyGBbn/34CKcQGKvmvRd31+3qaClIJLAwNuHfcVzWvCUGbAslsfvdbOpLju33pSQRA== + dependencies: + "@aws-sdk/middleware-signing" "3.110.0" + "@aws-sdk/property-provider" "3.110.0" + "@aws-sdk/protocol-http" "3.110.0" + "@aws-sdk/signature-v4" "3.110.0" + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-serde@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-serde/-/middleware-serde-3.110.0.tgz#603dcc1f68d78e9123f9b696150374a8357de6c3" + integrity sha512-brVupxgEAmcZ9cZvdHEH8zncjvGKIiud8pOe4fiimp5NpHmjBLew4jUbnOKNZNAjaidcKUtz//cxtutD6yXEww== + dependencies: + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-signing@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-signing/-/middleware-signing-3.110.0.tgz#8faa6acdaedb1c29614fe7ba88a74534db38f3bb" + integrity sha512-y6ZKrGYfgDlFMzWhZmoq5J1UctBgZOUvMmnU9sSeZ020IlEPiOxFMvR0Zu6TcYThp8uy3P0wyjQtGYeTl9Z/kA== + dependencies: + "@aws-sdk/property-provider" "3.110.0" + "@aws-sdk/protocol-http" "3.110.0" + "@aws-sdk/signature-v4" "3.110.0" + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/middleware-stack@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-stack/-/middleware-stack-3.110.0.tgz#5a531c83ec375adf9d7f1bd80b725cebf7b2f01d" + integrity sha512-iaLHw6ctOuGa9UxNueU01Xes+15dR+mqioRpUOUZ9Zx+vhXVpD7C8lnNqhRnYeFXs10/rNIzASgsIrAHTlnlIQ== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/middleware-user-agent@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.110.0.tgz#52f32e99ecb641babcd59bb010527d5614e908f4" + integrity sha512-Y6FgiZr99DilYq6AjeaaWcNwVlSQpNGKrILzvV4Tmz03OaBIspe4KL+8EZ2YA/sAu5Lpw80vItdezqDOwGAlnQ== + dependencies: + "@aws-sdk/protocol-http" "3.110.0" + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/node-config-provider@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/node-config-provider/-/node-config-provider-3.110.0.tgz#7d032082b85458ea4959f744d473e328be024359" + integrity sha512-46p4dCPGYctuybTQTwLpjenA1QFHeyJw/OyggGbtUJUy+833+ldnAwcPVML2aXJKUKv3APGI8vq1kaloyNku3Q== + dependencies: + "@aws-sdk/property-provider" "3.110.0" + "@aws-sdk/shared-ini-file-loader" "3.110.0" + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/node-http-handler@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/node-http-handler/-/node-http-handler-3.110.0.tgz#b29ba034558ec3cddae69860d49766a27ee73354" + integrity sha512-/rP+hY516DpP8fZhwFW5xM/ElH0w6lxw/15VvZCoY5EnOLAF5XIsJdzscWPSEW2FHCylBM4SNrKhGar14BDXhA== + dependencies: + "@aws-sdk/abort-controller" "3.110.0" + "@aws-sdk/protocol-http" "3.110.0" + "@aws-sdk/querystring-builder" "3.110.0" + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/property-provider@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/property-provider/-/property-provider-3.110.0.tgz#ea60c33a8e243246fc21d478ff009063825b9abd" + integrity sha512-7NkpmYeOkK3mhWBNU+/zSDqwzeaSPH1qrq4L//WV7WS/weYyE/jusQeZoOxVsuZQnQEXHt5O2hKVeUwShl12xA== + dependencies: + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/protocol-http@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/protocol-http/-/protocol-http-3.110.0.tgz#ff3cffa5b1eb7c8564a9e9019a8842b429c7f85c" + integrity sha512-qdi2gCbJiyPyLn+afebPNp/5nVCRh1X7t7IRIFl3FHVEC+o54u/ojay/MLZ4M/+X9Fa4Zxsb0Wpp3T0xAHVDBg== + dependencies: + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/querystring-builder@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/querystring-builder/-/querystring-builder-3.110.0.tgz#c7f63262e898ab38cdbbbfcd03ddbfde346c9595" + integrity sha512-7V3CDXj519izmbBn9ZE68ymASwGriA+Aq+cb/yHSVtffnvXjPtvONNw7G/5iVblisGLSCUe2hSvpYtcaXozbHw== + dependencies: + "@aws-sdk/types" "3.110.0" + "@aws-sdk/util-uri-escape" "3.55.0" + tslib "^2.3.1" + +"@aws-sdk/querystring-parser@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/querystring-parser/-/querystring-parser-3.110.0.tgz#0551efb7aaa867d3b6705f62d798a45247f5f44b" + integrity sha512-//pJHH7hrhdDMZGBPKXKymmC/tJM7gFT0w/qbu/yd3Wm4W2fMB+8gkmj6EZctx7jrsWlfRQuvFejKqEfapur/g== + dependencies: + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/service-error-classification@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/service-error-classification/-/service-error-classification-3.110.0.tgz#09398517d4ad9787bd0d904816bfe0ffd68b1f5f" + integrity sha512-ccgCE0pU/4RmXR6CP3fLAdhPAve7bK/yXBbGzpSHGAQOXqNxYzOsAvQ30Jg6X+qjLHsI/HR2pLIE65z4k6tynw== + +"@aws-sdk/shared-ini-file-loader@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.110.0.tgz#f91b66e7084312df2b337cc990c9585e832fc2fc" + integrity sha512-E1ERoqEoG206XNBYWCKLgHkzCbTxdpDEGbsLET2DnvjFsT0s9p2dPvVux3bYl7JVAhyGduE+qcqWk7MzhFCBNQ== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/signature-v4@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4/-/signature-v4-3.110.0.tgz#9dba5d06345fa756b4c23deeec7086f6148a5bf1" + integrity sha512-utxxdllOnmQDhbpipnFAbuQ4c2pwefZ+2hi48jKvQRULQ2PO4nxLmdZm6B0FXaTijbKsyO7GrMik+EZ6mi3ARQ== + dependencies: + "@aws-sdk/is-array-buffer" "3.55.0" + "@aws-sdk/types" "3.110.0" + "@aws-sdk/util-hex-encoding" "3.109.0" + "@aws-sdk/util-middleware" "3.110.0" + "@aws-sdk/util-uri-escape" "3.55.0" + tslib "^2.3.1" + +"@aws-sdk/smithy-client@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/smithy-client/-/smithy-client-3.110.0.tgz#397c0e7ef56ffa058469c641b586978400c09dd7" + integrity sha512-gNLYrmdAe/1hVF2Nv2LF4OkL1A0a1o708pEMZHzql9xP164omRDaLrGDhz9tH7tsJEgLz+Bf4E8nTuISeDwvGg== + dependencies: + "@aws-sdk/middleware-stack" "3.110.0" + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/types@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.110.0.tgz#09404533b507925eadf9acf9c4356667048e45bd" + integrity sha512-dLVoqODU3laaqNFPyN1QLtlQnwX4gNPMXptEBIt/iJpuZf66IYJe6WCzVZGt4Zfa1CnUmrlA428AzdcA/KCr2A== + +"@aws-sdk/types@^3.1.0": + version "3.127.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.127.0.tgz#a7bafc47ee2328eee2453087521e6c3a39e7278d" + integrity sha512-e0wtx2IkOl7rwfKfLH5pPTzQ+d45V7b1WrjeL0WDI8kOu6w+sXmhNxI6uM2kf0k4NiTLN84lW290AEWupey9Og== + +"@aws-sdk/url-parser@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/url-parser/-/url-parser-3.110.0.tgz#87d5c0a6f6d2f29027c747c65d8a2846302bc792" + integrity sha512-tILFB8/Q73yzgO0dErJNnELmmBszd0E6FucwAnG3hfDefjqCBe09Q/1yhu2aARXyRmZa4AKp0sWcdwIWHc8dnA== + dependencies: + "@aws-sdk/querystring-parser" "3.110.0" + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/util-base64-browser@3.109.0": + version "3.109.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-base64-browser/-/util-base64-browser-3.109.0.tgz#e7faf5c4cbb88bc39b9c1c5a1a79e4c869e9f645" + integrity sha512-lAZ6fyDGiRLaIsKT9qh7P9FGuNyZ4gAbr1YOSQk/5mHtaTuUvxlPptZuInNM/0MPQm6lpcot00D8IWTucn4PbA== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-base64-node@3.55.0": + version "3.55.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-base64-node/-/util-base64-node-3.55.0.tgz#da9a3fd6752be49163572144793e6b23d0186ff4" + integrity sha512-UQ/ZuNoAc8CFMpSiRYmevaTsuRKzLwulZTnM8LNlIt9Wx1tpNvqp80cfvVj7yySKROtEi20wq29h31dZf1eYNQ== + dependencies: + "@aws-sdk/util-buffer-from" "3.55.0" + tslib "^2.3.1" + +"@aws-sdk/util-body-length-browser@3.55.0": + version "3.55.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-body-length-browser/-/util-body-length-browser-3.55.0.tgz#9c2637097501032f6a1afddb76687415fe9b44b6" + integrity sha512-Ei2OCzXQw5N6ZkTMZbamUzc1z+z1R1Ja5tMEagz5BxuX4vWdBObT+uGlSzL8yvTbjoPjnxWA2aXyEqaUP3JS8Q== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-body-length-node@3.55.0": + version "3.55.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-body-length-node/-/util-body-length-node-3.55.0.tgz#67049bbb6c62d794a1bb5a13b9a678988c925489" + integrity sha512-lU1d4I+9wJwydduXs0SxSfd+mHKjxeyd39VwOv6i2KSwWkPbji9UQqpflKLKw+r45jL7+xU/zfeTUg5Tt/3Gew== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-buffer-from@3.55.0": + version "3.55.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-buffer-from/-/util-buffer-from-3.55.0.tgz#e7c927974b07a29502aa1ad58509b91d0d7cf0f7" + integrity sha512-uVzKG1UgvnV7XX2FPTylBujYMKBPBaq/qFBxfl0LVNfrty7YjpfieQxAe6yRLD+T0Kir/WDQwGvYC+tOYG3IGA== + dependencies: + "@aws-sdk/is-array-buffer" "3.55.0" + tslib "^2.3.1" + +"@aws-sdk/util-config-provider@3.109.0": + version "3.109.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-config-provider/-/util-config-provider-3.109.0.tgz#7828b8894b2b23c289ffc5c106cbced7a5d6ee86" + integrity sha512-GrAZl/aBv0A28LkyNyq8SPJ5fmViCwz80fWLMeWx/6q5AbivuILogjlWwEZSvZ9zrlHOcFC0+AnCa5pQrjaslw== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-defaults-mode-browser@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-defaults-mode-browser/-/util-defaults-mode-browser-3.110.0.tgz#b72331874da2c5e8a366cd98828a06fe19b52ae5" + integrity sha512-Y2dcOOD20S3bv/IjUqpdKIiDt6995SXNG5Pu/LeSdXNyLCOIm9rX4gHTxl9fC1KK5M/gR9fGJ362f67WwqEEqw== + dependencies: + "@aws-sdk/property-provider" "3.110.0" + "@aws-sdk/types" "3.110.0" + bowser "^2.11.0" + tslib "^2.3.1" + +"@aws-sdk/util-defaults-mode-node@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-defaults-mode-node/-/util-defaults-mode-node-3.110.0.tgz#52b4c84fc7aa06838ea6bb29d216a2d7615b9036" + integrity sha512-Cr3Z5nyrw1KowjbW76xp8hkT/zJtYjAVZ9PS4l84KxIicbVvDOBpxG3yNddkuQcavmlH6G4wH9uM5DcnpKDncg== + dependencies: + "@aws-sdk/config-resolver" "3.110.0" + "@aws-sdk/credential-provider-imds" "3.110.0" + "@aws-sdk/node-config-provider" "3.110.0" + "@aws-sdk/property-provider" "3.110.0" + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/util-dynamodb@3.113.0": + version "3.113.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-dynamodb/-/util-dynamodb-3.113.0.tgz#42c54441e2b355a4560db93bd1c82c0cf18257be" + integrity sha512-BXmQWjsA3c4O55qu0RT1QgrMa/9i6EwCTwKbA/obbvzO6ZFqggpu4CjuM3NdhKAmZo72+vFcj4Xbll296DH2fQ== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-hex-encoding@3.109.0": + version "3.109.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-hex-encoding/-/util-hex-encoding-3.109.0.tgz#93b20acc27c0a1d7d80f653bf19d3dd01c2ccc65" + integrity sha512-s8CgTNrn3cLkrdiohfxLuOYPCanzvHn/aH5RW6DaMoeQiG5Hl9QUiP/WtdQ9QQx3xvpQFpmvxIaSBwSgFNLQxA== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-locate-window@^3.0.0": + version "3.55.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-locate-window/-/util-locate-window-3.55.0.tgz#a4136a20ee1bfcb73967a6614caf769ef79db070" + integrity sha512-0sPmK2JaJE2BbTcnvybzob/VrFKCXKfN4CUKcvn0yGg/me7Bz+vtzQRB3Xp+YSx+7OtWxzv63wsvHoAnXvgxgg== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-middleware@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-middleware/-/util-middleware-3.110.0.tgz#00a727273974f54424954235867c1ddb0f6dad56" + integrity sha512-PTVWrI5fA9d5hHJs6RzX2dIS2jRQ3uW073Fm0BePpQeDdZrEk+S5KNwRhUtpN6sdSV45vm6S9rrjZUG51qwGmA== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-uri-escape@3.55.0": + version "3.55.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-uri-escape/-/util-uri-escape-3.55.0.tgz#ee57743c628a1c9f942dfe73205ce890ec011916" + integrity sha512-mmdDLUpFCN2nkfwlLdOM54lTD528GiGSPN1qb8XtGLgZsJUmg3uJSFIN2lPeSbEwJB3NFjVas/rnQC48i7mV8w== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-user-agent-browser@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.110.0.tgz#e0643e6047aab5137540259a42fbfdc37ae4abee" + integrity sha512-rNdhmHDMV5dNJctqlBWimkZLJRB+x03DB+61pm+SKSFk6gPIVIvc1WNXqDFphkiswT4vA13ZUkGHzt+N4+noQQ== + dependencies: + "@aws-sdk/types" "3.110.0" + bowser "^2.11.0" + tslib "^2.3.1" + +"@aws-sdk/util-user-agent-node@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.110.0.tgz#750abd6bb14f25a11e09d764f724b0d0e1c1248b" + integrity sha512-OQ915TPCCBwZWz5Np8zkNWn7U6KvrTZfFoCOy/VIemK3dUqmnBZ7HqGpuZx8SwJ2R9JE1x+j0niYSJ5fWJZZKA== + dependencies: + "@aws-sdk/node-config-provider" "3.110.0" + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + +"@aws-sdk/util-utf8-browser@3.109.0", "@aws-sdk/util-utf8-browser@^3.0.0": + version "3.109.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.109.0.tgz#d013272e1981b23a4c84ac06f154db686c0cf84e" + integrity sha512-FmcGSz0v7Bqpl1SE8G1Gc0CtDpug+rvqNCG/szn86JApD/f5x8oByjbEiAyTU2ZH2VevUntx6EW68ulHyH+x+w== + dependencies: + tslib "^2.3.1" + +"@aws-sdk/util-utf8-node@3.109.0": + version "3.109.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-utf8-node/-/util-utf8-node-3.109.0.tgz#89e06d916f5b246c7265f59bac742973ac0767ac" + integrity sha512-Ti/ZBdvz2eSTElsucjzNmzpyg2MwfD1rXmxD0hZuIF8bPON/0+sZYnWd5CbDw9kgmhy28dmKue086tbZ1G0iLQ== + dependencies: + "@aws-sdk/util-buffer-from" "3.55.0" + tslib "^2.3.1" + +"@aws-sdk/util-waiter@3.110.0": + version "3.110.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-waiter/-/util-waiter-3.110.0.tgz#fa1321024f4ffb270f4b09b703802b1730220f0e" + integrity sha512-8dE6W6XYfjk1gx/aeb8NeLfMMLkLFhlV1lmKpFSBJhY8msajU8aQahTuykq5JW8QT/wCGbqbu7dH35SdX7kO+A== + dependencies: + "@aws-sdk/abort-controller" "3.110.0" + "@aws-sdk/types" "3.110.0" + tslib "^2.3.1" + "@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 +1635,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 +2041,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 +2201,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 +3348,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 +3467,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 +4233,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.1: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + tunnel-agent@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" From c59f07d4707b9e4f798668c1bede78093f2adfea Mon Sep 17 00:00:00 2001 From: Bryan Gaston Date: Wed, 17 Aug 2022 21:37:59 -0700 Subject: [PATCH 2/5] upgrade jest-dynamodb --- jest-dynamodb-config.js | 6 ++++-- package.json | 2 +- src/test/dynamo/Beyonce.test.ts | 7 +++---- src/test/dynamo/util.ts | 7 ++++++- yarn.lock | 29 ++++++++++++++++++++--------- 5 files changed, 34 insertions(+), 17 deletions(-) diff --git a/jest-dynamodb-config.js b/jest-dynamodb-config.js index 18a9ede..a9f831d 100644 --- a/jest-dynamodb-config.js +++ b/jest-dynamodb-config.js @@ -12,8 +12,10 @@ module.exports = async () => { port, clientConfig: { endpoint, - sslEnabled: false, - region: "local", + credentials: { + accessKeyId: "foo", + secretAccessKey: "baz", + }, }, options: ["-inMemory"], tables: [], diff --git a/package.json b/package.json index 8169b42..90b88e5 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "yargs": "^15.3.1" }, "devDependencies": { - "@shelf/jest-dynamodb": "^1.7.0", + "@shelf/jest-dynamodb": "3.3.0", "@types/jest": "^25.1.4", "@types/js-yaml": "^3.12.2", "@types/libsodium-wrappers": "^0.7.8", diff --git a/src/test/dynamo/Beyonce.test.ts b/src/test/dynamo/Beyonce.test.ts index 8ef9a3d..cd9d388 100644 --- a/src/test/dynamo/Beyonce.test.ts +++ b/src/test/dynamo/Beyonce.test.ts @@ -1,5 +1,4 @@ import { JayZ } from "@ginger.io/jay-z" -import { DynamoDB } from "@aws-sdk/client-dynamodb" import crypto from "crypto" import { Beyonce } from "../../main/dynamo/Beyonce" import { @@ -13,7 +12,7 @@ import { SongModel, table } from "./models" -import { createJayZ, setup } from "./util" +import { createDynamoDB, createJayZ, setup } from "./util" describe("Beyonce", () => { // Without encryption @@ -90,7 +89,7 @@ describe("Beyonce", () => { }) })) - const db = new Beyonce(table, new DynamoDB({ region: "us-west-2" })) + const db = new Beyonce(table, createDynamoDB()) ;(db as any).client.send = mockGet await db.get(MusicianModel.key({ id: musician.id }), { @@ -158,7 +157,7 @@ describe("Beyonce", () => { }) })) - const db = new Beyonce(table, new DynamoDB({ region: "us-west-2" })) + const db = new Beyonce(table, createDynamoDB()) ;(db as any).client.send = mockGet await db.batchGet({ diff --git a/src/test/dynamo/util.ts b/src/test/dynamo/util.ts index ce01879..8ccc58a 100644 --- a/src/test/dynamo/util.ts +++ b/src/test/dynamo/util.ts @@ -30,7 +30,12 @@ export async function setup(jayz?: JayZ): Promise { export function createDynamoDB(): DynamoDB { return new DynamoDB({ endpoint, - region: "us-west-2" // silly, but still need to specify region for LocalDynamo + tls: false, + region: "local-env", + credentials: { + accessKeyId: "foo", + secretAccessKey: "baz", + }, }) } diff --git a/yarn.lock b/yarn.lock index 7c09129..4a46dfa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1139,12 +1139,16 @@ "@types/yargs" "^15.0.0" chalk "^4.0.0" -"@shelf/jest-dynamodb@^1.7.0": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@shelf/jest-dynamodb/-/jest-dynamodb-1.7.0.tgz#94d6cc08984f2449db951a7ba8fcd1883f330c32" +"@shelf/jest-dynamodb@3.3.0": + version "3.3.0" + resolved "https://registry.yarnpkg.com/@shelf/jest-dynamodb/-/jest-dynamodb-3.3.0.tgz#9723306a3fdbe098de6d7f8f77d001902ad0a20c" + integrity sha512-YPwP8IxgMM+Gf/PcO+x7zm1vHR8sqVCa7nLAJkkKz+3rddOFchf10LXhfTxpM/wIfMTtUjKCdZlH9fhXWormUQ== dependencies: + "@aws-sdk/client-dynamodb" "3.113.0" + "@aws-sdk/lib-dynamodb" "3.113.0" + "@aws-sdk/util-dynamodb" "3.113.0" cwd "0.10.0" - debug "4.1.1" + debug "4.3.4" dynamodb-local "0.0.31" "@sinonjs/commons@^1.7.0": @@ -1923,11 +1927,12 @@ data-urls@^2.0.0: whatwg-mimetype "^2.3.0" whatwg-url "^8.0.0" -debug@4.1.1, debug@^4.1.0, debug@^4.1.1, debug@~4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" +debug@4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: - ms "^2.1.1" + ms "2.1.2" debug@^2.2.0, debug@^2.3.3: version "2.6.9" @@ -1935,6 +1940,12 @@ debug@^2.2.0, debug@^2.3.3: dependencies: ms "2.0.0" +debug@^4.1.0, debug@^4.1.1, debug@~4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + dependencies: + ms "^2.1.1" + decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -3359,7 +3370,7 @@ ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" -ms@^2.1.1: +ms@2.1.2, ms@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" From c520dc9232905c7db562aaafbe5b69ea415ca96f Mon Sep 17 00:00:00 2001 From: Bryan Gaston Date: Thu, 18 Aug 2022 16:10:38 -0700 Subject: [PATCH 3/5] convert UInt8Array to Buffer on returned items --- src/main/dynamo/Beyonce.ts | 5 +++-- src/main/dynamo/iterators/pagedIterator.ts | 6 +++--- src/main/dynamo/util.ts | 23 +++++++++++++++++++++- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/src/main/dynamo/Beyonce.ts b/src/main/dynamo/Beyonce.ts index 639f395..c24b527 100644 --- a/src/main/dynamo/Beyonce.ts +++ b/src/main/dynamo/Beyonce.ts @@ -17,7 +17,7 @@ import { ParallelScanConfig, ScanBuilder } from "./ScanBuilder" import { Table } from "./Table" import { ExtractKeyType, GroupedModels, TaggedModel } from "./types" import { updateItemProxy } from "./updateItemProxy" -import { decryptOrPassThroughItem, encryptOrPassThroughItem, MaybeEncryptedItem } from "./util" +import { decryptOrPassThroughItem, encryptOrPassThroughItem, formatDynamoDBItem, MaybeEncryptedItem } from "./util" export interface Options { jayz?: JayZ @@ -142,7 +142,8 @@ export class Beyonce { const items: ExtractKeyType[] = [] const unprocessedKeys: T[] = [] results.forEach((result) => { - items.push(...result.items) + const formattedItems = result.items.map(item => item ? formatDynamoDBItem(item) : item) + items.push(...formattedItems) unprocessedKeys.push(...result.unprocessedKeys) }) diff --git a/src/main/dynamo/iterators/pagedIterator.ts b/src/main/dynamo/iterators/pagedIterator.ts index 0ca2dc1..c3e6ef8 100644 --- a/src/main/dynamo/iterators/pagedIterator.ts +++ b/src/main/dynamo/iterators/pagedIterator.ts @@ -3,7 +3,7 @@ import { QueryCommandOutput } from "@aws-sdk/lib-dynamodb" import { CompositeError } from "../../CompositeError" import { groupModelsByType } from "../groupModelsByType" import { GroupedModels, Key, TaggedModel } from "../types" -import { decryptOrPassThroughItem } from "../util" +import { decryptOrPassThroughItem, formatDynamoDBItem } from "../util" import { InternalIteratorOptions } from "./types" export type PageResults = { @@ -21,8 +21,8 @@ export async function groupAllPages( if (errors.length > 0) { throw new CompositeError("Error(s) encountered trying to process interator page", errors) } - - results.push(...items) + const formattedItems = items.map(item => formatDynamoDBItem(item)) + results.push(...formattedItems) } return groupModelsByType(results, modelTags) diff --git a/src/main/dynamo/util.ts b/src/main/dynamo/util.ts index 462e9b3..979f86a 100644 --- a/src/main/dynamo/util.ts +++ b/src/main/dynamo/util.ts @@ -1,4 +1,5 @@ import { EncryptedJayZItem, JayZ } from "@ginger.io/jay-z" +import { NativeAttributeValue } from "@aws-sdk/util-dynamodb" export type MaybeEncryptedItem = EncryptedJayZItem, string> | (T & Record) @@ -28,6 +29,26 @@ export async function decryptOrPassThroughItem( await jayz.ready return jayz.decryptItem(item as EncryptedJayZItem) } else { - return item + return formatDynamoDBItem(item) } } + +/** + * The DynamoDB v3 lib returns items with values that are UInt8Array instead of Buffer, so this function + * converts those values to Buffers. + */ +export function formatDynamoDBItem>( + item: T +): T { + const formattedItem: Record = {} + + Object.entries(item).forEach(([key, value]) => { + if (value instanceof Uint8Array) { + formattedItem[key] = Buffer.from(value) + } else { + formattedItem[key] = value + } + }) + + return formattedItem as T +} From 30304636bc7bb2ef626f0730ceda98274595b9d9 Mon Sep 17 00:00:00 2001 From: Bryan Gaston Date: Thu, 18 Aug 2022 19:20:05 -0700 Subject: [PATCH 4/5] Upgrade Node to support new version of jest-dynamodb --- Dockerfile | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 8385cb0..6933e42 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,14 +1,14 @@ -# Needed for DynamoDB Local, which is used for unit tests -FROM openjdk:15-alpine3.11 +# Java JDK is needed for DynamoDB Local, which is used for unit tests +FROM amazoncorretto:18-alpine3.15 # And our own stuff goes here WORKDIR /usr/app COPY . ./ RUN apk add --update \ yarn \ - python \ - python-dev \ + python3 \ + python3-dev \ py-pip \ build-base \ nodejs \ - npm \ No newline at end of file + npm From 472d2e9bdd453193635b8857e40cc771b68478e3 Mon Sep 17 00:00:00 2001 From: Bryan Gaston Date: Thu, 18 Aug 2022 19:20:24 -0700 Subject: [PATCH 5/5] bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 90b88e5..44a11b4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@ginger.io/beyonce", - "version": "0.0.66", + "version": "0.0.67", "description": "Type-safe DynamoDB query builder for TypeScript. Designed with single-table architecture in mind.", "main": "dist/index.js", "types": "dist/index.d.ts",