diff --git a/.projen/deps.json b/.projen/deps.json index 94566f2..fab3298 100644 --- a/.projen/deps.json +++ b/.projen/deps.json @@ -22,7 +22,11 @@ }, { "name": "aws-cdk-lib", - "version": "2.1.0", + "version": "2.58.0", + "type": "build" + }, + { + "name": "aws-sdk", "type": "build" }, { @@ -112,7 +116,7 @@ }, { "name": "aws-cdk-lib", - "version": "^2.1.0", + "version": "^2.58.0", "type": "peer" }, { diff --git a/.projenrc.js b/.projenrc.js index 99d02df..1fa691a 100644 --- a/.projenrc.js +++ b/.projenrc.js @@ -2,14 +2,21 @@ const { awscdk } = require('projen'); const project = new awscdk.AwsCdkConstructLibrary({ author: 'John Ferlito', authorAddress: 'johnf@inodes.org', - cdkVersion: '2.1.0', + cdkVersion: '2.58.0', defaultReleaseBranch: 'main', name: 'cdk-cross-account-route53', + description: 'CDK Construct to allow creation of Route 53 records in a different account', repositoryUrl: 'https://github.com/johnf/cdk-cross-account-route53.git', - - // deps: [], /* Runtime dependencies of this module. */ - // description: undefined, /* The description is just a string that helps people understand the purpose of the package. */ - // devDeps: [], /* Build dependencies for this module. */ - // packageName: undefined, /* The "name" in package.json. */ + keywords: [ + 'aws', + 'aws-cdk', + 'awscdk', + 'cdk', + 'route53', + 'cross-account', + 'role', + 'records', + ], + devDeps: ['aws-sdk'], }); -project.synth(); \ No newline at end of file +project.synth(); diff --git a/API.md b/API.md new file mode 100644 index 0000000..b37588c --- /dev/null +++ b/API.md @@ -0,0 +1,378 @@ +# API Reference + +## Constructs + +### CrossAccountRoute53RecordSet + +#### Initializers + +```typescript +import { CrossAccountRoute53RecordSet } from 'cdk-cross-account-route53' + +new CrossAccountRoute53RecordSet(scope: Construct, id: string, props: CrossAccountRoute53RecordSetProps) +``` + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| scope | constructs.Construct | *No description.* | +| id | string | *No description.* | +| props | CrossAccountRoute53RecordSetProps | *No description.* | + +--- + +##### `scope`Required + +- *Type:* constructs.Construct + +--- + +##### `id`Required + +- *Type:* string + +--- + +##### `props`Required + +- *Type:* CrossAccountRoute53RecordSetProps + +--- + +#### Methods + +| **Name** | **Description** | +| --- | --- | +| toString | Returns a string representation of this construct. | + +--- + +##### `toString` + +```typescript +public toString(): string +``` + +Returns a string representation of this construct. + +#### Static Functions + +| **Name** | **Description** | +| --- | --- | +| isConstruct | Checks if `x` is a construct. | + +--- + +##### ~~`isConstruct`~~ + +```typescript +import { CrossAccountRoute53RecordSet } from 'cdk-cross-account-route53' + +CrossAccountRoute53RecordSet.isConstruct(x: any) +``` + +Checks if `x` is a construct. + +###### `x`Required + +- *Type:* any + +Any object. + +--- + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| node | constructs.Node | The tree node. | + +--- + +##### `node`Required + +```typescript +public readonly node: Node; +``` + +- *Type:* constructs.Node + +The tree node. + +--- + + +### CrossAccountRoute53Role + +#### Initializers + +```typescript +import { CrossAccountRoute53Role } from 'cdk-cross-account-route53' + +new CrossAccountRoute53Role(scope: Construct, id: string, props: CrossAccountRoute53RoleProps) +``` + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| scope | constructs.Construct | *No description.* | +| id | string | *No description.* | +| props | CrossAccountRoute53RoleProps | *No description.* | + +--- + +##### `scope`Required + +- *Type:* constructs.Construct + +--- + +##### `id`Required + +- *Type:* string + +--- + +##### `props`Required + +- *Type:* CrossAccountRoute53RoleProps + +--- + +#### Methods + +| **Name** | **Description** | +| --- | --- | +| toString | Returns a string representation of this construct. | + +--- + +##### `toString` + +```typescript +public toString(): string +``` + +Returns a string representation of this construct. + +#### Static Functions + +| **Name** | **Description** | +| --- | --- | +| isConstruct | Checks if `x` is a construct. | + +--- + +##### ~~`isConstruct`~~ + +```typescript +import { CrossAccountRoute53Role } from 'cdk-cross-account-route53' + +CrossAccountRoute53Role.isConstruct(x: any) +``` + +Checks if `x` is a construct. + +###### `x`Required + +- *Type:* any + +Any object. + +--- + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| node | constructs.Node | The tree node. | + +--- + +##### `node`Required + +```typescript +public readonly node: Node; +``` + +- *Type:* constructs.Node + +The tree node. + +--- + + +## Structs + +### CrossAccountRoute53RecordSetProps + +#### Initializer + +```typescript +import { CrossAccountRoute53RecordSetProps } from 'cdk-cross-account-route53' + +const crossAccountRoute53RecordSetProps: CrossAccountRoute53RecordSetProps = { ... } +``` + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| delegationRoleAccount | string | *No description.* | +| delegationRoleName | string | *No description.* | +| hostedZoneId | string | *No description.* | +| resourceRecordSets | any | *No description.* | + +--- + +##### `delegationRoleAccount`Required + +```typescript +public readonly delegationRoleAccount: string; +``` + +- *Type:* string + +--- + +##### `delegationRoleName`Required + +```typescript +public readonly delegationRoleName: string; +``` + +- *Type:* string + +--- + +##### `hostedZoneId`Required + +```typescript +public readonly hostedZoneId: string; +``` + +- *Type:* string + +--- + +##### `resourceRecordSets`Required + +```typescript +public readonly resourceRecordSets: any; +``` + +- *Type:* any + +--- + +### CrossAccountRoute53RoleProps + +#### Initializer + +```typescript +import { CrossAccountRoute53RoleProps } from 'cdk-cross-account-route53' + +const crossAccountRoute53RoleProps: CrossAccountRoute53RoleProps = { ... } +``` + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| assumedBy | aws-cdk-lib.aws_iam.IPrincipal | *No description.* | +| records | CrossAccountRoute53RolePropsRecord[] | *No description.* | +| roleName | string | *No description.* | +| zone | aws-cdk-lib.aws_route53.IHostedZone | *No description.* | + +--- + +##### `assumedBy`Required + +```typescript +public readonly assumedBy: IPrincipal; +``` + +- *Type:* aws-cdk-lib.aws_iam.IPrincipal + +--- + +##### `records`Required + +```typescript +public readonly records: CrossAccountRoute53RolePropsRecord[]; +``` + +- *Type:* CrossAccountRoute53RolePropsRecord[] + +--- + +##### `roleName`Required + +```typescript +public readonly roleName: string; +``` + +- *Type:* string + +--- + +##### `zone`Required + +```typescript +public readonly zone: IHostedZone; +``` + +- *Type:* aws-cdk-lib.aws_route53.IHostedZone + +--- + +### CrossAccountRoute53RolePropsRecord + +#### Initializer + +```typescript +import { CrossAccountRoute53RolePropsRecord } from 'cdk-cross-account-route53' + +const crossAccountRoute53RolePropsRecord: CrossAccountRoute53RolePropsRecord = { ... } +``` + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| domainNames | string \| string[] | *No description.* | +| actions | string[] | *No description.* | +| types | string[] | *No description.* | + +--- + +##### `domainNames`Required + +```typescript +public readonly domainNames: string | string[]; +``` + +- *Type:* string | string[] + +--- + +##### `actions`Optional + +```typescript +public readonly actions: string[]; +``` + +- *Type:* string[] + +--- + +##### `types`Optional + +```typescript +public readonly types: string[]; +``` + +- *Type:* string[] + +--- + + + diff --git a/README.md b/README.md index b3fa7dd..d436746 100644 --- a/README.md +++ b/README.md @@ -1 +1,119 @@ -# replace this \ No newline at end of file +# AWS CDK Cross Account Route53 + +AWS [CDK](https://aws.amazon.com/cdk/) Constructs that define: +- IAM role that can be used to allow discrete Route53 Record changes +- Cross Account Record construct to create Route53 cross account Route53 records + +These constructs allow you to create Route53 records where the zone exists in a separate AWS account to the Cloudformation Stack. + +## Getting started + +```shell +yarn add cdk-cross-account-route53 +``` + +First create the role in the stack for the AWS account which contains the hosted zone. + +```typescript +// DNS Stack +const zone = new route53.PublicHostedZone(this, 'HostedZone', { + zoneName: 'example.com', +}); + +new CrossAccountRoute53Role(this, 'WebRoute53Role', { + roleName: 'WebRoute53Role', + assumedBy: new iam.AccountPrincipal('22222222'), // Web Stack Account + zone, + records: [{ domainNames: 'www.example.com' }], + }); +``` + +Then in the child stack create the records + +```typescript +const hostedZoneId = 'Z12345'; // ID of the zone in the other account + +const distribution = new cloudfront.Distribution(this, 'Distribution', { + domainNames: ['example.com'], +}); + +new CrossAccountRoute53RecordSet(this, 'ARecord', { + delegationRoleName: 'WebRoute53Role', + delegationRoleAccount: '111111111', // The account that contains the zone and role + hostedZoneId, + resourceRecordSets: [{ + Name: `example.com`, + Type: 'A', + AliasTarget: { + DNSName: distribution.distributionDomainName, + HostedZoneId: 'Z2FDTNDATAQYW2', // Cloudfront Hosted Zone Id + EvaluateTargetHealth: false, + }, + }], +}); +``` + +## CrossAccountRoute53Role + +### Initializer +```typescript +new CrossAccountRoute53Role(scope: Construct, id: string, props: CrossAccountRoute53RoleProps) +``` + +*Parameters* + +* **scope** Construct +* **id** string +* **props** CrossAccountRoute53RoleProps + +### Construct Props + +| Name | Type | Description | +| ---- | ---- | ----------- | +| roleName | `string` | The role name | +| assumedBy | `iam.IPrincipal` | The principals that are allowed to assume the role | +| zone | `route53.IHostedZone` | The hosted zone. | +| records | `CrossAccountRoute53RolePropsRecord[]` | The records that can be created by this role | + +### CrossAccountRoute53RolePropsRecords + +| Name | Type | Description | +| ---- | ---- | ----------- | +| domainNames | `string \| string[]` | The names of the records that can be created or changed | +| types | `route53.RecordType[]` | The typepsof records that can be created. Default `['A', 'AAAA']` | +| actions | `'CREATE' \| 'UPSERT' \| 'DELETE'` | The allowed actions. Default `['CREATE', 'UPSERT', 'DELETE']` | + +## CrossAccountRoute53RecordSet + +### Initializer +```typescript +new CrossAccountRoute53RecordSet(scope: Construct, id: string, props: CrossAccountRoute53RecordSetProps) +``` + +*Parameters* + +* **scope** Construct +* **id** string +* **props** CrossAccountRoute53RecordSet + +### Construct Props + +| Name | Type | Description | +| ---- | ---- | ----------- | +| delegationRoleName | `string` | The role name created in the account with the hosted zone | +| delegationRoleAccount | `string` | The account identfier of the account with the hosted zone | +| hostedZoneId | `string` | The hosted zoned id | +| resourceRecordSets | `Route53.ResourceRecordSets` | The changes to be applied. These are in the same format as taken by [ChangeResourceRecordSets Action](https://docs.aws.amazon.com/Route53/latest/APIReference/API_ResourceRecordSet.html) | + +## Development Status + +These constructs will stay in `v0.x.x` for a while, to allow easier bug fixing & breaking changes _if absolutely needed_. +Once bugs are fixed (if any), the constructs will be published with `v1` major version and will be marked as stable. + +Only typescript has been tested. + +## Development + +* `npm run build` compile typescript to js +* `npm run watch` watch for changes and compile +* `npm run test` perform the jest unit tests diff --git a/package.json b/package.json index 8f97092..2676d6c 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "name": "cdk-cross-account-route53", + "description": "CDK Construct to allow creation of Route 53 records in a different account", "repository": { "type": "git", "url": "https://github.com/johnf/cdk-cross-account-route53.git" @@ -38,7 +39,8 @@ "@types/node": "^14", "@typescript-eslint/eslint-plugin": "^5", "@typescript-eslint/parser": "^5", - "aws-cdk-lib": "2.1.0", + "aws-cdk-lib": "2.58.0", + "aws-sdk": "^2.1286.0", "constructs": "10.0.5", "eslint": "^8", "eslint-import-resolver-node": "^0.3.6", @@ -58,11 +60,18 @@ "typescript": "^4.9.4" }, "peerDependencies": { - "aws-cdk-lib": "^2.1.0", + "aws-cdk-lib": "^2.58.0", "constructs": "^10.0.5" }, "keywords": [ - "cdk" + "aws", + "aws-cdk", + "awscdk", + "cdk", + "cross-account", + "records", + "role", + "route53" ], "main": "lib/index.js", "license": "Apache-2.0", diff --git a/src/cross-account-record-set-handler/index.ts b/src/cross-account-record-set-handler/index.ts new file mode 100644 index 0000000..7ca566a --- /dev/null +++ b/src/cross-account-record-set-handler/index.ts @@ -0,0 +1,64 @@ +// eslint-disable-next-line import/no-extraneous-dependencies +import { Credentials, Route53, STS } from 'aws-sdk'; + +interface ResourceProperties { + AssumeRoleArn: string; + HostedZoneId: string; + ResourceRecordSets: Route53.ResourceRecordSet[]; +} + +export async function handler(event: any /* : AWSLambda.CloudFormationCustomResourceEvent */) { + const resourceProps = event.ResourceProperties as unknown as ResourceProperties; + + switch (event.RequestType) { + case 'Create': + case 'Update': + return cfnEventHandler(resourceProps, false); + case 'Delete': + return cfnEventHandler(resourceProps, true); + } +} + +async function cfnEventHandler(props: ResourceProperties, isDeleteEvent: boolean) { + const { + AssumeRoleArn, + HostedZoneId, + } = props; + + const credentials = await getCrossAccountCredentials(AssumeRoleArn); + const route53 = new Route53({ credentials }); + + const Changes = props.ResourceRecordSets.map((set) => ({ + Action: isDeleteEvent ? 'DELETE' : 'UPSERT', + ResourceRecordSet: set, + })); + + await route53.changeResourceRecordSets({ + HostedZoneId, + ChangeBatch: { + Changes, + }, + }).promise(); +} + +async function getCrossAccountCredentials(roleArn: string): Promise { + const sts = new STS(); + const timestamp = (new Date()).getTime(); + + const { Credentials: assumedCredentials } = await sts + .assumeRole({ + RoleArn: roleArn, + RoleSessionName: `cross-account-record-set-${timestamp}`, + }) + .promise(); + + if (!assumedCredentials) { + throw Error('Error getting assume role credentials'); + } + + return new Credentials({ + accessKeyId: assumedCredentials.AccessKeyId, + secretAccessKey: assumedCredentials.SecretAccessKey, + sessionToken: assumedCredentials.SessionToken, + }); +} diff --git a/src/index.ts b/src/index.ts index 92c94b8..7324ea2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,131 @@ -export class Hello { - public sayHello() { - return 'hello, world!'; +import * as path from 'path'; + +import { CustomResource, CustomResourceProvider, CustomResourceProviderRuntime, Stack } from 'aws-cdk-lib'; + +import * as iam from 'aws-cdk-lib/aws-iam'; +import * as route53 from 'aws-cdk-lib/aws-route53'; +import { Construct } from 'constructs'; +// import { Route53 } from 'aws-sdk'; + +export type CrossAccountRoute53RolePropsRecordAction = 'CREATE' | 'UPSERT' | 'DELETE'; + +export interface CrossAccountRoute53RolePropsRecord { + readonly domainNames: string | string[]; + readonly types?: (keyof typeof route53.RecordType)[]; + readonly actions?: CrossAccountRoute53RolePropsRecordAction[]; +} + +export interface CrossAccountRoute53RoleProps { + readonly roleName: string; + readonly assumedBy: iam.IPrincipal; + readonly zone: route53.IHostedZone; + readonly records: CrossAccountRoute53RolePropsRecord[]; +} + +export class CrossAccountRoute53Role extends Construct { + constructor(scope: Construct, id: string, props: CrossAccountRoute53RoleProps) { + super(scope, id); + + const { roleName, assumedBy, zone, records } = props; + + const statements = records.map((record) => { + const domainNames = Array.isArray(record.domainNames) ? record.domainNames : [record.domainNames]; + + return new iam.PolicyStatement({ + actions: ['route53:ChangeResourceRecordSets'], + resources: [zone.hostedZoneArn], + conditions: { + 'ForAllValues:StringEquals': { + 'route53:ChangeResourceRecordSetsRecordTypes': record.types || ['A', 'AAAA'], + 'route53:ChangeResourceRecordSetsActions': record.actions || ['CREATE', 'UPSERT', 'DELETE'], + 'route53:ChangeResourceRecordSetsNormalizedRecordNames': domainNames.map((domainName) => this.normaliseDomainName(domainName)), + }, + }, + }); + }); + + statements.push(new iam.PolicyStatement({ + actions: ['route53:ListHostedZonesByName'], + resources: [zone.hostedZoneArn], + })); + + // Can we be more specific here? + statements.push(new iam.PolicyStatement({ + actions: ['route53:GetChange'], + resources: ['*'], + })); + + new iam.Role(this, id, { + roleName, + assumedBy, + inlinePolicies: { + delegation: new iam.PolicyDocument({ statements }), + }, + }); } -} \ No newline at end of file + + private normaliseDomainName(domainName: string): string { + return domainName + .replace(/\.$/, '') + .toLowerCase() + .split('').map((char) => { + if (char.match(/[a-z0-9_.-]/)) { + return char; + } + + const octal = '000' + char.charCodeAt(0).toString(8); + return `\\${octal.substring(octal.length - 3)}`; + }) + .join(''); + } +} + +export interface CrossAccountRoute53RecordSetProps { + readonly delegationRoleName: string; + readonly delegationRoleAccount: string; + readonly hostedZoneId: string; + readonly resourceRecordSets: any; // Route53.ResourceRecordSet[], +} + +export class CrossAccountRoute53RecordSet extends Construct { + constructor(scope: Construct, id: string, props: CrossAccountRoute53RecordSetProps) { + super(scope, id); + + const delegationRoleArn = Stack.of(this).formatArn({ + region: '', + service: 'iam', + account: props.delegationRoleAccount, + resource: 'role', + resourceName: props.delegationRoleName, + }); + + const customResourceType = 'Custom::CrossAccountRoute53RecordSet'; + + const provider = CustomResourceProvider.getOrCreateProvider(this, customResourceType, { + codeDirectory: path.join(__dirname, 'cross-account-record-set-handler'), + runtime: CustomResourceProviderRuntime.NODEJS_16_X, + }); + + const role = iam.Role.fromRoleArn(this, 'cross-account-record-set-handler-role', provider.roleArn); + + const addToPrinciplePolicyResult = role.addToPrincipalPolicy(new iam.PolicyStatement({ + effect: iam.Effect.ALLOW, + actions: ['sts:AssumeRole'], + resources: [delegationRoleArn], + })); + + const customResource = new CustomResource(this, 'CrossAccountRecordSetCustomResource', { + resourceType: customResourceType, + serviceToken: provider.serviceToken, + properties: { + AssumeRoleArn: delegationRoleArn, + HostedZoneId: props.hostedZoneId, + ResourceRecordSets: props.resourceRecordSets, + }, + }); + + if (addToPrinciplePolicyResult.policyDependable) { + customResource.node.addDependency(addToPrinciplePolicyResult.policyDependable); + } + } +} diff --git a/test/cross-account-route53.test.ts b/test/cross-account-route53.test.ts new file mode 100644 index 0000000..6c3df58 --- /dev/null +++ b/test/cross-account-route53.test.ts @@ -0,0 +1,85 @@ +import * as cdk from 'aws-cdk-lib'; +import { Capture, Template } from 'aws-cdk-lib/assertions'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import * as route53 from 'aws-cdk-lib/aws-route53'; +import { CrossAccountRoute53Role, CrossAccountRoute53RoleProps } from '../lib/index'; + +test('Role Created (defaults)', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'TestStack'); + + const props: CrossAccountRoute53RoleProps = { + roleName: 'TestRole', + assumedBy: new iam.AccountPrincipal('123456789012'), + zone: new route53.HostedZone(stack, 'HostedZone', { zoneName: 'inodes.org' }), + records: [ + { domainNames: 'test.inodes.org' }, + ], + }; + + new CrossAccountRoute53Role(stack, 'MyTestConstruct', props); + + const template = Template.fromStack(stack); + const policies = new Capture(); + // const assumedBy = new Capture(); + + template.hasResourceProperties('AWS::IAM::Role', { + RoleName: 'TestRole', + Policies: policies, + // AssumeRolePolicyDocument: assumedBy, + }); + // console.debug(assumedBy.asString()); + + const policy = policies.asArray()[0]; + const statements = policy.PolicyDocument.Statement; + + const recordStatement = statements.find((statement: any) => statement.Action === 'route53:ChangeResourceRecordSets'); + + expect(recordStatement).toBeDefined(); + const condition = recordStatement.Condition; + expect(condition).toBeDefined(); + + expect(condition['ForAllValues:StringEquals']['route53:ChangeResourceRecordSetsRecordTypes']).toEqual(['A', 'AAAA']); + expect(condition['ForAllValues:StringEquals']['route53:ChangeResourceRecordSetsActions']).toEqual(['CREATE', 'UPSERT', 'DELETE']); + expect(condition['ForAllValues:StringEquals']['route53:ChangeResourceRecordSetsNormalizedRecordNames']).toEqual(['test.inodes.org']); +}); + +test('Role Created (custom)', () => { + const app = new cdk.App(); + const stack = new cdk.Stack(app, 'TestStack'); + + const props: CrossAccountRoute53RoleProps = { + roleName: 'TestRole', + assumedBy: new iam.AccountPrincipal('123456789012'), + zone: new route53.HostedZone(stack, 'HostedZone', { zoneName: 'inodes.org' }), + records: [ + { domainNames: 'test%.inodes.org.', types: ['TXT'], actions: ['UPSERT'] }, + ], + }; + + new CrossAccountRoute53Role(stack, 'MyTestConstruct', props); + + const template = Template.fromStack(stack); + const policies = new Capture(); + // const assumedBy = new Capture(); + + template.hasResourceProperties('AWS::IAM::Role', { + RoleName: 'TestRole', + Policies: policies, + // AssumeRolePolicyDocument: assumedBy, + }); + // console.debug(assumedBy.asString()); + + const policy = policies.asArray()[0]; + const statements = policy.PolicyDocument.Statement; + + const recordStatement = statements.find((statement: any) => statement.Action === 'route53:ChangeResourceRecordSets'); + + expect(recordStatement).toBeDefined(); + const condition = recordStatement.Condition; + expect(condition).toBeDefined(); + + expect(condition['ForAllValues:StringEquals']['route53:ChangeResourceRecordSetsRecordTypes']).toEqual(['TXT']); + expect(condition['ForAllValues:StringEquals']['route53:ChangeResourceRecordSetsActions']).toEqual(['UPSERT']); + expect(condition['ForAllValues:StringEquals']['route53:ChangeResourceRecordSetsNormalizedRecordNames']).toEqual(['test\\045.inodes.org']); +}); diff --git a/test/hello.test.ts b/test/hello.test.ts deleted file mode 100644 index acbacd4..0000000 --- a/test/hello.test.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Hello } from '../src'; - -test('hello', () => { - expect(new Hello().sayHello()).toBe('hello, world!'); -}); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 02fb592..80d8008 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,6 +10,21 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" +"@aws-cdk/asset-awscli-v1@^2.2.30": + version "2.2.48" + resolved "https://registry.yarnpkg.com/@aws-cdk/asset-awscli-v1/-/asset-awscli-v1-2.2.48.tgz#b0aa6d082419f7e6562d3c268121facbcfdd3445" + integrity sha512-lBDe9flsUqKajlf3/7mCDqXdQp+onH5WCNBRSXyUHeXwLwJIVCVFEyW2WvPSxF1y6bgs9oG8AFAXLWRscomgrw== + +"@aws-cdk/asset-kubectl-v20@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@aws-cdk/asset-kubectl-v20/-/asset-kubectl-v20-2.1.1.tgz#d01c1efb867fb7f2cfd8c8b230b8eae16447e156" + integrity sha512-U1ntiX8XiMRRRH5J1IdC+1t5CE89015cwyt5U63Cpk0GnMlN5+h9WsWMlKlPXZR4rdq/m806JRlBMRpBUB2Dhw== + +"@aws-cdk/asset-node-proxy-agent-v5@^2.0.38": + version "2.0.38" + resolved "https://registry.yarnpkg.com/@aws-cdk/asset-node-proxy-agent-v5/-/asset-node-proxy-agent-v5-2.0.38.tgz#6765bef55f95220c52decb4adba8f75c1817b0f7" + integrity sha512-BBwAjORhuUkTGO3CxGS5Evcp5n20h9v06Sftn2R1DuSm8zIoUlPsNlI1HUk8XqYuoEI4aD7IKRQBLglv09ciJQ== + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" @@ -1219,21 +1234,45 @@ at-least-node@^1.0.0: resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== -aws-cdk-lib@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/aws-cdk-lib/-/aws-cdk-lib-2.1.0.tgz#2497484cfd4e2eeaba99b070bbfa54486d52ae34" - integrity sha512-W607G3aSrWpawpcqzIuUYKlU+grfvkbszyqikyVYqJgMHFCCQXq0S1ynPMzfQ49CwjlwZsu4LIsPM+dNR+Yj6g== +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + +aws-cdk-lib@2.58.0: + version "2.58.0" + resolved "https://registry.yarnpkg.com/aws-cdk-lib/-/aws-cdk-lib-2.58.0.tgz#83ab3b145db3c13d833b79c23dd98c71c0b7f23d" + integrity sha512-L5l5KvTO15ENhjB1aYi+Bl0CnWsOBN3ARb9E5qjXnaWQNMta0vWpSz/CjpkfHtJGXpyRXEg99vXOY5ocVMKrmw== dependencies: + "@aws-cdk/asset-awscli-v1" "^2.2.30" + "@aws-cdk/asset-kubectl-v20" "^2.1.1" + "@aws-cdk/asset-node-proxy-agent-v5" "^2.0.38" "@balena/dockerignore" "^1.0.2" case "1.6.3" fs-extra "^9.1.0" - ignore "^5.1.9" - jsonschema "^1.4.0" - minimatch "^3.0.4" + ignore "^5.2.1" + jsonschema "^1.4.1" + minimatch "^3.1.2" punycode "^2.1.1" - semver "^7.3.5" + semver "^7.3.8" yaml "1.10.2" +aws-sdk@^2.1286.0: + version "2.1286.0" + resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.1286.0.tgz#700d6081222a400c753aff640d679714a7ab3e87" + integrity sha512-CvkCD1+NSk2MPOutD2hEPhXDET/79w/gd9a359QWb9Ja0Fd4vVFXPkhlm1DTGzuwqFKGinpCMxDP4md7QPsVvw== + dependencies: + buffer "4.9.2" + events "1.1.1" + ieee754 "1.1.13" + jmespath "0.16.0" + querystring "0.2.0" + sax "1.2.1" + url "0.10.3" + util "^0.12.4" + uuid "8.0.0" + xml2js "0.4.19" + babel-jest@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444" @@ -1300,6 +1339,11 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +base64-js@^1.0.2: + version "1.5.1" + resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" + integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== + boxen@^7.0.0: version "7.0.1" resolved "https://registry.yarnpkg.com/boxen/-/boxen-7.0.1.tgz#cd84db4364a8bae65f1f016ce94a21ec2c832c16" @@ -1370,6 +1414,15 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== +buffer@4.9.2: + version "4.9.2" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" + integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== + dependencies: + base64-js "^1.0.2" + ieee754 "^1.1.4" + isarray "^1.0.0" + builtins@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/builtins/-/builtins-5.0.1.tgz#87f6db9ab0458be728564fa81d876d8d74552fa9" @@ -2418,6 +2471,11 @@ esutils@^2.0.2: resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== +events@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/events/-/events-1.1.1.tgz#9ebdb7635ad099c70dcc4c2a1f5004288e8bd924" + integrity sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw== + execa@^5.0.0: version "5.1.1" resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" @@ -2562,6 +2620,13 @@ flatted@^3.1.0, flatted@^3.2.7: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + form-data-encoder@^2.1.2: version "2.1.4" resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-2.1.4.tgz#261ea35d2a70d48d30ec7a9603130fa5515e9cd5" @@ -3064,6 +3129,16 @@ iconv-lite@^0.6.2: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" +ieee754@1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.1.13.tgz#ec168558e95aa181fd87d37f55c32bbcb6708b84" + integrity sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg== + +ieee754@^1.1.4: + version "1.2.1" + resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" + integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== + ignore-walk@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-6.0.0.tgz#1dd41c6eb4f661a49750a510a10c2cd934583fd8" @@ -3071,7 +3146,7 @@ ignore-walk@^6.0.0: dependencies: minimatch "^5.0.1" -ignore@^5.1.9, ignore@^5.2.0: +ignore@^5.2.0, ignore@^5.2.1: version "5.2.4" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.4.tgz#a291c0c6178ff1b960befe47fcdec301674a6324" integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== @@ -3159,6 +3234,14 @@ ip@^2.0.0: resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.0.tgz#4cf4ab182fee2314c75ede1276f8c80b479936da" integrity sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ== +is-arguments@^1.0.4: + version "1.1.1" + resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.1.tgz#15b3f88fda01f2a97fec84ca761a560f123efa9b" + integrity sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -3179,7 +3262,7 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-callable@^1.1.4, is-callable@^1.2.7: +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== @@ -3225,6 +3308,13 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== +is-generator-function@^1.0.7: + version "1.0.10" + resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" + integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== + dependencies: + has-tostringtag "^1.0.0" + is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" @@ -3328,6 +3418,17 @@ is-text-path@^1.0.1: dependencies: text-extensions "^1.0.0" +is-typed-array@^1.1.10, is-typed-array@^1.1.3: + version "1.1.10" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.10.tgz#36a5b5cb4189b575d1a3e4b08536bfb485801e3f" + integrity sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -3352,7 +3453,7 @@ is-yarn-global@^0.4.0: resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.4.1.tgz#b312d902b313f81e4eaf98b6361ba2b45cd694bb" integrity sha512-/kppl+R+LO5VmhYSEWARUFjodS25D68gvj8W7z0I7OWhUla5xWu8KL6CtB2V0R6yqhnRgbcaREMr4EEM6htLPQ== -isarray@~1.0.0: +isarray@^1.0.0, isarray@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" integrity sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ== @@ -3824,6 +3925,11 @@ jju@^1.1.0: resolved "https://registry.yarnpkg.com/jju/-/jju-1.4.0.tgz#a3abe2718af241a2b2904f84a625970f389ae32a" integrity sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA== +jmespath@0.16.0: + version "0.16.0" + resolved "https://registry.yarnpkg.com/jmespath/-/jmespath-0.16.0.tgz#b15b0a85dfd4d930d43e69ed605943c802785076" + integrity sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw== + js-sdsl@^4.1.4: version "4.2.0" resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.2.0.tgz#278e98b7bea589b8baaf048c20aeb19eb7ad09d0" @@ -4079,7 +4185,7 @@ jsonparse@^1.2.0, jsonparse@^1.3.1: resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== -jsonschema@^1.4.0: +jsonschema@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.4.1.tgz#cc4c3f0077fb4542982973d8a083b6b34f482dab" integrity sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ== @@ -5104,6 +5210,11 @@ psl@^1.1.33: resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== +punycode@1.3.2: + version "1.3.2" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" + integrity sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw== + punycode@^2.1.0, punycode@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" @@ -5121,6 +5232,11 @@ q@^1.5.1: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== +querystring@0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" + integrity sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g== + querystringify@^2.1.1: version "2.2.0" resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" @@ -5406,6 +5522,16 @@ safe-regex-test@^1.0.0: resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== +sax@1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.1.tgz#7b8e656190b228e81a66aea748480d828cd2d37a" + integrity sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA== + +sax@>=0.6.0: + version "1.2.4" + resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + saxes@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d" @@ -6175,11 +6301,35 @@ url-parse@^1.5.3: querystringify "^2.1.1" requires-port "^1.0.0" +url@0.10.3: + version "0.10.3" + resolved "https://registry.yarnpkg.com/url/-/url-0.10.3.tgz#021e4d9c7705f21bbf37d03ceb58767402774c64" + integrity sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ== + dependencies: + punycode "1.3.2" + querystring "0.2.0" + util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== +util@^0.12.4: + version "0.12.5" + resolved "https://registry.yarnpkg.com/util/-/util-0.12.5.tgz#5f17a6059b73db61a875668781a1c2b136bd6fbc" + integrity sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA== + dependencies: + inherits "^2.0.3" + is-arguments "^1.0.4" + is-generator-function "^1.0.7" + is-typed-array "^1.1.3" + which-typed-array "^1.1.2" + +uuid@8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.0.0.tgz#bc6ccf91b5ff0ac07bbcdbf1c7c4e150db4dbb6c" + integrity sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw== + uuid@^8.3.2: version "8.3.2" resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" @@ -6272,6 +6422,18 @@ which-boxed-primitive@^1.0.2: is-string "^1.0.5" is-symbol "^1.0.3" +which-typed-array@^1.1.2: + version "1.1.9" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.9.tgz#307cf898025848cf995e795e8423c7f337efbde6" + integrity sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + is-typed-array "^1.1.10" + which@^2.0.1, which@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -6363,6 +6525,14 @@ xml-name-validator@^3.0.0: resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a" integrity sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw== +xml2js@0.4.19: + version "0.4.19" + resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.19.tgz#686c20f213209e94abf0d1bcf1efaa291c7827a7" + integrity sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q== + dependencies: + sax ">=0.6.0" + xmlbuilder "~9.0.1" + xml@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/xml/-/xml-1.0.1.tgz#78ba72020029c5bc87b8a81a3cfcd74b4a2fc1e5" @@ -6384,6 +6554,11 @@ xmlbuilder@^15.1.1: resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5" integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== +xmlbuilder@~9.0.1: + version "9.0.7" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" + integrity sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ== + xmlchars@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"