├── apps ├── app1 │ ├── test │ │ ├── hello.test.d.ts │ │ ├── hello.test.ts │ │ └── hello.test.js │ ├── bin │ │ ├── hello.d.ts │ │ ├── hello.ts │ │ └── hello.js │ ├── jest.config.js │ ├── lib │ │ ├── hello-stack.d.ts │ │ ├── hello-stack.ts │ │ └── hello-stack.js │ ├── buildspec.yml │ ├── README.md │ ├── package.json │ ├── tsconfig.json │ └── cdk.json ├── app2 │ ├── test │ │ ├── hello.test.d.ts │ │ ├── hello.test.ts │ │ └── hello.test.js │ ├── bin │ │ ├── hello.d.ts │ │ ├── hello.ts │ │ └── hello.js │ ├── jest.config.js │ ├── lib │ │ ├── hello-stack.d.ts │ │ ├── hello-stack.ts │ │ └── hello-stack.js │ ├── buildspec.yml │ ├── README.md │ ├── package.json │ ├── tsconfig.json │ └── cdk.json ├── app3 │ ├── test │ │ ├── hello.test.d.ts │ │ ├── hello.test.ts │ │ └── hello.test.js │ ├── bin │ │ ├── hello.d.ts │ │ ├── hello.ts │ │ └── hello.js │ ├── jest.config.js │ ├── lib │ │ ├── hello-stack.d.ts │ │ ├── hello-stack.ts │ │ └── hello-stack.js │ ├── buildspec.yml │ ├── README.md │ ├── package.json │ ├── tsconfig.json │ └── cdk.json ├── python-cdk-app │ ├── tests │ │ ├── __init__.py │ │ └── unit │ │ │ ├── __init__.py │ │ │ └── test_python_cdk_stack.py │ ├── python_cdk │ │ ├── __init__.py │ │ └── python_cdk_stack.py │ ├── .python-version │ ├── requirements-dev.txt │ ├── requirements.txt │ ├── .gitignore │ ├── buildspec.yml │ ├── source.bat │ ├── app.py │ ├── cdk.json │ └── README.md ├── app1.json ├── app2.json ├── app3.json └── python-cdk-app.json ├── assets ├── ui.png ├── styles.css ├── square-jelly-box.css └── pico.min.css ├── .gitignore ├── CODE_OF_CONDUCT.md ├── cfn-templates └── sqs-example.json ├── LICENSE ├── pipeline-assets └── codepipeline-service-role-policy.json ├── main.js ├── package.json ├── CONTRIBUTING.md ├── index.html ├── README.md ├── renderer.js └── preload.js /apps/app1/test/hello.test.d.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/app2/test/hello.test.d.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/app3/test/hello.test.d.ts: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/python-cdk-app/tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/python-cdk-app/python_cdk/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/python-cdk-app/tests/unit/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/python-cdk-app/.python-version: -------------------------------------------------------------------------------- 1 | 3.9.10 2 | -------------------------------------------------------------------------------- /apps/python-cdk-app/requirements-dev.txt: -------------------------------------------------------------------------------- 1 | pytest==6.2.5 2 | -------------------------------------------------------------------------------- /apps/app1/bin/hello.d.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | -------------------------------------------------------------------------------- /apps/app2/bin/hello.d.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | -------------------------------------------------------------------------------- /apps/app3/bin/hello.d.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | -------------------------------------------------------------------------------- /apps/python-cdk-app/requirements.txt: -------------------------------------------------------------------------------- 1 | aws-cdk-lib==2.60.0 2 | constructs>=10.0.0,<11.0.0 3 | -------------------------------------------------------------------------------- /assets/ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aws-samples/aws-cdk-in-electron/HEAD/assets/ui.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | out/ 4 | apps/pipeline* 5 | apps/wordpress* 6 | .vscode 7 | .github 8 | apps/*.zip 9 | electron-fiddle -------------------------------------------------------------------------------- /apps/python-cdk-app/.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | package-lock.json 3 | __pycache__ 4 | .pytest_cache 5 | .venv 6 | *.egg-info 7 | 8 | # CDK asset staging directory 9 | .cdk.staging 10 | cdk.out 11 | -------------------------------------------------------------------------------- /apps/app1/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | roots: ['/test'], 4 | testMatch: ['**/*.test.ts'], 5 | transform: { 6 | '^.+\\.tsx?$': 'ts-jest' 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /apps/app2/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | roots: ['/test'], 4 | testMatch: ['**/*.test.ts'], 5 | transform: { 6 | '^.+\\.tsx?$': 'ts-jest' 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /apps/app3/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | roots: ['/test'], 4 | testMatch: ['**/*.test.ts'], 5 | transform: { 6 | '^.+\\.tsx?$': 'ts-jest' 7 | } 8 | }; 9 | -------------------------------------------------------------------------------- /apps/app1/lib/hello-stack.d.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from 'aws-cdk-lib'; 2 | import { Construct } from 'constructs'; 3 | export declare class HelloStack extends cdk.Stack { 4 | constructor(scope: Construct, id: string, props?: cdk.StackProps); 5 | } 6 | -------------------------------------------------------------------------------- /apps/app2/lib/hello-stack.d.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from 'aws-cdk-lib'; 2 | import { Construct } from 'constructs'; 3 | export declare class HelloStack extends cdk.Stack { 4 | constructor(scope: Construct, id: string, props?: cdk.StackProps); 5 | } 6 | -------------------------------------------------------------------------------- /apps/app3/lib/hello-stack.d.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from 'aws-cdk-lib'; 2 | import { Construct } from 'constructs'; 3 | export declare class HelloStack extends cdk.Stack { 4 | constructor(scope: Construct, id: string, props?: cdk.StackProps); 5 | } 6 | -------------------------------------------------------------------------------- /apps/app1/buildspec.yml: -------------------------------------------------------------------------------- 1 | # buildspec.yml 2 | version: 0.2 3 | phases: 4 | install: 5 | runtime-versions: 6 | nodejs: 16 7 | commands: 8 | - npm install -g aws-cdk@latest 9 | - npm install 10 | build: 11 | commands: 12 | - npm run build 13 | - cdk deploy 14 | -------------------------------------------------------------------------------- /apps/app2/buildspec.yml: -------------------------------------------------------------------------------- 1 | # buildspec.yml 2 | version: 0.2 3 | phases: 4 | install: 5 | runtime-versions: 6 | nodejs: 16 7 | commands: 8 | - npm install -g aws-cdk@latest 9 | - npm install 10 | build: 11 | commands: 12 | - npm run build 13 | - cdk deploy 14 | -------------------------------------------------------------------------------- /apps/app3/buildspec.yml: -------------------------------------------------------------------------------- 1 | # buildspec.yml 2 | version: 0.2 3 | phases: 4 | install: 5 | runtime-versions: 6 | nodejs: 16 7 | commands: 8 | - npm install -g aws-cdk@latest 9 | - npm install 10 | build: 11 | commands: 12 | - npm run build 13 | - cdk deploy 14 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /apps/python-cdk-app/buildspec.yml: -------------------------------------------------------------------------------- 1 | # buildspec.yml 2 | version: 0.2 3 | phases: 4 | install: 5 | runtime-versions: 6 | nodejs: 16 7 | python: 3.9 8 | commands: 9 | - npm install aws-cdk -g 10 | - python -m venv .venv 11 | - source .venv/bin/activate 12 | - pip install -r requirements.txt 13 | build: 14 | commands: 15 | - cdk deploy 16 | -------------------------------------------------------------------------------- /apps/app1.json: -------------------------------------------------------------------------------- 1 | { 2 | "Stacks": [ 3 | { 4 | "name": "HelloStack1", 5 | "hasOutputs": false 6 | } 7 | ], 8 | "CodeBuildPolicy": { 9 | "Version": "2012-10-17", 10 | "Statement": [ 11 | { 12 | "Sid": "AllowAllAccess", 13 | "Effect": "Allow", 14 | "Action": "*", 15 | "Resource": "*" 16 | } 17 | ] 18 | } 19 | } -------------------------------------------------------------------------------- /apps/app2.json: -------------------------------------------------------------------------------- 1 | { 2 | "Stacks": [ 3 | { 4 | "name": "HelloStack2", 5 | "hasOutputs": false 6 | } 7 | ], 8 | "CodeBuildPolicy": { 9 | "Version": "2012-10-17", 10 | "Statement": [ 11 | { 12 | "Sid": "AllowAllAccess", 13 | "Effect": "Allow", 14 | "Action": "*", 15 | "Resource": "*" 16 | } 17 | ] 18 | } 19 | } -------------------------------------------------------------------------------- /apps/app3.json: -------------------------------------------------------------------------------- 1 | { 2 | "Stacks": [ 3 | { 4 | "name": "HelloStack3", 5 | "hasOutputs": false 6 | } 7 | ], 8 | "CodeBuildPolicy": { 9 | "Version": "2012-10-17", 10 | "Statement": [ 11 | { 12 | "Sid": "AllowAllAccess", 13 | "Effect": "Allow", 14 | "Action": "*", 15 | "Resource": "*" 16 | } 17 | ] 18 | } 19 | } -------------------------------------------------------------------------------- /apps/python-cdk-app.json: -------------------------------------------------------------------------------- 1 | { 2 | "Stacks": [ 3 | { 4 | "name": "PythonCdkStack", 5 | "hasOutputs": true 6 | } 7 | ], 8 | "CodeBuildPolicy": { 9 | "Version": "2012-10-17", 10 | "Statement": [ 11 | { 12 | "Sid": "AllowAllAccess", 13 | "Effect": "Allow", 14 | "Action": "*", 15 | "Resource": "*" 16 | } 17 | ] 18 | } 19 | } -------------------------------------------------------------------------------- /apps/python-cdk-app/source.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem The sole purpose of this script is to make the command 4 | rem 5 | rem source .venv/bin/activate 6 | rem 7 | rem (which activates a Python virtualenv on Linux or Mac OS X) work on Windows. 8 | rem On Windows, this command just runs this batch file (the argument is ignored). 9 | rem 10 | rem Now we don't need to document a Windows command for activating a virtualenv. 11 | 12 | echo Executing .venv\Scripts\activate.bat for you 13 | .venv\Scripts\activate.bat 14 | -------------------------------------------------------------------------------- /apps/app1/lib/hello-stack.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from 'aws-cdk-lib'; 2 | import { Construct } from 'constructs'; 3 | // import * as sqs from 'aws-cdk-lib/aws-sqs'; 4 | 5 | export class HelloStack extends cdk.Stack { 6 | constructor(scope: Construct, id: string, props?: cdk.StackProps) { 7 | super(scope, id, props); 8 | 9 | // The code that defines your stack goes here 10 | 11 | // example resource 12 | // const queue = new sqs.Queue(this, 'HelloQueue', { 13 | // visibilityTimeout: cdk.Duration.seconds(300) 14 | // }); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /apps/app2/lib/hello-stack.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from 'aws-cdk-lib'; 2 | import { Construct } from 'constructs'; 3 | // import * as sqs from 'aws-cdk-lib/aws-sqs'; 4 | 5 | export class HelloStack extends cdk.Stack { 6 | constructor(scope: Construct, id: string, props?: cdk.StackProps) { 7 | super(scope, id, props); 8 | 9 | // The code that defines your stack goes here 10 | 11 | // example resource 12 | // const queue = new sqs.Queue(this, 'HelloQueue', { 13 | // visibilityTimeout: cdk.Duration.seconds(300) 14 | // }); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /apps/app3/lib/hello-stack.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from 'aws-cdk-lib'; 2 | import { Construct } from 'constructs'; 3 | // import * as sqs from 'aws-cdk-lib/aws-sqs'; 4 | 5 | export class HelloStack extends cdk.Stack { 6 | constructor(scope: Construct, id: string, props?: cdk.StackProps) { 7 | super(scope, id, props); 8 | 9 | // The code that defines your stack goes here 10 | 11 | // example resource 12 | // const queue = new sqs.Queue(this, 'HelloQueue', { 13 | // visibilityTimeout: cdk.Duration.seconds(300) 14 | // }); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /apps/app1/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to your CDK TypeScript project 2 | 3 | This is a blank project for CDK development with TypeScript. 4 | 5 | The `cdk.json` file tells the CDK Toolkit how to execute your app. 6 | 7 | ## Useful commands 8 | 9 | * `npm run build` compile typescript to js 10 | * `npm run watch` watch for changes and compile 11 | * `npm run test` perform the jest unit tests 12 | * `cdk deploy` deploy this stack to your default AWS account/region 13 | * `cdk diff` compare deployed stack with current state 14 | * `cdk synth` emits the synthesized CloudFormation template 15 | -------------------------------------------------------------------------------- /apps/app2/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to your CDK TypeScript project 2 | 3 | This is a blank project for CDK development with TypeScript. 4 | 5 | The `cdk.json` file tells the CDK Toolkit how to execute your app. 6 | 7 | ## Useful commands 8 | 9 | * `npm run build` compile typescript to js 10 | * `npm run watch` watch for changes and compile 11 | * `npm run test` perform the jest unit tests 12 | * `cdk deploy` deploy this stack to your default AWS account/region 13 | * `cdk diff` compare deployed stack with current state 14 | * `cdk synth` emits the synthesized CloudFormation template 15 | -------------------------------------------------------------------------------- /apps/app3/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to your CDK TypeScript project 2 | 3 | This is a blank project for CDK development with TypeScript. 4 | 5 | The `cdk.json` file tells the CDK Toolkit how to execute your app. 6 | 7 | ## Useful commands 8 | 9 | * `npm run build` compile typescript to js 10 | * `npm run watch` watch for changes and compile 11 | * `npm run test` perform the jest unit tests 12 | * `cdk deploy` deploy this stack to your default AWS account/region 13 | * `cdk diff` compare deployed stack with current state 14 | * `cdk synth` emits the synthesized CloudFormation template 15 | -------------------------------------------------------------------------------- /apps/python-cdk-app/tests/unit/test_python_cdk_stack.py: -------------------------------------------------------------------------------- 1 | import aws_cdk as core 2 | import aws_cdk.assertions as assertions 3 | 4 | from python_cdk.python_cdk_stack import PythonCdkStack 5 | 6 | # example tests. To run these tests, uncomment this file along with the example 7 | # resource in python_cdk/python_cdk_stack.py 8 | def test_sqs_queue_created(): 9 | app = core.App() 10 | stack = PythonCdkStack(app, "python-cdk") 11 | template = assertions.Template.from_stack(stack) 12 | 13 | # template.has_resource_properties("AWS::SQS::Queue", { 14 | # "VisibilityTimeout": 300 15 | # }) 16 | -------------------------------------------------------------------------------- /apps/app1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello", 3 | "version": "0.1.0", 4 | "bin": { 5 | "hello": "bin/hello.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^29.2.5", 15 | "@types/node": "18.11.18", 16 | "jest": "^29.3.1", 17 | "ts-jest": "^29.0.3", 18 | "aws-cdk": "2.60.0", 19 | "ts-node": "^10.9.1", 20 | "typescript": "~4.9.4" 21 | }, 22 | "dependencies": { 23 | "aws-cdk-lib": "2.60.0", 24 | "constructs": "^10.0.0", 25 | "source-map-support": "^0.5.21" 26 | } 27 | } -------------------------------------------------------------------------------- /apps/app1/test/hello.test.ts: -------------------------------------------------------------------------------- 1 | // import * as cdk from 'aws-cdk-lib'; 2 | // import { Template } from 'aws-cdk-lib/assertions'; 3 | // import * as Hello from '../lib/hello-stack'; 4 | 5 | // example test. To run these tests, uncomment this file along with the 6 | // example resource in lib/hello-stack.ts 7 | test('SQS Queue Created', () => { 8 | // const app = new cdk.App(); 9 | // // WHEN 10 | // const stack = new Hello.HelloStack(app, 'MyTestStack'); 11 | // // THEN 12 | // const template = Template.fromStack(stack); 13 | 14 | // template.hasResourceProperties('AWS::SQS::Queue', { 15 | // VisibilityTimeout: 300 16 | // }); 17 | }); 18 | -------------------------------------------------------------------------------- /apps/app2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello", 3 | "version": "0.1.0", 4 | "bin": { 5 | "hello": "bin/hello.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^29.2.5", 15 | "@types/node": "18.11.18", 16 | "jest": "^29.3.1", 17 | "ts-jest": "^29.0.3", 18 | "aws-cdk": "2.60.0", 19 | "ts-node": "^10.9.1", 20 | "typescript": "~4.9.4" 21 | }, 22 | "dependencies": { 23 | "aws-cdk-lib": "2.60.0", 24 | "constructs": "^10.0.0", 25 | "source-map-support": "^0.5.21" 26 | } 27 | } -------------------------------------------------------------------------------- /apps/app2/test/hello.test.ts: -------------------------------------------------------------------------------- 1 | // import * as cdk from 'aws-cdk-lib'; 2 | // import { Template } from 'aws-cdk-lib/assertions'; 3 | // import * as Hello from '../lib/hello-stack'; 4 | 5 | // example test. To run these tests, uncomment this file along with the 6 | // example resource in lib/hello-stack.ts 7 | test('SQS Queue Created', () => { 8 | // const app = new cdk.App(); 9 | // // WHEN 10 | // const stack = new Hello.HelloStack(app, 'MyTestStack'); 11 | // // THEN 12 | // const template = Template.fromStack(stack); 13 | 14 | // template.hasResourceProperties('AWS::SQS::Queue', { 15 | // VisibilityTimeout: 300 16 | // }); 17 | }); 18 | -------------------------------------------------------------------------------- /apps/app3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello", 3 | "version": "0.1.0", 4 | "bin": { 5 | "hello": "bin/hello.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@types/jest": "^29.2.5", 15 | "@types/node": "18.11.18", 16 | "jest": "^29.3.1", 17 | "ts-jest": "^29.0.3", 18 | "aws-cdk": "2.60.0", 19 | "ts-node": "^10.9.1", 20 | "typescript": "~4.9.4" 21 | }, 22 | "dependencies": { 23 | "aws-cdk-lib": "2.60.0", 24 | "constructs": "^10.0.0", 25 | "source-map-support": "^0.5.21" 26 | } 27 | } -------------------------------------------------------------------------------- /apps/app3/test/hello.test.ts: -------------------------------------------------------------------------------- 1 | // import * as cdk from 'aws-cdk-lib'; 2 | // import { Template } from 'aws-cdk-lib/assertions'; 3 | // import * as Hello from '../lib/hello-stack'; 4 | 5 | // example test. To run these tests, uncomment this file along with the 6 | // example resource in lib/hello-stack.ts 7 | test('SQS Queue Created', () => { 8 | // const app = new cdk.App(); 9 | // // WHEN 10 | // const stack = new Hello.HelloStack(app, 'MyTestStack'); 11 | // // THEN 12 | // const template = Template.fromStack(stack); 13 | 14 | // template.hasResourceProperties('AWS::SQS::Queue', { 15 | // VisibilityTimeout: 300 16 | // }); 17 | }); 18 | -------------------------------------------------------------------------------- /apps/app1/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "commonjs", 5 | "lib": [ 6 | "es2020" 7 | ], 8 | "declaration": true, 9 | "strict": true, 10 | "noImplicitAny": true, 11 | "strictNullChecks": true, 12 | "noImplicitThis": true, 13 | "alwaysStrict": true, 14 | "noUnusedLocals": false, 15 | "noUnusedParameters": false, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": false, 18 | "inlineSourceMap": true, 19 | "inlineSources": true, 20 | "experimentalDecorators": true, 21 | "strictPropertyInitialization": false, 22 | "typeRoots": [ 23 | "./node_modules/@types" 24 | ] 25 | }, 26 | "exclude": [ 27 | "node_modules", 28 | "cdk.out" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /apps/app2/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "commonjs", 5 | "lib": [ 6 | "es2020" 7 | ], 8 | "declaration": true, 9 | "strict": true, 10 | "noImplicitAny": true, 11 | "strictNullChecks": true, 12 | "noImplicitThis": true, 13 | "alwaysStrict": true, 14 | "noUnusedLocals": false, 15 | "noUnusedParameters": false, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": false, 18 | "inlineSourceMap": true, 19 | "inlineSources": true, 20 | "experimentalDecorators": true, 21 | "strictPropertyInitialization": false, 22 | "typeRoots": [ 23 | "./node_modules/@types" 24 | ] 25 | }, 26 | "exclude": [ 27 | "node_modules", 28 | "cdk.out" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /apps/app3/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "commonjs", 5 | "lib": [ 6 | "es2020" 7 | ], 8 | "declaration": true, 9 | "strict": true, 10 | "noImplicitAny": true, 11 | "strictNullChecks": true, 12 | "noImplicitThis": true, 13 | "alwaysStrict": true, 14 | "noUnusedLocals": false, 15 | "noUnusedParameters": false, 16 | "noImplicitReturns": true, 17 | "noFallthroughCasesInSwitch": false, 18 | "inlineSourceMap": true, 19 | "inlineSources": true, 20 | "experimentalDecorators": true, 21 | "strictPropertyInitialization": false, 22 | "typeRoots": [ 23 | "./node_modules/@types" 24 | ] 25 | }, 26 | "exclude": [ 27 | "node_modules", 28 | "cdk.out" 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /apps/python-cdk-app/python_cdk/python_cdk_stack.py: -------------------------------------------------------------------------------- 1 | from aws_cdk import ( 2 | Duration, 3 | Stack, 4 | aws_sqs as sqs, 5 | ) 6 | import aws_cdk as cdk 7 | from constructs import Construct 8 | 9 | class PythonCdkStack(Stack): 10 | 11 | def __init__(self, scope: Construct, construct_id: str, **kwargs) -> None: 12 | super().__init__(scope, construct_id, **kwargs) 13 | 14 | # The code that defines your stack goes here 15 | 16 | # example resource 17 | queue = sqs.Queue( 18 | self, "PythonCdkQueue", 19 | encryption=sqs.QueueEncryption.KMS_MANAGED, 20 | visibility_timeout=Duration.seconds(300), 21 | removal_policy=cdk.RemovalPolicy.DESTROY 22 | ) 23 | 24 | cdk.CfnOutput(self, "QueueStackOutput", value=queue.queue_url, export_name="QueueUrl") 25 | -------------------------------------------------------------------------------- /cfn-templates/sqs-example.json: -------------------------------------------------------------------------------- 1 | { 2 | "AWSTemplateFormatVersion": "2010-09-09", 3 | "Resources": { 4 | "MyQueue": { 5 | "Properties": { 6 | "QueueName": "MyQueue" 7 | }, 8 | "Type": "AWS::SQS::Queue" 9 | } 10 | }, 11 | "Outputs": { 12 | "QueueName": { 13 | "Description": "The name of the queue", 14 | "Value": { 15 | "Fn::GetAtt": [ 16 | "MyQueue", 17 | "QueueName" 18 | ] 19 | } 20 | }, 21 | "QueueURL": { 22 | "Description": "The URL of the queue", 23 | "Value": { 24 | "Ref": "MyQueue" 25 | } 26 | }, 27 | "QueueARN": { 28 | "Description": "The ARN of the queue", 29 | "Value": { 30 | "Fn::GetAtt": [ 31 | "MyQueue", 32 | "Arn" 33 | ] 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | -------------------------------------------------------------------------------- /apps/app1/bin/hello.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from 'aws-cdk-lib'; 4 | import { HelloStack } from '../lib/hello-stack'; 5 | 6 | export const app = new cdk.App(); 7 | new HelloStack(app, 'HelloStack1', { 8 | /* If you don't specify 'env', this stack will be environment-agnostic. 9 | * Account/Region-dependent features and context lookups will not work, 10 | * but a single synthesized template can be deployed anywhere. */ 11 | 12 | /* Uncomment the next line to specialize this stack for the AWS Account 13 | * and Region that are implied by the current CLI configuration. */ 14 | // env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, 15 | 16 | /* Uncomment the next line if you know exactly what Account and Region you 17 | * want to deploy the stack to. */ 18 | // env: { account: '123456789012', region: 'us-east-1' }, 19 | 20 | /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ 21 | }); -------------------------------------------------------------------------------- /apps/app2/bin/hello.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from 'aws-cdk-lib'; 4 | import { HelloStack } from '../lib/hello-stack'; 5 | 6 | export const app = new cdk.App(); 7 | new HelloStack(app, 'HelloStack2', { 8 | /* If you don't specify 'env', this stack will be environment-agnostic. 9 | * Account/Region-dependent features and context lookups will not work, 10 | * but a single synthesized template can be deployed anywhere. */ 11 | 12 | /* Uncomment the next line to specialize this stack for the AWS Account 13 | * and Region that are implied by the current CLI configuration. */ 14 | // env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, 15 | 16 | /* Uncomment the next line if you know exactly what Account and Region you 17 | * want to deploy the stack to. */ 18 | // env: { account: '123456789012', region: 'us-east-1' }, 19 | 20 | /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ 21 | }); -------------------------------------------------------------------------------- /apps/app3/bin/hello.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | import 'source-map-support/register'; 3 | import * as cdk from 'aws-cdk-lib'; 4 | import { HelloStack } from '../lib/hello-stack'; 5 | 6 | export const app = new cdk.App(); 7 | new HelloStack(app, 'HelloStack3', { 8 | /* If you don't specify 'env', this stack will be environment-agnostic. 9 | * Account/Region-dependent features and context lookups will not work, 10 | * but a single synthesized template can be deployed anywhere. */ 11 | 12 | /* Uncomment the next line to specialize this stack for the AWS Account 13 | * and Region that are implied by the current CLI configuration. */ 14 | // env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, 15 | 16 | /* Uncomment the next line if you know exactly what Account and Region you 17 | * want to deploy the stack to. */ 18 | // env: { account: '123456789012', region: 'us-east-1' }, 19 | 20 | /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ 21 | }); -------------------------------------------------------------------------------- /apps/python-cdk-app/app.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import os 3 | 4 | import aws_cdk as cdk 5 | 6 | from python_cdk.python_cdk_stack import PythonCdkStack 7 | 8 | 9 | app = cdk.App() 10 | PythonCdkStack(app, "PythonCdkStack", 11 | # If you don't specify 'env', this stack will be environment-agnostic. 12 | # Account/Region-dependent features and context lookups will not work, 13 | # but a single synthesized template can be deployed anywhere. 14 | 15 | # Uncomment the next line to specialize this stack for the AWS Account 16 | # and Region that are implied by the current CLI configuration. 17 | 18 | #env=cdk.Environment(account=os.getenv('CDK_DEFAULT_ACCOUNT'), region=os.getenv('CDK_DEFAULT_REGION')), 19 | 20 | # Uncomment the next line if you know exactly what Account and Region you 21 | # want to deploy the stack to. */ 22 | 23 | #env=cdk.Environment(account='123456789012', region='us-east-1'), 24 | 25 | # For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html 26 | ) 27 | 28 | app.synth() 29 | -------------------------------------------------------------------------------- /apps/app1/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/hello.ts", 3 | "watch": { 4 | "include": [ 5 | "**" 6 | ], 7 | "exclude": [ 8 | "README.md", 9 | "cdk*.json", 10 | "**/*.d.ts", 11 | "**/*.js", 12 | "tsconfig.json", 13 | "package*.json", 14 | "yarn.lock", 15 | "node_modules", 16 | "test" 17 | ] 18 | }, 19 | "context": { 20 | "@aws-cdk/aws-lambda:recognizeLayerVersion": true, 21 | "@aws-cdk/core:checkSecretUsage": true, 22 | "@aws-cdk/core:target-partitions": [ 23 | "aws", 24 | "aws-cn" 25 | ], 26 | "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, 27 | "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, 28 | "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, 29 | "@aws-cdk/aws-iam:minimizePolicies": true, 30 | "@aws-cdk/core:validateSnapshotRemovalPolicy": true, 31 | "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, 32 | "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, 33 | "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, 34 | "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, 35 | "@aws-cdk/core:enablePartitionLiterals": true, 36 | "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, 37 | "@aws-cdk/aws-iam:standardizedServicePrincipals": true, 38 | "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /apps/app2/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/hello.ts", 3 | "watch": { 4 | "include": [ 5 | "**" 6 | ], 7 | "exclude": [ 8 | "README.md", 9 | "cdk*.json", 10 | "**/*.d.ts", 11 | "**/*.js", 12 | "tsconfig.json", 13 | "package*.json", 14 | "yarn.lock", 15 | "node_modules", 16 | "test" 17 | ] 18 | }, 19 | "context": { 20 | "@aws-cdk/aws-lambda:recognizeLayerVersion": true, 21 | "@aws-cdk/core:checkSecretUsage": true, 22 | "@aws-cdk/core:target-partitions": [ 23 | "aws", 24 | "aws-cn" 25 | ], 26 | "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, 27 | "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, 28 | "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, 29 | "@aws-cdk/aws-iam:minimizePolicies": true, 30 | "@aws-cdk/core:validateSnapshotRemovalPolicy": true, 31 | "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, 32 | "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, 33 | "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, 34 | "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, 35 | "@aws-cdk/core:enablePartitionLiterals": true, 36 | "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, 37 | "@aws-cdk/aws-iam:standardizedServicePrincipals": true, 38 | "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /apps/app3/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/hello.ts", 3 | "watch": { 4 | "include": [ 5 | "**" 6 | ], 7 | "exclude": [ 8 | "README.md", 9 | "cdk*.json", 10 | "**/*.d.ts", 11 | "**/*.js", 12 | "tsconfig.json", 13 | "package*.json", 14 | "yarn.lock", 15 | "node_modules", 16 | "test" 17 | ] 18 | }, 19 | "context": { 20 | "@aws-cdk/aws-lambda:recognizeLayerVersion": true, 21 | "@aws-cdk/core:checkSecretUsage": true, 22 | "@aws-cdk/core:target-partitions": [ 23 | "aws", 24 | "aws-cn" 25 | ], 26 | "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, 27 | "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, 28 | "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, 29 | "@aws-cdk/aws-iam:minimizePolicies": true, 30 | "@aws-cdk/core:validateSnapshotRemovalPolicy": true, 31 | "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, 32 | "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, 33 | "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, 34 | "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, 35 | "@aws-cdk/core:enablePartitionLiterals": true, 36 | "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, 37 | "@aws-cdk/aws-iam:standardizedServicePrincipals": true, 38 | "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /pipeline-assets/codepipeline-service-role-policy.json: -------------------------------------------------------------------------------- 1 | { 2 | "Statement": [ 3 | { 4 | "Action": [ 5 | "iam:PassRole" 6 | ], 7 | "Resource": "*", 8 | "Effect": "Allow", 9 | "Condition": { 10 | "StringEqualsIfExists": { 11 | "iam:PassedToService": [ 12 | "cloudformation.amazonaws.com", 13 | "elasticbeanstalk.amazonaws.com", 14 | "ec2.amazonaws.com", 15 | "ecs-tasks.amazonaws.com" 16 | ] 17 | } 18 | } 19 | }, 20 | { 21 | "Action": [ 22 | "cloudwatch:*", 23 | "s3:*", 24 | "sns:*", 25 | "cloudformation:*" 26 | ], 27 | "Resource": "*", 28 | "Effect": "Allow" 29 | }, 30 | { 31 | "Action": [ 32 | "codebuild:BatchGetBuilds", 33 | "codebuild:StartBuild", 34 | "codebuild:BatchGetBuildBatches", 35 | "codebuild:StartBuildBatch" 36 | ], 37 | "Resource": "*", 38 | "Effect": "Allow" 39 | }, 40 | { 41 | "Effect": "Allow", 42 | "Action": [ 43 | "states:DescribeExecution", 44 | "states:DescribeStateMachine", 45 | "states:StartExecution" 46 | ], 47 | "Resource": "*" 48 | } 49 | ], 50 | "Version": "2012-10-17" 51 | } -------------------------------------------------------------------------------- /apps/python-cdk-app/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "python3 app.py", 3 | "watch": { 4 | "include": [ 5 | "**" 6 | ], 7 | "exclude": [ 8 | "README.md", 9 | "cdk*.json", 10 | "requirements*.txt", 11 | "source.bat", 12 | "**/__init__.py", 13 | "python/__pycache__", 14 | "tests" 15 | ] 16 | }, 17 | "context": { 18 | "@aws-cdk/aws-lambda:recognizeLayerVersion": true, 19 | "@aws-cdk/core:checkSecretUsage": true, 20 | "@aws-cdk/core:target-partitions": [ 21 | "aws", 22 | "aws-cn" 23 | ], 24 | "@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true, 25 | "@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true, 26 | "@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true, 27 | "@aws-cdk/aws-iam:minimizePolicies": true, 28 | "@aws-cdk/core:validateSnapshotRemovalPolicy": true, 29 | "@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true, 30 | "@aws-cdk/aws-s3:createDefaultLoggingPolicy": true, 31 | "@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true, 32 | "@aws-cdk/aws-apigateway:disableCloudWatchRole": true, 33 | "@aws-cdk/core:enablePartitionLiterals": true, 34 | "@aws-cdk/aws-events:eventsTargetQueueSameAccount": true, 35 | "@aws-cdk/aws-iam:standardizedServicePrincipals": true, 36 | "@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true, 37 | "@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true, 38 | "@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true, 39 | "@aws-cdk/customresources:installLatestAwsSdkDefault": false 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /apps/app1/lib/hello-stack.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.HelloStack = void 0; 4 | const cdk = require("aws-cdk-lib"); 5 | // import * as sqs from 'aws-cdk-lib/aws-sqs'; 6 | class HelloStack extends cdk.Stack { 7 | constructor(scope, id, props) { 8 | super(scope, id, props); 9 | // The code that defines your stack goes here 10 | // example resource 11 | // const queue = new sqs.Queue(this, 'HelloQueue', { 12 | // visibilityTimeout: cdk.Duration.seconds(300) 13 | // }); 14 | } 15 | } 16 | exports.HelloStack = HelloStack; 17 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVsbG8tc3RhY2suanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJoZWxsby1zdGFjay50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxtQ0FBbUM7QUFFbkMsOENBQThDO0FBRTlDLE1BQWEsVUFBVyxTQUFRLEdBQUcsQ0FBQyxLQUFLO0lBQ3ZDLFlBQVksS0FBZ0IsRUFBRSxFQUFVLEVBQUUsS0FBc0I7UUFDOUQsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFFeEIsNkNBQTZDO1FBRTdDLG1CQUFtQjtRQUNuQixvREFBb0Q7UUFDcEQsaURBQWlEO1FBQ2pELE1BQU07SUFDUixDQUFDO0NBQ0Y7QUFYRCxnQ0FXQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGNkayBmcm9tICdhd3MtY2RrLWxpYic7XG5pbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tICdjb25zdHJ1Y3RzJztcbi8vIGltcG9ydCAqIGFzIHNxcyBmcm9tICdhd3MtY2RrLWxpYi9hd3Mtc3FzJztcblxuZXhwb3J0IGNsYXNzIEhlbGxvU3RhY2sgZXh0ZW5kcyBjZGsuU3RhY2sge1xuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wcz86IGNkay5TdGFja1Byb3BzKSB7XG4gICAgc3VwZXIoc2NvcGUsIGlkLCBwcm9wcyk7XG5cbiAgICAvLyBUaGUgY29kZSB0aGF0IGRlZmluZXMgeW91ciBzdGFjayBnb2VzIGhlcmVcblxuICAgIC8vIGV4YW1wbGUgcmVzb3VyY2VcbiAgICAvLyBjb25zdCBxdWV1ZSA9IG5ldyBzcXMuUXVldWUodGhpcywgJ0hlbGxvUXVldWUnLCB7XG4gICAgLy8gICB2aXNpYmlsaXR5VGltZW91dDogY2RrLkR1cmF0aW9uLnNlY29uZHMoMzAwKVxuICAgIC8vIH0pO1xuICB9XG59XG4iXX0= -------------------------------------------------------------------------------- /apps/app2/lib/hello-stack.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.HelloStack = void 0; 4 | const cdk = require("aws-cdk-lib"); 5 | // import * as sqs from 'aws-cdk-lib/aws-sqs'; 6 | class HelloStack extends cdk.Stack { 7 | constructor(scope, id, props) { 8 | super(scope, id, props); 9 | // The code that defines your stack goes here 10 | // example resource 11 | // const queue = new sqs.Queue(this, 'HelloQueue', { 12 | // visibilityTimeout: cdk.Duration.seconds(300) 13 | // }); 14 | } 15 | } 16 | exports.HelloStack = HelloStack; 17 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVsbG8tc3RhY2suanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJoZWxsby1zdGFjay50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxtQ0FBbUM7QUFFbkMsOENBQThDO0FBRTlDLE1BQWEsVUFBVyxTQUFRLEdBQUcsQ0FBQyxLQUFLO0lBQ3ZDLFlBQVksS0FBZ0IsRUFBRSxFQUFVLEVBQUUsS0FBc0I7UUFDOUQsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFFeEIsNkNBQTZDO1FBRTdDLG1CQUFtQjtRQUNuQixvREFBb0Q7UUFDcEQsaURBQWlEO1FBQ2pELE1BQU07SUFDUixDQUFDO0NBQ0Y7QUFYRCxnQ0FXQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGNkayBmcm9tICdhd3MtY2RrLWxpYic7XG5pbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tICdjb25zdHJ1Y3RzJztcbi8vIGltcG9ydCAqIGFzIHNxcyBmcm9tICdhd3MtY2RrLWxpYi9hd3Mtc3FzJztcblxuZXhwb3J0IGNsYXNzIEhlbGxvU3RhY2sgZXh0ZW5kcyBjZGsuU3RhY2sge1xuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wcz86IGNkay5TdGFja1Byb3BzKSB7XG4gICAgc3VwZXIoc2NvcGUsIGlkLCBwcm9wcyk7XG5cbiAgICAvLyBUaGUgY29kZSB0aGF0IGRlZmluZXMgeW91ciBzdGFjayBnb2VzIGhlcmVcblxuICAgIC8vIGV4YW1wbGUgcmVzb3VyY2VcbiAgICAvLyBjb25zdCBxdWV1ZSA9IG5ldyBzcXMuUXVldWUodGhpcywgJ0hlbGxvUXVldWUnLCB7XG4gICAgLy8gICB2aXNpYmlsaXR5VGltZW91dDogY2RrLkR1cmF0aW9uLnNlY29uZHMoMzAwKVxuICAgIC8vIH0pO1xuICB9XG59XG4iXX0= -------------------------------------------------------------------------------- /apps/app3/lib/hello-stack.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | Object.defineProperty(exports, "__esModule", { value: true }); 3 | exports.HelloStack = void 0; 4 | const cdk = require("aws-cdk-lib"); 5 | // import * as sqs from 'aws-cdk-lib/aws-sqs'; 6 | class HelloStack extends cdk.Stack { 7 | constructor(scope, id, props) { 8 | super(scope, id, props); 9 | // The code that defines your stack goes here 10 | // example resource 11 | // const queue = new sqs.Queue(this, 'HelloQueue', { 12 | // visibilityTimeout: cdk.Duration.seconds(300) 13 | // }); 14 | } 15 | } 16 | exports.HelloStack = HelloStack; 17 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVsbG8tc3RhY2suanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJoZWxsby1zdGFjay50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxtQ0FBbUM7QUFFbkMsOENBQThDO0FBRTlDLE1BQWEsVUFBVyxTQUFRLEdBQUcsQ0FBQyxLQUFLO0lBQ3ZDLFlBQVksS0FBZ0IsRUFBRSxFQUFVLEVBQUUsS0FBc0I7UUFDOUQsS0FBSyxDQUFDLEtBQUssRUFBRSxFQUFFLEVBQUUsS0FBSyxDQUFDLENBQUM7UUFFeEIsNkNBQTZDO1FBRTdDLG1CQUFtQjtRQUNuQixvREFBb0Q7UUFDcEQsaURBQWlEO1FBQ2pELE1BQU07SUFDUixDQUFDO0NBQ0Y7QUFYRCxnQ0FXQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCAqIGFzIGNkayBmcm9tICdhd3MtY2RrLWxpYic7XG5pbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tICdjb25zdHJ1Y3RzJztcbi8vIGltcG9ydCAqIGFzIHNxcyBmcm9tICdhd3MtY2RrLWxpYi9hd3Mtc3FzJztcblxuZXhwb3J0IGNsYXNzIEhlbGxvU3RhY2sgZXh0ZW5kcyBjZGsuU3RhY2sge1xuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wcz86IGNkay5TdGFja1Byb3BzKSB7XG4gICAgc3VwZXIoc2NvcGUsIGlkLCBwcm9wcyk7XG5cbiAgICAvLyBUaGUgY29kZSB0aGF0IGRlZmluZXMgeW91ciBzdGFjayBnb2VzIGhlcmVcblxuICAgIC8vIGV4YW1wbGUgcmVzb3VyY2VcbiAgICAvLyBjb25zdCBxdWV1ZSA9IG5ldyBzcXMuUXVldWUodGhpcywgJ0hlbGxvUXVldWUnLCB7XG4gICAgLy8gICB2aXNpYmlsaXR5VGltZW91dDogY2RrLkR1cmF0aW9uLnNlY29uZHMoMzAwKVxuICAgIC8vIH0pO1xuICB9XG59XG4iXX0= -------------------------------------------------------------------------------- /main.js: -------------------------------------------------------------------------------- 1 | // Based on Electron quickstart https://www.electronjs.org/docs/latest/tutorial/quick-start 2 | // Licensed under the MIT License 3 | 4 | // Modules to control application life and create native browser window 5 | 6 | const { app, BrowserWindow, ipcMain } = require('electron') 7 | const path = require('path') 8 | 9 | 10 | function createWindow() { 11 | 12 | // Create the browser window. 13 | const mainWindow = new BrowserWindow({ 14 | width: !app.isPackaged ? 1400 : 900, 15 | height: 800, 16 | webPreferences: { 17 | sandbox: false, 18 | preload: path.join(__dirname, 'preload.js') 19 | } 20 | }) 21 | 22 | // and load the index.html of the app. 23 | mainWindow.loadFile('index.html') 24 | 25 | // Open the DevTools. 26 | if (!app.isPackaged) { 27 | mainWindow.webContents.openDevTools() 28 | } 29 | 30 | } 31 | 32 | // This method will be called when Electron has finished 33 | // initialization and is ready to create browser windows. 34 | // Some APIs can only be used after this event occurs. 35 | app.whenReady().then(() => { 36 | createWindow() 37 | 38 | app.on('activate', function () { 39 | // On macOS it's common to re-create a window in the app when the 40 | // dock icon is clicked and there are no other windows open. 41 | if (BrowserWindow.getAllWindows().length === 0) createWindow() 42 | }) 43 | }) 44 | 45 | // Quit when all windows are closed, except on macOS. There, it's common 46 | // for applications and their menu bar to stay active until the user quits 47 | // explicitly with Cmd + Q. 48 | app.on('window-all-closed', function () { 49 | //if (process.platform !== 'darwin') 50 | app.quit() 51 | }) 52 | 53 | // In this file you can include the rest of your app's specific main process 54 | // code. You can also put them in separate files and require them here. 55 | -------------------------------------------------------------------------------- /apps/python-cdk-app/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Welcome to your CDK Python project! 3 | 4 | This is a blank project for CDK development with Python. 5 | 6 | The `cdk.json` file tells the CDK Toolkit how to execute your app. 7 | 8 | This project is set up like a standard Python project. The initialization 9 | process also creates a virtualenv within this project, stored under the `.venv` 10 | directory. To create the virtualenv it assumes that there is a `python3` 11 | (or `python` for Windows) executable in your path with access to the `venv` 12 | package. If for any reason the automatic creation of the virtualenv fails, 13 | you can create the virtualenv manually. 14 | 15 | To manually create a virtualenv on MacOS and Linux: 16 | 17 | ``` 18 | $ python3 -m venv .venv 19 | ``` 20 | 21 | After the init process completes and the virtualenv is created, you can use the following 22 | step to activate your virtualenv. 23 | 24 | ``` 25 | $ source .venv/bin/activate 26 | ``` 27 | 28 | If you are a Windows platform, you would activate the virtualenv like this: 29 | 30 | ``` 31 | % .venv\Scripts\activate.bat 32 | ``` 33 | 34 | Once the virtualenv is activated, you can install the required dependencies. 35 | 36 | ``` 37 | $ pip install -r requirements.txt 38 | ``` 39 | 40 | At this point you can now synthesize the CloudFormation template for this code. 41 | 42 | ``` 43 | $ cdk synth 44 | ``` 45 | 46 | To add additional dependencies, for example other CDK libraries, just add 47 | them to your `setup.py` file and rerun the `pip install -r requirements.txt` 48 | command. 49 | 50 | ## Useful commands 51 | 52 | * `cdk ls` list all stacks in the app 53 | * `cdk synth` emits the synthesized CloudFormation template 54 | * `cdk deploy` deploy this stack to your default AWS account/region 55 | * `cdk diff` compare deployed stack with current state 56 | * `cdk docs` open CDK documentation 57 | 58 | Enjoy! 59 | -------------------------------------------------------------------------------- /apps/app1/test/hello.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | // import * as cdk from 'aws-cdk-lib'; 3 | // import { Template } from 'aws-cdk-lib/assertions'; 4 | // import * as Hello from '../lib/hello-stack'; 5 | // example test. To run these tests, uncomment this file along with the 6 | // example resource in lib/hello-stack.ts 7 | test('SQS Queue Created', () => { 8 | // const app = new cdk.App(); 9 | // // WHEN 10 | // const stack = new Hello.HelloStack(app, 'MyTestStack'); 11 | // // THEN 12 | // const template = Template.fromStack(stack); 13 | // template.hasResourceProperties('AWS::SQS::Queue', { 14 | // VisibilityTimeout: 300 15 | // }); 16 | }); 17 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVsbG8udGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImhlbGxvLnRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLHNDQUFzQztBQUN0QyxxREFBcUQ7QUFDckQsK0NBQStDO0FBRS9DLHVFQUF1RTtBQUN2RSx5Q0FBeUM7QUFDekMsSUFBSSxDQUFDLG1CQUFtQixFQUFFLEdBQUcsRUFBRTtJQUMvQiwrQkFBK0I7SUFDL0IsY0FBYztJQUNkLDREQUE0RDtJQUM1RCxjQUFjO0lBQ2QsZ0RBQWdEO0lBRWhELHdEQUF3RDtJQUN4RCw2QkFBNkI7SUFDN0IsUUFBUTtBQUNSLENBQUMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gaW1wb3J0ICogYXMgY2RrIGZyb20gJ2F3cy1jZGstbGliJztcbi8vIGltcG9ydCB7IFRlbXBsYXRlIH0gZnJvbSAnYXdzLWNkay1saWIvYXNzZXJ0aW9ucyc7XG4vLyBpbXBvcnQgKiBhcyBIZWxsbyBmcm9tICcuLi9saWIvaGVsbG8tc3RhY2snO1xuXG4vLyBleGFtcGxlIHRlc3QuIFRvIHJ1biB0aGVzZSB0ZXN0cywgdW5jb21tZW50IHRoaXMgZmlsZSBhbG9uZyB3aXRoIHRoZVxuLy8gZXhhbXBsZSByZXNvdXJjZSBpbiBsaWIvaGVsbG8tc3RhY2sudHNcbnRlc3QoJ1NRUyBRdWV1ZSBDcmVhdGVkJywgKCkgPT4ge1xuLy8gICBjb25zdCBhcHAgPSBuZXcgY2RrLkFwcCgpO1xuLy8gICAgIC8vIFdIRU5cbi8vICAgY29uc3Qgc3RhY2sgPSBuZXcgSGVsbG8uSGVsbG9TdGFjayhhcHAsICdNeVRlc3RTdGFjaycpO1xuLy8gICAgIC8vIFRIRU5cbi8vICAgY29uc3QgdGVtcGxhdGUgPSBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spO1xuXG4vLyAgIHRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcygnQVdTOjpTUVM6OlF1ZXVlJywge1xuLy8gICAgIFZpc2liaWxpdHlUaW1lb3V0OiAzMDBcbi8vICAgfSk7XG59KTtcbiJdfQ== -------------------------------------------------------------------------------- /apps/app2/test/hello.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | // import * as cdk from 'aws-cdk-lib'; 3 | // import { Template } from 'aws-cdk-lib/assertions'; 4 | // import * as Hello from '../lib/hello-stack'; 5 | // example test. To run these tests, uncomment this file along with the 6 | // example resource in lib/hello-stack.ts 7 | test('SQS Queue Created', () => { 8 | // const app = new cdk.App(); 9 | // // WHEN 10 | // const stack = new Hello.HelloStack(app, 'MyTestStack'); 11 | // // THEN 12 | // const template = Template.fromStack(stack); 13 | // template.hasResourceProperties('AWS::SQS::Queue', { 14 | // VisibilityTimeout: 300 15 | // }); 16 | }); 17 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVsbG8udGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImhlbGxvLnRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLHNDQUFzQztBQUN0QyxxREFBcUQ7QUFDckQsK0NBQStDO0FBRS9DLHVFQUF1RTtBQUN2RSx5Q0FBeUM7QUFDekMsSUFBSSxDQUFDLG1CQUFtQixFQUFFLEdBQUcsRUFBRTtJQUMvQiwrQkFBK0I7SUFDL0IsY0FBYztJQUNkLDREQUE0RDtJQUM1RCxjQUFjO0lBQ2QsZ0RBQWdEO0lBRWhELHdEQUF3RDtJQUN4RCw2QkFBNkI7SUFDN0IsUUFBUTtBQUNSLENBQUMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gaW1wb3J0ICogYXMgY2RrIGZyb20gJ2F3cy1jZGstbGliJztcbi8vIGltcG9ydCB7IFRlbXBsYXRlIH0gZnJvbSAnYXdzLWNkay1saWIvYXNzZXJ0aW9ucyc7XG4vLyBpbXBvcnQgKiBhcyBIZWxsbyBmcm9tICcuLi9saWIvaGVsbG8tc3RhY2snO1xuXG4vLyBleGFtcGxlIHRlc3QuIFRvIHJ1biB0aGVzZSB0ZXN0cywgdW5jb21tZW50IHRoaXMgZmlsZSBhbG9uZyB3aXRoIHRoZVxuLy8gZXhhbXBsZSByZXNvdXJjZSBpbiBsaWIvaGVsbG8tc3RhY2sudHNcbnRlc3QoJ1NRUyBRdWV1ZSBDcmVhdGVkJywgKCkgPT4ge1xuLy8gICBjb25zdCBhcHAgPSBuZXcgY2RrLkFwcCgpO1xuLy8gICAgIC8vIFdIRU5cbi8vICAgY29uc3Qgc3RhY2sgPSBuZXcgSGVsbG8uSGVsbG9TdGFjayhhcHAsICdNeVRlc3RTdGFjaycpO1xuLy8gICAgIC8vIFRIRU5cbi8vICAgY29uc3QgdGVtcGxhdGUgPSBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spO1xuXG4vLyAgIHRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcygnQVdTOjpTUVM6OlF1ZXVlJywge1xuLy8gICAgIFZpc2liaWxpdHlUaW1lb3V0OiAzMDBcbi8vICAgfSk7XG59KTtcbiJdfQ== -------------------------------------------------------------------------------- /apps/app3/test/hello.test.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | // import * as cdk from 'aws-cdk-lib'; 3 | // import { Template } from 'aws-cdk-lib/assertions'; 4 | // import * as Hello from '../lib/hello-stack'; 5 | // example test. To run these tests, uncomment this file along with the 6 | // example resource in lib/hello-stack.ts 7 | test('SQS Queue Created', () => { 8 | // const app = new cdk.App(); 9 | // // WHEN 10 | // const stack = new Hello.HelloStack(app, 'MyTestStack'); 11 | // // THEN 12 | // const template = Template.fromStack(stack); 13 | // template.hasResourceProperties('AWS::SQS::Queue', { 14 | // VisibilityTimeout: 300 15 | // }); 16 | }); 17 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVsbG8udGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImhlbGxvLnRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLHNDQUFzQztBQUN0QyxxREFBcUQ7QUFDckQsK0NBQStDO0FBRS9DLHVFQUF1RTtBQUN2RSx5Q0FBeUM7QUFDekMsSUFBSSxDQUFDLG1CQUFtQixFQUFFLEdBQUcsRUFBRTtJQUMvQiwrQkFBK0I7SUFDL0IsY0FBYztJQUNkLDREQUE0RDtJQUM1RCxjQUFjO0lBQ2QsZ0RBQWdEO0lBRWhELHdEQUF3RDtJQUN4RCw2QkFBNkI7SUFDN0IsUUFBUTtBQUNSLENBQUMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLy8gaW1wb3J0ICogYXMgY2RrIGZyb20gJ2F3cy1jZGstbGliJztcbi8vIGltcG9ydCB7IFRlbXBsYXRlIH0gZnJvbSAnYXdzLWNkay1saWIvYXNzZXJ0aW9ucyc7XG4vLyBpbXBvcnQgKiBhcyBIZWxsbyBmcm9tICcuLi9saWIvaGVsbG8tc3RhY2snO1xuXG4vLyBleGFtcGxlIHRlc3QuIFRvIHJ1biB0aGVzZSB0ZXN0cywgdW5jb21tZW50IHRoaXMgZmlsZSBhbG9uZyB3aXRoIHRoZVxuLy8gZXhhbXBsZSByZXNvdXJjZSBpbiBsaWIvaGVsbG8tc3RhY2sudHNcbnRlc3QoJ1NRUyBRdWV1ZSBDcmVhdGVkJywgKCkgPT4ge1xuLy8gICBjb25zdCBhcHAgPSBuZXcgY2RrLkFwcCgpO1xuLy8gICAgIC8vIFdIRU5cbi8vICAgY29uc3Qgc3RhY2sgPSBuZXcgSGVsbG8uSGVsbG9TdGFjayhhcHAsICdNeVRlc3RTdGFjaycpO1xuLy8gICAgIC8vIFRIRU5cbi8vICAgY29uc3QgdGVtcGxhdGUgPSBUZW1wbGF0ZS5mcm9tU3RhY2soc3RhY2spO1xuXG4vLyAgIHRlbXBsYXRlLmhhc1Jlc291cmNlUHJvcGVydGllcygnQVdTOjpTUVM6OlF1ZXVlJywge1xuLy8gICAgIFZpc2liaWxpdHlUaW1lb3V0OiAzMDBcbi8vICAgfSk7XG59KTtcbiJdfQ== -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "aws-electron-sdk-cdk", 3 | "version": "1.0.0", 4 | "description": "A demonstration of using AWS SDK and AWS CDK inside an Electron application.", 5 | "main": "main.js", 6 | "scripts": { 7 | "start": "electron-forge start", 8 | "package": "electron-forge package", 9 | "make": "electron-forge make" 10 | }, 11 | "keywords": [ 12 | "Electron", 13 | "AWS", 14 | "SDK", 15 | "CDK", 16 | "PoC" 17 | ], 18 | "author": "guymor@amazon.com", 19 | "license": "", 20 | "devDependencies": { 21 | "@electron-forge/cli": "^6.0.4", 22 | "@electron-forge/maker-deb": "^6.0.4", 23 | "@electron-forge/maker-rpm": "^6.0.4", 24 | "@electron-forge/maker-squirrel": "^6.0.4", 25 | "@electron-forge/maker-zip": "^6.0.4", 26 | "electron": "^39.2.1", 27 | "electron-rebuild": "^3.2.9" 28 | }, 29 | "dependencies": { 30 | "@aws-cdk/cloudformation-diff": "2.79.1", 31 | "@aws-cdk/cx-api": "2.79.1", 32 | "archiver": "^5.3.1", 33 | "aws-cdk": "2.79.1", 34 | "aws-cdk-lib": "2.79.1", 35 | "aws-sdk": "2.1343.0", 36 | "cdk-assets": "^2.33.0", 37 | "constructs": "^10.1.43", 38 | "electron-root-path": "^1.1.0", 39 | "electron-squirrel-startup": "^1.0.0", 40 | "promptly": "^3.2.0", 41 | "proxy-agent": "^5.0.0", 42 | "semver": "6.0.0", 43 | "wrap-ansi": "^7.0.0", 44 | "yaml": "^1.10.2" 45 | }, 46 | "config": { 47 | "forge": { 48 | "packagerConfig": {}, 49 | "makers": [ 50 | { 51 | "name": "@electron-forge/maker-squirrel", 52 | "config": { 53 | "name": "electron_quick_start" 54 | } 55 | }, 56 | { 57 | "name": "@electron-forge/maker-zip", 58 | "platforms": [ 59 | "darwin" 60 | ] 61 | }, 62 | { 63 | "name": "@electron-forge/maker-deb", 64 | "config": {} 65 | }, 66 | { 67 | "name": "@electron-forge/maker-rpm", 68 | "config": {} 69 | } 70 | ] 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /assets/styles.css: -------------------------------------------------------------------------------- 1 | /* styles.css */ 2 | 3 | /* Add styles here to customize the appearance of your app */ 4 | 5 | 6 | html { 7 | font-family: sans-serif; 8 | } 9 | 10 | body { 11 | margin: 1rem; 12 | font-family: sans-serif; 13 | width: 95%; 14 | } 15 | 16 | h1, 17 | h2, 18 | h3, 19 | h4, 20 | h5, 21 | h6 { 22 | margin-bottom: .2rem; 23 | margin-top: .2rem; 24 | font-family: sans-serif; 25 | } 26 | 27 | h1 { 28 | font-size: 1.6rem; 29 | } 30 | 31 | h2 { 32 | font-size: 1.3rem; 33 | } 34 | 35 | h3 { 36 | font-size: 1rem; 37 | } 38 | 39 | p { 40 | margin-bottom: .3rem; 41 | margin-top: .3rem; 42 | font-size: 1rem; 43 | } 44 | 45 | 46 | hr { 47 | margin-top: 20px; 48 | margin-bottom: 20px; 49 | border-style: dashed; 50 | border-color: grey; 51 | border-width: 1px; 52 | } 53 | 54 | #error-block { 55 | color: red; 56 | font-weight: bold; 57 | } 58 | 59 | footer { 60 | font-size: smaller; 61 | font-style: italic; 62 | } 63 | 64 | .output { 65 | border-radius: 5px; 66 | border: 1px solid grey; 67 | padding: 10px; 68 | background-color: #fafafa; 69 | font-size: smaller; 70 | margin: .3rem .0rem .9rem; 71 | } 72 | 73 | .list-output { 74 | border-radius: 5px; 75 | border: 1px solid grey; 76 | padding: 5px; 77 | background-color: #f0f0f0; 78 | } 79 | 80 | .logs { 81 | border-radius: 5px; 82 | border: 1px dotted; 83 | padding: 0.3rem 0.6rem; 84 | background-color: rgb(15 149 193 / 22%); 85 | } 86 | 87 | p { 88 | margin-top: 3px; 89 | margin-bottom: 3px; 90 | color: inherit; 91 | font-weight: inherit; 92 | } 93 | 94 | .log-line { 95 | font-size: smaller; 96 | } 97 | 98 | button, 99 | select { 100 | max-width: fit-content; 101 | display: inline; 102 | margin: .2rem .6rem .6rem 0rem; 103 | padding: 0.3rem 0.6rem; 104 | } 105 | 106 | #error-block { 107 | font-weight: bold; 108 | margin-left: 3px; 109 | color: black; 110 | border-radius: 5px; 111 | border: 1px solid rgb(174, 89, 89); 112 | padding: 10px; 113 | background-color: #fecfcf; 114 | } 115 | 116 | #pasted-credentials { 117 | width: 100%; 118 | margin-bottom: 0px; 119 | line-height: 2.6ex; 120 | height: 11ex; 121 | } 122 | 123 | label { 124 | font-weight: bold; 125 | display: inline-block; 126 | margin-right: 0.6rem; 127 | } 128 | 129 | input { 130 | width: 45% !important; 131 | max-width: fit-content; 132 | padding: 0.3rem 0.6rem !important; 133 | height: fit-content !important; 134 | } -------------------------------------------------------------------------------- /apps/app1/bin/hello.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | "use strict"; 3 | Object.defineProperty(exports, "__esModule", { value: true }); 4 | require("source-map-support/register"); 5 | const cdk = require("aws-cdk-lib"); 6 | const hello_stack_1 = require("../lib/hello-stack"); 7 | export const app = new cdk.App(); 8 | new hello_stack_1.HelloStack(app, 'HelloStack1', { 9 | /* If you don't specify 'env', this stack will be environment-agnostic. 10 | * Account/Region-dependent features and context lookups will not work, 11 | * but a single synthesized template can be deployed anywhere. */ 12 | /* Uncomment the next line to specialize this stack for the AWS Account 13 | * and Region that are implied by the current CLI configuration. */ 14 | // env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, 15 | /* Uncomment the next line if you know exactly what Account and Region you 16 | * want to deploy the stack to. */ 17 | // env: { account: '123456789012', region: 'us-east-1' }, 18 | /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ 19 | }); 20 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVsbG8uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJoZWxsby50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFDQSx1Q0FBcUM7QUFDckMsbUNBQW1DO0FBQ25DLG9EQUFnRDtBQUVoRCxNQUFNLEdBQUcsR0FBRyxJQUFJLEdBQUcsQ0FBQyxHQUFHLEVBQUUsQ0FBQztBQUMxQixJQUFJLHdCQUFVLENBQUMsR0FBRyxFQUFFLFlBQVksRUFBRTtBQUNoQzs7aUVBRWlFO0FBRWpFO21FQUNtRTtBQUNuRSw2RkFBNkY7QUFFN0Y7a0NBQ2tDO0FBQ2xDLHlEQUF5RDtBQUV6RCw4RkFBOEY7Q0FDL0YsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiIyEvdXNyL2Jpbi9lbnYgbm9kZVxuaW1wb3J0ICdzb3VyY2UtbWFwLXN1cHBvcnQvcmVnaXN0ZXInO1xuaW1wb3J0ICogYXMgY2RrIGZyb20gJ2F3cy1jZGstbGliJztcbmltcG9ydCB7IEhlbGxvU3RhY2sgfSBmcm9tICcuLi9saWIvaGVsbG8tc3RhY2snO1xuXG5jb25zdCBhcHAgPSBuZXcgY2RrLkFwcCgpO1xubmV3IEhlbGxvU3RhY2soYXBwLCAnSGVsbG9TdGFjaycsIHtcbiAgLyogSWYgeW91IGRvbid0IHNwZWNpZnkgJ2VudicsIHRoaXMgc3RhY2sgd2lsbCBiZSBlbnZpcm9ubWVudC1hZ25vc3RpYy5cbiAgICogQWNjb3VudC9SZWdpb24tZGVwZW5kZW50IGZlYXR1cmVzIGFuZCBjb250ZXh0IGxvb2t1cHMgd2lsbCBub3Qgd29yayxcbiAgICogYnV0IGEgc2luZ2xlIHN5bnRoZXNpemVkIHRlbXBsYXRlIGNhbiBiZSBkZXBsb3llZCBhbnl3aGVyZS4gKi9cblxuICAvKiBVbmNvbW1lbnQgdGhlIG5leHQgbGluZSB0byBzcGVjaWFsaXplIHRoaXMgc3RhY2sgZm9yIHRoZSBBV1MgQWNjb3VudFxuICAgKiBhbmQgUmVnaW9uIHRoYXQgYXJlIGltcGxpZWQgYnkgdGhlIGN1cnJlbnQgQ0xJIGNvbmZpZ3VyYXRpb24uICovXG4gIC8vIGVudjogeyBhY2NvdW50OiBwcm9jZXNzLmVudi5DREtfREVGQVVMVF9BQ0NPVU5ULCByZWdpb246IHByb2Nlc3MuZW52LkNES19ERUZBVUxUX1JFR0lPTiB9LFxuXG4gIC8qIFVuY29tbWVudCB0aGUgbmV4dCBsaW5lIGlmIHlvdSBrbm93IGV4YWN0bHkgd2hhdCBBY2NvdW50IGFuZCBSZWdpb24geW91XG4gICAqIHdhbnQgdG8gZGVwbG95IHRoZSBzdGFjayB0by4gKi9cbiAgLy8gZW52OiB7IGFjY291bnQ6ICcxMjM0NTY3ODkwMTInLCByZWdpb246ICd1cy1lYXN0LTEnIH0sXG5cbiAgLyogRm9yIG1vcmUgaW5mb3JtYXRpb24sIHNlZSBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vY2RrL2xhdGVzdC9ndWlkZS9lbnZpcm9ubWVudHMuaHRtbCAqL1xufSk7Il19 -------------------------------------------------------------------------------- /apps/app2/bin/hello.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | "use strict"; 3 | Object.defineProperty(exports, "__esModule", { value: true }); 4 | require("source-map-support/register"); 5 | const cdk = require("aws-cdk-lib"); 6 | const hello_stack_1 = require("../lib/hello-stack"); 7 | export const app = new cdk.App(); 8 | new hello_stack_1.HelloStack(app, 'HelloStack2', { 9 | /* If you don't specify 'env', this stack will be environment-agnostic. 10 | * Account/Region-dependent features and context lookups will not work, 11 | * but a single synthesized template can be deployed anywhere. */ 12 | /* Uncomment the next line to specialize this stack for the AWS Account 13 | * and Region that are implied by the current CLI configuration. */ 14 | // env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, 15 | /* Uncomment the next line if you know exactly what Account and Region you 16 | * want to deploy the stack to. */ 17 | // env: { account: '123456789012', region: 'us-east-1' }, 18 | /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ 19 | }); 20 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVsbG8uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJoZWxsby50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFDQSx1Q0FBcUM7QUFDckMsbUNBQW1DO0FBQ25DLG9EQUFnRDtBQUVoRCxNQUFNLEdBQUcsR0FBRyxJQUFJLEdBQUcsQ0FBQyxHQUFHLEVBQUUsQ0FBQztBQUMxQixJQUFJLHdCQUFVLENBQUMsR0FBRyxFQUFFLFlBQVksRUFBRTtBQUNoQzs7aUVBRWlFO0FBRWpFO21FQUNtRTtBQUNuRSw2RkFBNkY7QUFFN0Y7a0NBQ2tDO0FBQ2xDLHlEQUF5RDtBQUV6RCw4RkFBOEY7Q0FDL0YsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiIyEvdXNyL2Jpbi9lbnYgbm9kZVxuaW1wb3J0ICdzb3VyY2UtbWFwLXN1cHBvcnQvcmVnaXN0ZXInO1xuaW1wb3J0ICogYXMgY2RrIGZyb20gJ2F3cy1jZGstbGliJztcbmltcG9ydCB7IEhlbGxvU3RhY2sgfSBmcm9tICcuLi9saWIvaGVsbG8tc3RhY2snO1xuXG5jb25zdCBhcHAgPSBuZXcgY2RrLkFwcCgpO1xubmV3IEhlbGxvU3RhY2soYXBwLCAnSGVsbG9TdGFjaycsIHtcbiAgLyogSWYgeW91IGRvbid0IHNwZWNpZnkgJ2VudicsIHRoaXMgc3RhY2sgd2lsbCBiZSBlbnZpcm9ubWVudC1hZ25vc3RpYy5cbiAgICogQWNjb3VudC9SZWdpb24tZGVwZW5kZW50IGZlYXR1cmVzIGFuZCBjb250ZXh0IGxvb2t1cHMgd2lsbCBub3Qgd29yayxcbiAgICogYnV0IGEgc2luZ2xlIHN5bnRoZXNpemVkIHRlbXBsYXRlIGNhbiBiZSBkZXBsb3llZCBhbnl3aGVyZS4gKi9cblxuICAvKiBVbmNvbW1lbnQgdGhlIG5leHQgbGluZSB0byBzcGVjaWFsaXplIHRoaXMgc3RhY2sgZm9yIHRoZSBBV1MgQWNjb3VudFxuICAgKiBhbmQgUmVnaW9uIHRoYXQgYXJlIGltcGxpZWQgYnkgdGhlIGN1cnJlbnQgQ0xJIGNvbmZpZ3VyYXRpb24uICovXG4gIC8vIGVudjogeyBhY2NvdW50OiBwcm9jZXNzLmVudi5DREtfREVGQVVMVF9BQ0NPVU5ULCByZWdpb246IHByb2Nlc3MuZW52LkNES19ERUZBVUxUX1JFR0lPTiB9LFxuXG4gIC8qIFVuY29tbWVudCB0aGUgbmV4dCBsaW5lIGlmIHlvdSBrbm93IGV4YWN0bHkgd2hhdCBBY2NvdW50IGFuZCBSZWdpb24geW91XG4gICAqIHdhbnQgdG8gZGVwbG95IHRoZSBzdGFjayB0by4gKi9cbiAgLy8gZW52OiB7IGFjY291bnQ6ICcxMjM0NTY3ODkwMTInLCByZWdpb246ICd1cy1lYXN0LTEnIH0sXG5cbiAgLyogRm9yIG1vcmUgaW5mb3JtYXRpb24sIHNlZSBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vY2RrL2xhdGVzdC9ndWlkZS9lbnZpcm9ubWVudHMuaHRtbCAqL1xufSk7Il19 -------------------------------------------------------------------------------- /apps/app3/bin/hello.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | "use strict"; 3 | Object.defineProperty(exports, "__esModule", { value: true }); 4 | require("source-map-support/register"); 5 | const cdk = require("aws-cdk-lib"); 6 | const hello_stack_1 = require("../lib/hello-stack"); 7 | export const app = new cdk.App(); 8 | new hello_stack_1.HelloStack(app, 'HelloStack3', { 9 | /* If you don't specify 'env', this stack will be environment-agnostic. 10 | * Account/Region-dependent features and context lookups will not work, 11 | * but a single synthesized template can be deployed anywhere. */ 12 | /* Uncomment the next line to specialize this stack for the AWS Account 13 | * and Region that are implied by the current CLI configuration. */ 14 | // env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, 15 | /* Uncomment the next line if you know exactly what Account and Region you 16 | * want to deploy the stack to. */ 17 | // env: { account: '123456789012', region: 'us-east-1' }, 18 | /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ 19 | }); 20 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaGVsbG8uanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJoZWxsby50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFDQSx1Q0FBcUM7QUFDckMsbUNBQW1DO0FBQ25DLG9EQUFnRDtBQUVoRCxNQUFNLEdBQUcsR0FBRyxJQUFJLEdBQUcsQ0FBQyxHQUFHLEVBQUUsQ0FBQztBQUMxQixJQUFJLHdCQUFVLENBQUMsR0FBRyxFQUFFLFlBQVksRUFBRTtBQUNoQzs7aUVBRWlFO0FBRWpFO21FQUNtRTtBQUNuRSw2RkFBNkY7QUFFN0Y7a0NBQ2tDO0FBQ2xDLHlEQUF5RDtBQUV6RCw4RkFBOEY7Q0FDL0YsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiIyEvdXNyL2Jpbi9lbnYgbm9kZVxuaW1wb3J0ICdzb3VyY2UtbWFwLXN1cHBvcnQvcmVnaXN0ZXInO1xuaW1wb3J0ICogYXMgY2RrIGZyb20gJ2F3cy1jZGstbGliJztcbmltcG9ydCB7IEhlbGxvU3RhY2sgfSBmcm9tICcuLi9saWIvaGVsbG8tc3RhY2snO1xuXG5jb25zdCBhcHAgPSBuZXcgY2RrLkFwcCgpO1xubmV3IEhlbGxvU3RhY2soYXBwLCAnSGVsbG9TdGFjaycsIHtcbiAgLyogSWYgeW91IGRvbid0IHNwZWNpZnkgJ2VudicsIHRoaXMgc3RhY2sgd2lsbCBiZSBlbnZpcm9ubWVudC1hZ25vc3RpYy5cbiAgICogQWNjb3VudC9SZWdpb24tZGVwZW5kZW50IGZlYXR1cmVzIGFuZCBjb250ZXh0IGxvb2t1cHMgd2lsbCBub3Qgd29yayxcbiAgICogYnV0IGEgc2luZ2xlIHN5bnRoZXNpemVkIHRlbXBsYXRlIGNhbiBiZSBkZXBsb3llZCBhbnl3aGVyZS4gKi9cblxuICAvKiBVbmNvbW1lbnQgdGhlIG5leHQgbGluZSB0byBzcGVjaWFsaXplIHRoaXMgc3RhY2sgZm9yIHRoZSBBV1MgQWNjb3VudFxuICAgKiBhbmQgUmVnaW9uIHRoYXQgYXJlIGltcGxpZWQgYnkgdGhlIGN1cnJlbnQgQ0xJIGNvbmZpZ3VyYXRpb24uICovXG4gIC8vIGVudjogeyBhY2NvdW50OiBwcm9jZXNzLmVudi5DREtfREVGQVVMVF9BQ0NPVU5ULCByZWdpb246IHByb2Nlc3MuZW52LkNES19ERUZBVUxUX1JFR0lPTiB9LFxuXG4gIC8qIFVuY29tbWVudCB0aGUgbmV4dCBsaW5lIGlmIHlvdSBrbm93IGV4YWN0bHkgd2hhdCBBY2NvdW50IGFuZCBSZWdpb24geW91XG4gICAqIHdhbnQgdG8gZGVwbG95IHRoZSBzdGFjayB0by4gKi9cbiAgLy8gZW52OiB7IGFjY291bnQ6ICcxMjM0NTY3ODkwMTInLCByZWdpb246ICd1cy1lYXN0LTEnIH0sXG5cbiAgLyogRm9yIG1vcmUgaW5mb3JtYXRpb24sIHNlZSBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vY2RrL2xhdGVzdC9ndWlkZS9lbnZpcm9ubWVudHMuaHRtbCAqL1xufSk7Il19 -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | AWS CDK (& SDK) in Electron 13 | 14 | 15 | 16 | 17 |

AWS CDK (& SDK) in Electron

18 |

This Electron app is a proof of concept that demonstrates the ability to build an Electron app that can operate on 19 | an AWS account using both SDK and CDK. It provides a model for deploying pre-built CDK apps, and for composing and 20 | deploying 21 | a CDK app on the fly. It can also do CDK bootstrapping, deploy standard CloudFormation templates and run SDK 22 | commands.

23 | 24 |
25 | 26 |
27 |

28 |
29 |
30 | 31 |
32 |

Credentials

33 |

Paste your credentials here. You can provide either an IAM user's AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, or 34 | temporary credentials that include the AWS_SESSION_TOKEN. It's not necessary to add "export" to each line, but 35 | it's ok if you do.

36 | 40 |

OR

41 |

42 | 44 |

45 |

47 |

48 |
49 |
50 |

Loading regions...please wait...

51 |
52 |
53 |

Select the region you want to work in first.

54 |
55 |
56 |

SDK commands

57 |

This button will run an S3 list-buckets SDK command.

58 |

59 |
60 |

Output from SDK

61 |
62 |
63 |
64 |
65 |
66 |
67 |

Deployment Status

68 |
71 |

Stack Outputs

72 |
73 |

DeployStack Response

74 |
76 |
77 |
78 |

CloudFormation Template Deployment

79 |
80 |

81 |

CDK App Operations

82 |

Bootstrap CDK will bootstrap the account for the chosen region.

83 | 84 |

Create a simple stack in code and deploy it (see source in preload.js). Specify a bucket name that will be 85 | used to name the bucket that will be created by CDK.

86 |

89 |

Dynamically compose CodePipeline pipelines for CDK apps bundled into the Electron app.

90 | 91 |

Clear Stack Monitors

92 | 93 |
94 |
95 |
96 | 97 |
98 |
99 |

Current credentials

100 |

These credentials are currently being used:

101 |

102 |

103 |
104 | 105 |
This app is using Node.js , 106 | Chromium , Electron , CDK & 108 | SDK .
109 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /assets/square-jelly-box.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Load Awesome v1.1.0 (http://github.danielcardoso.net/load-awesome/) 3 | * Copyright 2015 Daniel Cardoso <@DanielCardoso> 4 | * Licensed under MIT 5 | */ 6 | .la-square-jelly-box, 7 | .la-square-jelly-box > div { 8 | position: relative; 9 | -webkit-box-sizing: border-box; 10 | -moz-box-sizing: border-box; 11 | box-sizing: border-box; 12 | } 13 | 14 | .la-square-jelly-box { 15 | display: block; 16 | font-size: 0; 17 | color: #fff; 18 | } 19 | 20 | .la-square-jelly-box.la-dark { 21 | color: #333; 22 | } 23 | 24 | .la-square-jelly-box > div { 25 | display: inline-block; 26 | float: none; 27 | background-color: currentColor; 28 | border: 0 solid currentColor; 29 | } 30 | 31 | .la-square-jelly-box { 32 | width: 32px; 33 | height: 32px; 34 | } 35 | 36 | .la-square-jelly-box > div:nth-child(1), 37 | .la-square-jelly-box > div:nth-child(2) { 38 | position: absolute; 39 | left: 0; 40 | width: 100%; 41 | } 42 | 43 | .la-square-jelly-box > div:nth-child(1) { 44 | top: -25%; 45 | z-index: 1; 46 | height: 100%; 47 | border-radius: 10%; 48 | -webkit-animation: square-jelly-box-animate .6s -.1s linear infinite; 49 | -moz-animation: square-jelly-box-animate .6s -.1s linear infinite; 50 | -o-animation: square-jelly-box-animate .6s -.1s linear infinite; 51 | animation: square-jelly-box-animate .6s -.1s linear infinite; 52 | } 53 | 54 | .la-square-jelly-box > div:nth-child(2) { 55 | bottom: -9%; 56 | height: 10%; 57 | background: #000; 58 | border-radius: 50%; 59 | opacity: .2; 60 | -webkit-animation: square-jelly-box-shadow .6s -.1s linear infinite; 61 | -moz-animation: square-jelly-box-shadow .6s -.1s linear infinite; 62 | -o-animation: square-jelly-box-shadow .6s -.1s linear infinite; 63 | animation: square-jelly-box-shadow .6s -.1s linear infinite; 64 | } 65 | 66 | .la-square-jelly-box.la-sm { 67 | width: 16px; 68 | height: 16px; 69 | } 70 | 71 | .la-square-jelly-box.la-2x { 72 | width: 64px; 73 | height: 64px; 74 | } 75 | 76 | .la-square-jelly-box.la-3x { 77 | width: 96px; 78 | height: 96px; 79 | } 80 | 81 | /* 82 | * Animations 83 | */ 84 | @-webkit-keyframes square-jelly-box-animate { 85 | 17% { 86 | border-bottom-right-radius: 10%; 87 | } 88 | 25% { 89 | -webkit-transform: translateY(25%) rotate(22.5deg); 90 | transform: translateY(25%) rotate(22.5deg); 91 | } 92 | 50% { 93 | border-bottom-right-radius: 100%; 94 | -webkit-transform: translateY(50%) scale(1, .9) rotate(45deg); 95 | transform: translateY(50%) scale(1, .9) rotate(45deg); 96 | } 97 | 75% { 98 | -webkit-transform: translateY(25%) rotate(67.5deg); 99 | transform: translateY(25%) rotate(67.5deg); 100 | } 101 | 100% { 102 | -webkit-transform: translateY(0) rotate(90deg); 103 | transform: translateY(0) rotate(90deg); 104 | } 105 | } 106 | @-moz-keyframes square-jelly-box-animate { 107 | 17% { 108 | border-bottom-right-radius: 10%; 109 | } 110 | 25% { 111 | -moz-transform: translateY(25%) rotate(22.5deg); 112 | transform: translateY(25%) rotate(22.5deg); 113 | } 114 | 50% { 115 | border-bottom-right-radius: 100%; 116 | -moz-transform: translateY(50%) scale(1, .9) rotate(45deg); 117 | transform: translateY(50%) scale(1, .9) rotate(45deg); 118 | } 119 | 75% { 120 | -moz-transform: translateY(25%) rotate(67.5deg); 121 | transform: translateY(25%) rotate(67.5deg); 122 | } 123 | 100% { 124 | -moz-transform: translateY(0) rotate(90deg); 125 | transform: translateY(0) rotate(90deg); 126 | } 127 | } 128 | @-o-keyframes square-jelly-box-animate { 129 | 17% { 130 | border-bottom-right-radius: 10%; 131 | } 132 | 25% { 133 | -o-transform: translateY(25%) rotate(22.5deg); 134 | transform: translateY(25%) rotate(22.5deg); 135 | } 136 | 50% { 137 | border-bottom-right-radius: 100%; 138 | -o-transform: translateY(50%) scale(1, .9) rotate(45deg); 139 | transform: translateY(50%) scale(1, .9) rotate(45deg); 140 | } 141 | 75% { 142 | -o-transform: translateY(25%) rotate(67.5deg); 143 | transform: translateY(25%) rotate(67.5deg); 144 | } 145 | 100% { 146 | -o-transform: translateY(0) rotate(90deg); 147 | transform: translateY(0) rotate(90deg); 148 | } 149 | } 150 | @keyframes square-jelly-box-animate { 151 | 17% { 152 | border-bottom-right-radius: 10%; 153 | } 154 | 25% { 155 | -webkit-transform: translateY(25%) rotate(22.5deg); 156 | -moz-transform: translateY(25%) rotate(22.5deg); 157 | -o-transform: translateY(25%) rotate(22.5deg); 158 | transform: translateY(25%) rotate(22.5deg); 159 | } 160 | 50% { 161 | border-bottom-right-radius: 100%; 162 | -webkit-transform: translateY(50%) scale(1, .9) rotate(45deg); 163 | -moz-transform: translateY(50%) scale(1, .9) rotate(45deg); 164 | -o-transform: translateY(50%) scale(1, .9) rotate(45deg); 165 | transform: translateY(50%) scale(1, .9) rotate(45deg); 166 | } 167 | 75% { 168 | -webkit-transform: translateY(25%) rotate(67.5deg); 169 | -moz-transform: translateY(25%) rotate(67.5deg); 170 | -o-transform: translateY(25%) rotate(67.5deg); 171 | transform: translateY(25%) rotate(67.5deg); 172 | } 173 | 100% { 174 | -webkit-transform: translateY(0) rotate(90deg); 175 | -moz-transform: translateY(0) rotate(90deg); 176 | -o-transform: translateY(0) rotate(90deg); 177 | transform: translateY(0) rotate(90deg); 178 | } 179 | } 180 | 181 | @-webkit-keyframes square-jelly-box-shadow { 182 | 50% { 183 | -webkit-transform: scale(1.25, 1); 184 | transform: scale(1.25, 1); 185 | } 186 | } 187 | 188 | @-moz-keyframes square-jelly-box-shadow { 189 | 50% { 190 | -moz-transform: scale(1.25, 1); 191 | transform: scale(1.25, 1); 192 | } 193 | } 194 | 195 | @-o-keyframes square-jelly-box-shadow { 196 | 50% { 197 | -o-transform: scale(1.25, 1); 198 | transform: scale(1.25, 1); 199 | } 200 | } 201 | 202 | @keyframes square-jelly-box-shadow { 203 | 50% { 204 | -webkit-transform: scale(1.25, 1); 205 | -moz-transform: scale(1.25, 1); 206 | -o-transform: scale(1.25, 1); 207 | transform: scale(1.25, 1); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS CDK in Electron 2 | 3 | This project demonstrates how to 4 | 5 | * Deploy CDK projects, and 6 | * Run SDK commands 7 | 8 | from within an [Electron](https://www.electronjs.org/) app. The app can contain all the dependencies the end user needs, meaning they will not have to install [AWS SDK for JavaScript](https://aws.amazon.com/sdk-for-javascript/), [AWS CDK](https://aws.amazon.com/cdk/), or use the command line. 9 | 10 | This sample project can be built as a standalone app that is able to both query and deploy changes to an account. What the standalone app is able to do is dependant on the permissions you give it when you configure it with credentials. It is always recommended that you grant the [least privileges](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#grant-least-privilege) required to perform the tasks you want the app to do. 11 | 12 | The example app allows you to 13 | 14 | * use the SDK to list the buckets in your account, 15 | * deploy a bundled [AWS CloudFormation](https://aws.amazon.com/cloudformation/) template, 16 | * bootstrap an account for CDK, 17 | * compose a small CDK stack in code and deploy it, and 18 | * install bundled CDK apps using [AWS CodePipeline](https://aws.amazon.com/codepipeline/) and [AWS CodeBuild](https://aws.amazon.com/codebuild/), 19 | 20 | all from within the Electron app environment. 21 | 22 | >NOTE: These samples are not intended for production use and are provided to illustrate functionality only. 23 | 24 | ## To Install, Build and Run 25 | 26 | To clone and run this repository you'll need [Git](https://git-scm.com) and [Node.js](https://nodejs.org/en/download/) (which comes with [npm](http://npmjs.com)) installed on your computer. 27 | 28 | From your command line, `git clone` this repo and then `cd` into the directory root. Then: 29 | 30 | ```bash 31 | npm install 32 | ``` 33 | To install the dependencies, followed by 34 | 35 | ```bash 36 | npm start 37 | ``` 38 | To start the Electron app in developer mode. 39 | 40 | To build a standalone packaged Electron app: 41 | 42 | ``` 43 | npm run make 44 | ``` 45 | 46 | The binary will be output to a folder called `out` in your project directory. 47 | 48 | >Note: If you're using Linux Bash for Windows, [see this guide](https://www.howtogeek.com/261575/how-to-run-graphical-linux-desktop-applications-from-windows-10s-bash-shell/) or use `node` from the command prompt. 49 | 50 | 51 | ## To use the Electron App 52 | 53 | Once it opens you can paste temporary credentials for your account into the credentials box. The format for these credentials is the standard `Command line or programmatic access` output for setting AWS environment variables, as provided by [AWS Identity Center](https://aws.amazon.com/iam/identity-center/) (formerly AWS SSO). 54 | 55 | Using [temporary credentials is best practice](https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html#bp-users-federation-idp), but the sample app also supports [IAM user access keys](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_access-keys.html) if you need to use them. 56 | 57 | Once the credentials are pasted in, click `Submit Credentials`. 58 | 59 | If the provided credentials are valid, the `Regions` dropdown will be populated (via an SDK call), and the credentials, account and principal you are operating as will appear in the `Current credentials` box in the UI. You can change to different credentials using the `Change Credentials` button. 60 | 61 | ### SDK example 62 | To see a sample SDK call, click the `List Buckets` button to list the buckets in your account. Provided that the credentials you have supplied have the necessary permissions, the output should appear in the UI under the heading `Output from SDK`. 63 | 64 | [](assets/ui.png) 65 | 66 | ### CloudFormation example 67 | Any CloudFormation templates found in the `cfn-templates` directory will be listed in the drop-down and can be deployed by clicking the `Deploy Template` button. Output will appear in the outputs panel. 68 | 69 | ### CDK example 70 | Three CDK operations can be executed using the CDK test buttons. 71 | 72 | `Bootstrap CDK` will bootstrap the CDK into the account you have loaded credentials for, and the region selected in the dropdown. You may need to run this if you are using an account that you have not used with CDK before. 73 | 74 | The `Create Simple CDK stack` button will compose a simple CDK stack called `electron-bucket-stack` that contains an S3 Bucket and will deploy it into whichever region you have chosen. 75 | 76 | `Create CDK App Pipeline stack` will look in the `apps` directory and will create a deployment pipeline using CodePipeline for each zipped CDK project it finds. The pipelines will be deployed in a CDK stack called `cdk-app-delivery-pipeline-stack` and it will deploy into whichever region you have chosen. 77 | 78 | Zipped CDK projects must contain a `buildspec.yml` at the archive root that instructs CodeBuild how to deploy each app. 79 | 80 | > Note that you can install CDK apps written in any language that can be deployed using CodeBuild. 81 | 82 | Deploy CDK apps written in different languages by specifying different `runtime-versions` in the install phase, eg this is a sample `buildspec.yml` that will deploy a single-stack CDK project written in TypeScript: 83 | 84 | ``` 85 | # buildspec.yml 86 | version: 0.2 87 | phases: 88 | install: 89 | runtime-versions: 90 | nodejs: 16 91 | commands: 92 | - npm install -g aws-cdk@latest 93 | - npm install 94 | build: 95 | commands: 96 | - npm run build 97 | - cdk deploy 98 | ``` 99 | 100 | There are four sample apps you can use to test this in the `apps` folder, each with a working `buildspec.yaml`. Zip the contents (not the folder) of the CDK project (without any locally installed node_modules or python env directories) and place the archive at the root of the `apps` directory, eg 101 | 102 | ``` 103 | apps/ 104 | ├── app1.zip 105 | ├── app1.json 106 | ├── app2.zip 107 | ├── app2.json 108 | ├── app3.zip 109 | ├── app3.json 110 | ├── python-cdk-app.zip 111 | ├── python-cdk-app.json 112 | ``` 113 | 114 | The JSON manifest file that sits alongside the zipped CDK project contains a `Stacks` property that describes the stacks that will be tracked by the Electron app during deployment and a `CodeBuildPolicy` property that will be used to create the service role that will be assumed by CodeBuild when it runs the deployment commands. 115 | 116 | 117 | ``` json 118 | { 119 | "Stacks": [ 120 | { 121 | "name": "HelloStack1", 122 | "hasOutputs": false 123 | } 124 | ], 125 | "CodeBuildPolicy": { 126 | "Version": "2012-10-17", 127 | "Statement": [ 128 | { 129 | "Sid": "AllowAllAccess", 130 | "Effect": "Allow", 131 | "Action": "*", 132 | "Resource": "*" 133 | } 134 | ] 135 | } 136 | } 137 | ``` 138 | ------ 139 | 140 | ## Electron docs 141 | 142 | You can learn more about Electron on [electronjs.org](https://electronjs.org/). 143 | 144 | ## License 145 | 146 | See the LICENSE file. 147 | -------------------------------------------------------------------------------- /renderer.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | let region = null 5 | let account = null 6 | let cfMonitor = null; 7 | let stackStates = {}; 8 | let stackInfoRequestors = {}; 9 | let previousStatusReport = ""; 10 | let stackOutputs = {} 11 | let debugMessages = {}; 12 | let bouncyBox = `
`; 13 | 14 | document.documentElement.setAttribute('data-theme', 'light') 15 | document.getElementById("create-bucket-with-cdk").value = "electron-bucket-" + Math.random().toString(36).slice(2, 10); 16 | 17 | const cfnStates = function (stack, state) { 18 | let stacks = window.getStacksInProgress(); 19 | stackStates[stack] = state; 20 | // if this is a stack event, check to see if the stack is complete 21 | if (state.LogicalResourceId === stack) { 22 | stacks[stack].status = state.ResourceStatus; 23 | // if stack is complete, request stack info to retrieve outputs, if applicable 24 | if (stacks[stack].status.match(/_COMPLETE/) && stacks[stack].hasOutputs) { 25 | stackInfoRequestors[stack] = setInterval(requestStackInfo, 5000, stack); 26 | } 27 | } 28 | updateStackEventDisplay(stacks) 29 | } 30 | 31 | const updateStackEventDisplay = function (stacks) { 32 | let cfDiv = document.getElementById('cf-stack-states'); 33 | let htmlOutput = ""; 34 | inProgressStacks = Object.keys(stacks).length; 35 | if (Object.keys(stackStates).length === 0) { 36 | htmlOutput = bouncyBox; 37 | } 38 | for (thisStack in stackStates) { 39 | htmlOutput += `` + thisStack + ": "; 40 | if (stackStates[thisStack].LogicalResourceId === thisStack) { 41 | htmlOutput += stackStates[thisStack].ResourceStatus + "
"; 42 | if (!stackStates[thisStack].ResourceStatus.match(/_COMPLETE/)) { 43 | htmlOutput += bouncyBox; 44 | console.log(`Will keep checking state for ${thisStack} as its state is ${stackStates[thisStack].ResourceStatus}`) 45 | } 46 | else { 47 | inProgressStacks = --inProgressStacks; 48 | } 49 | } 50 | else { 51 | htmlOutput += stackStates[thisStack].LogicalResourceId + ": " + stackStates[thisStack].ResourceStatus + "
"; 52 | htmlOutput += bouncyBox; 53 | console.log("event is for " + stackStates[thisStack].LogicalResourceId); 54 | } 55 | htmlOutput += ` (Console)
`; 56 | } 57 | 58 | if (previousStatusReport !== htmlOutput) { 59 | cfDiv.innerHTML = previousStatusReport = htmlOutput; 60 | } 61 | if (inProgressStacks === 0) { 62 | console.log("all stacks done"); 63 | clearInterval(cfMonitor); 64 | } 65 | 66 | console.log(stackStates); 67 | console.log(stackInfoRequestors); 68 | } 69 | 70 | const cfnOutputs = function (stack, outputs) { 71 | // console.log("cfnOutputs for " + stack + ": " + JSON.stringify(outputs)) 72 | if (outputs.Stacks[0].Outputs.length > 0) { 73 | //we have the outputs so kill off the requestor 74 | clearInterval(stackInfoRequestors[stack]); 75 | } 76 | stackOutputs[stack] = outputs; 77 | showOutputs(); 78 | } 79 | 80 | const showOutputs = function () { 81 | let cfOutDiv = document.getElementById('cf-stack-outputs'); 82 | let htmlOutputs = ""; 83 | for (stack in stackOutputs) { 84 | if (stackOutputs[stack].hasOwnProperty("Stacks")) { 85 | htmlOutputs += "" + stack + ":
"; 86 | for (let i = 0; i < stackOutputs[stack].Stacks[0].Outputs.length; i++) { 87 | htmlOutputs += "" + stackOutputs[stack].Stacks[0].Outputs[i].OutputKey + ": " + stackOutputs[stack].Stacks[0].Outputs[i].OutputValue + "
"; 88 | } 89 | } 90 | } 91 | cfOutDiv.innerHTML = htmlOutputs; 92 | } 93 | 94 | const requestStackInfo = function (stack) { 95 | console.log("Getting outputs for " + stack); 96 | window.getStackInfo(stack, cfnOutputs) 97 | } 98 | 99 | const cfnMessages = function (failure, success, stackName) { 100 | if (!stackName) { 101 | if (success && success.hasOwnProperty("StackId")) { 102 | stackName = success.StackId.split('/')[1]; 103 | } 104 | else { 105 | document.getElementById('cf-stack-states').innerHTML = ""; 106 | stackName = "Unknown"; 107 | } 108 | } 109 | console.log("callback from cloudformation deploy") 110 | console.log(failure) 111 | console.log(success) 112 | if (failure) { 113 | debugMessages[stackName] = failure; 114 | } 115 | else { 116 | debugMessages[stackName] = success; 117 | } 118 | let htmlOutput = ""; 119 | for (stack in debugMessages) { 120 | htmlOutput += `${stack}: ` + JSON.stringify(debugMessages[stack]) + "
"; 121 | } 122 | document.getElementById('deploystack-output').innerHTML = htmlOutput; 123 | } 124 | 125 | function resetUi() { 126 | document.getElementById('credentials-block').hidden = false; 127 | document.getElementById('sdk-output').hidden = false; 128 | document.getElementById('loading-block').hidden = true; 129 | document.getElementById('commands-block').hidden = true; 130 | document.getElementById('error-block').hidden = true; 131 | document.getElementById('error-divider').hidden = true; 132 | document.getElementById('change-credentials-block').hidden = true; 133 | } 134 | 135 | resetUi(); 136 | 137 | function displayErrors(errorString) { 138 | if (errorString) { 139 | document.getElementById('errors').innerHTML = errorString; 140 | document.getElementById('error-block').hidden = false; 141 | document.getElementById('error-divider').hidden = false; 142 | document.getElementById('error-divider').scrollIntoView(); 143 | } 144 | else { 145 | document.getElementById('errors').innerHTML = ""; 146 | document.getElementById('error-block').hidden = true; 147 | document.getElementById('error-divider').hidden = true; 148 | } 149 | } 150 | 151 | function addCredentials() { 152 | let errors = ""; 153 | let credentials = document.getElementById("pasted-credentials").value; 154 | if (document.getElementById("access-key-id").value && document.getElementById("secret-access-key").value) { 155 | credentials = "AWS_ACCESS_KEY_ID=" + document.getElementById("access-key-id").value + "\nAWS_SECRET_ACCESS_KEY=" + document.getElementById("secret-access-key").value + "\n"; 156 | } 157 | // console.log(credentials); 158 | window.setCredentials(credentials, function (err, data) { 159 | if (err) { 160 | errors = err; 161 | } else { 162 | account = data.AWS_ACCOUNT_ID 163 | let credDisplay = ""; 164 | for (let key in data) { 165 | credDisplay += "" + key + ": " + data[key] + "
\n" 166 | } 167 | document.getElementById('debug').innerHTML = credDisplay; 168 | document.getElementById('credentials-block').hidden = true; 169 | document.getElementById('change-credentials-block').hidden = false; 170 | document.getElementById('loading-block').hidden = false; 171 | populateRegionsSelect() 172 | } 173 | }) 174 | displayErrors(errors); 175 | } 176 | 177 | function getBuckets() { 178 | let errors = ""; 179 | document.getElementById('errors').innerHTML = ""; 180 | document.getElementById('errors').hidden = true; 181 | document.getElementById('sdk-output').hidden = false; 182 | try { 183 | window.listBuckets(function (err, data) { 184 | if (err) { 185 | console.log("Error", err); 186 | errors = JSON.stringify(err); 187 | } else { 188 | console.log("Success", data.Buckets); 189 | document.getElementById('sdk-output').hidden = false; 190 | let output = ""; 191 | for (let i = 0; i < data.Buckets.length; i++) { 192 | output += "* " + data.Buckets[i].Name + "
\n"; 193 | } 194 | document.getElementById('sdk-result').innerHTML = output; 195 | } 196 | }) 197 | } catch (err) { 198 | console.log("Error", err); 199 | errors = JSON.stringify(err); 200 | } 201 | displayErrors(errors); 202 | } 203 | 204 | function bootstrapCdkApp() { 205 | document.getElementById('cf-stack-states').innerHTML = bouncyBox; 206 | window.bootstrapCdk(account, region, cfnMessages); 207 | } 208 | 209 | function createCdkStackFunc() { 210 | document.getElementById('cf-stack-states').innerHTML = bouncyBox; 211 | clearInterval(cfMonitor); 212 | window.createCdkStack(account, region, document.getElementById("create-bucket-with-cdk").value, cfnMessages); 213 | cfMonitor = setInterval(showStacksProgressFunc, 7000); 214 | } 215 | 216 | function createCdkAppPipelineStackFunc() { 217 | document.getElementById('cf-stack-states').innerHTML = bouncyBox; 218 | clearInterval(cfMonitor); 219 | window.createCdkAppPipelineStack(account, region, cfnMessages); 220 | cfMonitor = setInterval(showStacksProgressFunc, 7000); 221 | } 222 | 223 | function populateTemplateSelect() { 224 | window.getCfnTemplates((data) => { 225 | let sortedTemplates = data.sort(); 226 | let templateSelect = document.getElementById('template-select') 227 | templateSelect.innerHTML = ''; 228 | for (let i = 0; i < sortedTemplates.length; i++) { 229 | let option = document.createElement("option"); 230 | option.text = sortedTemplates[i].replace(/.json/, ""); 231 | option.value = sortedTemplates[i]; 232 | templateSelect.add(option); 233 | } 234 | }) 235 | } 236 | 237 | function deployCfnTemplate() { 238 | if (document.getElementById('template-select').value) { 239 | clearInterval(cfMonitor); 240 | window.deployCloudFormationTemplate(document.getElementById('template-select').value, cfnMessages); 241 | cfMonitor = setInterval(showStacksProgressFunc, 5000); 242 | } 243 | } 244 | 245 | function populateRegionsSelect() { 246 | window.getRegions((err, data) => { 247 | if (err) { 248 | console.error(err) 249 | } 250 | else { 251 | console.log(data.Regions) 252 | region = 'us-east-1'; 253 | let regionSelect = document.getElementById('region-select') 254 | regionSelect.innerHTML = ''; 255 | for (let i = 0; i < data.Regions.length; i++) { 256 | let option = document.createElement("option"); 257 | option.text = data.Regions[i].RegionName; 258 | regionSelect.add(option); 259 | } 260 | regionSelect.value = region; 261 | document.getElementById('commands-block').hidden = false; 262 | document.getElementById('loading-block').hidden = true; 263 | } 264 | }) 265 | } 266 | 267 | function updateRegion() { 268 | region = document.getElementById('region-select').value 269 | window.setRegion(region); 270 | console.log("region changed to " + region) 271 | } 272 | 273 | function showStacksProgressFunc() { 274 | let stacks = window.getStacksInProgress(); 275 | if (stacks) { 276 | console.log("checking stack progress for these stacks: " + JSON.stringify(stacks)); 277 | for (stack in stacks) { 278 | window.getStackEvents(stack, cfnStates); 279 | } 280 | } 281 | else { 282 | clearInterval(cfMonitor); 283 | } 284 | } 285 | 286 | function clearStackMonitors() { 287 | window.clearTrackedStacks(); 288 | } 289 | 290 | function openConsole(url) { 291 | window.openInBrowser(url); 292 | } 293 | 294 | 295 | populateTemplateSelect(); 296 | 297 | document.getElementById("region-select").addEventListener("change", updateRegion); 298 | document.getElementById("change-credentials-button").addEventListener("click", resetUi); 299 | document.getElementById("submit-credentials-button").addEventListener("click", addCredentials); 300 | document.getElementById("get-buckets-button").addEventListener("click", getBuckets); 301 | document.getElementById("bootstrap-cdk-button").addEventListener("click", bootstrapCdkApp); 302 | document.getElementById("create-cdk-stack-button").addEventListener("click", createCdkStackFunc); 303 | document.getElementById("create-cdk-app-pipeline-button").addEventListener("click", createCdkAppPipelineStackFunc); 304 | document.getElementById("deploy-template-button").addEventListener("click", deployCfnTemplate); 305 | document.getElementById("clear-stack-monitors").addEventListener("click", clearStackMonitors); 306 | 307 | -------------------------------------------------------------------------------- /preload.js: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | const sdk = require('aws-sdk'); 5 | const cdk = require('aws-cdk-lib'); 6 | const cxapi = require('@aws-cdk/cx-api') 7 | const cfDeployments = require('aws-cdk/lib/api/cloudformation-deployments') 8 | // const cfDeployments = require('aws-cdk/lib/api/deployments') 9 | const bootstrap = require('aws-cdk/lib/api/bootstrap') 10 | const cdkVersion = require('aws-cdk/lib/version'); 11 | const SdkProvider = require('aws-cdk/lib/api/aws-auth'); 12 | const { rootPath } = require('electron-root-path'); 13 | const path = require('path'); 14 | const fs = require("fs"); 15 | const { contextBridge } = require('electron'); 16 | 17 | const stacks = {}; 18 | 19 | process.versions['cdk'] = cdkVersion.DISPLAY_VERSION; 20 | process.versions['sdk'] = sdk.VERSION; 21 | 22 | //set default region 23 | const REGION = process.env.AWS_DEFAULT_REGION || 'us-east-1'; 24 | sdk.config.update({ region: REGION }); 25 | 26 | function openInBrowser(url) { 27 | require('electron').shell.openExternal(url); 28 | } 29 | contextBridge.exposeInMainWorld('openInBrowser', (url) => openInBrowser(url)) 30 | /* 31 | * When the user submits credentials in the UI, this gets called 32 | */ 33 | function setCredentials(data, callback) { 34 | try { 35 | let rows = data.split("\n"); 36 | if (rows.length < 2) { 37 | // Need at least 3 rows of data 38 | callback(new Error("You haven't supplied sufficient information in either the pasted credentials or the supplied key and secret. Please check your inputs and try again.")) 39 | } 40 | else { 41 | /* 42 | * Parse the input text and find the credentials. 43 | * NB we don't know if these creds are valid until we check them using 44 | * sts.getCallerIdentity further down 45 | */ 46 | let envVars = {}; 47 | for (let i = 0; i < rows.length; i++) { 48 | if (rows[i].match(/AWS_ACCESS_KEY_ID/)) { 49 | let accessKey = rows[i].split('=')[1].replace(/\W/, '').substring(0, 20) 50 | if (rows[i].includes(accessKey)) { 51 | process.env['AWS_ACCESS_KEY_ID'] = envVars.AWS_ACCESS_KEY_ID = accessKey 52 | } 53 | else { 54 | console.log("parsed 'AWS_ACCESS_KEY_ID' didn't match input") 55 | console.log(rows[i]) 56 | console.log(accessKey) 57 | } 58 | } 59 | else if (rows[i].match(/AWS_SECRET_ACCESS_KEY/)) { 60 | let secretKey = rows[i].split('=')[1].replace(/[^\w/+]/, '').substring(0, 40) 61 | if (rows[i].includes(secretKey)) { 62 | process.env['AWS_SECRET_ACCESS_KEY'] = secretKey 63 | envVars.AWS_SECRET_ACCESS_KEY = secretKey.split('').map(x => "*").join('') 64 | } 65 | else { 66 | console.log("parsed 'AWS_SECRET_ACCESS_KEY' didn't match input") 67 | console.log(rows[i]) 68 | console.log(secretKey) 69 | } 70 | } 71 | else if (rows[i].match(/AWS_SESSION_TOKEN/)) { 72 | let sessToken = rows[i].replace(/.+?=/, '').replace(/[^\w/+=]/, '') 73 | if (rows[i].includes(sessToken)) { 74 | process.env['AWS_SESSION_TOKEN'] = sessToken 75 | // envVars.AWS_SESSION_TOKEN = sessToken.split('').map(x => sessToken.charAt(Math.round(Math.random() * sessToken.length))).join('') 76 | } 77 | else { 78 | console.log("parsed 'AWS_SESSION_TOKEN' didn't match input") 79 | console.log(rows[i]) 80 | console.log(sessToken) 81 | } 82 | } 83 | } 84 | // console.log(envVars) 85 | if (envVars.hasOwnProperty('AWS_ACCESS_KEY_ID') && envVars.hasOwnProperty('AWS_SECRET_ACCESS_KEY')) { 86 | // Let's see if the credentials work 87 | let sts = new sdk.STS() 88 | sts.getCallerIdentity((err, data) => { 89 | if (err) { 90 | console.log(err) 91 | callback(new Error("Unable to create a session with the current supplied credentials.")) 92 | } 93 | else { 94 | // console.log(data) 95 | envVars.AWS_ACCOUNT_ID = process.env.CDK_DEFAULT_ACCOUNT = data.Account 96 | let identityArn = data.Arn.split(':') 97 | envVars.IDENTITY = identityArn.pop() 98 | envVars.SDK_VERSION = sdk.VERSION; 99 | callback(null, envVars) 100 | } 101 | }) 102 | } 103 | else { 104 | callback(new Error("Wasn't able to parse valid credentials from pasted text!")) 105 | } 106 | } 107 | } 108 | catch (e) { 109 | console.log(e); 110 | callback(new Error("Credential processing failed: " + JSON.stringify(e))) 111 | } 112 | } 113 | 114 | contextBridge.exposeInMainWorld('setCredentials', (data, cb) => setCredentials(data, cb)) 115 | 116 | 117 | /* 118 | * Once we have credentials, find the regions that are available to this identity 119 | */ 120 | function getRegions(callback) { 121 | let ec2 = new sdk.EC2(); 122 | ec2.describeRegions(function (err, data) { 123 | if (err) { 124 | console.log(err, err.stack); // an error occurred 125 | callback(err, null); 126 | } 127 | else { 128 | callback(null, data); 129 | } 130 | }) 131 | } 132 | contextBridge.exposeInMainWorld('getRegions', (cb) => getRegions(cb)) 133 | 134 | /* 135 | * When the user chooses a new region in the UI, this gets called 136 | */ 137 | function setRegion(region) { 138 | console.log("resetting SDK to region " + region) 139 | sdk.config.update({ region: region }); 140 | process.env.AWS_DEFAULT_REGION = process.env.CDK_DEFAULT_REGION = region; 141 | } 142 | 143 | contextBridge.exposeInMainWorld('setRegion', (region) => setRegion(region)) 144 | 145 | /* 146 | * Once we have credentials, there are some things we can do with SDK 147 | */ 148 | function listBuckets(callback) { 149 | let s3 = new sdk.S3({ apiVersion: '2006-03-01' }) 150 | s3.listBuckets(callback); 151 | } 152 | contextBridge.exposeInMainWorld('listBuckets', (cb) => listBuckets(cb)) 153 | 154 | 155 | /* 156 | * Poll for updates for stacks we're monitoring 157 | */ 158 | function getStackEvents(stackName, callback) { 159 | let cloudFormation = new sdk.CloudFormation(); 160 | console.log("trying to get stack events for " + stackName); 161 | try { 162 | cloudFormation.describeStackEvents({ StackName: stackName }, 163 | function (err, data) { 164 | if (err) { 165 | console.warn(err); // an error occurred 166 | } 167 | else { 168 | console.log(data.StackEvents); 169 | callback(stackName, data.StackEvents[0]); // successful response 170 | } 171 | }); 172 | } 173 | catch (e) { 174 | console.log(e); 175 | } 176 | } 177 | contextBridge.exposeInMainWorld('getStackEvents', (stack, cb) => getStackEvents(stack, cb)) 178 | 179 | /* 180 | * Get info for stacks we're monitoring 181 | */ 182 | function getStackInfo(stackName, callback) { 183 | let cloudFormation = new sdk.CloudFormation(); 184 | console.log("trying to get stack info for " + stackName); 185 | try { 186 | cloudFormation.describeStacks({ StackName: stackName }, 187 | function (err, data) { 188 | if (err) { 189 | console.warn(err); // an error occurred 190 | callback(stackName, err); 191 | } 192 | else { 193 | console.log(data); 194 | callback(stackName, data); // successful response 195 | } 196 | }); 197 | } 198 | catch (e) { 199 | console.log(e); 200 | } 201 | } 202 | contextBridge.exposeInMainWorld('getStackInfo', (stack, cb) => getStackInfo(stack, cb)) 203 | 204 | /* 205 | * Once we have credentials, find the regions that are available to this identity 206 | */ 207 | function getCfnTemplates(callback) { 208 | let templates = []; 209 | fs.readdir(path.join(rootPath, 'cfn-templates'), (err, files) => { 210 | if (err) 211 | console.log(err); 212 | else { 213 | files.forEach(file => { 214 | if (file.match(/.json$/)) { 215 | templates.push(file); 216 | } 217 | }) 218 | } 219 | callback(templates); 220 | }) 221 | } 222 | contextBridge.exposeInMainWorld('getCfnTemplates', (cb) => getCfnTemplates(cb)) 223 | /* 224 | * We can deploy pre-defined CFN templates that we bundle into the app via SDK 225 | */ 226 | function deployCloudFormationTemplate(template, callback) { 227 | console.log("deploying " + template); 228 | let templateBody = fs.readFileSync(__dirname + '/cfn-templates/' + template, 'utf-8'); 229 | let cloudformation = new sdk.CloudFormation(); 230 | let stackName = template.replace(/(.json|.yaml|.yml)/, '') + "-stack"; 231 | stacks[stackName] = { hasOutputs: JSON.parse(templateBody).hasOwnProperty("Outputs") }; 232 | try { 233 | let params = { 234 | StackName: stackName, 235 | Capabilities: ['CAPABILITY_NAMED_IAM'], 236 | TemplateBody: templateBody 237 | } 238 | cloudformation.createStack(params, callback); 239 | } 240 | catch (e) { 241 | delete stacks[stackName]; 242 | } 243 | } 244 | contextBridge.exposeInMainWorld('deployCloudFormationTemplate', (template, cb) => deployCloudFormationTemplate(template, cb)) 245 | 246 | /* 247 | * CDK-related functions are defined here 248 | * We can bootstrap an account and region 249 | */ 250 | async function bootstrapCdk(account, region, callback) { 251 | console.log('running bootstrapCdk for ' + "aws://" + account + "/" + region) 252 | const sdkProvider = await SdkProvider.SdkProvider.withAwsCliCompatibleDefaults({ 253 | profile: process.env.AWS_PROFILE, 254 | }) 255 | const bootstrapper = new bootstrap.Bootstrapper({ 'source': 'default' }) 256 | bootstrapper.bootstrapEnvironment({ name: 'electron-bootstrapper', account: account, region: region }, sdkProvider).then((result) => callback(null, result), (failure) => callback(failure, null)) 257 | } 258 | contextBridge.exposeInMainWorld('bootstrapCdk', (account, region, cb) => bootstrapCdk(account, region, cb)) 259 | 260 | /* 261 | * This function synths the CDK app and returns the stack template 262 | */ 263 | const getStackArtifact = (app, stack) => { 264 | const synthesized = app.synth(); 265 | 266 | // Reload the synthesized artifact for stack using the cxapi from dependencies 267 | const assembly = new cxapi.CloudAssembly(synthesized.directory); 268 | 269 | return cxapi.CloudFormationStackArtifact.fromManifest( 270 | assembly, 271 | stack.artifactId, 272 | synthesized.getStackArtifact(stack.artifactId).manifest 273 | ); 274 | }; 275 | 276 | /* 277 | * This function creates a basic CDK app that creates an S3 bucket 278 | */ 279 | async function createCdkStack(account, region, bucketName, callback) { 280 | 281 | let app = new cdk.App() 282 | let stackName = bucketName + '-stack' 283 | let stack = new cdk.Stack(app, stackName, { 284 | env: { 285 | region: region, 286 | account: account 287 | } 288 | }) 289 | stacks[stackName] = { hasOutputs: true }; 290 | 291 | // console.log(sdk.config) 292 | let bucket = new cdk.aws_s3.Bucket(stack, bucketName.replace(/\-(\w)/g, RegExp.$1.toUpperCase()), { 293 | removalPolicy: cdk.RemovalPolicy.DESTROY, 294 | bucketName: bucketName, 295 | blockPublicAccess: cdk.aws_s3.BlockPublicAccess.BLOCK_ALL, 296 | autoDeleteObjects: true, 297 | }); 298 | 299 | stack.exportValue(bucket.bucketName) 300 | 301 | // console.log(SdkProvider) 302 | const sdkProvider = await SdkProvider.SdkProvider.withAwsCliCompatibleDefaults({ 303 | profile: process.env.AWS_PROFILE, 304 | }) 305 | const cloudFormation = new cfDeployments.CloudFormationDeployments({ sdkProvider }); 306 | let stackArtifact = getStackArtifact(app, stack); 307 | 308 | // console.log(stackArtifact) 309 | try { 310 | cloudFormation.deployStack({ 311 | stack: stackArtifact, 312 | quiet: false, 313 | }).then((result) => callback(null, result, stackName), (failure) => callback(failure, null, stackName)); 314 | console.log(stacks); 315 | } 316 | catch (e) { 317 | console.log(e) 318 | delete stacks[stackName]; 319 | callback(e, null) 320 | } 321 | } 322 | contextBridge.exposeInMainWorld('createCdkStack', (account, region, bucketName, cb) => createCdkStack(account, region, bucketName, cb)) 323 | 324 | function getStacksInProgress() { 325 | return stacks; 326 | } 327 | contextBridge.exposeInMainWorld('getStacksInProgress', () => getStacksInProgress()) 328 | 329 | /* 330 | * This function creates a CDK app that builds an app deployment pipeline for bundled zipped cdk apps 331 | */ 332 | async function createCdkAppPipelineStack(account, region, callback) { 333 | 334 | let app = new cdk.App() 335 | let stack = new cdk.Stack(app, 'cdk-app-delivery-pipeline-stack', { 336 | env: { 337 | region: region, 338 | account: account 339 | } 340 | }) 341 | stacks[stack.stackName] = { hasOutputs: false }; 342 | let codepipelinePolicyJson = { 343 | "Version": "2012-10-17", 344 | "Statement": [ 345 | { 346 | "Sid": "AllowAdminAccess", 347 | "Effect": "Allow", 348 | "Action": "*", 349 | "Resource": "*" 350 | } 351 | ] 352 | }; 353 | //get the codepipeline policy document if one exists 354 | if (fs.existsSync(path.join(rootPath, 'pipeline-assets/codepipeline-service-role-policy.json'))) { 355 | codepipelinePolicyJson = JSON.parse(fs.readFileSync(path.join(rootPath, 'pipeline-assets/codepipeline-service-role-policy.json'), 356 | { encoding: 'utf8', flag: 'r' })); 357 | } 358 | else { 359 | console.log("Running CodePipeline as admin - provide a least-privileges codepipeline-service-role-policy.json in the pipeline-assets/ folder of this project.") 360 | } 361 | 362 | const codepipelineManagedPolicy = new cdk.aws_iam.ManagedPolicy(stack, "CodepipelineManagedPolicy", { 363 | document: cdk.aws_iam.PolicyDocument.fromJson(codepipelinePolicyJson) 364 | }); 365 | const codepipelineServiceRole = new cdk.aws_iam.Role(stack, "CodepipelineServiceRole", { 366 | assumedBy: new cdk.aws_iam.ServicePrincipal('codepipeline.amazonaws.com'), 367 | managedPolicies: [codepipelineManagedPolicy] 368 | }) 369 | 370 | const apps = []; 371 | // console.log(path.join(rootPath, 'apps')) 372 | fs.readdir(path.join(rootPath, 'apps'), (err, files) => { 373 | if (err) 374 | console.log(err); 375 | else { 376 | files.forEach(file => { 377 | if (file.match(/.zip$/)) { 378 | apps.push(file); 379 | // console.log(file); 380 | } 381 | }) 382 | } 383 | if (apps.length > 0) { 384 | console.log("Found apps to install:"); 385 | console.log(apps); 386 | } 387 | else { 388 | console.log("Found no apps to install!") 389 | } 390 | 391 | const appPaths = []; 392 | 393 | for (let i = 0; i < apps.length; i++) { 394 | // console.log(apps[i]) 395 | let codebuildPolicyJson = { 396 | "Version": "2012-10-17", 397 | "Statement": [ 398 | { 399 | "Sid": "AllowAdminAccess", 400 | "Effect": "Allow", 401 | "Action": "*", 402 | "Resource": "*" 403 | } 404 | ] 405 | }; 406 | 407 | //override the policy document if one exists 408 | if (fs.existsSync(path.join(rootPath, 'apps', apps[i].replace('.zip', '.json')))) { 409 | let manifestJson = JSON.parse(fs.readFileSync(path.join(rootPath, 'apps', apps[i].replace('.zip', '.json')), 410 | { encoding: 'utf8', flag: 'r' })); 411 | // console.log("overriding default policy for " + apps[i]); 412 | console.log(manifestJson); 413 | codebuildPolicyJson = manifestJson.CodeBuildPolicy; 414 | for (let i = 0; i < manifestJson.Stacks.length; i++) { 415 | stacks[manifestJson.Stacks[i].name] = { hasOutputs: manifestJson.Stacks[i].hasOutputs }; 416 | } 417 | console.log(stacks); 418 | } 419 | else { 420 | console.log(apps[i] + " - running CodeBuild as admin - it's preferable to provide a JSON policy document for " + apps[i] + " instead.") 421 | } 422 | 423 | let codebuildPolicyDocument = cdk.aws_iam.PolicyDocument.fromJson(codebuildPolicyJson); 424 | 425 | let artifactBucket = new cdk.aws_s3.Bucket(stack, "ArtifactBucket" + i, { 426 | blockPublicAccess: cdk.aws_s3.BlockPublicAccess.BLOCK_ALL, 427 | autoDeleteObjects: true, 428 | removalPolicy: cdk.RemovalPolicy.DESTROY 429 | }) 430 | 431 | let pipeline = new cdk.aws_codepipeline.Pipeline(stack, "CodePipeline" + i, { 432 | artifactBucket: artifactBucket, 433 | restartExecutionOnUpdate: true, 434 | role: codepipelineServiceRole 435 | }) 436 | 437 | let sourceOutput = new cdk.aws_codepipeline.Artifact() 438 | 439 | appPaths[i] = new cdk.aws_s3_assets.Asset(stack, 'AppAsset' + i, { 440 | path: path.join(rootPath, 'apps', apps[i]), 441 | }); 442 | // console.log(appPaths) 443 | 444 | let sourceAction = new cdk.aws_codepipeline_actions.S3SourceAction({ 445 | actionName: 'S3Source' + i, 446 | bucketKey: appPaths[i].s3ObjectKey, 447 | bucket: appPaths[i].bucket, 448 | output: sourceOutput, 449 | trigger: cdk.aws_codepipeline_actions.S3Trigger.NONE, 450 | }); 451 | 452 | pipeline.addStage({ stageName: "Source", actions: [sourceAction] }) 453 | 454 | let buildOutput = new cdk.aws_codepipeline.Artifact(); 455 | 456 | let codebuildServiceRole = new cdk.aws_iam.Role(stack, "CodebuildServiceRole" + i, { 457 | assumedBy: new cdk.aws_iam.ServicePrincipal('codebuild.amazonaws.com'), 458 | managedPolicies: [new cdk.aws_iam.ManagedPolicy(stack, "CodebuildManagedPolicy" + i, { 459 | document: codebuildPolicyDocument 460 | })] 461 | }) 462 | 463 | let buildProject = new cdk.aws_codebuild.PipelineProject(stack, "BuildProject" + i, { 464 | buildSpec: cdk.aws_codebuild.BuildSpec.fromSourceFilename("buildspec.yml"), environment: { 465 | buildImage: cdk.aws_codebuild.LinuxBuildImage.AMAZON_LINUX_2_4, 466 | privileged: true, 467 | }, 468 | role: codebuildServiceRole 469 | }) 470 | 471 | pipeline.addStage({ 472 | stageName: "Build", 473 | actions: [new cdk.aws_codepipeline_actions.CodeBuildAction({ 474 | actionName: "Build", 475 | project: buildProject, 476 | input: sourceOutput, 477 | outputs: [buildOutput] 478 | })] 479 | }); 480 | } 481 | 482 | // console.log(appPaths); 483 | const paths = [] 484 | for (let i = 0; i < apps.length; i++) { 485 | paths.push({ 486 | bucket: appPaths[i].bucket, 487 | objectPrefix: appPaths[i].s3ObjectKey, 488 | }) 489 | } 490 | // console.log(paths); 491 | /* 492 | * You could configure the pipeline to run when files change in the S3 bucket 493 | */ 494 | // const trail = new cdk.aws_cloudtrail.Trail(stack, 'CloudTrail'); 495 | // trail.addS3EventSelector(paths, { 496 | // readWriteType: cdk.aws_cloudtrail.ReadWriteType.WRITE_ONLY, 497 | // }); 498 | }) 499 | 500 | // console.log(SdkProvider) 501 | const sdkProvider = await SdkProvider.SdkProvider.withAwsCliCompatibleDefaults({ 502 | profile: process.env.AWS_PROFILE, 503 | }) 504 | const cloudFormation = new cfDeployments.CloudFormationDeployments({ sdkProvider }); 505 | let stackArtifact = getStackArtifact(app, stack); 506 | 507 | try { 508 | cloudFormation.deployStack({ 509 | stack: stackArtifact, 510 | quiet: false, 511 | }).then((result) => callback(null, result, stack.stackName), (failure) => callback(failure, null, stack.stackName)) 512 | } 513 | catch (e) { 514 | console.log(e) 515 | delete stacks[stack.stackName]; 516 | callback(e, null, stack.stackName) 517 | } 518 | } 519 | contextBridge.exposeInMainWorld('createCdkAppPipelineStack', (account, region, cb) => createCdkAppPipelineStack(account, region, cb)) 520 | 521 | 522 | /* 523 | * Reset the UI and list of tracked stacks 524 | */ 525 | function clearTrackedStacks() { 526 | for (const key in stacks) { 527 | delete stacks[key]; 528 | } 529 | } 530 | contextBridge.exposeInMainWorld('clearTrackedStacks', () => clearTrackedStacks()) 531 | 532 | /* 533 | * preps the page, from Electron quickstart https://www.electronjs.org/docs/latest/tutorial/quick-start 534 | * Licensed under the MIT License 535 | */ 536 | window.addEventListener('DOMContentLoaded', () => { 537 | 538 | const replaceText = (selector, text) => { 539 | const element = document.getElementById(selector) 540 | if (element) element.innerText = text 541 | } 542 | 543 | for (const type of ['chrome', 'node', 'electron', 'cdk', 'sdk']) { 544 | replaceText(`${type}-version`, process.versions[type]) 545 | } 546 | 547 | }) 548 | -------------------------------------------------------------------------------- /assets/pico.min.css: -------------------------------------------------------------------------------- 1 | @charset "UTF-8";/*! 2 | * Pico.css v1.5.3 (https://picocss.com) 3 | * Copyright 2019-2022 - Licensed under MIT 4 | */:root{--font-family:system-ui,-apple-system,"Segoe UI","Roboto","Ubuntu","Cantarell","Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--line-height:1.5;--font-weight:400;--font-size:16px;--border-radius:0.25rem;--border-width:1px;--outline-width:3px;--spacing:1rem;--typography-spacing-vertical:1.5rem;--block-spacing-vertical:calc(var(--spacing) * 2);--block-spacing-horizontal:var(--spacing);--grid-spacing-vertical:0;--grid-spacing-horizontal:var(--spacing);--form-element-spacing-vertical:0.75rem;--form-element-spacing-horizontal:1rem;--nav-element-spacing-vertical:1rem;--nav-element-spacing-horizontal:0.5rem;--nav-link-spacing-vertical:0.5rem;--nav-link-spacing-horizontal:0.5rem;--form-label-font-weight:var(--font-weight);--transition:0.2s ease-in-out}@media (min-width:576px){:root{--font-size:17px}}@media (min-width:768px){:root{--font-size:18px}}@media (min-width:992px){:root{--font-size:19px}}@media (min-width:1200px){:root{--font-size:20px}}@media (min-width:576px){body>footer,body>header,body>main,section{--block-spacing-vertical:calc(var(--spacing) * 2.5)}}@media (min-width:768px){body>footer,body>header,body>main,section{--block-spacing-vertical:calc(var(--spacing) * 3)}}@media (min-width:992px){body>footer,body>header,body>main,section{--block-spacing-vertical:calc(var(--spacing) * 3.5)}}@media (min-width:1200px){body>footer,body>header,body>main,section{--block-spacing-vertical:calc(var(--spacing) * 4)}}@media (min-width:576px){article{--block-spacing-horizontal:calc(var(--spacing) * 1.25)}}@media (min-width:768px){article{--block-spacing-horizontal:calc(var(--spacing) * 1.5)}}@media (min-width:992px){article{--block-spacing-horizontal:calc(var(--spacing) * 1.75)}}@media (min-width:1200px){article{--block-spacing-horizontal:calc(var(--spacing) * 2)}}dialog>article{--block-spacing-vertical:calc(var(--spacing) * 2);--block-spacing-horizontal:var(--spacing)}@media (min-width:576px){dialog>article{--block-spacing-vertical:calc(var(--spacing) * 2.5);--block-spacing-horizontal:calc(var(--spacing) * 1.25)}}@media (min-width:768px){dialog>article{--block-spacing-vertical:calc(var(--spacing) * 3);--block-spacing-horizontal:calc(var(--spacing) * 1.5)}}a{--text-decoration:none}a.contrast,a.secondary{--text-decoration:underline}small{--font-size:0.875em}h1,h2,h3,h4,h5,h6{--font-weight:700}h1{--font-size:2rem;--typography-spacing-vertical:3rem}h2{--font-size:1.75rem;--typography-spacing-vertical:2.625rem}h3{--font-size:1.5rem;--typography-spacing-vertical:2.25rem}h4{--font-size:1.25rem;--typography-spacing-vertical:1.874rem}h5{--font-size:1.125rem;--typography-spacing-vertical:1.6875rem}[type=checkbox],[type=radio]{--border-width:2px}[type=checkbox][role=switch]{--border-width:3px}tfoot td,tfoot th,thead td,thead th{--border-width:3px}:not(thead):not(tfoot)>*>td{--font-size:0.875em}code,kbd,pre,samp{--font-family:"Menlo","Consolas","Roboto Mono","Ubuntu Monospace","Noto Mono","Oxygen Mono","Liberation Mono",monospace,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji"}kbd{--font-weight:bolder}:root:not([data-theme=dark]),[data-theme=light]{color-scheme:light;--background-color:#fff;--color:hsl(205deg, 20%, 32%);--h1-color:hsl(205deg, 30%, 15%);--h2-color:#24333e;--h3-color:hsl(205deg, 25%, 23%);--h4-color:#374956;--h5-color:hsl(205deg, 20%, 32%);--h6-color:#4d606d;--muted-color:hsl(205deg, 10%, 50%);--muted-border-color:hsl(205deg, 20%, 94%);--primary:hsl(195deg, 85%, 41%);--primary-hover:hsl(195deg, 90%, 32%);--primary-focus:rgba(16, 149, 193, 0.125);--primary-inverse:#fff;--secondary:hsl(205deg, 15%, 41%);--secondary-hover:hsl(205deg, 20%, 32%);--secondary-focus:rgba(89, 107, 120, 0.125);--secondary-inverse:#fff;--contrast:hsl(205deg, 30%, 15%);--contrast-hover:#000;--contrast-focus:rgba(89, 107, 120, 0.125);--contrast-inverse:#fff;--mark-background-color:#fff2ca;--mark-color:#543a26;--ins-color:#388e3c;--del-color:#c62828;--blockquote-border-color:var(--muted-border-color);--blockquote-footer-color:var(--muted-color);--button-box-shadow:0 0 0 rgba(0, 0, 0, 0);--button-hover-box-shadow:0 0 0 rgba(0, 0, 0, 0);--form-element-background-color:transparent;--form-element-border-color:hsl(205deg, 14%, 68%);--form-element-color:var(--color);--form-element-placeholder-color:var(--muted-color);--form-element-active-background-color:transparent;--form-element-active-border-color:var(--primary);--form-element-focus-color:var(--primary-focus);--form-element-disabled-background-color:hsl(205deg, 18%, 86%);--form-element-disabled-border-color:hsl(205deg, 14%, 68%);--form-element-disabled-opacity:0.5;--form-element-invalid-border-color:#c62828;--form-element-invalid-active-border-color:#d32f2f;--form-element-invalid-focus-color:rgba(211, 47, 47, 0.125);--form-element-valid-border-color:#388e3c;--form-element-valid-active-border-color:#43a047;--form-element-valid-focus-color:rgba(67, 160, 71, 0.125);--switch-background-color:hsl(205deg, 16%, 77%);--switch-color:var(--primary-inverse);--switch-checked-background-color:var(--primary);--range-border-color:hsl(205deg, 18%, 86%);--range-active-border-color:hsl(205deg, 16%, 77%);--range-thumb-border-color:var(--background-color);--range-thumb-color:var(--secondary);--range-thumb-hover-color:var(--secondary-hover);--range-thumb-active-color:var(--primary);--table-border-color:var(--muted-border-color);--table-row-stripped-background-color:#f6f8f9;--code-background-color:hsl(205deg, 20%, 94%);--code-color:var(--muted-color);--code-kbd-background-color:var(--contrast);--code-kbd-color:var(--contrast-inverse);--code-tag-color:hsl(330deg, 40%, 50%);--code-property-color:hsl(185deg, 40%, 40%);--code-value-color:hsl(40deg, 20%, 50%);--code-comment-color:hsl(205deg, 14%, 68%);--accordion-border-color:var(--muted-border-color);--accordion-close-summary-color:var(--color);--accordion-open-summary-color:var(--muted-color);--card-background-color:var(--background-color);--card-border-color:var(--muted-border-color);--card-box-shadow:0.0145rem 0.029rem 0.174rem rgba(27, 40, 50, 0.01698),0.0335rem 0.067rem 0.402rem rgba(27, 40, 50, 0.024),0.0625rem 0.125rem 0.75rem rgba(27, 40, 50, 0.03),0.1125rem 0.225rem 1.35rem rgba(27, 40, 50, 0.036),0.2085rem 0.417rem 2.502rem rgba(27, 40, 50, 0.04302),0.5rem 1rem 6rem rgba(27, 40, 50, 0.06),0 0 0 0.0625rem rgba(27, 40, 50, 0.015);--card-sectionning-background-color:#fbfbfc;--dropdown-background-color:#fbfbfc;--dropdown-border-color:#e1e6eb;--dropdown-box-shadow:var(--card-box-shadow);--dropdown-color:var(--color);--dropdown-hover-background-color:hsl(205deg, 20%, 94%);--modal-overlay-background-color:rgba(213, 220, 226, 0.8);--progress-background-color:hsl(205deg, 18%, 86%);--progress-color:var(--primary);--loading-spinner-opacity:0.5;--tooltip-background-color:var(--contrast);--tooltip-color:var(--contrast-inverse);--icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23FFF' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(65, 84, 98, 0.999)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(255, 255, 255, 0.999)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button-inverse:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(255, 255, 255, 0.999)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-close:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(115, 130, 140, 0.999)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");--icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(65, 84, 98, 0.999)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--icon-invalid:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(198, 40, 40, 0.999)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");--icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23FFF' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--icon-search:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(65, 84, 98, 0.999)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(65, 84, 98, 0.999)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--icon-valid:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(56, 142, 60, 0.999)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E")}@media only screen and (prefers-color-scheme:dark){:root:not([data-theme=light]){color-scheme:dark;--background-color:#11191f;--color:hsl(205deg, 16%, 77%);--h1-color:hsl(205deg, 20%, 94%);--h2-color:#e1e6eb;--h3-color:hsl(205deg, 18%, 86%);--h4-color:#c8d1d8;--h5-color:hsl(205deg, 16%, 77%);--h6-color:#afbbc4;--muted-color:hsl(205deg, 10%, 50%);--muted-border-color:#1f2d38;--primary:hsl(195deg, 85%, 41%);--primary-hover:hsl(195deg, 80%, 50%);--primary-focus:rgba(16, 149, 193, 0.25);--primary-inverse:#fff;--secondary:hsl(205deg, 15%, 41%);--secondary-hover:hsl(205deg, 10%, 50%);--secondary-focus:rgba(115, 130, 140, 0.25);--secondary-inverse:#fff;--contrast:hsl(205deg, 20%, 94%);--contrast-hover:#fff;--contrast-focus:rgba(115, 130, 140, 0.25);--contrast-inverse:#000;--mark-background-color:#d1c284;--mark-color:#11191f;--ins-color:#388e3c;--del-color:#c62828;--blockquote-border-color:var(--muted-border-color);--blockquote-footer-color:var(--muted-color);--button-box-shadow:0 0 0 rgba(0, 0, 0, 0);--button-hover-box-shadow:0 0 0 rgba(0, 0, 0, 0);--form-element-background-color:#11191f;--form-element-border-color:#374956;--form-element-color:var(--color);--form-element-placeholder-color:var(--muted-color);--form-element-active-background-color:var(--form-element-background-color);--form-element-active-border-color:var(--primary);--form-element-focus-color:var(--primary-focus);--form-element-disabled-background-color:hsl(205deg, 25%, 23%);--form-element-disabled-border-color:hsl(205deg, 20%, 32%);--form-element-disabled-opacity:0.5;--form-element-invalid-border-color:#b71c1c;--form-element-invalid-active-border-color:#c62828;--form-element-invalid-focus-color:rgba(198, 40, 40, 0.25);--form-element-valid-border-color:#2e7d32;--form-element-valid-active-border-color:#388e3c;--form-element-valid-focus-color:rgba(56, 142, 60, 0.25);--switch-background-color:#374956;--switch-color:var(--primary-inverse);--switch-checked-background-color:var(--primary);--range-border-color:#24333e;--range-active-border-color:hsl(205deg, 25%, 23%);--range-thumb-border-color:var(--background-color);--range-thumb-color:var(--secondary);--range-thumb-hover-color:var(--secondary-hover);--range-thumb-active-color:var(--primary);--table-border-color:var(--muted-border-color);--table-row-stripped-background-color:rgba(115, 130, 140, 0.05);--code-background-color:#18232c;--code-color:var(--muted-color);--code-kbd-background-color:var(--contrast);--code-kbd-color:var(--contrast-inverse);--code-tag-color:hsl(330deg, 30%, 50%);--code-property-color:hsl(185deg, 30%, 50%);--code-value-color:hsl(40deg, 10%, 50%);--code-comment-color:#4d606d;--accordion-border-color:var(--muted-border-color);--accordion-active-summary-color:var(--primary);--accordion-close-summary-color:var(--color);--accordion-open-summary-color:var(--muted-color);--card-background-color:#141e26;--card-border-color:var(--card-background-color);--card-box-shadow:0.0145rem 0.029rem 0.174rem rgba(0, 0, 0, 0.01698),0.0335rem 0.067rem 0.402rem rgba(0, 0, 0, 0.024),0.0625rem 0.125rem 0.75rem rgba(0, 0, 0, 0.03),0.1125rem 0.225rem 1.35rem rgba(0, 0, 0, 0.036),0.2085rem 0.417rem 2.502rem rgba(0, 0, 0, 0.04302),0.5rem 1rem 6rem rgba(0, 0, 0, 0.06),0 0 0 0.0625rem rgba(0, 0, 0, 0.015);--card-sectionning-background-color:#18232c;--dropdown-background-color:hsl(205deg, 30%, 15%);--dropdown-border-color:#24333e;--dropdown-box-shadow:var(--card-box-shadow);--dropdown-color:var(--color);--dropdown-hover-background-color:rgba(36, 51, 62, 0.75);--modal-overlay-background-color:rgba(36, 51, 62, 0.9);--progress-background-color:#24333e;--progress-color:var(--primary);--loading-spinner-opacity:0.5;--tooltip-background-color:var(--contrast);--tooltip-color:var(--contrast-inverse);--icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23FFF' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(162, 175, 185, 0.999)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(255, 255, 255, 0.999)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button-inverse:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(0, 0, 0, 0.999)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-close:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(115, 130, 140, 0.999)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");--icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(162, 175, 185, 0.999)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--icon-invalid:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(183, 28, 28, 0.999)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");--icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23FFF' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--icon-search:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(162, 175, 185, 0.999)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(162, 175, 185, 0.999)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--icon-valid:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(46, 125, 50, 0.999)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E")}}[data-theme=dark]{color-scheme:dark;--background-color:#11191f;--color:hsl(205deg, 16%, 77%);--h1-color:hsl(205deg, 20%, 94%);--h2-color:#e1e6eb;--h3-color:hsl(205deg, 18%, 86%);--h4-color:#c8d1d8;--h5-color:hsl(205deg, 16%, 77%);--h6-color:#afbbc4;--muted-color:hsl(205deg, 10%, 50%);--muted-border-color:#1f2d38;--primary:hsl(195deg, 85%, 41%);--primary-hover:hsl(195deg, 80%, 50%);--primary-focus:rgba(16, 149, 193, 0.25);--primary-inverse:#fff;--secondary:hsl(205deg, 15%, 41%);--secondary-hover:hsl(205deg, 10%, 50%);--secondary-focus:rgba(115, 130, 140, 0.25);--secondary-inverse:#fff;--contrast:hsl(205deg, 20%, 94%);--contrast-hover:#fff;--contrast-focus:rgba(115, 130, 140, 0.25);--contrast-inverse:#000;--mark-background-color:#d1c284;--mark-color:#11191f;--ins-color:#388e3c;--del-color:#c62828;--blockquote-border-color:var(--muted-border-color);--blockquote-footer-color:var(--muted-color);--button-box-shadow:0 0 0 rgba(0, 0, 0, 0);--button-hover-box-shadow:0 0 0 rgba(0, 0, 0, 0);--form-element-background-color:#11191f;--form-element-border-color:#374956;--form-element-color:var(--color);--form-element-placeholder-color:var(--muted-color);--form-element-active-background-color:var(--form-element-background-color);--form-element-active-border-color:var(--primary);--form-element-focus-color:var(--primary-focus);--form-element-disabled-background-color:hsl(205deg, 25%, 23%);--form-element-disabled-border-color:hsl(205deg, 20%, 32%);--form-element-disabled-opacity:0.5;--form-element-invalid-border-color:#b71c1c;--form-element-invalid-active-border-color:#c62828;--form-element-invalid-focus-color:rgba(198, 40, 40, 0.25);--form-element-valid-border-color:#2e7d32;--form-element-valid-active-border-color:#388e3c;--form-element-valid-focus-color:rgba(56, 142, 60, 0.25);--switch-background-color:#374956;--switch-color:var(--primary-inverse);--switch-checked-background-color:var(--primary);--range-border-color:#24333e;--range-active-border-color:hsl(205deg, 25%, 23%);--range-thumb-border-color:var(--background-color);--range-thumb-color:var(--secondary);--range-thumb-hover-color:var(--secondary-hover);--range-thumb-active-color:var(--primary);--table-border-color:var(--muted-border-color);--table-row-stripped-background-color:rgba(115, 130, 140, 0.05);--code-background-color:#18232c;--code-color:var(--muted-color);--code-kbd-background-color:var(--contrast);--code-kbd-color:var(--contrast-inverse);--code-tag-color:hsl(330deg, 30%, 50%);--code-property-color:hsl(185deg, 30%, 50%);--code-value-color:hsl(40deg, 10%, 50%);--code-comment-color:#4d606d;--accordion-border-color:var(--muted-border-color);--accordion-active-summary-color:var(--primary);--accordion-close-summary-color:var(--color);--accordion-open-summary-color:var(--muted-color);--card-background-color:#141e26;--card-border-color:var(--card-background-color);--card-box-shadow:0.0145rem 0.029rem 0.174rem rgba(0, 0, 0, 0.01698),0.0335rem 0.067rem 0.402rem rgba(0, 0, 0, 0.024),0.0625rem 0.125rem 0.75rem rgba(0, 0, 0, 0.03),0.1125rem 0.225rem 1.35rem rgba(0, 0, 0, 0.036),0.2085rem 0.417rem 2.502rem rgba(0, 0, 0, 0.04302),0.5rem 1rem 6rem rgba(0, 0, 0, 0.06),0 0 0 0.0625rem rgba(0, 0, 0, 0.015);--card-sectionning-background-color:#18232c;--dropdown-background-color:hsl(205deg, 30%, 15%);--dropdown-border-color:#24333e;--dropdown-box-shadow:var(--card-box-shadow);--dropdown-color:var(--color);--dropdown-hover-background-color:rgba(36, 51, 62, 0.75);--modal-overlay-background-color:rgba(36, 51, 62, 0.9);--progress-background-color:#24333e;--progress-color:var(--primary);--loading-spinner-opacity:0.5;--tooltip-background-color:var(--contrast);--tooltip-color:var(--contrast-inverse);--icon-checkbox:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23FFF' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(162, 175, 185, 0.999)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(255, 255, 255, 0.999)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-chevron-button-inverse:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(0, 0, 0, 0.999)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");--icon-close:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(115, 130, 140, 0.999)' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='18' y1='6' x2='6' y2='18'%3E%3C/line%3E%3Cline x1='6' y1='6' x2='18' y2='18'%3E%3C/line%3E%3C/svg%3E");--icon-date:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(162, 175, 185, 0.999)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='3' y='4' width='18' height='18' rx='2' ry='2'%3E%3C/rect%3E%3Cline x1='16' y1='2' x2='16' y2='6'%3E%3C/line%3E%3Cline x1='8' y1='2' x2='8' y2='6'%3E%3C/line%3E%3Cline x1='3' y1='10' x2='21' y2='10'%3E%3C/line%3E%3C/svg%3E");--icon-invalid:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(183, 28, 28, 0.999)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");--icon-minus:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%23FFF' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cline x1='5' y1='12' x2='19' y2='12'%3E%3C/line%3E%3C/svg%3E");--icon-search:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(162, 175, 185, 0.999)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='11' cy='11' r='8'%3E%3C/circle%3E%3Cline x1='21' y1='21' x2='16.65' y2='16.65'%3E%3C/line%3E%3C/svg%3E");--icon-time:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(162, 175, 185, 0.999)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cpolyline points='12 6 12 12 16 14'%3E%3C/polyline%3E%3C/svg%3E");--icon-valid:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgba(46, 125, 50, 0.999)' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E")}*,::after,::before{box-sizing:border-box;background-repeat:no-repeat}::after,::before{text-decoration:inherit;vertical-align:inherit}:where(:root){-webkit-tap-highlight-color:transparent;-webkit-text-size-adjust:100%;-moz-text-size-adjust:100%;text-size-adjust:100%;text-rendering:optimizeLegibility;background-color:var(--background-color);color:var(--color);font-weight:var(--font-weight);font-size:var(--font-size);line-height:var(--line-height);font-family:var(--font-family);overflow-wrap:break-word;cursor:default;-moz-tab-size:4;-o-tab-size:4;tab-size:4}main{display:block}body{width:100%;margin:0}body>footer,body>header,body>main{width:100%;margin-right:auto;margin-left:auto;padding:var(--block-spacing-vertical) 0}.container,.container-fluid{width:100%;margin-right:auto;margin-left:auto;padding-right:var(--spacing);padding-left:var(--spacing)}@media (min-width:576px){.container{max-width:510px;padding-right:0;padding-left:0}}@media (min-width:768px){.container{max-width:700px}}@media (min-width:992px){.container{max-width:920px}}@media (min-width:1200px){.container{max-width:1130px}}section{margin-bottom:var(--block-spacing-vertical)}.grid{grid-column-gap:var(--grid-spacing-horizontal);grid-row-gap:var(--grid-spacing-vertical);display:grid;grid-template-columns:1fr;margin:0}@media (min-width:992px){.grid{grid-template-columns:repeat(auto-fit,minmax(0%,1fr))}}.grid>*{min-width:0}figure{display:block;margin:0;padding:0;overflow-x:auto}figure figcaption{padding:calc(var(--spacing) * .5) 0;color:var(--muted-color)}b,strong{font-weight:bolder}sub,sup{position:relative;font-size:.75em;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}address,blockquote,dl,figure,form,ol,p,pre,table,ul{margin-top:0;margin-bottom:var(--typography-spacing-vertical);color:var(--color);font-style:normal;font-weight:var(--font-weight);font-size:var(--font-size)}[role=link],a{--color:var(--primary);--background-color:transparent;outline:0;background-color:var(--background-color);color:var(--color);-webkit-text-decoration:var(--text-decoration);text-decoration:var(--text-decoration);transition:background-color var(--transition),color var(--transition),box-shadow var(--transition),-webkit-text-decoration var(--transition);transition:background-color var(--transition),color var(--transition),text-decoration var(--transition),box-shadow var(--transition);transition:background-color var(--transition),color var(--transition),text-decoration var(--transition),box-shadow var(--transition),-webkit-text-decoration var(--transition)}[role=link]:is([aria-current],:hover,:active,:focus),a:is([aria-current],:hover,:active,:focus){--color:var(--primary-hover);--text-decoration:underline}[role=link]:focus,a:focus{--background-color:var(--primary-focus)}[role=link].secondary,a.secondary{--color:var(--secondary)}[role=link].secondary:is([aria-current],:hover,:active,:focus),a.secondary:is([aria-current],:hover,:active,:focus){--color:var(--secondary-hover)}[role=link].secondary:focus,a.secondary:focus{--background-color:var(--secondary-focus)}[role=link].contrast,a.contrast{--color:var(--contrast)}[role=link].contrast:is([aria-current],:hover,:active,:focus),a.contrast:is([aria-current],:hover,:active,:focus){--color:var(--contrast-hover)}[role=link].contrast:focus,a.contrast:focus{--background-color:var(--contrast-focus)}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:var(--typography-spacing-vertical);color:var(--color);font-weight:var(--font-weight);font-size:var(--font-size);font-family:var(--font-family)}h1{--color:var(--h1-color)}h2{--color:var(--h2-color)}h3{--color:var(--h3-color)}h4{--color:var(--h4-color)}h5{--color:var(--h5-color)}h6{--color:var(--h6-color)}:where(address,blockquote,dl,figure,form,ol,p,pre,table,ul)~:is(h1,h2,h3,h4,h5,h6){margin-top:var(--typography-spacing-vertical)}.headings,hgroup{margin-bottom:var(--typography-spacing-vertical)}.headings>*,hgroup>*{margin-bottom:0}.headings>:last-child,hgroup>:last-child{--color:var(--muted-color);--font-weight:unset;font-size:1rem;font-family:unset}p{margin-bottom:var(--typography-spacing-vertical)}small{font-size:var(--font-size)}:where(dl,ol,ul){padding-right:0;padding-left:var(--spacing);-webkit-padding-start:var(--spacing);padding-inline-start:var(--spacing);-webkit-padding-end:0;padding-inline-end:0}:where(dl,ol,ul) li{margin-bottom:calc(var(--typography-spacing-vertical) * .25)}:where(dl,ol,ul) :is(dl,ol,ul){margin:0;margin-top:calc(var(--typography-spacing-vertical) * .25)}ul li{list-style:square}mark{padding:.125rem .25rem;background-color:var(--mark-background-color);color:var(--mark-color);vertical-align:baseline}blockquote{display:block;margin:var(--typography-spacing-vertical) 0;padding:var(--spacing);border-right:none;border-left:.25rem solid var(--blockquote-border-color);-webkit-border-start:0.25rem solid var(--blockquote-border-color);border-inline-start:0.25rem solid var(--blockquote-border-color);-webkit-border-end:none;border-inline-end:none}blockquote footer{margin-top:calc(var(--typography-spacing-vertical) * .5);color:var(--blockquote-footer-color)}abbr[title]{border-bottom:1px dotted;text-decoration:none;cursor:help}ins{color:var(--ins-color);text-decoration:none}del{color:var(--del-color)}::-moz-selection{background-color:var(--primary-focus)}::selection{background-color:var(--primary-focus)}:where(audio,canvas,iframe,img,svg,video){vertical-align:middle}audio,video{display:inline-block}audio:not([controls]){display:none;height:0}:where(iframe){border-style:none}img{max-width:100%;height:auto;border-style:none}:where(svg:not([fill])){fill:currentColor}svg:not(:root){overflow:hidden}button{margin:0;overflow:visible;font-family:inherit;text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}button{display:block;width:100%;margin-bottom:var(--spacing)}[role=button]{display:inline-block;text-decoration:none}[role=button],button,input[type=button],input[type=reset],input[type=submit]{--background-color:var(--primary);--border-color:var(--primary);--color:var(--primary-inverse);--box-shadow:var(--button-box-shadow, 0 0 0 rgba(0, 0, 0, 0));padding:var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:0;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}[role=button]:is([aria-current],:hover,:active,:focus),button:is([aria-current],:hover,:active,:focus),input[type=button]:is([aria-current],:hover,:active,:focus),input[type=reset]:is([aria-current],:hover,:active,:focus),input[type=submit]:is([aria-current],:hover,:active,:focus){--background-color:var(--primary-hover);--border-color:var(--primary-hover);--box-shadow:var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0));--color:var(--primary-inverse)}[role=button]:focus,button:focus,input[type=button]:focus,input[type=reset]:focus,input[type=submit]:focus{--box-shadow:var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),0 0 0 var(--outline-width) var(--primary-focus)}:is(button,input[type=submit],input[type=button],[role=button]).secondary,input[type=reset]{--background-color:var(--secondary);--border-color:var(--secondary);--color:var(--secondary-inverse);cursor:pointer}:is(button,input[type=submit],input[type=button],[role=button]).secondary:is([aria-current],:hover,:active,:focus),input[type=reset]:is([aria-current],:hover,:active,:focus){--background-color:var(--secondary-hover);--border-color:var(--secondary-hover);--color:var(--secondary-inverse)}:is(button,input[type=submit],input[type=button],[role=button]).secondary:focus,input[type=reset]:focus{--box-shadow:var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),0 0 0 var(--outline-width) var(--secondary-focus)}:is(button,input[type=submit],input[type=button],[role=button]).contrast{--background-color:var(--contrast);--border-color:var(--contrast);--color:var(--contrast-inverse)}:is(button,input[type=submit],input[type=button],[role=button]).contrast:is([aria-current],:hover,:active,:focus){--background-color:var(--contrast-hover);--border-color:var(--contrast-hover);--color:var(--contrast-inverse)}:is(button,input[type=submit],input[type=button],[role=button]).contrast:focus{--box-shadow:var(--button-hover-box-shadow, 0 0 0 rgba(0, 0, 0, 0)),0 0 0 var(--outline-width) var(--contrast-focus)}:is(button,input[type=submit],input[type=button],[role=button]).outline,input[type=reset].outline{--background-color:transparent;--color:var(--primary)}:is(button,input[type=submit],input[type=button],[role=button]).outline:is([aria-current],:hover,:active,:focus),input[type=reset].outline:is([aria-current],:hover,:active,:focus){--background-color:transparent;--color:var(--primary-hover)}:is(button,input[type=submit],input[type=button],[role=button]).outline.secondary,input[type=reset].outline{--color:var(--secondary)}:is(button,input[type=submit],input[type=button],[role=button]).outline.secondary:is([aria-current],:hover,:active,:focus),input[type=reset].outline:is([aria-current],:hover,:active,:focus){--color:var(--secondary-hover)}:is(button,input[type=submit],input[type=button],[role=button]).outline.contrast{--color:var(--contrast)}:is(button,input[type=submit],input[type=button],[role=button]).outline.contrast:is([aria-current],:hover,:active,:focus){--color:var(--contrast-hover)}:where(button,[type=submit],[type=button],[type=reset],[role=button])[disabled],:where(fieldset[disabled]) :is(button,[type=submit],[type=button],[type=reset],[role=button]),a[role=button]:not([href]){opacity:.5;pointer-events:none}input,optgroup,select,textarea{margin:0;font-size:1rem;line-height:var(--line-height);font-family:inherit;letter-spacing:inherit}input{overflow:visible}select{text-transform:none}legend{max-width:100%;padding:0;color:inherit;white-space:normal}textarea{overflow:auto}[type=checkbox],[type=radio]{padding:0}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}::-moz-focus-inner{padding:0;border-style:none}:-moz-focusring{outline:0}:-moz-ui-invalid{box-shadow:none}::-ms-expand{display:none}[type=file],[type=range]{padding:0;border-width:0}input:not([type=checkbox]):not([type=radio]):not([type=range]){height:calc(1rem * var(--line-height) + var(--form-element-spacing-vertical) * 2 + var(--border-width) * 2)}fieldset{margin:0;margin-bottom:var(--spacing);padding:0;border:0}fieldset legend,label{display:block;margin-bottom:calc(var(--spacing) * .25);font-weight:var(--form-label-font-weight,var(--font-weight))}input:not([type=checkbox]):not([type=radio]),select,textarea{width:100%}input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file]),select,textarea{-webkit-appearance:none;-moz-appearance:none;appearance:none;padding:var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal)}input,select,textarea{--background-color:var(--form-element-background-color);--border-color:var(--form-element-border-color);--color:var(--form-element-color);--box-shadow:none;border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:0;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}:where(select,textarea):is(:active,:focus),input:not([type=submit]):not([type=button]):not([type=reset]):not([type=checkbox]):not([type=radio]):not([readonly]):is(:active,:focus){--background-color:var(--form-element-active-background-color)}:where(select,textarea):is(:active,:focus),input:not([type=submit]):not([type=button]):not([type=reset]):not([role=switch]):not([readonly]):is(:active,:focus){--border-color:var(--form-element-active-border-color)}input:not([type=submit]):not([type=button]):not([type=reset]):not([type=range]):not([type=file]):not([readonly]):focus,select:focus,textarea:focus{--box-shadow:0 0 0 var(--outline-width) var(--form-element-focus-color)}:where(fieldset[disabled]) :is(input:not([type=submit]):not([type=button]):not([type=reset]),select,textarea),input:not([type=submit]):not([type=button]):not([type=reset])[disabled],select[disabled],textarea[disabled]{--background-color:var(--form-element-disabled-background-color);--border-color:var(--form-element-disabled-border-color);opacity:var(--form-element-disabled-opacity);pointer-events:none}:where(input,select,textarea):not([type=checkbox]):not([type=radio])[aria-invalid]{padding-right:calc(var(--form-element-spacing-horizontal) + 1.5rem)!important;padding-left:var(--form-element-spacing-horizontal);-webkit-padding-start:var(--form-element-spacing-horizontal)!important;padding-inline-start:var(--form-element-spacing-horizontal)!important;-webkit-padding-end:calc(var(--form-element-spacing-horizontal) + 1.5rem)!important;padding-inline-end:calc(var(--form-element-spacing-horizontal) + 1.5rem)!important;background-position:center right .75rem;background-size:1rem auto;background-repeat:no-repeat}:where(input,select,textarea):not([type=checkbox]):not([type=radio])[aria-invalid=false]{background-image:var(--icon-valid)}:where(input,select,textarea):not([type=checkbox]):not([type=radio])[aria-invalid=true]{background-image:var(--icon-invalid)}:where(input,select,textarea)[aria-invalid=false]{--border-color:var(--form-element-valid-border-color)}:where(input,select,textarea)[aria-invalid=false]:is(:active,:focus){--border-color:var(--form-element-valid-active-border-color)!important;--box-shadow:0 0 0 var(--outline-width) var(--form-element-valid-focus-color)!important}:where(input,select,textarea)[aria-invalid=true]{--border-color:var(--form-element-invalid-border-color)}:where(input,select,textarea)[aria-invalid=true]:is(:active,:focus){--border-color:var(--form-element-invalid-active-border-color)!important;--box-shadow:0 0 0 var(--outline-width) var(--form-element-invalid-focus-color)!important}[dir=rtl] :where(input,select,textarea):not([type=checkbox]):not([type=radio])[aria-invalid=false],[dir=rtl] :where(input,select,textarea):not([type=checkbox]):not([type=radio])[aria-invalid=true],[dir=rtl] :where(input,select,textarea):not([type=checkbox]):not([type=radio])[aria-invalid]{background-position:center left .75rem}input::-webkit-input-placeholder,input::placeholder,select:invalid,textarea::-webkit-input-placeholder,textarea::placeholder{color:var(--form-element-placeholder-color);opacity:1}input:not([type=checkbox]):not([type=radio]),select,textarea{margin-bottom:var(--spacing)}select::-ms-expand{border:0;background-color:transparent}select:not([multiple]):not([size]){padding-right:calc(var(--form-element-spacing-horizontal) + 1.5rem);padding-left:var(--form-element-spacing-horizontal);-webkit-padding-start:var(--form-element-spacing-horizontal);padding-inline-start:var(--form-element-spacing-horizontal);-webkit-padding-end:calc(var(--form-element-spacing-horizontal) + 1.5rem);padding-inline-end:calc(var(--form-element-spacing-horizontal) + 1.5rem);background-image:var(--icon-chevron);background-position:center right .75rem;background-size:1rem auto;background-repeat:no-repeat}[dir=rtl] select:not([multiple]):not([size]){background-position:center left .75rem}:where(input,select,textarea)+small{display:block;width:100%;margin-top:calc(var(--spacing) * -.75);margin-bottom:var(--spacing);color:var(--muted-color)}label>:where(input,select,textarea){margin-top:calc(var(--spacing) * .25)}[type=checkbox],[type=radio]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:1.25em;height:1.25em;margin-top:-.125em;margin-right:.375em;margin-left:0;-webkit-margin-start:0;margin-inline-start:0;-webkit-margin-end:.375em;margin-inline-end:.375em;border-width:var(--border-width);font-size:inherit;vertical-align:middle;cursor:pointer}[type=checkbox]::-ms-check,[type=radio]::-ms-check{display:none}[type=checkbox]:checked,[type=checkbox]:checked:active,[type=checkbox]:checked:focus,[type=radio]:checked,[type=radio]:checked:active,[type=radio]:checked:focus{--background-color:var(--primary);--border-color:var(--primary);background-image:var(--icon-checkbox);background-position:center;background-size:.75em auto;background-repeat:no-repeat}[type=checkbox]~label,[type=radio]~label{display:inline-block;margin-right:.375em;margin-bottom:0;cursor:pointer}[type=checkbox]:indeterminate{--background-color:var(--primary);--border-color:var(--primary);background-image:var(--icon-minus);background-position:center;background-size:.75em auto;background-repeat:no-repeat}[type=radio]{border-radius:50%}[type=radio]:checked,[type=radio]:checked:active,[type=radio]:checked:focus{--background-color:var(--primary-inverse);border-width:.35em;background-image:none}[type=checkbox][role=switch]{--background-color:var(--switch-background-color);--border-color:var(--switch-background-color);--color:var(--switch-color);width:2.25em;height:1.25em;border:var(--border-width) solid var(--border-color);border-radius:1.25em;background-color:var(--background-color);line-height:1.25em}[type=checkbox][role=switch]:focus{--background-color:var(--switch-background-color);--border-color:var(--switch-background-color)}[type=checkbox][role=switch]:checked{--background-color:var(--switch-checked-background-color);--border-color:var(--switch-checked-background-color)}[type=checkbox][role=switch]:before{display:block;width:calc(1.25em - (var(--border-width) * 2));height:100%;border-radius:50%;background-color:var(--color);content:"";transition:margin .1s ease-in-out}[type=checkbox][role=switch]:checked{background-image:none}[type=checkbox][role=switch]:checked::before{margin-left:calc(1.125em - var(--border-width));-webkit-margin-start:calc(1.125em - var(--border-width));margin-inline-start:calc(1.125em - var(--border-width))}[type=checkbox]:checked[aria-invalid=false],[type=checkbox][aria-invalid=false],[type=checkbox][role=switch]:checked[aria-invalid=false],[type=checkbox][role=switch][aria-invalid=false],[type=radio]:checked[aria-invalid=false],[type=radio][aria-invalid=false]{--border-color:var(--form-element-valid-border-color)}[type=checkbox]:checked[aria-invalid=true],[type=checkbox][aria-invalid=true],[type=checkbox][role=switch]:checked[aria-invalid=true],[type=checkbox][role=switch][aria-invalid=true],[type=radio]:checked[aria-invalid=true],[type=radio][aria-invalid=true]{--border-color:var(--form-element-invalid-border-color)}[type=color]::-webkit-color-swatch-wrapper{padding:0}[type=color]::-moz-focus-inner{padding:0}[type=color]::-webkit-color-swatch{border:0;border-radius:calc(var(--border-radius) * .5)}[type=color]::-moz-color-swatch{border:0;border-radius:calc(var(--border-radius) * .5)}input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file])[type=date],input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file])[type=datetime-local],input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file])[type=month],input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file])[type=time],input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file])[type=week]{--icon-position:0.75rem;--icon-width:1rem;padding-right:calc(var(--icon-width) + var(--icon-position));background-image:var(--icon-date);background-position:center right var(--icon-position);background-size:var(--icon-width) auto;background-repeat:no-repeat}input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file])[type=time]{background-image:var(--icon-time)}[type=date]::-webkit-calendar-picker-indicator,[type=datetime-local]::-webkit-calendar-picker-indicator,[type=month]::-webkit-calendar-picker-indicator,[type=time]::-webkit-calendar-picker-indicator,[type=week]::-webkit-calendar-picker-indicator{width:var(--icon-width);margin-right:calc(var(--icon-width) * -1);margin-left:var(--icon-position);opacity:0}[dir=rtl] :is([type=date],[type=datetime-local],[type=month],[type=time],[type=week]){text-align:right}[type=file]{--color:var(--muted-color);padding:calc(var(--form-element-spacing-vertical) * .5) 0;border:0;border-radius:0;background:0 0}[type=file]::-webkit-file-upload-button{--background-color:var(--secondary);--border-color:var(--secondary);--color:var(--secondary-inverse);margin-right:calc(var(--spacing)/ 2);margin-left:0;-webkit-margin-start:0;margin-inline-start:0;-webkit-margin-end:calc(var(--spacing)/ 2);margin-inline-end:calc(var(--spacing)/ 2);padding:calc(var(--form-element-spacing-vertical) * .5) calc(var(--form-element-spacing-horizontal) * .5);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:0;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;-webkit-transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition);transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}[type=file]::file-selector-button{--background-color:var(--secondary);--border-color:var(--secondary);--color:var(--secondary-inverse);margin-right:calc(var(--spacing)/ 2);margin-left:0;-webkit-margin-start:0;margin-inline-start:0;-webkit-margin-end:calc(var(--spacing)/ 2);margin-inline-end:calc(var(--spacing)/ 2);padding:calc(var(--form-element-spacing-vertical) * .5) calc(var(--form-element-spacing-horizontal) * .5);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:0;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}[type=file]::-webkit-file-upload-button:is(:hover,:active,:focus){--background-color:var(--secondary-hover);--border-color:var(--secondary-hover)}[type=file]::file-selector-button:is(:hover,:active,:focus){--background-color:var(--secondary-hover);--border-color:var(--secondary-hover)}[type=file]::-webkit-file-upload-button{--background-color:var(--secondary);--border-color:var(--secondary);--color:var(--secondary-inverse);margin-right:calc(var(--spacing)/ 2);margin-left:0;-webkit-margin-start:0;margin-inline-start:0;-webkit-margin-end:calc(var(--spacing)/ 2);margin-inline-end:calc(var(--spacing)/ 2);padding:calc(var(--form-element-spacing-vertical) * .5) calc(var(--form-element-spacing-horizontal) * .5);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:0;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;-webkit-transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition);transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}[type=file]::-webkit-file-upload-button:is(:hover,:active,:focus){--background-color:var(--secondary-hover);--border-color:var(--secondary-hover)}[type=file]::-ms-browse{--background-color:var(--secondary);--border-color:var(--secondary);--color:var(--secondary-inverse);margin-right:calc(var(--spacing)/ 2);margin-left:0;margin-inline-start:0;margin-inline-end:calc(var(--spacing)/ 2);padding:calc(var(--form-element-spacing-vertical) * .5) calc(var(--form-element-spacing-horizontal) * .5);border:var(--border-width) solid var(--border-color);border-radius:var(--border-radius);outline:0;background-color:var(--background-color);box-shadow:var(--box-shadow);color:var(--color);font-weight:var(--font-weight);font-size:1rem;line-height:var(--line-height);text-align:center;cursor:pointer;-ms-transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition);transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}[type=file]::-ms-browse:is(:hover,:active,:focus){--background-color:var(--secondary-hover);--border-color:var(--secondary-hover)}[type=range]{-webkit-appearance:none;-moz-appearance:none;appearance:none;width:100%;height:1.25rem;background:0 0}[type=range]::-webkit-slider-runnable-track{width:100%;height:.25rem;border-radius:var(--border-radius);background-color:var(--range-border-color);-webkit-transition:background-color var(--transition),box-shadow var(--transition);transition:background-color var(--transition),box-shadow var(--transition)}[type=range]::-moz-range-track{width:100%;height:.25rem;border-radius:var(--border-radius);background-color:var(--range-border-color);-moz-transition:background-color var(--transition),box-shadow var(--transition);transition:background-color var(--transition),box-shadow var(--transition)}[type=range]::-ms-track{width:100%;height:.25rem;border-radius:var(--border-radius);background-color:var(--range-border-color);-ms-transition:background-color var(--transition),box-shadow var(--transition);transition:background-color var(--transition),box-shadow var(--transition)}[type=range]::-webkit-slider-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.5rem;border:2px solid var(--range-thumb-border-color);border-radius:50%;background-color:var(--range-thumb-color);cursor:pointer;-webkit-transition:background-color var(--transition),transform var(--transition);transition:background-color var(--transition),transform var(--transition)}[type=range]::-moz-range-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.5rem;border:2px solid var(--range-thumb-border-color);border-radius:50%;background-color:var(--range-thumb-color);cursor:pointer;-moz-transition:background-color var(--transition),transform var(--transition);transition:background-color var(--transition),transform var(--transition)}[type=range]::-ms-thumb{-webkit-appearance:none;width:1.25rem;height:1.25rem;margin-top:-.5rem;border:2px solid var(--range-thumb-border-color);border-radius:50%;background-color:var(--range-thumb-color);cursor:pointer;-ms-transition:background-color var(--transition),transform var(--transition);transition:background-color var(--transition),transform var(--transition)}[type=range]:focus,[type=range]:hover{--range-border-color:var(--range-active-border-color);--range-thumb-color:var(--range-thumb-hover-color)}[type=range]:active{--range-thumb-color:var(--range-thumb-active-color)}[type=range]:active::-webkit-slider-thumb{transform:scale(1.25)}[type=range]:active::-moz-range-thumb{transform:scale(1.25)}[type=range]:active::-ms-thumb{transform:scale(1.25)}input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file])[type=search]{-webkit-padding-start:calc(var(--form-element-spacing-horizontal) + 1.75rem);padding-inline-start:calc(var(--form-element-spacing-horizontal) + 1.75rem);border-radius:5rem;background-image:var(--icon-search);background-position:center left 1.125rem;background-size:1rem auto;background-repeat:no-repeat}input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file])[type=search][aria-invalid]{-webkit-padding-start:calc(var(--form-element-spacing-horizontal) + 1.75rem)!important;padding-inline-start:calc(var(--form-element-spacing-horizontal) + 1.75rem)!important;background-position:center left 1.125rem,center right .75rem}input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file])[type=search][aria-invalid=false]{background-image:var(--icon-search),var(--icon-valid)}input:not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file])[type=search][aria-invalid=true]{background-image:var(--icon-search),var(--icon-invalid)}[type=search]::-webkit-search-cancel-button{-webkit-appearance:none;display:none}[dir=rtl] :where(input):not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file])[type=search]{background-position:center right 1.125rem}[dir=rtl] :where(input):not([type=checkbox]):not([type=radio]):not([type=range]):not([type=file])[type=search][aria-invalid]{background-position:center right 1.125rem,center left .75rem}:where(table){width:100%;border-collapse:collapse;border-spacing:0;text-indent:0}td,th{padding:calc(var(--spacing)/ 2) var(--spacing);border-bottom:var(--border-width) solid var(--table-border-color);color:var(--color);font-weight:var(--font-weight);font-size:var(--font-size);text-align:left;text-align:start}tfoot td,tfoot th{border-top:var(--border-width) solid var(--table-border-color);border-bottom:0}table[role=grid] tbody tr:nth-child(odd){background-color:var(--table-row-stripped-background-color)}code,kbd,pre,samp{font-size:.875em;font-family:var(--font-family)}pre{-ms-overflow-style:scrollbar;overflow:auto}code,kbd,pre{border-radius:var(--border-radius);background:var(--code-background-color);color:var(--code-color);font-weight:var(--font-weight);line-height:initial}code,kbd{display:inline-block;padding:.375rem .5rem}pre{display:block;margin-bottom:var(--spacing);overflow-x:auto}pre>code{display:block;padding:var(--spacing);background:0 0;font-size:14px;line-height:var(--line-height)}code b{color:var(--code-tag-color);font-weight:var(--font-weight)}code i{color:var(--code-property-color);font-style:normal}code u{color:var(--code-value-color);text-decoration:none}code em{color:var(--code-comment-color);font-style:normal}kbd{background-color:var(--code-kbd-background-color);color:var(--code-kbd-color);vertical-align:baseline}hr{height:0;border:0;border-top:1px solid var(--muted-border-color);color:inherit}[hidden],template{display:none!important}canvas{display:inline-block}details{display:block;margin-bottom:var(--spacing);padding-bottom:var(--spacing);border-bottom:var(--border-width) solid var(--accordion-border-color)}details summary{line-height:1rem;list-style-type:none;cursor:pointer;transition:color var(--transition)}details summary:not([role]){color:var(--accordion-close-summary-color)}details summary::-webkit-details-marker{display:none}details summary::marker{display:none}details summary::-moz-list-bullet{list-style-type:none}details summary::after{display:block;width:1rem;height:1rem;-webkit-margin-start:calc(var(--spacing,1rem) * 0.5);margin-inline-start:calc(var(--spacing,1rem) * .5);float:right;transform:rotate(-90deg);background-image:var(--icon-chevron);background-position:right center;background-size:1rem auto;background-repeat:no-repeat;content:"";transition:transform var(--transition)}details summary:focus{outline:0}details summary:focus:not([role=button]){color:var(--accordion-active-summary-color)}details summary[role=button]{width:100%;text-align:left}details summary[role=button]::after{height:calc(1rem * var(--line-height,1.5));background-image:var(--icon-chevron-button)}details summary[role=button]:not(.outline).contrast::after{background-image:var(--icon-chevron-button-inverse)}details[open]>summary{margin-bottom:calc(var(--spacing))}details[open]>summary:not([role]):not(:focus){color:var(--accordion-open-summary-color)}details[open]>summary::after{transform:rotate(0)}[dir=rtl] details summary{text-align:right}[dir=rtl] details summary::after{float:left;background-position:left center}article{margin:var(--block-spacing-vertical) 0;padding:var(--block-spacing-vertical) var(--block-spacing-horizontal);border-radius:var(--border-radius);background:var(--card-background-color);box-shadow:var(--card-box-shadow)}article>footer,article>header{margin-right:calc(var(--block-spacing-horizontal) * -1);margin-left:calc(var(--block-spacing-horizontal) * -1);padding:calc(var(--block-spacing-vertical) * .66) var(--block-spacing-horizontal);background-color:var(--card-sectionning-background-color)}article>header{margin-top:calc(var(--block-spacing-vertical) * -1);margin-bottom:var(--block-spacing-vertical);border-bottom:var(--border-width) solid var(--card-border-color);border-top-right-radius:var(--border-radius);border-top-left-radius:var(--border-radius)}article>footer{margin-top:var(--block-spacing-vertical);margin-bottom:calc(var(--block-spacing-vertical) * -1);border-top:var(--border-width) solid var(--card-border-color);border-bottom-right-radius:var(--border-radius);border-bottom-left-radius:var(--border-radius)}:root{--scrollbar-width:0px}dialog{display:flex;z-index:999;position:fixed;top:0;right:0;bottom:0;left:0;align-items:center;justify-content:center;width:inherit;min-width:100%;height:inherit;min-height:100%;padding:var(--spacing);border:0;background-color:var(--modal-overlay-background-color);color:var(--color)}dialog article{max-height:calc(100vh - var(--spacing) * 2);overflow:auto}@media (min-width:576px){dialog article{max-width:510px}}@media (min-width:768px){dialog article{max-width:700px}}dialog article>footer,dialog article>header{padding:calc(var(--block-spacing-vertical) * .5) var(--block-spacing-horizontal)}dialog article>header .close{margin:0;margin-left:var(--spacing);float:right}dialog article>footer{text-align:right}dialog article>footer [role=button]{margin-bottom:0}dialog article>footer [role=button]:not(:first-of-type){margin-left:calc(var(--spacing) * .5)}dialog article p:last-of-type{margin:0}dialog article .close{display:block;width:1rem;height:1rem;margin-top:calc(var(--block-spacing-vertical) * -.5);margin-bottom:var(--typography-spacing-vertical);margin-left:auto;background-image:var(--icon-close);background-position:center;background-size:auto 1rem;background-repeat:no-repeat;opacity:.5;transition:opacity var(--transition)}dialog article .close:is([aria-current],:hover,:active,:focus){opacity:1}dialog:not([open]),dialog[open=false]{display:none}.modal-is-open{padding-right:var(--scrollbar-width,0);overflow:hidden;pointer-events:none}.modal-is-open dialog{pointer-events:auto}:where(.modal-is-opening,.modal-is-closing) dialog,:where(.modal-is-opening,.modal-is-closing) dialog>article{-webkit-animation-duration:.2s;animation-duration:.2s;-webkit-animation-timing-function:ease-in-out;animation-timing-function:ease-in-out;-webkit-animation-fill-mode:both;animation-fill-mode:both}:where(.modal-is-opening,.modal-is-closing) dialog{-webkit-animation-duration:.8s;animation-duration:.8s;-webkit-animation-name:fadeIn;animation-name:fadeIn}:where(.modal-is-opening,.modal-is-closing) dialog>article{-webkit-animation-delay:.2s;animation-delay:.2s;-webkit-animation-name:slideInDown;animation-name:slideInDown}.modal-is-closing dialog,.modal-is-closing dialog>article{-webkit-animation-delay:0s;animation-delay:0s;animation-direction:reverse}@-webkit-keyframes fadeIn{from{background-color:transparent}to{background-color:var(--modal-overlay-background-color)}}@keyframes fadeIn{from{background-color:transparent}to{background-color:var(--modal-overlay-background-color)}}@-webkit-keyframes slideInDown{from{transform:translateY(-100%);opacity:0}to{transform:translateY(0);opacity:1}}@keyframes slideInDown{from{transform:translateY(-100%);opacity:0}to{transform:translateY(0);opacity:1}}:where(nav li)::before{float:left;content:"​"}nav,nav ul{display:flex}nav{justify-content:space-between}nav ol,nav ul{align-items:center;margin-bottom:0;padding:0;list-style:none}nav ol:first-of-type,nav ul:first-of-type{margin-left:calc(var(--nav-element-spacing-horizontal) * -1)}nav ol:last-of-type,nav ul:last-of-type{margin-right:calc(var(--nav-element-spacing-horizontal) * -1)}nav li{display:inline-block;margin:0;padding:var(--nav-element-spacing-vertical) var(--nav-element-spacing-horizontal)}nav li>*{--spacing:0}nav :where(a,[role=link]){display:inline-block;margin:calc(var(--nav-link-spacing-vertical) * -1) calc(var(--nav-link-spacing-horizontal) * -1);padding:var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal);border-radius:var(--border-radius);text-decoration:none}nav :where(a,[role=link]):is([aria-current],:hover,:active,:focus){text-decoration:none}nav [role=button]{margin-right:inherit;margin-left:inherit;padding:var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal)}aside li,aside nav,aside ol,aside ul{display:block}aside li{padding:calc(var(--nav-element-spacing-vertical) * .5) var(--nav-element-spacing-horizontal)}aside li a{display:block}aside li [role=button]{margin:inherit}progress{display:inline-block;vertical-align:baseline}progress{-webkit-appearance:none;-moz-appearance:none;display:inline-block;appearance:none;width:100%;height:.5rem;margin-bottom:calc(var(--spacing) * .5);overflow:hidden;border:0;border-radius:var(--border-radius);background-color:var(--progress-background-color);color:var(--progress-color)}progress::-webkit-progress-bar{border-radius:var(--border-radius);background:0 0}progress[value]::-webkit-progress-value{background-color:var(--progress-color)}progress::-moz-progress-bar{background-color:var(--progress-color)}@media (prefers-reduced-motion:no-preference){progress:indeterminate{background:var(--progress-background-color) linear-gradient(to right,var(--progress-color) 30%,var(--progress-background-color) 30%) top left/150% 150% no-repeat;-webkit-animation:progressIndeterminate 1s linear infinite;animation:progressIndeterminate 1s linear infinite}progress:indeterminate[value]::-webkit-progress-value{background-color:transparent}progress:indeterminate::-moz-progress-bar{background-color:transparent}}@media (prefers-reduced-motion:no-preference){[dir=rtl] progress:indeterminate{animation-direction:reverse}}@-webkit-keyframes progressIndeterminate{0%{background-position:200% 0}100%{background-position:-200% 0}}@keyframes progressIndeterminate{0%{background-position:200% 0}100%{background-position:-200% 0}}details[role=list],li[role=list]{position:relative}details[role=list] summary+ul,li[role=list]>ul{display:flex;z-index:99;position:absolute;top:auto;right:0;left:0;flex-direction:column;margin:0;padding:0;border:var(--border-width) solid var(--dropdown-border-color);border-radius:var(--border-radius);border-top-right-radius:0;border-top-left-radius:0;background-color:var(--dropdown-background-color);box-shadow:var(--card-box-shadow);color:var(--dropdown-color);white-space:nowrap}details[role=list] summary+ul li,li[role=list]>ul li{width:100%;margin-bottom:0;padding:calc(var(--form-element-spacing-vertical) * .5) var(--form-element-spacing-horizontal);list-style:none}details[role=list] summary+ul li:first-of-type,li[role=list]>ul li:first-of-type{margin-top:calc(var(--form-element-spacing-vertical) * .5)}details[role=list] summary+ul li:last-of-type,li[role=list]>ul li:last-of-type{margin-bottom:calc(var(--form-element-spacing-vertical) * .5)}details[role=list] summary+ul li a,li[role=list]>ul li a{display:block;margin:calc(var(--form-element-spacing-vertical) * -.5) calc(var(--form-element-spacing-horizontal) * -1);padding:calc(var(--form-element-spacing-vertical) * .5) var(--form-element-spacing-horizontal);overflow:hidden;color:var(--dropdown-color);text-decoration:none;text-overflow:ellipsis}details[role=list] summary+ul li a:hover,li[role=list]>ul li a:hover{background-color:var(--dropdown-hover-background-color)}details[role=list] summary::after,li[role=list]>a::after{display:block;width:1rem;height:calc(1rem * var(--line-height,1.5));-webkit-margin-start:0.5rem;margin-inline-start:.5rem;float:right;transform:rotate(0);background-position:right center;background-size:1rem auto;background-repeat:no-repeat;content:""}details[role=list]{padding:0;border-bottom:none}details[role=list] summary{margin-bottom:0}details[role=list] summary:not([role]){height:calc(1rem * var(--line-height) + var(--form-element-spacing-vertical) * 2 + var(--border-width) * 2);padding:var(--form-element-spacing-vertical) var(--form-element-spacing-horizontal);border:var(--border-width) solid var(--form-element-border-color);border-radius:var(--border-radius);background-color:var(--form-element-background-color);color:var(--form-element-placeholder-color);line-height:inherit;cursor:pointer;transition:background-color var(--transition),border-color var(--transition),color var(--transition),box-shadow var(--transition)}details[role=list] summary:not([role]):active,details[role=list] summary:not([role]):focus{border-color:var(--form-element-active-border-color);background-color:var(--form-element-active-background-color)}details[role=list] summary:not([role]):focus{box-shadow:0 0 0 var(--outline-width) var(--form-element-focus-color)}details[role=list][open] summary{border-bottom-right-radius:0;border-bottom-left-radius:0}details[role=list][open] summary::before{display:block;z-index:1;position:fixed;top:0;right:0;bottom:0;left:0;background:0 0;content:"";cursor:default}nav details[role=list] summary,nav li[role=list] a{display:flex;direction:ltr}nav details[role=list] summary+ul,nav li[role=list]>ul{min-width:-webkit-fit-content;min-width:-moz-fit-content;min-width:fit-content;border-radius:var(--border-radius)}nav details[role=list] summary+ul li a,nav li[role=list]>ul li a{border-radius:0}nav details[role=list] summary,nav details[role=list] summary:not([role]){height:auto;padding:var(--nav-link-spacing-vertical) var(--nav-link-spacing-horizontal)}nav details[role=list][open] summary{border-radius:var(--border-radius)}nav details[role=list] summary+ul{margin-top:var(--outline-width);-webkit-margin-start:0;margin-inline-start:0}nav details[role=list] summary[role=link]{margin-bottom:calc(var(--nav-link-spacing-vertical) * -1);line-height:var(--line-height)}nav details[role=list] summary[role=link]+ul{margin-top:calc(var(--nav-link-spacing-vertical) + var(--outline-width));-webkit-margin-start:calc(var(--nav-link-spacing-horizontal) * -1);margin-inline-start:calc(var(--nav-link-spacing-horizontal) * -1)}li[role=list] a:active~ul,li[role=list] a:focus~ul,li[role=list]:hover>ul{display:flex}li[role=list]>ul{display:none;margin-top:calc(var(--nav-link-spacing-vertical) + var(--outline-width));-webkit-margin-start:calc(var(--nav-element-spacing-horizontal) - var(--nav-link-spacing-horizontal));margin-inline-start:calc(var(--nav-element-spacing-horizontal) - var(--nav-link-spacing-horizontal))}li[role=list]>a::after{background-image:var(--icon-chevron)}[aria-busy=true]{cursor:progress}[aria-busy=true]:not(input):not(select):not(textarea)::before{display:inline-block;width:1em;height:1em;border:.1875em solid currentColor;border-radius:1em;border-right-color:transparent;content:"";vertical-align:text-bottom;vertical-align:-.125em;-webkit-animation:spinner .75s linear infinite;animation:spinner .75s linear infinite;opacity:var(--loading-spinner-opacity)}[aria-busy=true]:not(input):not(select):not(textarea):not(:empty)::before{margin-right:calc(var(--spacing) * .5);margin-left:0;-webkit-margin-start:0;margin-inline-start:0;-webkit-margin-end:calc(var(--spacing) * .5);margin-inline-end:calc(var(--spacing) * .5)}[aria-busy=true]:not(input):not(select):not(textarea):empty{text-align:center}a[aria-busy=true],button[aria-busy=true],input[type=button][aria-busy=true],input[type=reset][aria-busy=true],input[type=submit][aria-busy=true]{pointer-events:none}@-webkit-keyframes spinner{to{transform:rotate(360deg)}}@keyframes spinner{to{transform:rotate(360deg)}}[data-tooltip]{position:relative}[data-tooltip]:not(a):not(button):not(input){border-bottom:1px dotted;text-decoration:none;cursor:help}[data-tooltip]::after,[data-tooltip]::before{display:block;z-index:99;position:absolute;bottom:100%;left:50%;padding:.25rem .5rem;overflow:hidden;transform:translate(-50%,-.25rem);border-radius:var(--border-radius);background:var(--tooltip-background-color);content:attr(data-tooltip);color:var(--tooltip-color);font-style:normal;font-weight:var(--font-weight);font-size:.875rem;text-decoration:none;text-overflow:ellipsis;white-space:nowrap;opacity:0;pointer-events:none}[data-tooltip]::after{padding:0;transform:translate(-50%,0);border-top:.3rem solid;border-right:.3rem solid transparent;border-left:.3rem solid transparent;border-radius:0;background-color:transparent;content:"";color:var(--tooltip-background-color)}[data-tooltip]:focus::after,[data-tooltip]:focus::before,[data-tooltip]:hover::after,[data-tooltip]:hover::before{opacity:1}@media (hover:hover) and (pointer:fine){[data-tooltip]:focus::after,[data-tooltip]:focus::before,[data-tooltip]:hover::after,[data-tooltip]:hover::before{-webkit-animation-duration:.2s;animation-duration:.2s;-webkit-animation-name:slide;animation-name:slide}[data-tooltip]:focus::after,[data-tooltip]:hover::after{-webkit-animation-name:slideCaret;animation-name:slideCaret}}@-webkit-keyframes slide{from{transform:translate(-50%,.75rem);opacity:0}to{transform:translate(-50%,-.25rem);opacity:1}}@keyframes slide{from{transform:translate(-50%,.75rem);opacity:0}to{transform:translate(-50%,-.25rem);opacity:1}}@-webkit-keyframes slideCaret{from{opacity:0}50%{transform:translate(-50%,-.25rem);opacity:0}to{transform:translate(-50%,0);opacity:1}}@keyframes slideCaret{from{opacity:0}50%{transform:translate(-50%,-.25rem);opacity:0}to{transform:translate(-50%,0);opacity:1}}[aria-controls]{cursor:pointer}[aria-disabled=true],[disabled]{cursor:not-allowed}[aria-hidden=false][hidden]{display:initial}[aria-hidden=false][hidden]:not(:focus){clip:rect(0,0,0,0);position:absolute}[tabindex],a,area,button,input,label,select,summary,textarea{-ms-touch-action:manipulation}[dir=rtl]{direction:rtl}@media (prefers-reduced-motion:reduce){:not([aria-busy=true]),:not([aria-busy=true])::after,:not([aria-busy=true])::before{background-attachment:initial!important;-webkit-animation-duration:1ms!important;animation-duration:1ms!important;-webkit-animation-delay:-1ms!important;animation-delay:-1ms!important;-webkit-animation-iteration-count:1!important;animation-iteration-count:1!important;scroll-behavior:auto!important;transition-delay:0s!important;transition-duration:0s!important}} 5 | /*# sourceMappingURL=pico.min.css.map */ --------------------------------------------------------------------------------