From a17615ae53da196ff87a4bddd8c746e4cb106372 Mon Sep 17 00:00:00 2001 From: Chris Rybicki Date: Tue, 30 Jul 2024 18:56:27 -0400 Subject: [PATCH 1/8] feat: cloudv2 --- .github/workflows/canary.yaml | 33 + .github/workflows/cloudv2-pull.yaml | 29 + .github/workflows/cloudv2-release.yaml | 54 + .github/workflows/pull-request-lint.yaml | 1 + .mergify.yml | 6 + README.md | 1 + cloudv2/.gitignore | 2 + cloudv2/LICENSE | 21 + cloudv2/README.md | 23 + cloudv2/counter-aws.extern.d.ts | 6 + cloudv2/counter-aws.ts | 86 + cloudv2/counter.w | 235 +++ cloudv2/examples/counter.main.w | 17 + cloudv2/package-lock.json | 1971 ++++++++++++++++++++++ cloudv2/package.json | 21 + cloudv2/test/counter/dec.test.w | 47 + cloudv2/test/counter/inc.test.w | 47 + cloudv2/test/counter/initial.test.w | 21 + cloudv2/test/counter/peek.test.w | 18 + cloudv2/test/counter/set.test.w | 36 + cloudv2/util.w | 14 + 21 files changed, 2689 insertions(+) create mode 100644 .github/workflows/cloudv2-pull.yaml create mode 100644 .github/workflows/cloudv2-release.yaml create mode 100644 cloudv2/.gitignore create mode 100644 cloudv2/LICENSE create mode 100644 cloudv2/README.md create mode 100644 cloudv2/counter-aws.extern.d.ts create mode 100644 cloudv2/counter-aws.ts create mode 100644 cloudv2/counter.w create mode 100644 cloudv2/examples/counter.main.w create mode 100644 cloudv2/package-lock.json create mode 100644 cloudv2/package.json create mode 100644 cloudv2/test/counter/dec.test.w create mode 100644 cloudv2/test/counter/inc.test.w create mode 100644 cloudv2/test/counter/initial.test.w create mode 100644 cloudv2/test/counter/peek.test.w create mode 100644 cloudv2/test/counter/set.test.w create mode 100644 cloudv2/util.w diff --git a/.github/workflows/canary.yaml b/.github/workflows/canary.yaml index d5a1efdb..cd10abf7 100644 --- a/.github/workflows/canary.yaml +++ b/.github/workflows/canary.yaml @@ -103,6 +103,39 @@ jobs: cd checks wing test timeout_minutes: 5 + canary-cloudv2: + name: Test cloudv2 + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + sparse-checkout: cloudv2 + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 20.x + registry-url: https://registry.npmjs.org + - name: Install winglang and dependencies + uses: nick-fields/retry@v3 + with: + max_attempts: 3 + command: npm i -g winglang --loglevel verbose + timeout_minutes: 3 + - name: Install dependencies + uses: nick-fields/retry@v3 + with: + max_attempts: 3 + command: cd cloudv2 && npm i --include=dev --loglevel verbose + timeout_minutes: 3 + - name: Run tests + uses: nick-fields/retry@v3 + with: + max_attempts: 3 + command: |- + cd cloudv2 + wing test + timeout_minutes: 5 canary-dynamodb: name: Test dynamodb runs-on: ubuntu-latest diff --git a/.github/workflows/cloudv2-pull.yaml b/.github/workflows/cloudv2-pull.yaml new file mode 100644 index 00000000..fcd5c7a1 --- /dev/null +++ b/.github/workflows/cloudv2-pull.yaml @@ -0,0 +1,29 @@ +name: cloudv2-pull +on: + pull_request: + paths: + - cloudv2/** +jobs: + build-cloudv2: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + sparse-checkout: cloudv2 + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 20.x + registry-url: https://registry.npmjs.org + - name: Install winglang + run: npm i -g winglang + - name: Install dependencies + run: npm install --include=dev + working-directory: cloudv2 + - name: Test + run: wing test + working-directory: cloudv2 + - name: Pack + run: wing pack + working-directory: cloudv2 diff --git a/.github/workflows/cloudv2-release.yaml b/.github/workflows/cloudv2-release.yaml new file mode 100644 index 00000000..27b822d3 --- /dev/null +++ b/.github/workflows/cloudv2-release.yaml @@ -0,0 +1,54 @@ +name: cloudv2-release +on: + push: + branches: + - main + paths: + - cloudv2/** + - "!cloudv2/package-lock.json" +jobs: + build-cloudv2: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + sparse-checkout: cloudv2 + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 20.x + registry-url: https://registry.npmjs.org + - name: Install winglang + run: npm i -g winglang + - name: Install dependencies + run: npm install --include=dev + working-directory: cloudv2 + - name: Test + run: wing test + working-directory: cloudv2 + - name: Pack + run: wing pack + working-directory: cloudv2 + - name: Get package version + run: echo WINGLIB_VERSION=$(node -p "require('./package.json').version") >> + "$GITHUB_ENV" + working-directory: cloudv2 + - name: Publish + run: npm publish --access=public --registry https://registry.npmjs.org --tag + latest *.tgz + working-directory: cloudv2 + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Tag commit + uses: tvdias/github-tagger@v0.0.1 + with: + repo-token: ${{ secrets.PROJEN_GITHUB_TOKEN }} + tag: cloudv2-v${{ env.WINGLIB_VERSION }} + - name: GitHub release + uses: softprops/action-gh-release@v1 + with: + name: cloudv2 v${{ env.WINGLIB_VERSION }} + tag_name: cloudv2-v${{ env.WINGLIB_VERSION }} + files: "*.tgz" + token: ${{ secrets.PROJEN_GITHUB_TOKEN }} diff --git a/.github/workflows/pull-request-lint.yaml b/.github/workflows/pull-request-lint.yaml index 3dc24584..3661da8b 100644 --- a/.github/workflows/pull-request-lint.yaml +++ b/.github/workflows/pull-request-lint.yaml @@ -27,6 +27,7 @@ jobs: bedrock budget checks + cloudv2 cognito containers dynamodb diff --git a/.mergify.yml b/.mergify.yml index 50463e8d..690ef964 100644 --- a/.mergify.yml +++ b/.mergify.yml @@ -41,6 +41,9 @@ pull_request_rules: - -check-failure=build-checks - -check-pending=build-checks - -check-stale=build-checks + - -check-failure=build-cloudv2 + - -check-pending=build-cloudv2 + - -check-stale=build-cloudv2 - -check-failure=build-cognito - -check-pending=build-cognito - -check-stale=build-cognito @@ -144,6 +147,9 @@ pull_request_rules: - -check-failure=build-checks - -check-pending=build-checks - -check-stale=build-checks + - -check-failure=build-cloudv2 + - -check-pending=build-cloudv2 + - -check-stale=build-cloudv2 - -check-failure=build-cognito - -check-pending=build-cognito - -check-stale=build-cognito diff --git a/README.md b/README.md index fb60becc..dabe3471 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ publishing them for you. | [bedrock](./bedrock) | [@winglibs/bedrock](https://www.npmjs.com/package/@winglibs/bedrock) | sim, tf-aws | | [budget](./budget) | [@winglibs/budget](https://www.npmjs.com/package/@winglibs/budget) | sim, tf-aws | | [checks](./checks) | [@winglibs/checks](https://www.npmjs.com/package/@winglibs/checks) | * | +| [cloudv2](./cloudv2) | [@winglibs/cloudv2](https://www.npmjs.com/package/@winglibs/cloudv2) | sim, tf-aws | | [cognito](./cognito) | [@winglibs/cognito](https://www.npmjs.com/package/@winglibs/cognito) | sim, tf-aws | | [containers](./containers) | [@winglibs/containers](https://www.npmjs.com/package/@winglibs/containers) | sim, tf-aws | | [dynamodb](./dynamodb) | [@winglibs/dynamodb](https://www.npmjs.com/package/@winglibs/dynamodb) | sim, tf-aws | diff --git a/cloudv2/.gitignore b/cloudv2/.gitignore new file mode 100644 index 00000000..297fdef9 --- /dev/null +++ b/cloudv2/.gitignore @@ -0,0 +1,2 @@ +target/ +node_modules/ diff --git a/cloudv2/LICENSE b/cloudv2/LICENSE new file mode 100644 index 00000000..a875f479 --- /dev/null +++ b/cloudv2/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Wing + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/cloudv2/README.md b/cloudv2/README.md new file mode 100644 index 00000000..14a16d3f --- /dev/null +++ b/cloudv2/README.md @@ -0,0 +1,23 @@ +# cloudv2 + +## Prerequisites + +* [winglang](https://winglang.io). + +## Installation + +```sh +npm i @winglibs/cloudv2 +``` + +## Usage + +```js +bring cloudv2; + +let adder = new cloudv2.Adder(); +``` + +## License + +This library is licensed under the [MIT License](./LICENSE). diff --git a/cloudv2/counter-aws.extern.d.ts b/cloudv2/counter-aws.extern.d.ts new file mode 100644 index 00000000..ba46fccd --- /dev/null +++ b/cloudv2/counter-aws.extern.d.ts @@ -0,0 +1,6 @@ +export default interface extern { + _dec: (amount: number, key: string, tableName: string, hashKey: string, initial: number) => Promise, + _inc: (amount: number, key: string, tableName: string, hashKey: string, initial: number) => Promise, + _peek: (key: string, tableName: string, hashKey: string, initial: number) => Promise, + _set: (value: number, key: string, tableName: string, hashKey: string) => Promise, +} diff --git a/cloudv2/counter-aws.ts b/cloudv2/counter-aws.ts new file mode 100644 index 00000000..f588dacf --- /dev/null +++ b/cloudv2/counter-aws.ts @@ -0,0 +1,86 @@ +import { + UpdateItemCommand, + GetItemCommand, + DynamoDBClient, +} from "@aws-sdk/client-dynamodb"; + +import types from "./counter-aws.extern"; + +const AMOUNT_TOKEN = "amount"; +const INITIAL_VALUE_TOKEN = "initial"; +const VALUE_ATTRIBUTE = "counter_value"; +const SET_VALUE = "set_value"; + +const client = new DynamoDBClient(); + +export const _inc: types["_inc"] = async ( + amount, + key, + tableName, + hashKey, + initial +) => { + const command = new UpdateItemCommand({ + TableName: tableName, + Key: { [hashKey]: { S: key } }, + UpdateExpression: `SET ${VALUE_ATTRIBUTE} = if_not_exists(${VALUE_ATTRIBUTE}, :${INITIAL_VALUE_TOKEN}) + :${AMOUNT_TOKEN}`, + ExpressionAttributeValues: { + [`:${AMOUNT_TOKEN}`]: { N: `${amount}` }, + [`:${INITIAL_VALUE_TOKEN}`]: { N: `${initial}` }, + }, + ReturnValues: "UPDATED_NEW", + }); + + const result = await client.send(command); + let newValue = result.Attributes?.[VALUE_ATTRIBUTE].N; + if (!newValue) { + throw new Error(`${VALUE_ATTRIBUTE} attribute not found on table.`); + } + + // return the old value + return parseInt(newValue) - amount; +}; + +export const _dec: types["_dec"] = async ( + amount, + key, + tableName, + hashKey, + initial +) => { + return _inc(-1 * amount, key, tableName, hashKey, initial); +}; + +export const _peek: types["_peek"] = async ( + key, + tableName, + hashKey, + initial +) => { + const command = new GetItemCommand({ + TableName: tableName, + Key: { [hashKey]: { S: key } }, + }); + + const result = await client.send(command); + let value = result.Item?.[VALUE_ATTRIBUTE].N; + if (!value) { + return initial; + } + + return parseInt(value); +}; + +export const _set: types["_set"] = async (value, key, tableName, hashKey) => { + const command = new UpdateItemCommand({ + TableName: tableName, + Key: { [hashKey]: { S: key } }, + UpdateExpression: `SET ${VALUE_ATTRIBUTE} = :${SET_VALUE}`, + ExpressionAttributeValues: { + [`:${SET_VALUE}`]: { N: `${value}` }, + }, + ReturnValues: "UPDATED_NEW", + }); + + await client.send(command); +}; diff --git a/cloudv2/counter.w b/cloudv2/counter.w new file mode 100644 index 00000000..e0e7f846 --- /dev/null +++ b/cloudv2/counter.w @@ -0,0 +1,235 @@ +bring aws; +bring cloud; +bring fs; +bring ui; +bring util; +bring sim; + +bring "@cdktf/provider-aws" as tfaws; +bring "./util.w" as myutil; + +// TODO: Default values for struct fields - https://github.com/winglang/wing/issues/3121 + +pub struct CounterProps { + /// The initial value of the counter + /// @default 0 + initial: num?; +} + +interface ICounter { + /// Increments the counter atomically by a certain amount and returns the previous value. + /// - `amount` The amount to increment by (defaults to 1) + /// - `key` The key of the counter (defaults to "default") + inflight inc(amount: num?, key: str?): num; + + /// Decrements the counter atomically by a certain amount and returns the previous value. + /// - `amount` The amount to decrement by (defaults to 1) + /// - `key` The key of the counter (defaults to "default") + inflight dec(amount: num?, key: str?): num; + + /// Returns the current value of the counter. + /// - `key` The key of the counter (defaults to "default") + inflight peek(key: str?): num; + + /// Sets the value of the counter. + /// - `value` The new value of the counter + /// - `key` The key of the counter (defaults to "default") + inflight set(value: num, key: str?): void; +} + +pub class Counter impl ICounter { + inner: ICounter; + new(props: CounterProps) { + nodeof(this).title = "Counter"; + nodeof(this).description = "A distributed atomic counter"; + nodeof(this).icon = "calculator"; + nodeof(this).color = "lime"; + + let target = util.env("WING_TARGET"); + let id = nodeof(this).id; + if target == "sim" { + this.inner = new Counter_sim(props) as id; + } elif target == "tf-aws" { + this.inner = new Counter_tfaws(props) as id; + } else { + throw "Unsupported target: " + target; + } + nodeof(this.inner).hidden = true; + + this.addUI(); + } + + addUI() { + new ui.Field("Actual value", inflight () => { return "{this.peek()}"; }) as "ValueField"; + new ui.Button("Increment", inflight () => { this.inc(); }) as "IncrementButton"; + new ui.Button("Decrement", inflight () => { this.dec(); }) as "DecrementButton"; + new ui.Button("Reset", inflight () => { this.set(0); }) as "ResetButton"; + } + + pub inflight inc(amount: num?, key: str?): num { + return this.inner.inc(amount, key); + } + + pub inflight dec(amount: num?, key: str?): num { + return this.inner.dec(amount, key); + } + + pub inflight peek(key: str?): num { + return this.inner.peek(key); + } + + pub inflight set(value: num, key: str?): void { + this.inner.set(value, key); + } +} + +inflight class CounterBackend impl sim.IResource { + valuesFile: str; + initial: num; + statedir: str; + values: MutMap; + new(ctx: sim.IResourceContext, initial: num) { + this.initial = initial; + + this.statedir = ctx.statedir(); + this.valuesFile = fs.join(this.statedir, "values.json"); + if fs.exists(this.valuesFile) { + let data = fs.readJson(this.valuesFile); + // TODO: MutMap.fromJson(...) - https://github.com/winglang/wing/issues/1796 + this.values = unsafeCast(data["values"]); + } else { + this.values = {}; + } + } + + pub onStop() { + fs.writeJson(this.valuesFile, { "values": this.values.copy() }); + } + + pub inc(amount: num, key: str): num { + let prev = this.values.tryGet(key) ?? this.initial; + this.values[key] = prev + amount; + return prev; + } + + pub dec(amount: num, key: str): num { + let prev = this.values.tryGet(key) ?? this.initial; + this.values[key] = prev - amount; + return prev; + } + + pub peek(key: str): num { + return this.values.tryGet(key) ?? this.initial; + } + + pub set(value: num, key: str) { + this.values[key] = value; + } +} + +class Counter_sim impl ICounter { + initial: num; + backend: sim.Resource; + defaultKey: str; // TODO: module-level constants - https://github.com/winglang/wing/issues/3606 + new(props: CounterProps) { + this.initial = props.initial ?? 0; + this.backend = new sim.Resource(inflight (ctx) => { + return new CounterBackend(ctx, this.initial); + }) as "Backend"; + nodeof(this.backend).icon = "calculator"; + nodeof(this.backend).color = "lime"; + this.defaultKey = "default"; + } + + pub inflight inc(amount: num?, key: str?): num { + let response = this.backend.call("inc", Json [amount ?? 1, key ?? this.defaultKey]); + return num.fromJson(response); + } + + pub inflight dec(amount: num?, key: str?): num { + let response = this.backend.call("dec", Json [amount ?? 1, key ?? this.defaultKey]); + return num.fromJson(response); + } + + pub inflight peek(key: str?): num { + let response = this.backend.call("peek", Json [key ?? this.defaultKey]); + return num.fromJson(response); + } + + pub inflight set(value: num, key: str?): void { + this.backend.call("set", Json [value, key ?? this.defaultKey]); + } + + // TODO: rename this to std.IHost + pub onLift(host: std.IInflightHost, ops: Array) { + // TODO: check that host is sim.ISimHost + // if sim.Host.from(host) == nil { + // throw "Counter_sim can only be lifted by an ISimHost"; + // } + } +} + +class Counter_tfaws impl ICounter { + initial: num; + table: tfaws.dynamodbTable.DynamodbTable; + hashKey: str; + defaultKey: str; // TODO: module-level constants - https://github.com/winglang/wing/issues/3606 + envKey: str; + new(props: CounterProps) { + this.initial = props.initial ?? 0; + this.hashKey = "id"; + this.table = new tfaws.dynamodbTable.DynamodbTable( + name: myutil.friendlyName(this), + attribute: [{ name: this.hashKey, type: "S" }], + hashKey: this.hashKey, + billingMode: "PAY_PER_REQUEST", + ) as "Default"; + this.defaultKey = "default"; + this.envKey = "COUNTER_" + myutil.shortHash(this); + } + + extern "./counter-aws.ts" static inflight _inc(amount: num, key: str, tableName: str, hashKey: str, initial: num): num; + extern "./counter-aws.ts" static inflight _dec(amount: num, key: str, tableName: str, hashKey: str, initial: num): num; + extern "./counter-aws.ts" static inflight _peek(key: str, tableName: str, hashKey: str, initial: num): num; + extern "./counter-aws.ts" static inflight _set(value: num, key: str, tableName: str, hashKey: str): void; + + pub inflight inc(amount: num?, key: str?): num { + return Counter_tfaws._inc(amount ?? 1, key ?? this.defaultKey, this.table.name, this.hashKey, this.initial); + } + + pub inflight dec(amount: num?, key: str?): num { + return Counter_tfaws._dec(amount ?? 1, key ?? this.defaultKey, this.table.name, this.hashKey, this.initial); + } + + pub inflight peek(key: str?): num { + return Counter_tfaws._peek(key ?? this.defaultKey, this.table.name, this.hashKey, this.initial); + } + + pub inflight set(value: num, key: str?): void { + Counter_tfaws._set(value, key ?? this.defaultKey, this.table.name, this.hashKey); + } + + pub onLift(host: std.IInflightHost, ops: Array) { + // TODO: implement aws.Host.from + // if aws.Host.from(host) == nil { + // throw "Counter for \"tf-aws\" can only be lifted by a class implementing aws.IHost"; + // } + + // let awsHost: aws.IAwsInflightHost = aws.Host.from(host)!; + let awsHost: aws.IAwsInflightHost = unsafeCast(host); + let actions = MutArray[]; + if ops.contains("inc") || ops.contains("dec") || ops.contains("set") { + actions.push("dynamodb:UpdateItem"); + } + if ops.contains("peek") { + actions.push("dynamodb:GetItem"); + } + + awsHost.addPolicyStatements({ + actions: actions.copy(), + effect: aws.Effect.ALLOW, + resources: [this.table.arn], + }); + awsHost.addEnvironment(this.envKey, this.table.name); + } +} diff --git a/cloudv2/examples/counter.main.w b/cloudv2/examples/counter.main.w new file mode 100644 index 00000000..7deb638b --- /dev/null +++ b/cloudv2/examples/counter.main.w @@ -0,0 +1,17 @@ +bring cloud; +bring "../" as cloudv2; +bring expect; + +let bucket = new cloud.Bucket(); +let counter = new cloudv2.Counter(); + +let fn = new cloud.Function(inflight () => { + bucket.put("file-{counter.inc()}.txt", "Hello, World!"); +}); + +test "add files" { + fn.invoke(); + fn.invoke(); + fn.invoke(); + expect.equal(bucket.list(), ["file-0.txt", "file-1.txt", "file-2.txt"]); +} diff --git a/cloudv2/package-lock.json b/cloudv2/package-lock.json new file mode 100644 index 00000000..604ecf7d --- /dev/null +++ b/cloudv2/package-lock.json @@ -0,0 +1,1971 @@ +{ + "name": "@winglibs/cloudv2", + "version": "0.0.1", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@winglibs/cloudv2", + "version": "0.0.1", + "license": "MIT", + "peerDependencies": { + "@aws-sdk/client-dynamodb": "^3.621.0", + "@cdktf/provider-aws": "^19.28.0" + } + }, + "node_modules/@aws-crypto/sha256-browser": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", + "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", + "peer": true, + "dependencies": { + "@aws-crypto/sha256-js": "^5.2.0", + "@aws-crypto/supports-web-crypto": "^5.2.0", + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "@aws-sdk/util-locate-window": "^3.0.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "peer": true, + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "peer": true, + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", + "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", + "peer": true, + "dependencies": { + "@aws-crypto/util": "^5.2.0", + "@aws-sdk/types": "^3.222.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-crypto/supports-web-crypto": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", + "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", + "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", + "peer": true, + "dependencies": { + "@aws-sdk/types": "^3.222.0", + "@smithy/util-utf8": "^2.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", + "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", + "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", + "peer": true, + "dependencies": { + "@smithy/is-array-buffer": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", + "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", + "peer": true, + "dependencies": { + "@smithy/util-buffer-from": "^2.2.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/client-dynamodb": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-dynamodb/-/client-dynamodb-3.621.0.tgz", + "integrity": "sha512-aczOoVyufYwBCc/zZKJOP/xwbnojKQJ6Y8O7ZAZnxMPRyZXKXpoAxmlxLfOU6GUzXqzXdvj+Ir3VBd7MWB4KuQ==", + "peer": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.621.0", + "@aws-sdk/client-sts": "3.621.0", + "@aws-sdk/core": "3.621.0", + "@aws-sdk/credential-provider-node": "3.621.0", + "@aws-sdk/middleware-endpoint-discovery": "3.620.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.1", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.13", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.13", + "@smithy/util-defaults-mode-node": "^3.0.13", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "@smithy/util-waiter": "^3.1.2", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.621.0.tgz", + "integrity": "sha512-xpKfikN4u0BaUYZA9FGUMkkDmfoIP0Q03+A86WjqDWhcOoqNA1DkHsE4kZ+r064ifkPUfcNuUvlkVTEoBZoFjA==", + "peer": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.621.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.1", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.13", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.13", + "@smithy/util-defaults-mode-node": "^3.0.13", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/client-sso-oidc": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.621.0.tgz", + "integrity": "sha512-mMjk3mFUwV2Y68POf1BQMTF+F6qxt5tPu6daEUCNGC9Cenk3h2YXQQoS4/eSyYzuBiYk3vx49VgleRvdvkg8rg==", + "peer": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/core": "3.621.0", + "@aws-sdk/credential-provider-node": "3.621.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.1", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.13", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.13", + "@smithy/util-defaults-mode-node": "^3.0.13", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.621.0" + } + }, + "node_modules/@aws-sdk/client-sts": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/client-sts/-/client-sts-3.621.0.tgz", + "integrity": "sha512-707uiuReSt+nAx6d0c21xLjLm2lxeKc7padxjv92CIrIocnQSlJPxSCM7r5zBhwiahJA6MNQwmTl2xznU67KgA==", + "peer": true, + "dependencies": { + "@aws-crypto/sha256-browser": "5.2.0", + "@aws-crypto/sha256-js": "5.2.0", + "@aws-sdk/client-sso-oidc": "3.621.0", + "@aws-sdk/core": "3.621.0", + "@aws-sdk/credential-provider-node": "3.621.0", + "@aws-sdk/middleware-host-header": "3.620.0", + "@aws-sdk/middleware-logger": "3.609.0", + "@aws-sdk/middleware-recursion-detection": "3.620.0", + "@aws-sdk/middleware-user-agent": "3.620.0", + "@aws-sdk/region-config-resolver": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@aws-sdk/util-user-agent-browser": "3.609.0", + "@aws-sdk/util-user-agent-node": "3.614.0", + "@smithy/config-resolver": "^3.0.5", + "@smithy/core": "^2.3.1", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/hash-node": "^3.0.3", + "@smithy/invalid-dependency": "^3.0.3", + "@smithy/middleware-content-length": "^3.0.5", + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.13", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-body-length-browser": "^3.0.0", + "@smithy/util-body-length-node": "^3.0.0", + "@smithy/util-defaults-mode-browser": "^3.0.13", + "@smithy/util-defaults-mode-node": "^3.0.13", + "@smithy/util-endpoints": "^2.0.5", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/core": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.621.0.tgz", + "integrity": "sha512-CtOwWmDdEiINkGXD93iGfXjN0WmCp9l45cDWHHGa8lRgEDyhuL7bwd/pH5aSzj0j8SiQBG2k0S7DHbd5RaqvbQ==", + "peer": true, + "dependencies": { + "@smithy/core": "^2.3.1", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/signature-v4": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/util-middleware": "^3.0.3", + "fast-xml-parser": "4.4.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-env": { + "version": "3.620.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.620.1.tgz", + "integrity": "sha512-ExuILJ2qLW5ZO+rgkNRj0xiAipKT16Rk77buvPP8csR7kkCflT/gXTyzRe/uzIiETTxM7tr8xuO9MP/DQXqkfg==", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-http": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.621.0.tgz", + "integrity": "sha512-/jc2tEsdkT1QQAI5Dvoci50DbSxtJrevemwFsm0B73pwCcOQZ5ZwwSdVqGsPutzYzUVx3bcXg3LRL7jLACqRIg==", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/util-stream": "^3.1.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-ini": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.621.0.tgz", + "integrity": "sha512-0EWVnSc+JQn5HLnF5Xv405M8n4zfdx9gyGdpnCmAmFqEDHA8LmBdxJdpUk1Ovp/I5oPANhjojxabIW5f1uU0RA==", + "peer": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.621.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.621.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.621.0" + } + }, + "node_modules/@aws-sdk/credential-provider-node": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.621.0.tgz", + "integrity": "sha512-4JqpccUgz5Snanpt2+53hbOBbJQrSFq7E1sAAbgY6BKVQUsW5qyXqnjvSF32kDeKa5JpBl3bBWLZl04IadcPHw==", + "peer": true, + "dependencies": { + "@aws-sdk/credential-provider-env": "3.620.1", + "@aws-sdk/credential-provider-http": "3.621.0", + "@aws-sdk/credential-provider-ini": "3.621.0", + "@aws-sdk/credential-provider-process": "3.620.1", + "@aws-sdk/credential-provider-sso": "3.621.0", + "@aws-sdk/credential-provider-web-identity": "3.621.0", + "@aws-sdk/types": "3.609.0", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-process": { + "version": "3.620.1", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.620.1.tgz", + "integrity": "sha512-hWqFMidqLAkaV9G460+1at6qa9vySbjQKKc04p59OT7lZ5cO5VH5S4aI05e+m4j364MBROjjk2ugNvfNf/8ILg==", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-sso": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.621.0.tgz", + "integrity": "sha512-Kza0jcFeA/GEL6xJlzR2KFf1PfZKMFnxfGzJzl5yN7EjoGdMijl34KaRyVnfRjnCWcsUpBWKNIDk9WZVMY9yiw==", + "peer": true, + "dependencies": { + "@aws-sdk/client-sso": "3.621.0", + "@aws-sdk/token-providers": "3.614.0", + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/credential-provider-web-identity": { + "version": "3.621.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.621.0.tgz", + "integrity": "sha512-w7ASSyfNvcx7+bYGep3VBgC3K6vEdLmlpjT7nSIHxxQf+WSdvy+HynwJosrpZax0sK5q0D1Jpn/5q+r5lwwW6w==", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sts": "^3.621.0" + } + }, + "node_modules/@aws-sdk/endpoint-cache": { + "version": "3.572.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/endpoint-cache/-/endpoint-cache-3.572.0.tgz", + "integrity": "sha512-CzuRWMj/xtN9p9eP915nlPmlyniTzke732Ow/M60++gGgB3W+RtZyFftw3TEx+NzNhd1tH54dEcGiWdiNaBz3Q==", + "peer": true, + "dependencies": { + "mnemonist": "0.38.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-endpoint-discovery": { + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-endpoint-discovery/-/middleware-endpoint-discovery-3.620.0.tgz", + "integrity": "sha512-T6kuydHBF4BPP5CVH53Fze7c2b9rqxWP88XrGtmNMXXdY4sXur1v/itGdS2l3gqRjxKo0LsmjmuQm9zL4vGneQ==", + "peer": true, + "dependencies": { + "@aws-sdk/endpoint-cache": "3.572.0", + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-host-header": { + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.620.0.tgz", + "integrity": "sha512-VMtPEZwqYrII/oUkffYsNWY9PZ9xpNJpMgmyU0rlDQ25O1c0Hk3fJmZRe6pEkAJ0omD7kLrqGl1DUjQVxpd/Rg==", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-logger": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.609.0.tgz", + "integrity": "sha512-S62U2dy4jMDhDFDK5gZ4VxFdWzCtLzwbYyFZx2uvPYTECkepLUfzLic2BHg2Qvtu4QjX+oGE3P/7fwaGIsGNuQ==", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-recursion-detection": { + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.620.0.tgz", + "integrity": "sha512-nh91S7aGK3e/o1ck64sA/CyoFw+gAYj2BDOnoNa6ouyCrVJED96ZXWbhye/fz9SgmNUZR2g7GdVpiLpMKZoI5w==", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/middleware-user-agent": { + "version": "3.620.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.620.0.tgz", + "integrity": "sha512-bvS6etn+KsuL32ubY5D3xNof1qkenpbJXf/ugGXbg0n98DvDFQ/F+SMLxHgbnER5dsKYchNnhmtI6/FC3HFu/A==", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@aws-sdk/util-endpoints": "3.614.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/region-config-resolver": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.614.0.tgz", + "integrity": "sha512-vDCeMXvic/LU0KFIUjpC3RiSTIkkvESsEfbVHiHH0YINfl8HnEqR5rj+L8+phsCeVg2+LmYwYxd5NRz4PHxt5g==", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/token-providers": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.614.0.tgz", + "integrity": "sha512-okItqyY6L9IHdxqs+Z116y5/nda7rHxLvROxtAJdLavWTYDydxrZstImNgGWTeVdmc0xX2gJCI77UYUTQWnhRw==", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@aws-sdk/client-sso-oidc": "^3.614.0" + } + }, + "node_modules/@aws-sdk/types": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.609.0.tgz", + "integrity": "sha512-+Tqnh9w0h2LcrUsdXyT1F8mNhXz+tVYBtP19LpeEGntmvHwa2XzvLUCWpoIAIVsHp5+HdB2X9Sn0KAtmbFXc2Q==", + "peer": true, + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-endpoints": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.614.0.tgz", + "integrity": "sha512-wK2cdrXHH4oz4IomV/yrGkftU9A+ITB6nFL+rxxyO78is2ifHJpFdV4aqk4LSkXYPi6CXWNru/Dqc7yiKXgJPw==", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "@smithy/util-endpoints": "^2.0.5", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-locate-window": { + "version": "3.568.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.568.0.tgz", + "integrity": "sha512-3nh4TINkXYr+H41QaPelCceEB2FXP3fxp93YZXB/kqJvX0U9j0N0Uk45gvsjmEPzG8XxkPEeLIfT2I1M7A6Lig==", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@aws-sdk/util-user-agent-browser": { + "version": "3.609.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.609.0.tgz", + "integrity": "sha512-fojPU+mNahzQ0YHYBsx0ZIhmMA96H+ZIZ665ObU9tl+SGdbLneVZVikGve+NmHTQwHzwkFsZYYnVKAkreJLAtA==", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/types": "^3.3.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@aws-sdk/util-user-agent-node": { + "version": "3.614.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.614.0.tgz", + "integrity": "sha512-15ElZT88peoHnq5TEoEtZwoXTXRxNrk60TZNdpl/TUBJ5oNJ9Dqb5Z4ryb8ofN6nm9aFf59GVAerFDz8iUoHBA==", + "peer": true, + "dependencies": { + "@aws-sdk/types": "3.609.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "aws-crt": ">=1.0.0" + }, + "peerDependenciesMeta": { + "aws-crt": { + "optional": true + } + } + }, + "node_modules/@cdktf/provider-aws": { + "version": "19.28.0", + "resolved": "https://registry.npmjs.org/@cdktf/provider-aws/-/provider-aws-19.28.0.tgz", + "integrity": "sha512-7VNR/KOUtRySdDw/pIT1ZAHI+fdWZvmHO5XI5GmejmKR3UAt+kvQIFzsKcBkyYzmJUkmwoyVnG/RykUS99nqUg==", + "peer": true, + "engines": { + "node": ">= 18.12.0" + }, + "peerDependencies": { + "cdktf": "^0.20.0", + "constructs": "^10.3.0" + } + }, + "node_modules/@smithy/abort-controller": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-3.1.1.tgz", + "integrity": "sha512-MBJBiidoe+0cTFhyxT8g+9g7CeVccLM0IOKKUMCNQ1CNMJ/eIfoo0RTfVrXOONEI1UCN1W+zkiHSbzUNE9dZtQ==", + "peer": true, + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/config-resolver": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-3.0.5.tgz", + "integrity": "sha512-SkW5LxfkSI1bUC74OtfBbdz+grQXYiPYolyu8VfpLIjEoN/sHVBlLeGXMQ1vX4ejkgfv6sxVbQJ32yF2cl1veA==", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-config-provider": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/core": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@smithy/core/-/core-2.3.1.tgz", + "integrity": "sha512-BC7VMXx/1BCmRPCVzzn4HGWAtsrb7/0758EtwOGFJQrlSwJBEjCcDLNZLFoL/68JexYa2s+KmgL/UfmXdG6v1w==", + "peer": true, + "dependencies": { + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-retry": "^3.0.13", + "@smithy/middleware-serde": "^3.0.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/credential-provider-imds": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.0.tgz", + "integrity": "sha512-0SCIzgd8LYZ9EJxUjLXBmEKSZR/P/w6l7Rz/pab9culE/RWuqelAKGJvn5qUOl8BgX8Yj5HWM50A5hiB/RzsgA==", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/fetch-http-handler": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-3.2.4.tgz", + "integrity": "sha512-kBprh5Gs5h7ug4nBWZi1FZthdqSM+T7zMmsZxx0IBvWUn7dK3diz2SHn7Bs4dQGFDk8plDv375gzenDoNwrXjg==", + "peer": true, + "dependencies": { + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/hash-node": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-3.0.3.tgz", + "integrity": "sha512-2ctBXpPMG+B3BtWSGNnKELJ7SH9e4TNefJS0cd2eSkOOROeBnnVBnAy9LtJ8tY4vUEoe55N4CNPxzbWvR39iBw==", + "peer": true, + "dependencies": { + "@smithy/types": "^3.3.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/invalid-dependency": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-3.0.3.tgz", + "integrity": "sha512-ID1eL/zpDULmHJbflb864k72/SNOZCADRc9i7Exq3RUNJw6raWUSlFEQ+3PX3EYs++bTxZB2dE9mEHTQLv61tw==", + "peer": true, + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/is-array-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz", + "integrity": "sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ==", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-content-length": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-3.0.5.tgz", + "integrity": "sha512-ILEzC2eyxx6ncej3zZSwMpB5RJ0zuqH7eMptxC4KN3f+v9bqT8ohssKbhNR78k/2tWW+KS5Spw+tbPF4Ejyqvw==", + "peer": true, + "dependencies": { + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-endpoint": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-3.1.0.tgz", + "integrity": "sha512-5y5aiKCEwg9TDPB4yFE7H6tYvGFf1OJHNczeY10/EFF8Ir8jZbNntQJxMWNfeQjC1mxPsaQ6mR9cvQbf+0YeMw==", + "peer": true, + "dependencies": { + "@smithy/middleware-serde": "^3.0.3", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/url-parser": "^3.0.3", + "@smithy/util-middleware": "^3.0.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-retry": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-3.0.13.tgz", + "integrity": "sha512-zvCLfaRYCaUmjbF2yxShGZdolSHft7NNCTA28HVN9hKcEbOH+g5irr1X9s+in8EpambclGnevZY4A3lYpvDCFw==", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^3.1.4", + "@smithy/protocol-http": "^4.1.0", + "@smithy/service-error-classification": "^3.0.3", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-retry": "^3.0.3", + "tslib": "^2.6.2", + "uuid": "^9.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-serde": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-3.0.3.tgz", + "integrity": "sha512-puUbyJQBcg9eSErFXjKNiGILJGtiqmuuNKEYNYfUD57fUl4i9+mfmThtQhvFXU0hCVG0iEJhvQUipUf+/SsFdA==", + "peer": true, + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/middleware-stack": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-3.0.3.tgz", + "integrity": "sha512-r4klY9nFudB0r9UdSMaGSyjyQK5adUyPnQN/ZM6M75phTxOdnc/AhpvGD1fQUvgmqjQEBGCwpnPbDm8pH5PapA==", + "peer": true, + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/node-config-provider": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-3.1.4.tgz", + "integrity": "sha512-YvnElQy8HR4vDcAjoy7Xkx9YT8xZP4cBXcbJSgm/kxmiQu08DwUwj8rkGnyoJTpfl/3xYHH+d8zE+eHqoDCSdQ==", + "peer": true, + "dependencies": { + "@smithy/property-provider": "^3.1.3", + "@smithy/shared-ini-file-loader": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/node-http-handler": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-3.1.4.tgz", + "integrity": "sha512-+UmxgixgOr/yLsUxcEKGH0fMNVteJFGkmRltYFHnBMlogyFdpzn2CwqWmxOrfJELhV34v0WSlaqG1UtE1uXlJg==", + "peer": true, + "dependencies": { + "@smithy/abort-controller": "^3.1.1", + "@smithy/protocol-http": "^4.1.0", + "@smithy/querystring-builder": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/property-provider": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-3.1.3.tgz", + "integrity": "sha512-zahyOVR9Q4PEoguJ/NrFP4O7SMAfYO1HLhB18M+q+Z4KFd4V2obiMnlVoUFzFLSPeVt1POyNWneHHrZaTMoc/g==", + "peer": true, + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/protocol-http": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-4.1.0.tgz", + "integrity": "sha512-dPVoHYQ2wcHooGXg3LQisa1hH0e4y0pAddPMeeUPipI1tEOqL6A4N0/G7abeq+K8wrwSgjk4C0wnD1XZpJm5aA==", + "peer": true, + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/querystring-builder": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-3.0.3.tgz", + "integrity": "sha512-vyWckeUeesFKzCDaRwWLUA1Xym9McaA6XpFfAK5qI9DKJ4M33ooQGqvM4J+LalH4u/Dq9nFiC8U6Qn1qi0+9zw==", + "peer": true, + "dependencies": { + "@smithy/types": "^3.3.0", + "@smithy/util-uri-escape": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/querystring-parser": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-3.0.3.tgz", + "integrity": "sha512-zahM1lQv2YjmznnfQsWbYojFe55l0SLG/988brlLv1i8z3dubloLF+75ATRsqPBboUXsW6I9CPGE5rQgLfY0vQ==", + "peer": true, + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/service-error-classification": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-3.0.3.tgz", + "integrity": "sha512-Jn39sSl8cim/VlkLsUhRFq/dKDnRUFlfRkvhOJaUbLBXUsLRLNf9WaxDv/z9BjuQ3A6k/qE8af1lsqcwm7+DaQ==", + "peer": true, + "dependencies": { + "@smithy/types": "^3.3.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/shared-ini-file-loader": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.4.tgz", + "integrity": "sha512-qMxS4hBGB8FY2GQqshcRUy1K6k8aBWP5vwm8qKkCT3A9K2dawUwOIJfqh9Yste/Bl0J2lzosVyrXDj68kLcHXQ==", + "peer": true, + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/signature-v4": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-4.1.0.tgz", + "integrity": "sha512-aRryp2XNZeRcOtuJoxjydO6QTaVhxx/vjaR+gx7ZjaFgrgPRyZ3HCTbfwqYj6ZWEBHkCSUfcaymKPURaByukag==", + "peer": true, + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-middleware": "^3.0.3", + "@smithy/util-uri-escape": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/smithy-client": { + "version": "3.1.11", + "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-3.1.11.tgz", + "integrity": "sha512-l0BpyYkciNyMaS+PnFFz4aO5sBcXvGLoJd7mX9xrMBIm2nIQBVvYgp2ZpPDMzwjKCavsXu06iuCm0F6ZJZc6yQ==", + "peer": true, + "dependencies": { + "@smithy/middleware-endpoint": "^3.1.0", + "@smithy/middleware-stack": "^3.0.3", + "@smithy/protocol-http": "^4.1.0", + "@smithy/types": "^3.3.0", + "@smithy/util-stream": "^3.1.3", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/types": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-3.3.0.tgz", + "integrity": "sha512-IxvBBCTFDHbVoK7zIxqA1ZOdc4QfM5HM7rGleCuHi7L1wnKv5Pn69xXJQ9hgxH60ZVygH9/JG0jRgtUncE3QUA==", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/url-parser": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-3.0.3.tgz", + "integrity": "sha512-pw3VtZtX2rg+s6HMs6/+u9+hu6oY6U7IohGhVNnjbgKy86wcIsSZwgHrFR+t67Uyxvp4Xz3p3kGXXIpTNisq8A==", + "peer": true, + "dependencies": { + "@smithy/querystring-parser": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/util-base64": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-3.0.0.tgz", + "integrity": "sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ==", + "peer": true, + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-body-length-browser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz", + "integrity": "sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ==", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + } + }, + "node_modules/@smithy/util-body-length-node": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz", + "integrity": "sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA==", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-buffer-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz", + "integrity": "sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA==", + "peer": true, + "dependencies": { + "@smithy/is-array-buffer": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-config-provider": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz", + "integrity": "sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ==", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-browser": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.13.tgz", + "integrity": "sha512-ZIRSUsnnMRStOP6OKtW+gCSiVFkwnfQF2xtf32QKAbHR6ACjhbAybDvry+3L5qQYdh3H6+7yD/AiUE45n8mTTw==", + "peer": true, + "dependencies": { + "@smithy/property-provider": "^3.1.3", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "bowser": "^2.11.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-defaults-mode-node": { + "version": "3.0.13", + "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.13.tgz", + "integrity": "sha512-voUa8TFJGfD+U12tlNNLCDlXibt9vRdNzRX45Onk/WxZe7TS+hTOZouEZRa7oARGicdgeXvt1A0W45qLGYdy+g==", + "peer": true, + "dependencies": { + "@smithy/config-resolver": "^3.0.5", + "@smithy/credential-provider-imds": "^3.2.0", + "@smithy/node-config-provider": "^3.1.4", + "@smithy/property-provider": "^3.1.3", + "@smithy/smithy-client": "^3.1.11", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@smithy/util-endpoints": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-2.0.5.tgz", + "integrity": "sha512-ReQP0BWihIE68OAblC/WQmDD40Gx+QY1Ez8mTdFMXpmjfxSyz2fVQu3A4zXRfQU9sZXtewk3GmhfOHswvX+eNg==", + "peer": true, + "dependencies": { + "@smithy/node-config-provider": "^3.1.4", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-hex-encoding": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz", + "integrity": "sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ==", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-middleware": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-3.0.3.tgz", + "integrity": "sha512-l+StyYYK/eO3DlVPbU+4Bi06Jjal+PFLSMmlWM1BEwyLxZ3aKkf1ROnoIakfaA7mC6uw3ny7JBkau4Yc+5zfWw==", + "peer": true, + "dependencies": { + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-retry": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-3.0.3.tgz", + "integrity": "sha512-AFw+hjpbtVApzpNDhbjNG5NA3kyoMs7vx0gsgmlJF4s+yz1Zlepde7J58zpIRIsdjc+emhpAITxA88qLkPF26w==", + "peer": true, + "dependencies": { + "@smithy/service-error-classification": "^3.0.3", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-stream": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-3.1.3.tgz", + "integrity": "sha512-FIv/bRhIlAxC0U7xM1BCnF2aDRPq0UaelqBHkM2lsCp26mcBbgI0tCVTv+jGdsQLUmAMybua/bjDsSu8RQHbmw==", + "peer": true, + "dependencies": { + "@smithy/fetch-http-handler": "^3.2.4", + "@smithy/node-http-handler": "^3.1.4", + "@smithy/types": "^3.3.0", + "@smithy/util-base64": "^3.0.0", + "@smithy/util-buffer-from": "^3.0.0", + "@smithy/util-hex-encoding": "^3.0.0", + "@smithy/util-utf8": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-uri-escape": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz", + "integrity": "sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg==", + "peer": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-3.0.0.tgz", + "integrity": "sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA==", + "peer": true, + "dependencies": { + "@smithy/util-buffer-from": "^3.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@smithy/util-waiter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-3.1.2.tgz", + "integrity": "sha512-4pP0EV3iTsexDx+8PPGAKCQpd/6hsQBaQhqWzU4hqKPHN5epPsxKbvUTIiYIHTxaKt6/kEaqPBpu/ufvfbrRzw==", + "peer": true, + "dependencies": { + "@smithy/abort-controller": "^3.1.1", + "@smithy/types": "^3.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/bowser": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.11.0.tgz", + "integrity": "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==", + "peer": true + }, + "node_modules/cdktf": { + "version": "0.20.8", + "resolved": "https://registry.npmjs.org/cdktf/-/cdktf-0.20.8.tgz", + "integrity": "sha512-O4O5h0b1E6scc/tqq9EUIQGDbPmdrCQpdkPdbDtUHHzhZGtmFpIMc5MgP1SgB+EzAutnG2oUDefjCqWMnhDe9A==", + "bundleDependencies": [ + "archiver", + "json-stable-stringify", + "semver" + ], + "peer": true, + "dependencies": { + "archiver": "6.0.2", + "json-stable-stringify": "1.1.1", + "semver": "7.6.2" + }, + "peerDependencies": { + "constructs": "^10.3.0" + } + }, + "node_modules/cdktf/node_modules/archiver": { + "version": "6.0.2", + "inBundle": true, + "license": "MIT", + "peer": true, + "dependencies": { + "archiver-utils": "^4.0.1", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^5.0.1" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/cdktf/node_modules/archiver-utils": { + "version": "4.0.1", + "inBundle": true, + "license": "MIT", + "peer": true, + "dependencies": { + "glob": "^8.0.0", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/cdktf/node_modules/async": { + "version": "3.2.5", + "inBundle": true, + "license": "MIT", + "peer": true + }, + "node_modules/cdktf/node_modules/b4a": { + "version": "1.6.6", + "inBundle": true, + "license": "Apache-2.0", + "peer": true + }, + "node_modules/cdktf/node_modules/balanced-match": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT", + "peer": true + }, + "node_modules/cdktf/node_modules/bare-events": { + "version": "2.2.1", + "inBundle": true, + "license": "Apache-2.0", + "optional": true, + "peer": true + }, + "node_modules/cdktf/node_modules/brace-expansion": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/cdktf/node_modules/buffer-crc32": { + "version": "0.2.13", + "inBundle": true, + "license": "MIT", + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/cdktf/node_modules/call-bind": { + "version": "1.0.7", + "inBundle": true, + "license": "MIT", + "peer": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/cdktf/node_modules/compress-commons": { + "version": "5.0.3", + "inBundle": true, + "license": "MIT", + "peer": true, + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^5.0.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/cdktf/node_modules/core-util-is": { + "version": "1.0.3", + "inBundle": true, + "license": "MIT", + "peer": true + }, + "node_modules/cdktf/node_modules/crc-32": { + "version": "1.2.2", + "inBundle": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/cdktf/node_modules/crc32-stream": { + "version": "5.0.1", + "inBundle": true, + "license": "MIT", + "peer": true, + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/cdktf/node_modules/define-data-property": { + "version": "1.1.4", + "inBundle": true, + "license": "MIT", + "peer": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/cdktf/node_modules/es-define-property": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT", + "peer": true, + "dependencies": { + "get-intrinsic": "^1.2.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/cdktf/node_modules/es-errors": { + "version": "1.3.0", + "inBundle": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/cdktf/node_modules/fast-fifo": { + "version": "1.3.2", + "inBundle": true, + "license": "MIT", + "peer": true + }, + "node_modules/cdktf/node_modules/fs.realpath": { + "version": "1.0.0", + "inBundle": true, + "license": "ISC", + "peer": true + }, + "node_modules/cdktf/node_modules/function-bind": { + "version": "1.1.2", + "inBundle": true, + "license": "MIT", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/cdktf/node_modules/get-intrinsic": { + "version": "1.2.4", + "inBundle": true, + "license": "MIT", + "peer": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/cdktf/node_modules/glob": { + "version": "8.1.0", + "inBundle": true, + "license": "ISC", + "peer": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cdktf/node_modules/gopd": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT", + "peer": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/cdktf/node_modules/graceful-fs": { + "version": "4.2.11", + "inBundle": true, + "license": "ISC", + "peer": true + }, + "node_modules/cdktf/node_modules/has-property-descriptors": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT", + "peer": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/cdktf/node_modules/has-proto": { + "version": "1.0.3", + "inBundle": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/cdktf/node_modules/has-symbols": { + "version": "1.0.3", + "inBundle": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/cdktf/node_modules/hasown": { + "version": "2.0.2", + "inBundle": true, + "license": "MIT", + "peer": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/cdktf/node_modules/inflight": { + "version": "1.0.6", + "inBundle": true, + "license": "ISC", + "peer": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/cdktf/node_modules/inherits": { + "version": "2.0.4", + "inBundle": true, + "license": "ISC", + "peer": true + }, + "node_modules/cdktf/node_modules/isarray": { + "version": "2.0.5", + "inBundle": true, + "license": "MIT", + "peer": true + }, + "node_modules/cdktf/node_modules/json-stable-stringify": { + "version": "1.1.1", + "inBundle": true, + "license": "MIT", + "peer": true, + "dependencies": { + "call-bind": "^1.0.5", + "isarray": "^2.0.5", + "jsonify": "^0.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/cdktf/node_modules/jsonify": { + "version": "0.0.1", + "inBundle": true, + "license": "Public Domain", + "peer": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/cdktf/node_modules/lazystream": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT", + "peer": true, + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/cdktf/node_modules/lazystream/node_modules/isarray": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT", + "peer": true + }, + "node_modules/cdktf/node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "inBundle": true, + "license": "MIT", + "peer": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/cdktf/node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "inBundle": true, + "license": "MIT", + "peer": true + }, + "node_modules/cdktf/node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "inBundle": true, + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/cdktf/node_modules/lodash": { + "version": "4.17.21", + "inBundle": true, + "license": "MIT", + "peer": true + }, + "node_modules/cdktf/node_modules/minimatch": { + "version": "5.1.6", + "inBundle": true, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cdktf/node_modules/normalize-path": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/cdktf/node_modules/object-keys": { + "version": "1.1.1", + "inBundle": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/cdktf/node_modules/once": { + "version": "1.4.0", + "inBundle": true, + "license": "ISC", + "peer": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/cdktf/node_modules/process-nextick-args": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "peer": true + }, + "node_modules/cdktf/node_modules/queue-tick": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT", + "peer": true + }, + "node_modules/cdktf/node_modules/readable-stream": { + "version": "3.6.2", + "inBundle": true, + "license": "MIT", + "peer": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/cdktf/node_modules/readdir-glob": { + "version": "1.1.3", + "inBundle": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/cdktf/node_modules/safe-buffer": { + "version": "5.2.1", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "inBundle": true, + "license": "MIT", + "peer": true + }, + "node_modules/cdktf/node_modules/semver": { + "version": "7.6.2", + "inBundle": true, + "license": "ISC", + "peer": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cdktf/node_modules/set-function-length": { + "version": "1.2.2", + "inBundle": true, + "license": "MIT", + "peer": true, + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/cdktf/node_modules/streamx": { + "version": "2.16.1", + "inBundle": true, + "license": "MIT", + "peer": true, + "dependencies": { + "fast-fifo": "^1.1.0", + "queue-tick": "^1.0.1" + }, + "optionalDependencies": { + "bare-events": "^2.2.0" + } + }, + "node_modules/cdktf/node_modules/string_decoder": { + "version": "1.3.0", + "inBundle": true, + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/cdktf/node_modules/tar-stream": { + "version": "3.1.7", + "inBundle": true, + "license": "MIT", + "peer": true, + "dependencies": { + "b4a": "^1.6.4", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/cdktf/node_modules/util-deprecate": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT", + "peer": true + }, + "node_modules/cdktf/node_modules/wrappy": { + "version": "1.0.2", + "inBundle": true, + "license": "ISC", + "peer": true + }, + "node_modules/cdktf/node_modules/zip-stream": { + "version": "5.0.2", + "inBundle": true, + "license": "MIT", + "peer": true, + "dependencies": { + "archiver-utils": "^4.0.1", + "compress-commons": "^5.0.1", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/constructs": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/constructs/-/constructs-10.3.0.tgz", + "integrity": "sha512-vbK8i3rIb/xwZxSpTjz3SagHn1qq9BChLEfy5Hf6fB3/2eFbrwt2n9kHwQcS0CPTRBesreeAcsJfMq2229FnbQ==", + "peer": true, + "engines": { + "node": ">= 16.14.0" + } + }, + "node_modules/fast-xml-parser": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz", + "integrity": "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + }, + { + "type": "paypal", + "url": "https://paypal.me/naturalintelligence" + } + ], + "peer": true, + "dependencies": { + "strnum": "^1.0.5" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/mnemonist": { + "version": "0.38.3", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.3.tgz", + "integrity": "sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw==", + "peer": true, + "dependencies": { + "obliterator": "^1.6.1" + } + }, + "node_modules/obliterator": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-1.6.1.tgz", + "integrity": "sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig==", + "peer": true + }, + "node_modules/strnum": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", + "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "peer": true + }, + "node_modules/tslib": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", + "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", + "peer": true + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "peer": true, + "bin": { + "uuid": "dist/bin/uuid" + } + } + } +} diff --git a/cloudv2/package.json b/cloudv2/package.json new file mode 100644 index 00000000..f166d55e --- /dev/null +++ b/cloudv2/package.json @@ -0,0 +1,21 @@ +{ + "name": "@winglibs/cloudv2", + "description": "cloudv2 library for Wing", + "version": "0.0.1", + "repository": { + "type": "git", + "url": "https://github.com/winglang/winglibs.git", + "directory": "cloudv2" + }, + "wing": { + "platforms": [ + "sim", + "tf-aws" + ] + }, + "peerDependencies": { + "@aws-sdk/client-dynamodb": "^3.621.0", + "@cdktf/provider-aws": "^19.28.0" + }, + "license": "MIT" +} diff --git a/cloudv2/test/counter/dec.test.w b/cloudv2/test/counter/dec.test.w new file mode 100644 index 00000000..19c1fe31 --- /dev/null +++ b/cloudv2/test/counter/dec.test.w @@ -0,0 +1,47 @@ +bring cloud; +bring expect; + +// implicit initial (0) +let counter1 = new cloud.Counter() as "counter1"; +// explicit initial +let counter2 = new cloud.Counter(initial: -1) as "counter2"; + +test "dec()" { + // implicit decrement (-1) + let r0 = counter1.dec(); + expect.equal(r0, 0); + expect.equal(counter1.peek(), -1); + + // explicit decrement (positive int) + let r1 = counter1.dec(5); + expect.equal(r1, -1); + expect.equal(counter1.peek(), -6); + + // explicit decrement (negative int) + let r2 = counter1.dec(-4); + expect.equal(r2, -6); + expect.equal(counter1.peek(), -2); + + // explicit decrement (-0) + let r3 = counter1.dec(0); + expect.equal(r3, -2); + expect.equal(counter1.peek(), -2); + + // "dec() with custom key" + let key = "custom-key"; + + // explicit decrement (positive int) + let r4 = counter2.dec(5, key); + expect.equal(r4, -1); + expect.equal(counter2.peek(key), -6); + + // explicit decrement (negative int) + let r5 = counter2.dec(-4, key); + expect.equal(r5, -6); + expect.equal(counter2.peek(key), -2); + + // explicit decrement (-0) + let r6 = counter2.dec(0, key); + expect.equal(r6, -2); + expect.equal(counter2.peek(key), -2); +} diff --git a/cloudv2/test/counter/inc.test.w b/cloudv2/test/counter/inc.test.w new file mode 100644 index 00000000..fd5d0945 --- /dev/null +++ b/cloudv2/test/counter/inc.test.w @@ -0,0 +1,47 @@ +bring "../../" as cloud; +bring expect; + +// implicit initial (0) +let counter1 = new cloud.Counter() as "counter1"; +// explicit initial +let counter2 = new cloud.Counter(initial: -1) as "counter2"; + +test "inc()" { + // implicit increment (+1) + let r0 = counter1.inc(); + expect.equal(r0, 0); + expect.equal(counter1.peek(), 1); + + // explicit increment (positive int) + let r1 = counter1.inc(5); + expect.equal(r1, 1); + expect.equal(counter1.peek(), 6); + + // explicit increment (negative int) + let r2 = counter1.inc(-4); + expect.equal(r2, 6); + expect.equal(counter1.peek(), 2); + + // explicit increment (+0) + let r3 = counter1.inc(0); + expect.equal(r3, 2); + expect.equal(counter1.peek(), 2); + + // "inc() with custom key" + let key = "custom-key"; + + // explicit increment (positive int) + let r4 = counter2.inc(5, key); + expect.equal(r4, -1); + expect.equal(counter2.peek(key), 4); + + // explicit increment (negative int) + let r5 = counter2.inc(-4, key); + expect.equal(r5, 4); + expect.equal(counter2.peek(key), 0); + + // explicit increment (+0) + let r6 = counter2.inc(0, key); + expect.equal(r6, 0); + expect.equal(counter2.peek(key), 0); +} diff --git a/cloudv2/test/counter/initial.test.w b/cloudv2/test/counter/initial.test.w new file mode 100644 index 00000000..717b747d --- /dev/null +++ b/cloudv2/test/counter/initial.test.w @@ -0,0 +1,21 @@ +bring cloud; +bring expect; + +let counterA = new cloud.Counter() as "counterA"; +let counterB = new cloud.Counter(initial: 500) as "counterB"; +let counterC = new cloud.Counter(initial: -198) as "counterC"; + +expect.equal(counterA.initial, 0); +expect.equal(counterB.initial, 500); +expect.equal(counterC.initial, -198); + +test "initial" { + // "initial:default" + expect.equal(counterA.peek(), 0); + + // "initial:positive-value" + expect.equal(counterB.peek(), 500); + + // "initial:negative-value" + expect.equal(counterC.peek(), -198); +} diff --git a/cloudv2/test/counter/peek.test.w b/cloudv2/test/counter/peek.test.w new file mode 100644 index 00000000..24723408 --- /dev/null +++ b/cloudv2/test/counter/peek.test.w @@ -0,0 +1,18 @@ +bring cloud; +bring expect; + +let c = new cloud.Counter(); + +test "peek" { + expect.equal(c.peek(), 0); + expect.equal(c.peek(), 0); + c.inc(); + expect.equal(c.peek(), 1); + + // "key peek" + let key = "my-key"; + expect.equal(c.peek(key), 0); + expect.equal(c.peek(key), 0); + c.inc(nil, key); + expect.equal(c.peek(key), 1); +} diff --git a/cloudv2/test/counter/set.test.w b/cloudv2/test/counter/set.test.w new file mode 100644 index 00000000..1ecf5066 --- /dev/null +++ b/cloudv2/test/counter/set.test.w @@ -0,0 +1,36 @@ +bring cloud; +bring expect; + +// implicit initial (0) +let counter1 = new cloud.Counter() as "counter1"; +// explicit initial +let counter2 = new cloud.Counter(initial: -1) as "counter2"; + +test "set()" { + // set (positive int) + counter1.set(42); + expect.equal(counter1.peek(), 42); + + // set (negative int) + counter1.set(-100); + expect.equal(counter1.peek(), -100); + + // set (0) + counter1.set(0); + expect.equal(counter1.peek(), 0); + + // "set() with custom key" + let key = "custom-key"; + + // set (positive int) + counter2.set(42, key); + expect.equal(counter2.peek(key), 42); + + // set (negative int) + counter2.set(-100, key); + expect.equal(counter2.peek(key), -100); + + // set (0) + counter2.set(0, key); + expect.equal(counter2.peek(key), 0); +} diff --git a/cloudv2/util.w b/cloudv2/util.w new file mode 100644 index 00000000..db6e1933 --- /dev/null +++ b/cloudv2/util.w @@ -0,0 +1,14 @@ +bring "constructs" as constructs; + +pub class Util { + // TODO: private constructors - https://github.com/winglang/wing/issues/4377 + // TODO: expose ResourceNames class from SDK + pub static friendlyName(c: constructs.IConstruct): str { + return nodeof(c).id + Util.shortHash(c); + } + + pub static shortHash(c: constructs.IConstruct): str { + let hash = nodeof(c).addr; + return hash.substring(hash.length - 8); + } +} From 4d26cb3d54bd318c109c8f6cf751f4196a7b02ac Mon Sep 17 00:00:00 2001 From: Chris Rybicki Date: Tue, 30 Jul 2024 19:03:16 -0400 Subject: [PATCH 2/8] add author --- cloudv2/package.json | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cloudv2/package.json b/cloudv2/package.json index f166d55e..07599c47 100644 --- a/cloudv2/package.json +++ b/cloudv2/package.json @@ -1,6 +1,10 @@ { "name": "@winglibs/cloudv2", - "description": "cloudv2 library for Wing", + "description": "Standard cloud library for Wing", + "author": { + "email": "chrisr@wing.cloud", + "name": "Chris Rybicki" + }, "version": "0.0.1", "repository": { "type": "git", From 3910d49b6c5e3a87246ce0bcc14cf6afafd5110b Mon Sep 17 00:00:00 2001 From: Chris Rybicki Date: Tue, 30 Jul 2024 19:04:13 -0400 Subject: [PATCH 3/8] add missing test --- cloudv2/test/counter/aws-counter.test.w | 36 +++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 cloudv2/test/counter/aws-counter.test.w diff --git a/cloudv2/test/counter/aws-counter.test.w b/cloudv2/test/counter/aws-counter.test.w new file mode 100644 index 00000000..ad3959ba --- /dev/null +++ b/cloudv2/test/counter/aws-counter.test.w @@ -0,0 +1,36 @@ +bring cloud; +bring aws; +bring util; + +let target = util.env("WING_TARGET"); + +let counter = new cloud.Counter(initial: 1) as "aws-wing-counter"; + +let getCounterInfo = (c: cloud.Counter): Map? => { + if let counter = aws.Counter.from(c) { + return { + dynamoTableArn: counter.dynamoTableArn, + dynamoTableName: counter.dynamoTableName, + }; + } + return nil; +}; + +let counterInfo = getCounterInfo(counter); + +test "validates the AWS counter name" { + if let counter = counterInfo { + if target == "tf-aws" { + assert(counter.get("dynamoTableArn").contains("arn:aws:dynamodb:")); + assert(counter.get("dynamoTableArn").contains("aws-wing-counter")); + assert(counter.get("dynamoTableName").contains("aws-wing-counter")); + } else { + assert(counter.get("dynamoTableArn").contains("arn:aws:dynamodb:")); + assert(counter.get("dynamoTableArn").contains("awswingcounter")); + assert(counter.get("dynamoTableName").contains("awswingcounter")); + } + } else { + // If the test is not on AWS, it should not fail, so I am returning true. + assert(true); + } +} From 4979e9fd590660561652ba4020101375277d7dff Mon Sep 17 00:00:00 2001 From: Chris Rybicki Date: Tue, 30 Jul 2024 19:07:46 -0400 Subject: [PATCH 4/8] fix tests, comment out test momentarily --- cloudv2/counter.w | 2 + cloudv2/test/counter/aws-counter.test.w | 56 +++++++++++++------------ cloudv2/test/counter/dec.test.w | 2 +- cloudv2/test/counter/initial.test.w | 2 +- cloudv2/test/counter/peek.test.w | 2 +- cloudv2/test/counter/set.test.w | 2 +- 6 files changed, 35 insertions(+), 31 deletions(-) diff --git a/cloudv2/counter.w b/cloudv2/counter.w index e0e7f846..0ba04d1d 100644 --- a/cloudv2/counter.w +++ b/cloudv2/counter.w @@ -39,6 +39,7 @@ interface ICounter { pub class Counter impl ICounter { inner: ICounter; + pub initial: num; new(props: CounterProps) { nodeof(this).title = "Counter"; nodeof(this).description = "A distributed atomic counter"; @@ -47,6 +48,7 @@ pub class Counter impl ICounter { let target = util.env("WING_TARGET"); let id = nodeof(this).id; + this.initial = props.initial ?? 0; if target == "sim" { this.inner = new Counter_sim(props) as id; } elif target == "tf-aws" { diff --git a/cloudv2/test/counter/aws-counter.test.w b/cloudv2/test/counter/aws-counter.test.w index ad3959ba..acae8bf1 100644 --- a/cloudv2/test/counter/aws-counter.test.w +++ b/cloudv2/test/counter/aws-counter.test.w @@ -1,4 +1,4 @@ -bring cloud; +bring "../../" as cloud; bring aws; bring util; @@ -6,31 +6,33 @@ let target = util.env("WING_TARGET"); let counter = new cloud.Counter(initial: 1) as "aws-wing-counter"; -let getCounterInfo = (c: cloud.Counter): Map? => { - if let counter = aws.Counter.from(c) { - return { - dynamoTableArn: counter.dynamoTableArn, - dynamoTableName: counter.dynamoTableName, - }; - } - return nil; -}; +// TODO: implement aws.Counter.from -let counterInfo = getCounterInfo(counter); +// let getCounterInfo = (c: cloud.Counter): Map? => { +// if let counter = aws.Counter.from(c) { +// return { +// dynamoTableArn: counter.dynamoTableArn, +// dynamoTableName: counter.dynamoTableName, +// }; +// } +// return nil; +// }; -test "validates the AWS counter name" { - if let counter = counterInfo { - if target == "tf-aws" { - assert(counter.get("dynamoTableArn").contains("arn:aws:dynamodb:")); - assert(counter.get("dynamoTableArn").contains("aws-wing-counter")); - assert(counter.get("dynamoTableName").contains("aws-wing-counter")); - } else { - assert(counter.get("dynamoTableArn").contains("arn:aws:dynamodb:")); - assert(counter.get("dynamoTableArn").contains("awswingcounter")); - assert(counter.get("dynamoTableName").contains("awswingcounter")); - } - } else { - // If the test is not on AWS, it should not fail, so I am returning true. - assert(true); - } -} +// let counterInfo = getCounterInfo(counter); + +// test "validates the AWS counter name" { +// if let counter = counterInfo { +// if target == "tf-aws" { +// assert(counter.get("dynamoTableArn").contains("arn:aws:dynamodb:")); +// assert(counter.get("dynamoTableArn").contains("aws-wing-counter")); +// assert(counter.get("dynamoTableName").contains("aws-wing-counter")); +// } else { +// assert(counter.get("dynamoTableArn").contains("arn:aws:dynamodb:")); +// assert(counter.get("dynamoTableArn").contains("awswingcounter")); +// assert(counter.get("dynamoTableName").contains("awswingcounter")); +// } +// } else { +// // If the test is not on AWS, it should not fail, so I am returning true. +// assert(true); +// } +// } diff --git a/cloudv2/test/counter/dec.test.w b/cloudv2/test/counter/dec.test.w index 19c1fe31..e7b2ff83 100644 --- a/cloudv2/test/counter/dec.test.w +++ b/cloudv2/test/counter/dec.test.w @@ -1,4 +1,4 @@ -bring cloud; +bring "../../" as cloud; bring expect; // implicit initial (0) diff --git a/cloudv2/test/counter/initial.test.w b/cloudv2/test/counter/initial.test.w index 717b747d..7ee1ecb8 100644 --- a/cloudv2/test/counter/initial.test.w +++ b/cloudv2/test/counter/initial.test.w @@ -1,4 +1,4 @@ -bring cloud; +bring "../../" as cloud; bring expect; let counterA = new cloud.Counter() as "counterA"; diff --git a/cloudv2/test/counter/peek.test.w b/cloudv2/test/counter/peek.test.w index 24723408..5698dd2a 100644 --- a/cloudv2/test/counter/peek.test.w +++ b/cloudv2/test/counter/peek.test.w @@ -1,4 +1,4 @@ -bring cloud; +bring "../../" as cloud; bring expect; let c = new cloud.Counter(); diff --git a/cloudv2/test/counter/set.test.w b/cloudv2/test/counter/set.test.w index 1ecf5066..74796b56 100644 --- a/cloudv2/test/counter/set.test.w +++ b/cloudv2/test/counter/set.test.w @@ -1,4 +1,4 @@ -bring cloud; +bring "../../" as cloud; bring expect; // implicit initial (0) From 9f850d2ad0b8a170379fa681808397d140b4ca4c Mon Sep 17 00:00:00 2001 From: Chris Rybicki Date: Wed, 31 Jul 2024 20:16:40 -0400 Subject: [PATCH 5/8] fix aws bugs --- cloudv2/.gitignore | 1 + cloudv2/counter.w | 16 ++++++++++------ 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/cloudv2/.gitignore b/cloudv2/.gitignore index 297fdef9..b55fb0c3 100644 --- a/cloudv2/.gitignore +++ b/cloudv2/.gitignore @@ -1,2 +1,3 @@ target/ node_modules/ +*.snap.md diff --git a/cloudv2/counter.w b/cloudv2/counter.w index 0ba04d1d..2ca0bc51 100644 --- a/cloudv2/counter.w +++ b/cloudv2/counter.w @@ -177,6 +177,8 @@ class Counter_tfaws impl ICounter { hashKey: str; defaultKey: str; // TODO: module-level constants - https://github.com/winglang/wing/issues/3606 envKey: str; + tableName: str; + tableArn: str; new(props: CounterProps) { this.initial = props.initial ?? 0; this.hashKey = "id"; @@ -188,6 +190,8 @@ class Counter_tfaws impl ICounter { ) as "Default"; this.defaultKey = "default"; this.envKey = "COUNTER_" + myutil.shortHash(this); + this.tableName = this.table.name; + this.tableArn = this.table.arn; } extern "./counter-aws.ts" static inflight _inc(amount: num, key: str, tableName: str, hashKey: str, initial: num): num; @@ -196,19 +200,19 @@ class Counter_tfaws impl ICounter { extern "./counter-aws.ts" static inflight _set(value: num, key: str, tableName: str, hashKey: str): void; pub inflight inc(amount: num?, key: str?): num { - return Counter_tfaws._inc(amount ?? 1, key ?? this.defaultKey, this.table.name, this.hashKey, this.initial); + return Counter_tfaws._inc(amount ?? 1, key ?? this.defaultKey, this.tableName, this.hashKey, this.initial); } pub inflight dec(amount: num?, key: str?): num { - return Counter_tfaws._dec(amount ?? 1, key ?? this.defaultKey, this.table.name, this.hashKey, this.initial); + return Counter_tfaws._dec(amount ?? 1, key ?? this.defaultKey, this.tableName, this.hashKey, this.initial); } pub inflight peek(key: str?): num { - return Counter_tfaws._peek(key ?? this.defaultKey, this.table.name, this.hashKey, this.initial); + return Counter_tfaws._peek(key ?? this.defaultKey, this.tableName, this.hashKey, this.initial); } pub inflight set(value: num, key: str?): void { - Counter_tfaws._set(value, key ?? this.defaultKey, this.table.name, this.hashKey); + Counter_tfaws._set(value, key ?? this.defaultKey, this.tableName, this.hashKey); } pub onLift(host: std.IInflightHost, ops: Array) { @@ -230,8 +234,8 @@ class Counter_tfaws impl ICounter { awsHost.addPolicyStatements({ actions: actions.copy(), effect: aws.Effect.ALLOW, - resources: [this.table.arn], + resources: [this.tableArn], }); - awsHost.addEnvironment(this.envKey, this.table.name); + awsHost.addEnvironment(this.envKey, this.tableName); } } From 59c45c6160ec7850e0ea85197667011f71126192 Mon Sep 17 00:00:00 2001 From: Chris Rybicki Date: Wed, 31 Jul 2024 20:34:25 -0400 Subject: [PATCH 6/8] split into multiple files --- cloudv2/counter-aws.w | 97 ++++++++++++ cloudv2/counter-sim.w | 92 +++++++++++ cloudv2/counter-types.w | 28 ++++ cloudv2/counter.w | 199 +----------------------- cloudv2/test/counter/aws-counter.test.w | 53 ++++--- 5 files changed, 251 insertions(+), 218 deletions(-) create mode 100644 cloudv2/counter-aws.w create mode 100644 cloudv2/counter-sim.w create mode 100644 cloudv2/counter-types.w diff --git a/cloudv2/counter-aws.w b/cloudv2/counter-aws.w new file mode 100644 index 00000000..afe92ec0 --- /dev/null +++ b/cloudv2/counter-aws.w @@ -0,0 +1,97 @@ +bring aws; + +bring "./counter-types.w" as counter_types; +bring "@cdktf/provider-aws" as tfaws; +bring "./util.w" as myutil; + +pub interface IAwsCounter { + dynamoTableArn(): str; // TODO: support properties on interfaces - https://github.com/winglang/wing/issues/4961 + dynamoTableName(): str; +} + +pub class Counter_tfaws impl counter_types.ICounter, IAwsCounter { + initial: num; + table: tfaws.dynamodbTable.DynamodbTable; + hashKey: str; + defaultKey: str; // TODO: module-level constants - https://github.com/winglang/wing/issues/3606 + envKey: str; + tableName: str; + tableArn: str; + new(props: counter_types.CounterProps) { + this.initial = props.initial ?? 0; + this.hashKey = "id"; + this.table = new tfaws.dynamodbTable.DynamodbTable( + name: myutil.friendlyName(this), + attribute: [{ name: this.hashKey, type: "S" }], + hashKey: this.hashKey, + billingMode: "PAY_PER_REQUEST", + ) as "Default"; + this.defaultKey = "default"; + this.envKey = "COUNTER_" + myutil.shortHash(this); + this.tableName = this.table.name; + this.tableArn = this.table.arn; + } + + extern "./counter-aws.ts" static inflight _inc(amount: num, key: str, tableName: str, hashKey: str, initial: num): num; + extern "./counter-aws.ts" static inflight _dec(amount: num, key: str, tableName: str, hashKey: str, initial: num): num; + extern "./counter-aws.ts" static inflight _peek(key: str, tableName: str, hashKey: str, initial: num): num; + extern "./counter-aws.ts" static inflight _set(value: num, key: str, tableName: str, hashKey: str): void; + + pub inflight inc(amount: num?, key: str?): num { + return Counter_tfaws._inc(amount ?? 1, key ?? this.defaultKey, this.tableName, this.hashKey, this.initial); + } + + pub inflight dec(amount: num?, key: str?): num { + return Counter_tfaws._dec(amount ?? 1, key ?? this.defaultKey, this.tableName, this.hashKey, this.initial); + } + + pub inflight peek(key: str?): num { + return Counter_tfaws._peek(key ?? this.defaultKey, this.tableName, this.hashKey, this.initial); + } + + pub inflight set(value: num, key: str?): void { + Counter_tfaws._set(value, key ?? this.defaultKey, this.tableName, this.hashKey); + } + + pub onLift(host: std.IInflightHost, ops: Array) { + // TODO: implement aws.Host.from + // if aws.Host.from(host) == nil { + // throw "Counter for \"tf-aws\" can only be lifted by a class implementing aws.IHost"; + // } + + // let awsHost: aws.IAwsInflightHost = aws.Host.from(host)!; + let awsHost: aws.IAwsInflightHost = unsafeCast(host); + let actions = MutArray[]; + if ops.contains("inc") || ops.contains("dec") || ops.contains("set") { + actions.push("dynamodb:UpdateItem"); + } + if ops.contains("peek") { + actions.push("dynamodb:GetItem"); + } + + awsHost.addPolicyStatements({ + actions: actions.copy(), + effect: aws.Effect.ALLOW, + resources: [this.tableArn], + }); + awsHost.addEnvironment(this.envKey, this.tableName); + } + + pub dynamoTableArn(): str { + return this.tableArn; + } + + pub dynamoTableName(): str { + return this.tableName; + } +} + +pub class AwsCounter { + pub static from(c: counter_types.ICounter): IAwsCounter? { + let obj = unsafeCast(c); + if obj?.dynamoTableArn != nil && obj?.dynamoTableName != nil { + return obj; + } + return nil; + } +} diff --git a/cloudv2/counter-sim.w b/cloudv2/counter-sim.w new file mode 100644 index 00000000..6a7d3478 --- /dev/null +++ b/cloudv2/counter-sim.w @@ -0,0 +1,92 @@ +bring fs; +bring sim; + +bring "./counter-types.w" as counter_types; + +inflight class CounterBackend impl sim.IResource { + valuesFile: str; + initial: num; + statedir: str; + values: MutMap; + new(ctx: sim.IResourceContext, initial: num) { + this.initial = initial; + + this.statedir = ctx.statedir(); + this.valuesFile = fs.join(this.statedir, "values.json"); + if fs.exists(this.valuesFile) { + let data = fs.readJson(this.valuesFile); + // TODO: MutMap.fromJson(...) - https://github.com/winglang/wing/issues/1796 + this.values = unsafeCast(data["values"]); + } else { + this.values = {}; + } + } + + pub onStop() { + fs.writeJson(this.valuesFile, { "values": this.values.copy() }); + } + + pub inc(amount: num, key: str): num { + let prev = this.values.tryGet(key) ?? this.initial; + this.values[key] = prev + amount; + return prev; + } + + pub dec(amount: num, key: str): num { + let prev = this.values.tryGet(key) ?? this.initial; + this.values[key] = prev - amount; + return prev; + } + + pub peek(key: str): num { + return this.values.tryGet(key) ?? this.initial; + } + + pub set(value: num, key: str) { + this.values[key] = value; + } +} + +// TODO: internal access modifiers - https://github.com/winglang/wing/issues/4156 + +pub class Counter_sim impl counter_types.ICounter { + initial: num; + backend: sim.Resource; + defaultKey: str; // TODO: module-level constants - https://github.com/winglang/wing/issues/3606 + new(props: counter_types.CounterProps) { + this.initial = props.initial ?? 0; + this.backend = new sim.Resource(inflight (ctx) => { + return new CounterBackend(ctx, this.initial); + }) as "Backend"; + nodeof(this.backend).icon = "calculator"; + nodeof(this.backend).color = "lime"; + this.defaultKey = "default"; + } + + pub inflight inc(amount: num?, key: str?): num { + let response = this.backend.call("inc", Json [amount ?? 1, key ?? this.defaultKey]); + return num.fromJson(response); + } + + pub inflight dec(amount: num?, key: str?): num { + let response = this.backend.call("dec", Json [amount ?? 1, key ?? this.defaultKey]); + return num.fromJson(response); + } + + pub inflight peek(key: str?): num { + let response = this.backend.call("peek", Json [key ?? this.defaultKey]); + return num.fromJson(response); + } + + pub inflight set(value: num, key: str?): void { + this.backend.call("set", Json [value, key ?? this.defaultKey]); + } + + // TODO: rename this to std.IHost + pub onLift(host: std.IInflightHost, ops: Array) { + // TODO: check that host is sim.ISimHost + // if sim.Host.from(host) == nil { + // throw "Counter_sim can only be lifted by an ISimHost"; + // } + } +} diff --git a/cloudv2/counter-types.w b/cloudv2/counter-types.w new file mode 100644 index 00000000..5712b101 --- /dev/null +++ b/cloudv2/counter-types.w @@ -0,0 +1,28 @@ +// TODO: Default values for struct fields - https://github.com/winglang/wing/issues/3121 + +pub struct CounterProps { + /// The initial value of the counter + /// @default 0 + initial: num?; +} + +pub interface ICounter { + /// Increments the counter atomically by a certain amount and returns the previous value. + /// - `amount` The amount to increment by (defaults to 1) + /// - `key` The key of the counter (defaults to "default") + inflight inc(amount: num?, key: str?): num; + + /// Decrements the counter atomically by a certain amount and returns the previous value. + /// - `amount` The amount to decrement by (defaults to 1) + /// - `key` The key of the counter (defaults to "default") + inflight dec(amount: num?, key: str?): num; + + /// Returns the current value of the counter. + /// - `key` The key of the counter (defaults to "default") + inflight peek(key: str?): num; + + /// Sets the value of the counter. + /// - `value` The new value of the counter + /// - `key` The key of the counter (defaults to "default") + inflight set(value: num, key: str?): void; +} diff --git a/cloudv2/counter.w b/cloudv2/counter.w index 2ca0bc51..ef21803a 100644 --- a/cloudv2/counter.w +++ b/cloudv2/counter.w @@ -5,42 +5,14 @@ bring ui; bring util; bring sim; -bring "@cdktf/provider-aws" as tfaws; -bring "./util.w" as myutil; +bring "./counter-aws.w" as counter_aws; +bring "./counter-sim.w" as counter_sim; +bring "./counter-types.w" as counter_types; -// TODO: Default values for struct fields - https://github.com/winglang/wing/issues/3121 - -pub struct CounterProps { - /// The initial value of the counter - /// @default 0 - initial: num?; -} - -interface ICounter { - /// Increments the counter atomically by a certain amount and returns the previous value. - /// - `amount` The amount to increment by (defaults to 1) - /// - `key` The key of the counter (defaults to "default") - inflight inc(amount: num?, key: str?): num; - - /// Decrements the counter atomically by a certain amount and returns the previous value. - /// - `amount` The amount to decrement by (defaults to 1) - /// - `key` The key of the counter (defaults to "default") - inflight dec(amount: num?, key: str?): num; - - /// Returns the current value of the counter. - /// - `key` The key of the counter (defaults to "default") - inflight peek(key: str?): num; - - /// Sets the value of the counter. - /// - `value` The new value of the counter - /// - `key` The key of the counter (defaults to "default") - inflight set(value: num, key: str?): void; -} - -pub class Counter impl ICounter { - inner: ICounter; +pub class Counter impl counter_types.ICounter { + inner: counter_types.ICounter; pub initial: num; - new(props: CounterProps) { + new(props: counter_types.CounterProps) { nodeof(this).title = "Counter"; nodeof(this).description = "A distributed atomic counter"; nodeof(this).icon = "calculator"; @@ -50,9 +22,9 @@ pub class Counter impl ICounter { let id = nodeof(this).id; this.initial = props.initial ?? 0; if target == "sim" { - this.inner = new Counter_sim(props) as id; + this.inner = new counter_sim.Counter_sim(props) as id; } elif target == "tf-aws" { - this.inner = new Counter_tfaws(props) as id; + this.inner = new counter_aws.Counter_tfaws(props) as id; } else { throw "Unsupported target: " + target; } @@ -84,158 +56,3 @@ pub class Counter impl ICounter { this.inner.set(value, key); } } - -inflight class CounterBackend impl sim.IResource { - valuesFile: str; - initial: num; - statedir: str; - values: MutMap; - new(ctx: sim.IResourceContext, initial: num) { - this.initial = initial; - - this.statedir = ctx.statedir(); - this.valuesFile = fs.join(this.statedir, "values.json"); - if fs.exists(this.valuesFile) { - let data = fs.readJson(this.valuesFile); - // TODO: MutMap.fromJson(...) - https://github.com/winglang/wing/issues/1796 - this.values = unsafeCast(data["values"]); - } else { - this.values = {}; - } - } - - pub onStop() { - fs.writeJson(this.valuesFile, { "values": this.values.copy() }); - } - - pub inc(amount: num, key: str): num { - let prev = this.values.tryGet(key) ?? this.initial; - this.values[key] = prev + amount; - return prev; - } - - pub dec(amount: num, key: str): num { - let prev = this.values.tryGet(key) ?? this.initial; - this.values[key] = prev - amount; - return prev; - } - - pub peek(key: str): num { - return this.values.tryGet(key) ?? this.initial; - } - - pub set(value: num, key: str) { - this.values[key] = value; - } -} - -class Counter_sim impl ICounter { - initial: num; - backend: sim.Resource; - defaultKey: str; // TODO: module-level constants - https://github.com/winglang/wing/issues/3606 - new(props: CounterProps) { - this.initial = props.initial ?? 0; - this.backend = new sim.Resource(inflight (ctx) => { - return new CounterBackend(ctx, this.initial); - }) as "Backend"; - nodeof(this.backend).icon = "calculator"; - nodeof(this.backend).color = "lime"; - this.defaultKey = "default"; - } - - pub inflight inc(amount: num?, key: str?): num { - let response = this.backend.call("inc", Json [amount ?? 1, key ?? this.defaultKey]); - return num.fromJson(response); - } - - pub inflight dec(amount: num?, key: str?): num { - let response = this.backend.call("dec", Json [amount ?? 1, key ?? this.defaultKey]); - return num.fromJson(response); - } - - pub inflight peek(key: str?): num { - let response = this.backend.call("peek", Json [key ?? this.defaultKey]); - return num.fromJson(response); - } - - pub inflight set(value: num, key: str?): void { - this.backend.call("set", Json [value, key ?? this.defaultKey]); - } - - // TODO: rename this to std.IHost - pub onLift(host: std.IInflightHost, ops: Array) { - // TODO: check that host is sim.ISimHost - // if sim.Host.from(host) == nil { - // throw "Counter_sim can only be lifted by an ISimHost"; - // } - } -} - -class Counter_tfaws impl ICounter { - initial: num; - table: tfaws.dynamodbTable.DynamodbTable; - hashKey: str; - defaultKey: str; // TODO: module-level constants - https://github.com/winglang/wing/issues/3606 - envKey: str; - tableName: str; - tableArn: str; - new(props: CounterProps) { - this.initial = props.initial ?? 0; - this.hashKey = "id"; - this.table = new tfaws.dynamodbTable.DynamodbTable( - name: myutil.friendlyName(this), - attribute: [{ name: this.hashKey, type: "S" }], - hashKey: this.hashKey, - billingMode: "PAY_PER_REQUEST", - ) as "Default"; - this.defaultKey = "default"; - this.envKey = "COUNTER_" + myutil.shortHash(this); - this.tableName = this.table.name; - this.tableArn = this.table.arn; - } - - extern "./counter-aws.ts" static inflight _inc(amount: num, key: str, tableName: str, hashKey: str, initial: num): num; - extern "./counter-aws.ts" static inflight _dec(amount: num, key: str, tableName: str, hashKey: str, initial: num): num; - extern "./counter-aws.ts" static inflight _peek(key: str, tableName: str, hashKey: str, initial: num): num; - extern "./counter-aws.ts" static inflight _set(value: num, key: str, tableName: str, hashKey: str): void; - - pub inflight inc(amount: num?, key: str?): num { - return Counter_tfaws._inc(amount ?? 1, key ?? this.defaultKey, this.tableName, this.hashKey, this.initial); - } - - pub inflight dec(amount: num?, key: str?): num { - return Counter_tfaws._dec(amount ?? 1, key ?? this.defaultKey, this.tableName, this.hashKey, this.initial); - } - - pub inflight peek(key: str?): num { - return Counter_tfaws._peek(key ?? this.defaultKey, this.tableName, this.hashKey, this.initial); - } - - pub inflight set(value: num, key: str?): void { - Counter_tfaws._set(value, key ?? this.defaultKey, this.tableName, this.hashKey); - } - - pub onLift(host: std.IInflightHost, ops: Array) { - // TODO: implement aws.Host.from - // if aws.Host.from(host) == nil { - // throw "Counter for \"tf-aws\" can only be lifted by a class implementing aws.IHost"; - // } - - // let awsHost: aws.IAwsInflightHost = aws.Host.from(host)!; - let awsHost: aws.IAwsInflightHost = unsafeCast(host); - let actions = MutArray[]; - if ops.contains("inc") || ops.contains("dec") || ops.contains("set") { - actions.push("dynamodb:UpdateItem"); - } - if ops.contains("peek") { - actions.push("dynamodb:GetItem"); - } - - awsHost.addPolicyStatements({ - actions: actions.copy(), - effect: aws.Effect.ALLOW, - resources: [this.tableArn], - }); - awsHost.addEnvironment(this.envKey, this.tableName); - } -} diff --git a/cloudv2/test/counter/aws-counter.test.w b/cloudv2/test/counter/aws-counter.test.w index acae8bf1..67d3f398 100644 --- a/cloudv2/test/counter/aws-counter.test.w +++ b/cloudv2/test/counter/aws-counter.test.w @@ -1,5 +1,4 @@ bring "../../" as cloud; -bring aws; bring util; let target = util.env("WING_TARGET"); @@ -8,31 +7,31 @@ let counter = new cloud.Counter(initial: 1) as "aws-wing-counter"; // TODO: implement aws.Counter.from -// let getCounterInfo = (c: cloud.Counter): Map? => { -// if let counter = aws.Counter.from(c) { -// return { -// dynamoTableArn: counter.dynamoTableArn, -// dynamoTableName: counter.dynamoTableName, -// }; -// } -// return nil; -// }; +let getCounterInfo = (c: cloud.Counter): Map? => { + if let counter = cloud.AwsCounter.from(c) { + return { + dynamoTableArn: counter.dynamoTableArn(), + dynamoTableName: counter.dynamoTableName(), + }; + } + return nil; +}; -// let counterInfo = getCounterInfo(counter); +let counterInfo = getCounterInfo(counter); -// test "validates the AWS counter name" { -// if let counter = counterInfo { -// if target == "tf-aws" { -// assert(counter.get("dynamoTableArn").contains("arn:aws:dynamodb:")); -// assert(counter.get("dynamoTableArn").contains("aws-wing-counter")); -// assert(counter.get("dynamoTableName").contains("aws-wing-counter")); -// } else { -// assert(counter.get("dynamoTableArn").contains("arn:aws:dynamodb:")); -// assert(counter.get("dynamoTableArn").contains("awswingcounter")); -// assert(counter.get("dynamoTableName").contains("awswingcounter")); -// } -// } else { -// // If the test is not on AWS, it should not fail, so I am returning true. -// assert(true); -// } -// } +test "validates the AWS counter name" { + if let counter = counterInfo { + if target == "tf-aws" { + assert(counter.get("dynamoTableArn").contains("arn:aws:dynamodb:")); + assert(counter.get("dynamoTableArn").contains("aws-wing-counter")); + assert(counter.get("dynamoTableName").contains("aws-wing-counter")); + } else { + assert(counter.get("dynamoTableArn").contains("arn:aws:dynamodb:")); + assert(counter.get("dynamoTableArn").contains("awswingcounter")); + assert(counter.get("dynamoTableName").contains("awswingcounter")); + } + } else { + // If the test is not on AWS, it should not fail, so I am returning true. + assert(true); + } +} From f41a099d7d123ba054d20c75d5761e4f00ce8751 Mon Sep 17 00:00:00 2001 From: Chris Rybicki Date: Wed, 31 Jul 2024 20:38:37 -0400 Subject: [PATCH 7/8] update comments --- cloudv2/counter-aws.w | 2 ++ cloudv2/test/counter/aws-counter.test.w | 2 -- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cloudv2/counter-aws.w b/cloudv2/counter-aws.w index afe92ec0..5d478de6 100644 --- a/cloudv2/counter-aws.w +++ b/cloudv2/counter-aws.w @@ -86,6 +86,8 @@ pub class Counter_tfaws impl counter_types.ICounter, IAwsCounter { } } +// TODO: move this into an "aws" namespace + pub class AwsCounter { pub static from(c: counter_types.ICounter): IAwsCounter? { let obj = unsafeCast(c); diff --git a/cloudv2/test/counter/aws-counter.test.w b/cloudv2/test/counter/aws-counter.test.w index 67d3f398..c749dd99 100644 --- a/cloudv2/test/counter/aws-counter.test.w +++ b/cloudv2/test/counter/aws-counter.test.w @@ -5,8 +5,6 @@ let target = util.env("WING_TARGET"); let counter = new cloud.Counter(initial: 1) as "aws-wing-counter"; -// TODO: implement aws.Counter.from - let getCounterInfo = (c: cloud.Counter): Map? => { if let counter = cloud.AwsCounter.from(c) { return { From 40a61365e17103d43479b35b23e2a4d4647b4e48 Mon Sep 17 00:00:00 2001 From: Chris Rybicki Date: Wed, 31 Jul 2024 20:40:20 -0400 Subject: [PATCH 8/8] update README --- cloudv2/README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cloudv2/README.md b/cloudv2/README.md index 14a16d3f..c0285984 100644 --- a/cloudv2/README.md +++ b/cloudv2/README.md @@ -1,5 +1,10 @@ # cloudv2 +This module contains a Wing-based implementation of the built-in `cloud` library. It's a work in progresss as part of a migration for our internal codebase. + +Here are the resources available so far: +- Counter + ## Prerequisites * [winglang](https://winglang.io). @@ -13,9 +18,9 @@ npm i @winglibs/cloudv2 ## Usage ```js -bring cloudv2; +bring cloudv2 as cloud; -let adder = new cloudv2.Adder(); +let counter = new cloud.Counter(); ``` ## License