├── .dockerignore ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature-request---suggestion.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── build.yml │ ├── release-next.yml │ └── release.yml ├── .gitignore ├── CHANGELOG.md ├── Readme.md ├── examples ├── provider-schema │ ├── Readme.md │ ├── aws.tf.json │ └── schema.json ├── simple-python │ ├── .packages │ │ ├── terrastack-0.15.0-py3-none-any.whl │ │ ├── terrastack-0.15.0.tar.gz │ │ ├── terrastack_aws_provider-0.0.0-py3-none-any.whl │ │ └── terrastack_aws_provider-0.0.0.tar.gz │ ├── Pipfile │ ├── Pipfile.lock │ ├── Readme.md │ └── main.py └── simple │ ├── .schema │ └── aws-provider.json │ ├── Readme.md │ ├── package.json │ ├── stacks │ └── simple.ts │ ├── tsconfig.json │ └── yarn.lock ├── images └── terrastack.png ├── lerna.json ├── package.json ├── packages ├── @terrastack │ └── core │ │ ├── .npmignore │ │ ├── lib │ │ ├── _tokens.ts │ │ ├── _util.ts │ │ ├── app.ts │ │ ├── index.ts │ │ ├── names.ts │ │ ├── resource-object.ts │ │ ├── stack.ts │ │ └── tf-reference.ts │ │ ├── package.json │ │ └── tsconfig.json └── terrastack-cli │ ├── bin │ ├── cmds │ │ └── generate.ts │ ├── terrastack │ └── terrastack.ts │ ├── lib │ ├── codegen-constructs.ts │ ├── codegen-types.ts │ └── import.ts │ ├── package.json │ ├── package.sh │ ├── spec │ └── fixtures │ │ ├── foo.json │ │ └── main.tf │ ├── test │ ├── __snapshots__ │ │ ├── codegen-construct.test.js.snap │ │ └── codegen-types.test.js.snap │ ├── codegen-construct.test.ts │ └── codegen-types.test.ts │ └── tsconfig.json ├── schema.json ├── tools ├── align-version.sh ├── bump.sh ├── collect-dist.sh └── release.sh ├── tsconfig.json └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | **/**/node_modules 2 | .dockerignore 3 | Dockerfile -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG] new bug" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the issue. 15 | 16 | **Expected behavior** 17 | A clear and concise description of what you expected to happen. 18 | 19 | **Additional context** 20 | Add any other context about the problem here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request---suggestion.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request / suggestion 3 | about: Suggest an idea for this project 4 | title: "[Suggestion] new suggestion" 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | *By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license* 2 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: [pull_request] 3 | 4 | jobs: 5 | build: 6 | runs-on: ubuntu-latest 7 | container: 8 | image: jsii/superchain 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | - run: yarn install 13 | - run: yarn build 14 | - run: yarn test 15 | - run: yarn package 16 | -------------------------------------------------------------------------------- /.github/workflows/release-next.yml: -------------------------------------------------------------------------------- 1 | name: Release @next 2 | on: 3 | push: 4 | branches: 5 | - none 6 | 7 | jobs: 8 | release: 9 | runs-on: ubuntu-latest 10 | container: 11 | image: jsii/superchain 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - run: yarn install 16 | - run: tools/align-version.sh "-pre.${{ github.sha }}" 17 | - run: yarn build 18 | - run: yarn test 19 | - run: yarn package 20 | 21 | # publish to package managers only if this is a new version 22 | - run: npx jsii-release-npm 23 | env: 24 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 25 | NPM_DIST_TAG: next 26 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | on: 3 | push: 4 | branches: 5 | - none 6 | 7 | jobs: 8 | release: 9 | runs-on: ubuntu-latest 10 | container: 11 | image: jsii/superchain 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - run: yarn install 16 | - run: tools/align-version.sh 17 | - run: yarn build 18 | # - run: yarn test 19 | - run: yarn package 20 | 21 | # publish to package managers only if this is a new version 22 | - run: yarn release 23 | env: 24 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 25 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 26 | MAVEN_GPG_PRIVATE_KEY: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} 27 | MAVEN_GPG_PRIVATE_KEY_PASSPHRASE: ${{ secrets.MAVEN_GPG_PRIVATE_KEY_PASSPHRASE }} 28 | MAVEN_PASSWORD: ${{ secrets.MAVEN_PASSWORD }} 29 | MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} 30 | MAVEN_STAGING_PROFILE_ID: ${{ secrets.MAVEN_STAGING_PROFILE_ID }} 31 | NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }} 32 | TWINE_USERNAME: ${{ secrets.TWINE_USERNAME }} 33 | TWINE_PASSWORD: ${{ secrets.TWINE_PASSWORD }} 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | .gen 3 | .generated 4 | .vscode/ 5 | 6 | cdk.out 7 | *.d.ts 8 | *.js 9 | 10 | # Created by https://www.gitignore.io/api/node 11 | # Edit at https://www.gitignore.io/?templates=node 12 | 13 | ### Node ### 14 | # Logs 15 | logs 16 | *.log 17 | npm-debug.log* 18 | yarn-debug.log* 19 | yarn-error.log* 20 | lerna-debug.log* 21 | 22 | # Diagnostic reports (https://nodejs.org/api/report.html) 23 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 24 | 25 | # Runtime data 26 | pids 27 | *.pid 28 | *.seed 29 | *.pid.lock 30 | 31 | # Directory for instrumented libs generated by jscoverage/JSCover 32 | lib-cov 33 | 34 | # Coverage directory used by tools like istanbul 35 | coverage 36 | *.lcov 37 | 38 | # nyc test coverage 39 | .nyc_output 40 | 41 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 42 | .grunt 43 | 44 | # Bower dependency directory (https://bower.io/) 45 | bower_components 46 | 47 | # node-waf configuration 48 | .lock-wscript 49 | 50 | # Compiled binary addons (https://nodejs.org/api/addons.html) 51 | build/Release 52 | 53 | # Dependency directories 54 | node_modules/ 55 | jspm_packages/ 56 | 57 | # TypeScript v1 declaration files 58 | typings/ 59 | 60 | # TypeScript cache 61 | *.tsbuildinfo 62 | 63 | # Optional npm cache directory 64 | .npm 65 | 66 | # Optional eslint cache 67 | .eslintcache 68 | 69 | # Optional REPL history 70 | .node_repl_history 71 | 72 | # Output of 'npm pack' 73 | *.tgz 74 | 75 | # Yarn Integrity file 76 | .yarn-integrity 77 | 78 | # dotenv environment variables file 79 | .env 80 | .env.test 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | 85 | # next.js build output 86 | .next 87 | 88 | # nuxt.js build output 89 | .nuxt 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # End of https://www.gitignore.io/api/node 104 | 105 | # Created by https://www.gitignore.io/api/terraform 106 | # Edit at https://www.gitignore.io/?templates=terraform 107 | 108 | ### Terraform ### 109 | # Local .terraform directories 110 | **/.terraform/* 111 | 112 | # .tfstate files 113 | *.tfstate 114 | *.tfstate.* 115 | 116 | # Crash log files 117 | crash.log 118 | 119 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 120 | # .tfvars files are managed as part of configuration and so should be included in 121 | # version control. 122 | # 123 | # example.tfvars 124 | 125 | # Ignore override files as they are usually used to override resources locally and so 126 | # are not checked in 127 | override.tf 128 | override.tf.json 129 | *_override.tf 130 | *_override.tf.json 131 | 132 | # Include override files you do wish to add to version control using negated pattern 133 | # !example_override.tf 134 | 135 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 136 | # example: *tfplan* 137 | 138 | # End of https://www.gitignore.io/api/terraform -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ## 0.15.0 (2020-03-11) 6 | 7 | ## 0.14.0 (2020-03-11) 8 | 9 | ## 0.13.0 (2020-03-11) 10 | 11 | ## 0.12.0 (2020-03-11) 12 | 13 | ## 0.11.0 (2020-03-11) 14 | 15 | ## 0.10.0 (2020-03-11) 16 | 17 | ## 0.10.0 (2020-03-11) 18 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Terrastack is now Terraform CDK 2 | 3 | This project is archived, but the idea of Terrastack lives on in the [Terraform CDK](https://github.com/hashicorp/terraform-cdk). 4 | 5 | --- 6 | 7 | ## Terrastack - Polyglot Terraform supercharged by the CDK 8 | 9 | Terrastack enables you to keep using Terraform as engine, while defining your resources in actual programming languages such as Typescript, Python, Java or C# - with more to come ([perhaps Ruby?](https://github.com/aws/jsii/issues/144)). 10 | 11 | This is made possible by the [Cloud Development Kit (CDK)](https://github.com/aws/aws-cdk/) and [jsii](https://github.com/aws/jsii) for generating the polyglot libraries. While the major use-case for the CDK is generating Cloudformation configuration as YAML, it's capable of generating pretty much any configuration. 12 | 13 | Applying it Terraform was inspired by the release of [cdk8s](https://github.com/awslabs/cdk8s), which also paved the way for rapid progress towards a working Terraform version. 14 | 15 | ![terrastack](./images/terrastack.png) 16 | 17 | ## How does it work? 18 | 19 | Terrastack makes use of the following things: 20 | 21 | - Terraform [provider schemas](https://www.terraform.io/docs/commands/providers/schema.html) - See [an example](./examples/provider-schema) 22 | - Besides from HCL, Terraform [understands JSON as well](https://www.terraform.io/docs/configuration/syntax-json.html) 23 | - [Code generator](./packages/terrastack-cli) to transform the provider schema in proper Typescript classes 24 | - A custom [Stack](./packages/@terrastack/core/lib/stack.ts) and [ResourceObject](./packages/@terrastack/core/lib/resource-object.ts) class to interface with the CDK 25 | 26 | With that, we're able to generate packages for any given Terraform provider in the following languages: 27 | 28 | - Typescript ([tested](./examples/simple)) 29 | - Python ([tested](./examples/simple-python)) 30 | - C# (yet to test) 31 | - Java (yet to test) 32 | 33 | ## Current Status 34 | 35 | In the current state, this is mostly a prototype. It demonstrates that it's possible and quite useful to leverage the CDK. Interfaces and Apis will certainly change and there are still some problems to solve. 36 | 37 | However, it's possible to generate full bindings for a given Terraform provider (tested with AWS and Google Cloud so far). 38 | 39 | ## Roadmap 40 | 41 | - [x] Generate Terraform constructs (Level 1 in CDK speak) from the Terraform JSON schema 42 | - [x] Build Terraform stacks out of those resources in Typescript, Python, Java or C# 43 | - [x] Reference resources within a stack 44 | - [x] Synthesize valid Terraform JSON which is plannable and applyable 45 | - [x] Leverage existing CDK packages which are not bound to cloudformation directly, such as global tagging or an IAM Policy builder 46 | - [ ] Improve developer experience 47 | - [ ] Multiple Stacks with individual state 48 | - [ ] Input Variables 49 | - [ ] Outputs 50 | - [ ] Remote State 51 | - [ ] Pregenerate schemas for Terraform Providers 52 | - [ ] Built-in functions 53 | - [ ] Generic resource features such as lifecycles and dependencies 54 | - [ ] Modules 55 | - [ ] Dynamic blocks 56 | - [ ] count / for each 57 | - [ ] Assets 58 | - [ ] Converting HCL to Terrastack resources 59 | - [ ] Publish easy consumable packages for providers and open source modules 60 | - [ ] Better Terraform integration 61 | - [ ] Unit / Integration test examples 62 | - [ ] More examples 63 | 64 | ## Author 65 | 66 | - [Sebastian Korfmann](https://skorfmann.com) - Cloud Solutions Architect based in Hamburg. [Available for hire](https://twitter.com/skorfmann/status/1226874389389545472) starting from Q2/2020 -------------------------------------------------------------------------------- /examples/provider-schema/Readme.md: -------------------------------------------------------------------------------- 1 | # Generate Terraform Provider Schema 2 | 3 | Starting from Terraform `0.12` you can use the Terraform CLI to generate a JSON representation of a given provider. Find the details and spec [over here](https://www.terraform.io/docs/commands/providers/schema.html) 4 | 5 | ### AWS Provider Example 6 | 7 | Get the provider version you want and edit [./aws.tf.json](./aws.tf.json) accordingly. 8 | 9 | Find available versions like this 10 | 11 | ```bash 12 | curl -s https://registry.terraform.io/v1/providers/hashicorp/aws/versions | jq -r '.versions | .[] | .version' 13 | ``` 14 | 15 | The run the following 16 | 17 | ```bash 18 | terraform init 19 | terraform providers schema -json | jq > schema.json 20 | ``` 21 | Inspect the schema 22 | 23 | ```bash 24 | cat schema.json 25 | ``` -------------------------------------------------------------------------------- /examples/provider-schema/aws.tf.json: -------------------------------------------------------------------------------- 1 | { 2 | "provider": { 3 | "aws": { 4 | "version": "2.52.0" 5 | }, 6 | "google": { 7 | "version": "3.11.0" 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /examples/simple-python/.packages/terrastack-0.15.0-py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TerraStackIO/terrastack/0380fda9698580522e4e3d007786c37b04bb2cae/examples/simple-python/.packages/terrastack-0.15.0-py3-none-any.whl -------------------------------------------------------------------------------- /examples/simple-python/.packages/terrastack-0.15.0.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TerraStackIO/terrastack/0380fda9698580522e4e3d007786c37b04bb2cae/examples/simple-python/.packages/terrastack-0.15.0.tar.gz -------------------------------------------------------------------------------- /examples/simple-python/.packages/terrastack_aws_provider-0.0.0-py3-none-any.whl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TerraStackIO/terrastack/0380fda9698580522e4e3d007786c37b04bb2cae/examples/simple-python/.packages/terrastack_aws_provider-0.0.0-py3-none-any.whl -------------------------------------------------------------------------------- /examples/simple-python/.packages/terrastack_aws_provider-0.0.0.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TerraStackIO/terrastack/0380fda9698580522e4e3d007786c37b04bb2cae/examples/simple-python/.packages/terrastack_aws_provider-0.0.0.tar.gz -------------------------------------------------------------------------------- /examples/simple-python/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [packages] 7 | aws-cdk-core = "*" 8 | terrastack = {path = "./.packages/terrastack-0.15.0.tar.gz"} 9 | terrastack-aws-provider = {path = "./.packages/terrastack_aws_provider-0.0.0.tar.gz"} 10 | 11 | [requires] 12 | python_version = "3.7" 13 | -------------------------------------------------------------------------------- /examples/simple-python/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "e7bf9c551cf45151280babd251ef4362c472f6ad7f299fa306abd279488053d6" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.7" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "attrs": { 20 | "hashes": [ 21 | "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c", 22 | "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72" 23 | ], 24 | "version": "==19.3.0" 25 | }, 26 | "aws-cdk-core": { 27 | "hashes": [ 28 | "sha256:38af9bc1ab164e72806eabbb7f84b77c007d68f3aea869858092fb0331a75aed" 29 | ], 30 | "index": "pypi", 31 | "version": "==1.27.0" 32 | }, 33 | "aws-cdk.cx-api": { 34 | "hashes": [ 35 | "sha256:8d527c479e77aff962810af8b5bd1f1dcddaa77c0310d080ea8608941c344bd9", 36 | "sha256:cbe497d176f030c5c7dd76a7ac4dd3f160f82bf0d64dc3327d3a692d9f0f3e2e" 37 | ], 38 | "version": "==1.27.0" 39 | }, 40 | "cattrs": { 41 | "hashes": [ 42 | "sha256:616972ae3dfa6e623a40ad3cb845420e64942989152774ab055e5c2b2f89f997", 43 | "sha256:b7ab5cf8ad127c42eefd01410c1c6e28569a45a255ea80ed968511873c433c7a" 44 | ], 45 | "version": "==1.0.0" 46 | }, 47 | "jsii": { 48 | "hashes": [ 49 | "sha256:1f1a0984b2106395b9efbea03c81fc1091c54c89915bc200ab313b4daba9851a", 50 | "sha256:6933c08ecaba4932b465ea527b2237de4eedb702fa334884b8a5d66df0b52b02" 51 | ], 52 | "version": "==0.22.0" 53 | }, 54 | "publication": { 55 | "hashes": [ 56 | "sha256:0248885351febc11d8a1098d5c8e3ab2dabcf3e8c0c96db1e17ecd12b53afbe6", 57 | "sha256:68416a0de76dddcdd2930d1c8ef853a743cc96c82416c4e4d3b5d901c6276dc4" 58 | ], 59 | "version": "==0.0.3" 60 | }, 61 | "python-dateutil": { 62 | "hashes": [ 63 | "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c", 64 | "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a" 65 | ], 66 | "version": "==2.8.1" 67 | }, 68 | "six": { 69 | "hashes": [ 70 | "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a", 71 | "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c" 72 | ], 73 | "version": "==1.14.0" 74 | }, 75 | "terrastack": { 76 | "path": "./.packages/terrastack-0.15.0.tar.gz" 77 | }, 78 | "terrastack-aws-provider": { 79 | "path": "./.packages/terrastack_aws_provider-0.0.0.tar.gz" 80 | }, 81 | "typing-extensions": { 82 | "hashes": [ 83 | "sha256:091ecc894d5e908ac75209f10d5b4f118fbdb2eb1ede6a63544054bb1edb41f2", 84 | "sha256:910f4656f54de5993ad9304959ce9bb903f90aadc7c67a0bef07e678014e892d", 85 | "sha256:cf8b63fedea4d89bab840ecbb93e75578af28f76f66c35889bd7065f5af88575" 86 | ], 87 | "version": "==3.7.4.1" 88 | } 89 | }, 90 | "develop": {} 91 | } 92 | -------------------------------------------------------------------------------- /examples/simple-python/Readme.md: -------------------------------------------------------------------------------- 1 | # Simple Python Example 2 | 3 | This demonstrates a simple example based on Python. For conevnience, the current Python packages are distributed alongside this code under [./.packages](./.packages). 4 | 5 | ## Getting started 6 | 7 | ```bash 8 | pipenv install 9 | pipenv run python main.py 10 | cat dist/MySimpleStack.tf.json 11 | cd dist 12 | terraform init 13 | terraform plan 14 | ``` -------------------------------------------------------------------------------- /examples/simple-python/main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from aws_cdk.core import Construct 3 | from terrastack import App, Stack 4 | from terrastack_aws_provider import AwsProvider, AwsS3Bucket 5 | 6 | class SimpleStack(Stack): 7 | def __init__(self, scope: Construct, ns: str): 8 | super().__init__(scope, ns) 9 | 10 | AwsProvider(self, 'aws', region='eu-central-1') 11 | 12 | AwsS3Bucket(self, 'hello', bucket='world') 13 | 14 | app = App() 15 | SimpleStack(app, "MySimpleStack") 16 | app.synth() -------------------------------------------------------------------------------- /examples/simple/Readme.md: -------------------------------------------------------------------------------- 1 | # Simple Typescript Example 2 | 3 | This demonstrates a simple example based on Typescript. For convenience, the current Terraform AWS provider schema is commited under [./.schema/aws-provider.json](./.schema/aws-provider.json) 4 | 5 | ## Getting started 6 | 7 | Make sure to run `yarn && yarn build` in the [root directory](../..) of the project 8 | 9 | ```bash 10 | yarn 11 | terrastack import -i .schema/aws-provider.json 12 | yarn build 13 | node stacks/simple.js 14 | cat dist/mys3bucketstack.tf.json 15 | cd dist 16 | terraform init 17 | terraform plan 18 | ``` -------------------------------------------------------------------------------- /examples/simple/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple", 3 | "version": "1.0.0", 4 | "description": "Simple Terrastack Example", 5 | "main": "stacks/simple.js", 6 | "types": "stacks/simple.d.ts", 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w" 10 | }, 11 | "author": "Sebastian Korfmann", 12 | "license": "Apache-2.0", 13 | "dependencies": { 14 | "@aws-cdk/aws-iam": "^1.27.0", 15 | "@aws-cdk/core": "^1.27.0", 16 | "@types/node": "^13.7.7", 17 | "crypto": "^1.0.1", 18 | "fs": "^0.0.1-security", 19 | "path": "^0.12.7", 20 | "typescript": "^3.8.3", 21 | "terrastack-cli": "file:../../packages/terrastack-cli" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/simple/stacks/simple.ts: -------------------------------------------------------------------------------- 1 | import { Construct, Tag } from '@aws-cdk/core'; 2 | import { App, Stack } from '../../../packages/@terrastack/core'; 3 | import { AwsProvider, AwsS3Bucket, AwsIamPolicy } from '../.generated/aws'; 4 | import { PolicyDocument, PolicyStatement, AnyPrincipal, Effect } from "@aws-cdk/aws-iam" 5 | 6 | const app = new App(); 7 | 8 | class MyBucketStack extends Stack { 9 | constructor(scope: Construct, ns: string) { 10 | super(scope, ns); 11 | 12 | new AwsProvider(this, 'aws', { 13 | region: 'eu-central-1' 14 | }) 15 | 16 | const bucket = new AwsS3Bucket(this, 'hello', { 17 | bucket: 'world' 18 | }); 19 | 20 | const bucketPolicyDocument = new PolicyDocument({ 21 | statements: [ 22 | new PolicyStatement({ 23 | effect: Effect.ALLOW, 24 | principals: [new AnyPrincipal()], 25 | actions: [ 26 | "s3:Get*" 27 | ], 28 | resources: [bucket.arn] 29 | }) 30 | ] 31 | }) 32 | 33 | new AwsIamPolicy(this, "helloPolicy", { 34 | name: "hello-bucket", 35 | policy: JSON.stringify(bucketPolicyDocument.toJSON()) 36 | }) 37 | } 38 | } 39 | 40 | const stack = new MyBucketStack(app, 'my-s3-bucket-stack'); 41 | Tag.add(stack, 'StackType', 'Terraform'); 42 | app.synth(); -------------------------------------------------------------------------------- /examples/simple/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "alwaysStrict": true, 4 | "charset": "utf8", 5 | "declaration": true, 6 | "experimentalDecorators": true, 7 | "inlineSourceMap": true, 8 | "inlineSources": true, 9 | "lib": [ 10 | "es2018" 11 | ], 12 | "module": "CommonJS", 13 | "noEmitOnError": false, 14 | "noFallthroughCasesInSwitch": true, 15 | "noImplicitAny": true, 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "resolveJsonModule": true, 21 | "strict": true, 22 | "strictNullChecks": true, 23 | "strictPropertyInitialization": true, 24 | "stripInternal": true, 25 | "target": "ES2018" 26 | }, 27 | "include": [ 28 | "**/*.ts" 29 | ], 30 | "exclude": [ 31 | "node_modules" 32 | ] 33 | } 34 | -------------------------------------------------------------------------------- /examples/simple/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@aws-cdk/aws-iam@^1.27.0": 6 | version "1.27.0" 7 | resolved "https://registry.yarnpkg.com/@aws-cdk/aws-iam/-/aws-iam-1.27.0.tgz#3e88098b3c0f732bf51c326440fa8f1a3bf3231d" 8 | integrity sha512-KkI7m28JcuC+64biE0FOF/CNuk1nRPxzatUbXUc+ZKwcYV8NBONrRcTRENQLe9+FRQ554Vww7IEEgKwuLSXusg== 9 | dependencies: 10 | "@aws-cdk/core" "1.27.0" 11 | "@aws-cdk/region-info" "1.27.0" 12 | 13 | "@aws-cdk/core@1.27.0", "@aws-cdk/core@^1.27.0": 14 | version "1.27.0" 15 | resolved "https://registry.yarnpkg.com/@aws-cdk/core/-/core-1.27.0.tgz#736a43953421b98244bad55e906b87ac34fe6a97" 16 | integrity sha512-RejU46I1ozZELsSrb2MMa7hmtBjGv5sppC7PYwTgAYVJTqMpPAQEYAriiW2yJDaKZr9FN/l3wTGyJID4VZSfcQ== 17 | dependencies: 18 | "@aws-cdk/cx-api" "1.27.0" 19 | 20 | "@aws-cdk/cx-api@1.27.0", "@aws-cdk/cx-api@^1.24.0": 21 | version "1.27.0" 22 | resolved "https://registry.yarnpkg.com/@aws-cdk/cx-api/-/cx-api-1.27.0.tgz#715068271a1bb60d0b3b015f54373e228458a9b8" 23 | integrity sha512-CqWnXm/lCzRfUsXWh1/umN6G+akop++zMzCZrjsOa9LXdd8YJinibsYTUINJVVO7Dd0I8HtLNj+NaRiWAywadg== 24 | dependencies: 25 | semver "^7.1.3" 26 | 27 | "@aws-cdk/region-info@1.27.0": 28 | version "1.27.0" 29 | resolved "https://registry.yarnpkg.com/@aws-cdk/region-info/-/region-info-1.27.0.tgz#c91334db22ca5e520b9b3ac8bc338b370932d3bc" 30 | integrity sha512-LlvzKZQjxUN/97u49WIM5y+qLQWiRhLUzwg53F9kvF8BGmPW6v6TW62yOAr6nfmQjtZkgu0FdTXA4qHyrM2H2g== 31 | 32 | "@babel/runtime@^7.8.7": 33 | version "7.8.7" 34 | resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.8.7.tgz#8fefce9802db54881ba59f90bb28719b4996324d" 35 | integrity sha512-+AATMUFppJDw6aiR5NVPHqIQBlV/Pj8wY/EZH+lmvRdUo9xBaz/rF3alAwFJQavvKfeOlPE7oaaDHVbcySbCsg== 36 | dependencies: 37 | regenerator-runtime "^0.13.4" 38 | 39 | "@jsii/spec@^1.1.0": 40 | version "1.1.0" 41 | resolved "https://registry.yarnpkg.com/@jsii/spec/-/spec-1.1.0.tgz#1e7093807d191970bf2b22c537b905e298091d1f" 42 | integrity sha512-zi5/k1oqkCGwoWutm9ZXnC9pZXILREvtLOMg4dci8jeotHYxnrAk7Wk6FskLD9StzHzf0hzewa8cJPqfPiZ+ZA== 43 | dependencies: 44 | jsonschema "^1.2.5" 45 | 46 | "@types/color-name@^1.1.1": 47 | version "1.1.1" 48 | resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" 49 | integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== 50 | 51 | "@types/json-schema@^7.0.4": 52 | version "7.0.4" 53 | resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339" 54 | integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA== 55 | 56 | "@types/node@^13.7.7": 57 | version "13.7.7" 58 | resolved "https://registry.yarnpkg.com/@types/node/-/node-13.7.7.tgz#1628e6461ba8cc9b53196dfeaeec7b07fa6eea99" 59 | integrity sha512-Uo4chgKbnPNlxQwoFmYIwctkQVkMMmsAoGGU4JKwLuvBefF0pCq4FybNSnfkfRCpC7ZW7kttcC/TrRtAJsvGtg== 60 | 61 | "@types/yaml@^1.2.0": 62 | version "1.2.0" 63 | resolved "https://registry.yarnpkg.com/@types/yaml/-/yaml-1.2.0.tgz#4ed577fc4ebbd6b829b28734e56d10c9e6984e09" 64 | integrity sha512-GW8b9qM+ebgW3/zjzPm0I1NxMvLaz/YKT9Ph6tTb+Fkeyzd9yLTvQ6ciQ2MorTRmb/qXmfjMerRpG4LviixaqQ== 65 | 66 | "@types/yargs-parser@*": 67 | version "15.0.0" 68 | resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d" 69 | integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw== 70 | 71 | "@types/yargs@^15.0.4": 72 | version "15.0.4" 73 | resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.4.tgz#7e5d0f8ca25e9d5849f2ea443cf7c402decd8299" 74 | integrity sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg== 75 | dependencies: 76 | "@types/yargs-parser" "*" 77 | 78 | ansi-regex@^5.0.0: 79 | version "5.0.0" 80 | resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" 81 | integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== 82 | 83 | ansi-styles@^4.0.0: 84 | version "4.2.1" 85 | resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359" 86 | integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA== 87 | dependencies: 88 | "@types/color-name" "^1.1.1" 89 | color-convert "^2.0.1" 90 | 91 | camel-case@^4.1.1: 92 | version "4.1.1" 93 | resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.1.tgz#1fc41c854f00e2f7d0139dfeba1542d6896fe547" 94 | integrity sha512-7fa2WcG4fYFkclIvEmxBbTvmibwF2/agfEBc6q3lOpVu0A13ltLsA+Hr/8Hp6kp5f+G7hKi6t8lys6XxP+1K6Q== 95 | dependencies: 96 | pascal-case "^3.1.1" 97 | tslib "^1.10.0" 98 | 99 | camelcase@^5.0.0, camelcase@^5.1.3, camelcase@^5.3.1: 100 | version "5.3.1" 101 | resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" 102 | integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== 103 | 104 | capital-case@^1.0.3: 105 | version "1.0.3" 106 | resolved "https://registry.yarnpkg.com/capital-case/-/capital-case-1.0.3.tgz#339bd77e8fab6cf75111d4fca509b3edf7c117c8" 107 | integrity sha512-OlUSJpUr7SY0uZFOxcwnDOU7/MpHlKTZx2mqnDYQFrDudXLFm0JJ9wr/l4csB+rh2Ug0OPuoSO53PqiZBqno9A== 108 | dependencies: 109 | no-case "^3.0.3" 110 | tslib "^1.10.0" 111 | upper-case-first "^2.0.1" 112 | 113 | case@^1.6.2: 114 | version "1.6.2" 115 | resolved "https://registry.yarnpkg.com/case/-/case-1.6.2.tgz#2ea68af6956752cd69c349c8b3e6bc860d1cba95" 116 | integrity sha512-ll380ZRoraT7mUK2G92UbH+FJVD5AwdVIAYk9xhV1tauh0carDgYByUD1HhjCWsWgxrfQvCeHvtfj7IYR6TKeg== 117 | 118 | change-case@^4.1.1: 119 | version "4.1.1" 120 | resolved "https://registry.yarnpkg.com/change-case/-/change-case-4.1.1.tgz#d5005709275952e7963fed7b91e4f9fdb6180afa" 121 | integrity sha512-qRlUWn/hXnX1R1LBDF/RelJLiqNjKjUqlmuBVSEIyye8kq49CXqkZWKmi8XeUAdDXWFOcGLUMZ+aHn3Q5lzUXw== 122 | dependencies: 123 | camel-case "^4.1.1" 124 | capital-case "^1.0.3" 125 | constant-case "^3.0.3" 126 | dot-case "^3.0.3" 127 | header-case "^2.0.3" 128 | no-case "^3.0.3" 129 | param-case "^3.0.3" 130 | pascal-case "^3.1.1" 131 | path-case "^3.0.3" 132 | sentence-case "^3.0.3" 133 | snake-case "^3.0.3" 134 | tslib "^1.10.0" 135 | 136 | cliui@^6.0.0: 137 | version "6.0.0" 138 | resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" 139 | integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== 140 | dependencies: 141 | string-width "^4.2.0" 142 | strip-ansi "^6.0.0" 143 | wrap-ansi "^6.2.0" 144 | 145 | clone@^2.1.2: 146 | version "2.1.2" 147 | resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" 148 | integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= 149 | 150 | codemaker@^1.0.0, codemaker@^1.1.0: 151 | version "1.1.0" 152 | resolved "https://registry.yarnpkg.com/codemaker/-/codemaker-1.1.0.tgz#157020837e6bbb6bdae55f6b3ac9fce8d7676b69" 153 | integrity sha512-XkPDpK8tagP+UUo+/d/4xD1nbP9LxefMLWDE772QmOR4tTINc4WYZkLPLdpnU1v+qRKX1kALD1dQ2MtsC3ZHYw== 154 | dependencies: 155 | camelcase "^5.3.1" 156 | decamelize "^1.2.0" 157 | fs-extra "^8.1.0" 158 | 159 | color-convert@^2.0.1: 160 | version "2.0.1" 161 | resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" 162 | integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== 163 | dependencies: 164 | color-name "~1.1.4" 165 | 166 | color-name@~1.1.4: 167 | version "1.1.4" 168 | resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" 169 | integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== 170 | 171 | colors@^1.4.0: 172 | version "1.4.0" 173 | resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" 174 | integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== 175 | 176 | commonmark@^0.29.1: 177 | version "0.29.1" 178 | resolved "https://registry.yarnpkg.com/commonmark/-/commonmark-0.29.1.tgz#fdbf5970ca23600f4a27487e30eed43b66b83ef5" 179 | integrity sha512-DafPdNYFXoEhsSiR4O+dJ45UJBfDL4cBTks4B+agKiaWt7qjG0bIhg5xuCE0RqU71ikJcBIf4/sRHh9vYQVF8Q== 180 | dependencies: 181 | entities "~1.1.1" 182 | mdurl "~1.0.1" 183 | minimist "~1.2.0" 184 | string.prototype.repeat "^0.2.0" 185 | 186 | constant-case@^3.0.3: 187 | version "3.0.3" 188 | resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.3.tgz#ac910a99caf3926ac5112f352e3af599d8c5fc0a" 189 | integrity sha512-FXtsSnnrFYpzDmvwDGQW+l8XK3GV1coLyBN0eBz16ZUzGaZcT2ANVCJmLeuw2GQgxKHQIe9e0w2dzkSfaRlUmA== 190 | dependencies: 191 | no-case "^3.0.3" 192 | tslib "^1.10.0" 193 | upper-case "^2.0.1" 194 | 195 | crypto@^1.0.1: 196 | version "1.0.1" 197 | resolved "https://registry.yarnpkg.com/crypto/-/crypto-1.0.1.tgz#2af1b7cad8175d24c8a1b0778255794a21803037" 198 | integrity sha512-VxBKmeNcqQdiUQUW2Tzq0t377b54N2bMtXO/qiLa+6eRRmmC4qT3D4OnTGoT/U6O9aklQ/jTwbOtRMTTY8G0Ig== 199 | 200 | date-format@^2.1.0: 201 | version "2.1.0" 202 | resolved "https://registry.yarnpkg.com/date-format/-/date-format-2.1.0.tgz#31d5b5ea211cf5fd764cd38baf9d033df7e125cf" 203 | integrity sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA== 204 | 205 | date-format@^3.0.0: 206 | version "3.0.0" 207 | resolved "https://registry.yarnpkg.com/date-format/-/date-format-3.0.0.tgz#eb8780365c7d2b1511078fb491e6479780f3ad95" 208 | integrity sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w== 209 | 210 | debug@^4.1.1: 211 | version "4.1.1" 212 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" 213 | integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== 214 | dependencies: 215 | ms "^2.1.1" 216 | 217 | decamelize@^1.2.0: 218 | version "1.2.0" 219 | resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" 220 | integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= 221 | 222 | deep-equal@^2.0.1: 223 | version "2.0.1" 224 | resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-2.0.1.tgz#fc12bbd6850e93212f21344748682ccc5a8813cf" 225 | integrity sha512-7Et6r6XfNW61CPPCIYfm1YPGSmh6+CliYeL4km7GWJcpX5LTAflGF8drLLR+MZX+2P3NZfAfSduutBbSWqER4g== 226 | dependencies: 227 | es-abstract "^1.16.3" 228 | es-get-iterator "^1.0.1" 229 | is-arguments "^1.0.4" 230 | is-date-object "^1.0.1" 231 | is-regex "^1.0.4" 232 | isarray "^2.0.5" 233 | object-is "^1.0.1" 234 | object-keys "^1.1.1" 235 | regexp.prototype.flags "^1.2.0" 236 | side-channel "^1.0.1" 237 | which-boxed-primitive "^1.0.1" 238 | which-collection "^1.0.0" 239 | 240 | define-properties@^1.1.2, define-properties@^1.1.3: 241 | version "1.1.3" 242 | resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" 243 | integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== 244 | dependencies: 245 | object-keys "^1.0.12" 246 | 247 | detect-indent@^5.0.0: 248 | version "5.0.0" 249 | resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-5.0.0.tgz#3871cc0a6a002e8c3e5b3cf7f336264675f06b9d" 250 | integrity sha1-OHHMCmoALow+Wzz38zYmRnXwa50= 251 | 252 | detect-newline@^2.1.0: 253 | version "2.1.0" 254 | resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-2.1.0.tgz#f41f1c10be4b00e87b5f13da680759f2c5bfd3e2" 255 | integrity sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I= 256 | 257 | dot-case@^3.0.3: 258 | version "3.0.3" 259 | resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.3.tgz#21d3b52efaaba2ea5fda875bb1aa8124521cf4aa" 260 | integrity sha512-7hwEmg6RiSQfm/GwPL4AAWXKy3YNNZA3oFv2Pdiey0mwkRCPZ9x6SZbkLcn8Ma5PYeVokzoD4Twv2n7LKp5WeA== 261 | dependencies: 262 | no-case "^3.0.3" 263 | tslib "^1.10.0" 264 | 265 | emoji-regex@^8.0.0: 266 | version "8.0.0" 267 | resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" 268 | integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== 269 | 270 | entities@~1.1.1: 271 | version "1.1.2" 272 | resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" 273 | integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== 274 | 275 | es-abstract@^1.16.3, es-abstract@^1.17.0-next.1, es-abstract@^1.17.4: 276 | version "1.17.4" 277 | resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.4.tgz#e3aedf19706b20e7c2594c35fc0d57605a79e184" 278 | integrity sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ== 279 | dependencies: 280 | es-to-primitive "^1.2.1" 281 | function-bind "^1.1.1" 282 | has "^1.0.3" 283 | has-symbols "^1.0.1" 284 | is-callable "^1.1.5" 285 | is-regex "^1.0.5" 286 | object-inspect "^1.7.0" 287 | object-keys "^1.1.1" 288 | object.assign "^4.1.0" 289 | string.prototype.trimleft "^2.1.1" 290 | string.prototype.trimright "^2.1.1" 291 | 292 | es-get-iterator@^1.0.1: 293 | version "1.1.0" 294 | resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.0.tgz#bb98ad9d6d63b31aacdc8f89d5d0ee57bcb5b4c8" 295 | integrity sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ== 296 | dependencies: 297 | es-abstract "^1.17.4" 298 | has-symbols "^1.0.1" 299 | is-arguments "^1.0.4" 300 | is-map "^2.0.1" 301 | is-set "^2.0.1" 302 | is-string "^1.0.5" 303 | isarray "^2.0.5" 304 | 305 | es-to-primitive@^1.2.1: 306 | version "1.2.1" 307 | resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" 308 | integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== 309 | dependencies: 310 | is-callable "^1.1.4" 311 | is-date-object "^1.0.1" 312 | is-symbol "^1.0.2" 313 | 314 | escape-string-regexp@^2.0.0: 315 | version "2.0.0" 316 | resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" 317 | integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== 318 | 319 | find-up@^4.1.0: 320 | version "4.1.0" 321 | resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" 322 | integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== 323 | dependencies: 324 | locate-path "^5.0.0" 325 | path-exists "^4.0.0" 326 | 327 | flatted@^2.0.1: 328 | version "2.0.1" 329 | resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" 330 | integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg== 331 | 332 | fs-extra@^8.1.0: 333 | version "8.1.0" 334 | resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" 335 | integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== 336 | dependencies: 337 | graceful-fs "^4.2.0" 338 | jsonfile "^4.0.0" 339 | universalify "^0.1.0" 340 | 341 | fs@^0.0.1-security: 342 | version "0.0.1-security" 343 | resolved "https://registry.yarnpkg.com/fs/-/fs-0.0.1-security.tgz#8a7bd37186b6dddf3813f23858b57ecaaf5e41d4" 344 | integrity sha1-invTcYa23d84E/I4WLV+yq9eQdQ= 345 | 346 | function-bind@^1.1.1: 347 | version "1.1.1" 348 | resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" 349 | integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== 350 | 351 | get-caller-file@^2.0.1: 352 | version "2.0.5" 353 | resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" 354 | integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== 355 | 356 | graceful-fs@^4.1.6, graceful-fs@^4.2.0: 357 | version "4.2.3" 358 | resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423" 359 | integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== 360 | 361 | has-symbols@^1.0.0, has-symbols@^1.0.1: 362 | version "1.0.1" 363 | resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" 364 | integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== 365 | 366 | has@^1.0.3: 367 | version "1.0.3" 368 | resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" 369 | integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== 370 | dependencies: 371 | function-bind "^1.1.1" 372 | 373 | header-case@^2.0.3: 374 | version "2.0.3" 375 | resolved "https://registry.yarnpkg.com/header-case/-/header-case-2.0.3.tgz#8a7407d16edfd5c970f8ebb116e6383f855b5a72" 376 | integrity sha512-LChe/V32mnUQnTwTxd3aAlNMk8ia9tjCDb/LjYtoMrdAPApxLB+azejUk5ERZIZdIqvinwv6BAUuFXH/tQPdZA== 377 | dependencies: 378 | capital-case "^1.0.3" 379 | tslib "^1.10.0" 380 | 381 | inherits@2.0.3: 382 | version "2.0.3" 383 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 384 | integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= 385 | 386 | is-arguments@^1.0.4: 387 | version "1.0.4" 388 | resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.0.4.tgz#3faf966c7cba0ff437fb31f6250082fcf0448cf3" 389 | integrity sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA== 390 | 391 | is-bigint@^1.0.0: 392 | version "1.0.0" 393 | resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.0.tgz#73da8c33208d00f130e9b5e15d23eac9215601c4" 394 | integrity sha512-t5mGUXC/xRheCK431ylNiSkGGpBp8bHENBcENTkDT6ppwPzEVxNGZRvgvmOEfbWkFhA7D2GEuE2mmQTr78sl2g== 395 | 396 | is-boolean-object@^1.0.0: 397 | version "1.0.1" 398 | resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.1.tgz#10edc0900dd127697a92f6f9807c7617d68ac48e" 399 | integrity sha512-TqZuVwa/sppcrhUCAYkGBk7w0yxfQQnxq28fjkO53tnK9FQXmdwz2JS5+GjsWQ6RByES1K40nI+yDic5c9/aAQ== 400 | 401 | is-callable@^1.1.4, is-callable@^1.1.5: 402 | version "1.1.5" 403 | resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab" 404 | integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q== 405 | 406 | is-date-object@^1.0.1: 407 | version "1.0.2" 408 | resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" 409 | integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== 410 | 411 | is-fullwidth-code-point@^3.0.0: 412 | version "3.0.0" 413 | resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" 414 | integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== 415 | 416 | is-map@^2.0.1: 417 | version "2.0.1" 418 | resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.1.tgz#520dafc4307bb8ebc33b813de5ce7c9400d644a1" 419 | integrity sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw== 420 | 421 | is-number-object@^1.0.3: 422 | version "1.0.4" 423 | resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" 424 | integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== 425 | 426 | is-regex@^1.0.4, is-regex@^1.0.5: 427 | version "1.0.5" 428 | resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae" 429 | integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ== 430 | dependencies: 431 | has "^1.0.3" 432 | 433 | is-set@^2.0.1: 434 | version "2.0.1" 435 | resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.1.tgz#d1604afdab1724986d30091575f54945da7e5f43" 436 | integrity sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA== 437 | 438 | is-string@^1.0.4, is-string@^1.0.5: 439 | version "1.0.5" 440 | resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" 441 | integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== 442 | 443 | is-symbol@^1.0.2: 444 | version "1.0.3" 445 | resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" 446 | integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== 447 | dependencies: 448 | has-symbols "^1.0.1" 449 | 450 | is-weakmap@^2.0.1: 451 | version "2.0.1" 452 | resolved "https://registry.yarnpkg.com/is-weakmap/-/is-weakmap-2.0.1.tgz#5008b59bdc43b698201d18f62b37b2ca243e8cf2" 453 | integrity sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA== 454 | 455 | is-weakset@^2.0.1: 456 | version "2.0.1" 457 | resolved "https://registry.yarnpkg.com/is-weakset/-/is-weakset-2.0.1.tgz#e9a0af88dbd751589f5e50d80f4c98b780884f83" 458 | integrity sha512-pi4vhbhVHGLxohUw7PhGsueT4vRGFoXhP7+RGN0jKIv9+8PWYCQTqtADngrxOm2g46hoH0+g8uZZBzMrvVGDmw== 459 | 460 | isarray@^2.0.5: 461 | version "2.0.5" 462 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723" 463 | integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== 464 | 465 | jsii-pacmak@^1.0.0: 466 | version "1.1.0" 467 | resolved "https://registry.yarnpkg.com/jsii-pacmak/-/jsii-pacmak-1.1.0.tgz#a5363bde828bda6d24cbc33a8dd13971eea28e67" 468 | integrity sha512-TLJjLN53fwA5n7hQ2UKRxRYUhead7sRILo4KOJQxN8dVAI1+oocp198QaVGbDD6ZE3aIUKKVThOkBZkY8K3zbw== 469 | dependencies: 470 | "@jsii/spec" "^1.1.0" 471 | camelcase "^5.1.3" 472 | clone "^2.1.2" 473 | codemaker "^1.1.0" 474 | commonmark "^0.29.1" 475 | escape-string-regexp "^2.0.0" 476 | fs-extra "^8.1.0" 477 | jsii-reflect "^1.1.0" 478 | jsii-rosetta "^1.1.0" 479 | semver "^7.1.3" 480 | spdx-license-list "^6.1.0" 481 | xmlbuilder "^15.0.0" 482 | yargs "^15.3.0" 483 | 484 | jsii-reflect@^1.1.0: 485 | version "1.1.0" 486 | resolved "https://registry.yarnpkg.com/jsii-reflect/-/jsii-reflect-1.1.0.tgz#5f013e2d90f8d97299402e14355bb666b1147084" 487 | integrity sha512-/Ik0DKL/PhS5LDbwY4GrgaVIulc2SPqJAIuLJVwGeEQgBqj9kgPto0Huh5icMw3WK4+qwp/B/WCPbYivnkjF+w== 488 | dependencies: 489 | "@jsii/spec" "^1.1.0" 490 | colors "^1.4.0" 491 | fs-extra "^8.1.0" 492 | oo-ascii-tree "^1.1.0" 493 | yargs "^15.3.0" 494 | 495 | jsii-rosetta@^1.1.0: 496 | version "1.1.0" 497 | resolved "https://registry.yarnpkg.com/jsii-rosetta/-/jsii-rosetta-1.1.0.tgz#c70fa9d6d78e116c4308415a8bc918b05a94fe2d" 498 | integrity sha512-GWLntW4U7i+fnjmPfVaU7qjx+U6eU9BHMufpZVhtQVh7IGoY81dQSnDT1bAJQLSNqvmbaada4CEj8FuR9xdDeQ== 499 | dependencies: 500 | "@jsii/spec" "^1.1.0" 501 | commonmark "^0.29.1" 502 | fs-extra "^8.1.0" 503 | typescript "~3.8.3" 504 | xmldom "^0.3.0" 505 | yargs "^15.3.0" 506 | 507 | jsii@^1.0.0: 508 | version "1.1.0" 509 | resolved "https://registry.yarnpkg.com/jsii/-/jsii-1.1.0.tgz#cc76544fd67793f92f3fbe8f0d1ff22d75f61092" 510 | integrity sha512-QmtKu2ZEXwMop+An4AnDsOZJr5EObcXtGiuw8bVy8ldq1WHiri4mvSWwZQI3ekUcWyOGjwFY9CuDy+xYbsf+Pg== 511 | dependencies: 512 | "@jsii/spec" "^1.1.0" 513 | case "^1.6.2" 514 | colors "^1.4.0" 515 | deep-equal "^2.0.1" 516 | fs-extra "^8.1.0" 517 | log4js "^6.1.2" 518 | semver "^7.1.3" 519 | semver-intersect "^1.4.0" 520 | sort-json "^2.0.0" 521 | spdx-license-list "^6.1.0" 522 | typescript "~3.8.3" 523 | yargs "^15.3.0" 524 | 525 | json-schema@^0.2.5: 526 | version "0.2.5" 527 | resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.5.tgz#97997f50972dd0500214e208c407efa4b5d7063b" 528 | integrity sha512-gWJOWYFrhQ8j7pVm0EM8Slr+EPVq1Phf6lvzvD/WCeqkrx/f2xBI0xOsRRS9xCn3I4vKtP519dvs3TP09r24wQ== 529 | 530 | jsonfile@^4.0.0: 531 | version "4.0.0" 532 | resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" 533 | integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= 534 | optionalDependencies: 535 | graceful-fs "^4.1.6" 536 | 537 | jsonschema@^1.2.5: 538 | version "1.2.5" 539 | resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.2.5.tgz#bab69d97fa28946aec0a56a9cc266d23fe80ae61" 540 | integrity sha512-kVTF+08x25PQ0CjuVc0gRM9EUPb0Fe9Ln/utFOgcdxEIOHuU7ooBk/UPTd7t1M91pP35m0MU1T8M5P7vP1bRRw== 541 | 542 | locate-path@^5.0.0: 543 | version "5.0.0" 544 | resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" 545 | integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== 546 | dependencies: 547 | p-locate "^4.1.0" 548 | 549 | log4js@^6.1.2: 550 | version "6.1.2" 551 | resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.1.2.tgz#04688e1f4b8080c127b7dccb0db1c759cbb25dc4" 552 | integrity sha512-knS4Y30pC1e0n7rfx3VxcLOdBCsEo0o6/C7PVTGxdVK+5b1TYOSGQPn9FDcrhkoQBV29qwmA2mtkznPAQKnxQg== 553 | dependencies: 554 | date-format "^3.0.0" 555 | debug "^4.1.1" 556 | flatted "^2.0.1" 557 | rfdc "^1.1.4" 558 | streamroller "^2.2.3" 559 | 560 | lower-case@^2.0.1: 561 | version "2.0.1" 562 | resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.1.tgz#39eeb36e396115cc05e29422eaea9e692c9408c7" 563 | integrity sha512-LiWgfDLLb1dwbFQZsSglpRj+1ctGnayXz3Uv0/WO8n558JycT5fg6zkNcnW0G68Nn0aEldTFeEfmjCfmqry/rQ== 564 | dependencies: 565 | tslib "^1.10.0" 566 | 567 | mdurl@~1.0.1: 568 | version "1.0.1" 569 | resolved "https://registry.yarnpkg.com/mdurl/-/mdurl-1.0.1.tgz#fe85b2ec75a59037f2adfec100fd6c601761152e" 570 | integrity sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= 571 | 572 | minimist@^1.2.0, minimist@~1.2.0: 573 | version "1.2.3" 574 | resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.3.tgz#3db5c0765545ab8637be71f333a104a965a9ca3f" 575 | integrity sha512-+bMdgqjMN/Z77a6NlY/I3U5LlRDbnmaAk6lDveAPKwSpcPM4tKAuYsvYF8xjhOPXhOYGe/73vVLVez5PW+jqhw== 576 | 577 | ms@^2.1.1: 578 | version "2.1.2" 579 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" 580 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== 581 | 582 | no-case@^3.0.3: 583 | version "3.0.3" 584 | resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.3.tgz#c21b434c1ffe48b39087e86cfb4d2582e9df18f8" 585 | integrity sha512-ehY/mVQCf9BL0gKfsJBvFJen+1V//U+0HQMPrWct40ixE4jnv0bfvxDbWtAHL9EcaPEOJHVVYKoQn1TlZUB8Tw== 586 | dependencies: 587 | lower-case "^2.0.1" 588 | tslib "^1.10.0" 589 | 590 | object-inspect@^1.7.0: 591 | version "1.7.0" 592 | resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67" 593 | integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw== 594 | 595 | object-is@^1.0.1: 596 | version "1.0.2" 597 | resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.2.tgz#6b80eb84fe451498f65007982f035a5b445edec4" 598 | integrity sha512-Epah+btZd5wrrfjkJZq1AOB9O6OxUQto45hzFd7lXGrpHPGE0W1k+426yrZV+k6NJOzLNNW/nVsmZdIWsAqoOQ== 599 | 600 | object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1: 601 | version "1.1.1" 602 | resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" 603 | integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== 604 | 605 | object.assign@^4.1.0: 606 | version "4.1.0" 607 | resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da" 608 | integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w== 609 | dependencies: 610 | define-properties "^1.1.2" 611 | function-bind "^1.1.1" 612 | has-symbols "^1.0.0" 613 | object-keys "^1.0.11" 614 | 615 | oo-ascii-tree@^1.1.0: 616 | version "1.1.0" 617 | resolved "https://registry.yarnpkg.com/oo-ascii-tree/-/oo-ascii-tree-1.1.0.tgz#e5bc65db665cfb0d6e4fc750620f7d6c11bc2702" 618 | integrity sha512-GAmkryQIl44INfzyAHEnVSPzG1T2PTG8S+F1BvTvoW3Cr3pbDaPljW4prV6y1hg7YZ4isEI1dcoIQ7k4gyVKeA== 619 | 620 | p-limit@^2.2.0: 621 | version "2.2.2" 622 | resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.2.2.tgz#61279b67721f5287aa1c13a9a7fbbc48c9291b1e" 623 | integrity sha512-WGR+xHecKTr7EbUEhyLSh5Dube9JtdiG78ufaeLxTgpudf/20KqyMioIUZJAezlTIi6evxuoUs9YXc11cU+yzQ== 624 | dependencies: 625 | p-try "^2.0.0" 626 | 627 | p-locate@^4.1.0: 628 | version "4.1.0" 629 | resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" 630 | integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== 631 | dependencies: 632 | p-limit "^2.2.0" 633 | 634 | p-try@^2.0.0: 635 | version "2.2.0" 636 | resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" 637 | integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== 638 | 639 | param-case@^3.0.3: 640 | version "3.0.3" 641 | resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.3.tgz#4be41f8399eff621c56eebb829a5e451d9801238" 642 | integrity sha512-VWBVyimc1+QrzappRs7waeN2YmoZFCGXWASRYX1/rGHtXqEcrGEIDm+jqIwFa2fRXNgQEwrxaYuIrX0WcAguTA== 643 | dependencies: 644 | dot-case "^3.0.3" 645 | tslib "^1.10.0" 646 | 647 | pascal-case@^3.1.1: 648 | version "3.1.1" 649 | resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.1.tgz#5ac1975133ed619281e88920973d2cd1f279de5f" 650 | integrity sha512-XIeHKqIrsquVTQL2crjq3NfJUxmdLasn3TYOU0VBM+UX2a6ztAWBlJQBePLGY7VHW8+2dRadeIPK5+KImwTxQA== 651 | dependencies: 652 | no-case "^3.0.3" 653 | tslib "^1.10.0" 654 | 655 | path-case@^3.0.3: 656 | version "3.0.3" 657 | resolved "https://registry.yarnpkg.com/path-case/-/path-case-3.0.3.tgz#d48119aed52c4712e036ca40c6b15984f909554f" 658 | integrity sha512-UMFU6UETFpCNWbIWNczshPrnK/7JAXBP2NYw80ojElbQ2+JYxdqWDBkvvqM93u4u6oLmuJ/tPOf2tM8KtXv4eg== 659 | dependencies: 660 | dot-case "^3.0.3" 661 | tslib "^1.10.0" 662 | 663 | path-exists@^4.0.0: 664 | version "4.0.0" 665 | resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" 666 | integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== 667 | 668 | path@^0.12.7: 669 | version "0.12.7" 670 | resolved "https://registry.yarnpkg.com/path/-/path-0.12.7.tgz#d4dc2a506c4ce2197eb481ebfcd5b36c0140b10f" 671 | integrity sha1-1NwqUGxM4hl+tIHr/NWzbAFAsQ8= 672 | dependencies: 673 | process "^0.11.1" 674 | util "^0.10.3" 675 | 676 | process@^0.11.1: 677 | version "0.11.10" 678 | resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" 679 | integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= 680 | 681 | regenerator-runtime@^0.13.4: 682 | version "0.13.4" 683 | resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz#e96bf612a3362d12bb69f7e8f74ffeab25c7ac91" 684 | integrity sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g== 685 | 686 | regexp.prototype.flags@^1.2.0: 687 | version "1.3.0" 688 | resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz#7aba89b3c13a64509dabcf3ca8d9fbb9bdf5cb75" 689 | integrity sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ== 690 | dependencies: 691 | define-properties "^1.1.3" 692 | es-abstract "^1.17.0-next.1" 693 | 694 | require-directory@^2.1.1: 695 | version "2.1.1" 696 | resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" 697 | integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= 698 | 699 | require-main-filename@^2.0.0: 700 | version "2.0.0" 701 | resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" 702 | integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== 703 | 704 | rfdc@^1.1.4: 705 | version "1.1.4" 706 | resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.1.4.tgz#ba72cc1367a0ccd9cf81a870b3b58bd3ad07f8c2" 707 | integrity sha512-5C9HXdzK8EAqN7JDif30jqsBzavB7wLpaubisuQIGHWf2gUXSpzy6ArX/+Da8RjFpagWsCn+pIgxTMAmKw9Zug== 708 | 709 | semver-intersect@^1.4.0: 710 | version "1.4.0" 711 | resolved "https://registry.yarnpkg.com/semver-intersect/-/semver-intersect-1.4.0.tgz#bdd9c06bedcdd2fedb8cd352c3c43ee8c61321f3" 712 | integrity sha512-d8fvGg5ycKAq0+I6nfWeCx6ffaWJCsBYU0H2Rq56+/zFePYfT8mXkB3tWBSjR5BerkHNZ5eTPIk1/LBYas35xQ== 713 | dependencies: 714 | semver "^5.0.0" 715 | 716 | semver@^5.0.0: 717 | version "5.7.1" 718 | resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" 719 | integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== 720 | 721 | semver@^7.1.3: 722 | version "7.1.3" 723 | resolved "https://registry.yarnpkg.com/semver/-/semver-7.1.3.tgz#e4345ce73071c53f336445cfc19efb1c311df2a6" 724 | integrity sha512-ekM0zfiA9SCBlsKa2X1hxyxiI4L3B6EbVJkkdgQXnSEEaHlGdvyodMruTiulSRWMMB4NeIuYNMC9rTKTz97GxA== 725 | 726 | sentence-case@^3.0.3: 727 | version "3.0.3" 728 | resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-3.0.3.tgz#47576e4adff7abf42c63c815b0543c9d2f85a930" 729 | integrity sha512-ZPr4dgTcNkEfcGOMFQyDdJrTU9uQO1nb1cjf+nuzb6FxgMDgKddZOM29qEsB7jvsZSMruLRcL2KfM4ypKpa0LA== 730 | dependencies: 731 | no-case "^3.0.3" 732 | tslib "^1.10.0" 733 | upper-case-first "^2.0.1" 734 | 735 | set-blocking@^2.0.0: 736 | version "2.0.0" 737 | resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" 738 | integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= 739 | 740 | side-channel@^1.0.1: 741 | version "1.0.2" 742 | resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.2.tgz#df5d1abadb4e4bf4af1cd8852bf132d2f7876947" 743 | integrity sha512-7rL9YlPHg7Ancea1S96Pa8/QWb4BtXL/TZvS6B8XFetGBeuhAsfmUspK6DokBeZ64+Kj9TCNRD/30pVz1BvQNA== 744 | dependencies: 745 | es-abstract "^1.17.0-next.1" 746 | object-inspect "^1.7.0" 747 | 748 | snake-case@^3.0.3: 749 | version "3.0.3" 750 | resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.3.tgz#c598b822ab443fcbb145ae8a82c5e43526d5bbee" 751 | integrity sha512-WM1sIXEO+rsAHBKjGf/6R1HBBcgbncKS08d2Aqec/mrDSpU80SiOU41hO7ny6DToHSyrlwTYzQBIK1FPSx4Y3Q== 752 | dependencies: 753 | dot-case "^3.0.3" 754 | tslib "^1.10.0" 755 | 756 | sort-json@^2.0.0: 757 | version "2.0.0" 758 | resolved "https://registry.yarnpkg.com/sort-json/-/sort-json-2.0.0.tgz#a7030d8875adbd4a5ea39a000567ed94c1aa3c50" 759 | integrity sha512-OgXPErPJM/rBK5OhzIJ+etib/BmLQ1JY55Nb/ElhoWUec62pXNF/X6DrecHq3NW5OAGX0KxYD7m0HtgB9dvGeA== 760 | dependencies: 761 | detect-indent "^5.0.0" 762 | detect-newline "^2.1.0" 763 | minimist "^1.2.0" 764 | 765 | spdx-license-list@^6.1.0: 766 | version "6.1.0" 767 | resolved "https://registry.yarnpkg.com/spdx-license-list/-/spdx-license-list-6.1.0.tgz#a79cf9fb0222db69dfef067f61c52f0eb3ad36bb" 768 | integrity sha512-xiaE3KtBiylVmZrlux8tHR28HZgZ921HTXbx2fEZaDloRjbBOro79LeKttcQJ5MSDYFKG7in9v2GTAEhcR9/Qg== 769 | 770 | sscaff@^1.2.0: 771 | version "1.2.0" 772 | resolved "https://registry.yarnpkg.com/sscaff/-/sscaff-1.2.0.tgz#d015f199ac53c2df66c4b6135b29bd01f7885445" 773 | integrity sha512-Xyf2tWLnO0Z297FKag0e8IXFIpnYRWZ3FBn4dN2qlMRsOcpf0P54FPhvdcb1Es0Fm4hbhYYXa23jR+VPGPQhSg== 774 | 775 | streamroller@^2.2.3: 776 | version "2.2.3" 777 | resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-2.2.3.tgz#b95c9fad44e2e89005d242141486b3b4962c2d28" 778 | integrity sha512-AegmvQsscTRhHVO46PhCDerjIpxi7E+d2GxgUDu+nzw/HuLnUdxHWr6WQ+mVn/4iJgMKKFFdiUwFcFRDvcjCtw== 779 | dependencies: 780 | date-format "^2.1.0" 781 | debug "^4.1.1" 782 | fs-extra "^8.1.0" 783 | 784 | string-width@^4.1.0, string-width@^4.2.0: 785 | version "4.2.0" 786 | resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" 787 | integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== 788 | dependencies: 789 | emoji-regex "^8.0.0" 790 | is-fullwidth-code-point "^3.0.0" 791 | strip-ansi "^6.0.0" 792 | 793 | string.prototype.repeat@^0.2.0: 794 | version "0.2.0" 795 | resolved "https://registry.yarnpkg.com/string.prototype.repeat/-/string.prototype.repeat-0.2.0.tgz#aba36de08dcee6a5a337d49b2ea1da1b28fc0ecf" 796 | integrity sha1-q6Nt4I3O5qWjN9SbLqHaGyj8Ds8= 797 | 798 | string.prototype.trimleft@^2.1.1: 799 | version "2.1.1" 800 | resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz#9bdb8ac6abd6d602b17a4ed321870d2f8dcefc74" 801 | integrity sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag== 802 | dependencies: 803 | define-properties "^1.1.3" 804 | function-bind "^1.1.1" 805 | 806 | string.prototype.trimright@^2.1.1: 807 | version "2.1.1" 808 | resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz#440314b15996c866ce8a0341894d45186200c5d9" 809 | integrity sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g== 810 | dependencies: 811 | define-properties "^1.1.3" 812 | function-bind "^1.1.1" 813 | 814 | strip-ansi@^6.0.0: 815 | version "6.0.0" 816 | resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" 817 | integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== 818 | dependencies: 819 | ansi-regex "^5.0.0" 820 | 821 | "terrastack-cli@file:../../packages/terrastack-cli": 822 | version "0.15.0" 823 | dependencies: 824 | "@aws-cdk/core" "^1.27.0" 825 | "@aws-cdk/cx-api" "^1.24.0" 826 | "@types/json-schema" "^7.0.4" 827 | "@types/node" "^13.7.7" 828 | "@types/yaml" "^1.2.0" 829 | "@types/yargs" "^15.0.4" 830 | change-case "^4.1.1" 831 | codemaker "^1.0.0" 832 | crypto "^1.0.1" 833 | fs "^0.0.1-security" 834 | fs-extra "^8.1.0" 835 | jsii "^1.0.0" 836 | jsii-pacmak "^1.0.0" 837 | json-schema "^0.2.5" 838 | path "^0.12.7" 839 | sscaff "^1.2.0" 840 | typescript "^3.8.3" 841 | yaml "^1.7.2" 842 | yargs "^15.1.0" 843 | 844 | tslib@^1.10.0: 845 | version "1.11.1" 846 | resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35" 847 | integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA== 848 | 849 | typescript@^3.8.3, typescript@~3.8.3: 850 | version "3.8.3" 851 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061" 852 | integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w== 853 | 854 | universalify@^0.1.0: 855 | version "0.1.2" 856 | resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" 857 | integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== 858 | 859 | upper-case-first@^2.0.1: 860 | version "2.0.1" 861 | resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-2.0.1.tgz#32ab436747d891cc20ab1e43d601cb4d0a7fbf4a" 862 | integrity sha512-105J8XqQ+9RxW3l9gHZtgve5oaiR9TIwvmZAMAIZWRHe00T21cdvewKORTlOJf/zXW6VukuTshM+HXZNWz7N5w== 863 | dependencies: 864 | tslib "^1.10.0" 865 | 866 | upper-case@^2.0.1: 867 | version "2.0.1" 868 | resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-2.0.1.tgz#6214d05e235dc817822464ccbae85822b3d8665f" 869 | integrity sha512-laAsbea9SY5osxrv7S99vH9xAaJKrw5Qpdh4ENRLcaxipjKsiaBwiAsxfa8X5mObKNTQPsupSq0J/VIxsSJe3A== 870 | dependencies: 871 | tslib "^1.10.0" 872 | 873 | util@^0.10.3: 874 | version "0.10.4" 875 | resolved "https://registry.yarnpkg.com/util/-/util-0.10.4.tgz#3aa0125bfe668a4672de58857d3ace27ecb76901" 876 | integrity sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A== 877 | dependencies: 878 | inherits "2.0.3" 879 | 880 | which-boxed-primitive@^1.0.1: 881 | version "1.0.1" 882 | resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.1.tgz#cbe8f838ebe91ba2471bb69e9edbda67ab5a5ec1" 883 | integrity sha512-7BT4TwISdDGBgaemWU0N0OU7FeAEJ9Oo2P1PHRm/FCWoEi2VLWC9b6xvxAA3C/NMpxg3HXVgi0sMmGbNUbNepQ== 884 | dependencies: 885 | is-bigint "^1.0.0" 886 | is-boolean-object "^1.0.0" 887 | is-number-object "^1.0.3" 888 | is-string "^1.0.4" 889 | is-symbol "^1.0.2" 890 | 891 | which-collection@^1.0.0: 892 | version "1.0.1" 893 | resolved "https://registry.yarnpkg.com/which-collection/-/which-collection-1.0.1.tgz#70eab71ebbbd2aefaf32f917082fc62cdcb70906" 894 | integrity sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A== 895 | dependencies: 896 | is-map "^2.0.1" 897 | is-set "^2.0.1" 898 | is-weakmap "^2.0.1" 899 | is-weakset "^2.0.1" 900 | 901 | which-module@^2.0.0: 902 | version "2.0.0" 903 | resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" 904 | integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= 905 | 906 | wrap-ansi@^6.2.0: 907 | version "6.2.0" 908 | resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" 909 | integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== 910 | dependencies: 911 | ansi-styles "^4.0.0" 912 | string-width "^4.1.0" 913 | strip-ansi "^6.0.0" 914 | 915 | xmlbuilder@^15.0.0: 916 | version "15.0.0" 917 | resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.0.0.tgz#de9a078a0b82ef0c6da5c76e58813a879eec31ec" 918 | integrity sha512-KLu/G0DoWhkncQ9eHSI6s0/w+T4TM7rQaLhtCaL6tORv8jFlJPlnGumsgTcGfYeS1qZ/IHqrvDG7zJZ4d7e+nw== 919 | 920 | xmldom@^0.3.0: 921 | version "0.3.0" 922 | resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.3.0.tgz#e625457f4300b5df9c2e1ecb776147ece47f3e5a" 923 | integrity sha512-z9s6k3wxE+aZHgXYxSTpGDo7BYOUfJsIRyoZiX6HTjwpwfS2wpQBQKa2fD+ShLyPkqDYo5ud7KitmLZ2Cd6r0g== 924 | 925 | y18n@^4.0.0: 926 | version "4.0.0" 927 | resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" 928 | integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w== 929 | 930 | yaml@^1.7.2: 931 | version "1.8.2" 932 | resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.8.2.tgz#a29c03f578faafd57dcb27055f9a5d569cb0c3d9" 933 | integrity sha512-omakb0d7FjMo3R1D2EbTKVIk6dAVLRxFXdLZMEUToeAvuqgG/YuHMuQOZ5fgk+vQ8cx+cnGKwyg+8g8PNT0xQg== 934 | dependencies: 935 | "@babel/runtime" "^7.8.7" 936 | 937 | yargs-parser@^18.1.0: 938 | version "18.1.0" 939 | resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.0.tgz#1b0ab1118ebd41f68bb30e729f4c83df36ae84c3" 940 | integrity sha512-o/Jr6JBOv6Yx3pL+5naWSoIA2jJ+ZkMYQG/ie9qFbukBe4uzmBatlXFOiu/tNKRWEtyf+n5w7jc/O16ufqOTdQ== 941 | dependencies: 942 | camelcase "^5.0.0" 943 | decamelize "^1.2.0" 944 | 945 | yargs@^15.1.0, yargs@^15.3.0: 946 | version "15.3.0" 947 | resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.3.0.tgz#403af6edc75b3ae04bf66c94202228ba119f0976" 948 | integrity sha512-g/QCnmjgOl1YJjGsnUg2SatC7NUYEiLXJqxNOQU9qSpjzGtGXda9b+OKccr1kLTy8BN9yqEyqfq5lxlwdc13TA== 949 | dependencies: 950 | cliui "^6.0.0" 951 | decamelize "^1.2.0" 952 | find-up "^4.1.0" 953 | get-caller-file "^2.0.1" 954 | require-directory "^2.1.1" 955 | require-main-filename "^2.0.0" 956 | set-blocking "^2.0.0" 957 | string-width "^4.2.0" 958 | which-module "^2.0.0" 959 | y18n "^4.0.0" 960 | yargs-parser "^18.1.0" 961 | -------------------------------------------------------------------------------- /images/terrastack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TerraStackIO/terrastack/0380fda9698580522e4e3d007786c37b04bb2cae/images/terrastack.png -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "packages/*", 4 | "packages/@terrastack/*" 5 | ], 6 | "npmClient": "yarn", 7 | "useWorkspaces": true, 8 | "version": "0.15.0" 9 | } 10 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "terrastack", 3 | "version": "0.15.0", 4 | "description": "Polyglot Terraform supercharged by the Cloud Development Kit (CDK)", 5 | "private": true, 6 | "keywords": [ 7 | "terrastack", 8 | "terraform", 9 | "cdk", 10 | "cloud", 11 | "aws", 12 | "azure", 13 | "gcp" 14 | ], 15 | "scripts": { 16 | "bump": "tools/bump.sh", 17 | "build": "lerna run build", 18 | "test": "lerna run test", 19 | "package": "lerna run package", 20 | "release": "tools/release.sh" 21 | }, 22 | "author": "", 23 | "license": "ISC", 24 | "workspaces": { 25 | "packages": [ 26 | "packages/*", 27 | "packages/@terrastack/*" 28 | ] 29 | }, 30 | "devDependencies": { 31 | "changelog-parser": "^2.8.0", 32 | "jsii-release": "^0.1.5", 33 | "lerna": "^3.20.2", 34 | "standard-version": "^7.1.0" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/@terrastack/core/.npmignore: -------------------------------------------------------------------------------- 1 | 2 | # Exclude typescript source and config 3 | *.ts 4 | tsconfig.json 5 | 6 | # Include javascript files and typescript declarations 7 | !*.js 8 | !*.d.ts 9 | 10 | # Exclude jsii outdir 11 | dist 12 | 13 | # Include .jsii 14 | !.jsii 15 | -------------------------------------------------------------------------------- /packages/@terrastack/core/lib/_tokens.ts: -------------------------------------------------------------------------------- 1 | import { Tokenization, DefaultTokenResolver, StringConcat, Construct } from "@aws-cdk/core"; 2 | 3 | const TOKEN_RESOLVER = new DefaultTokenResolver(new StringConcat()); 4 | 5 | export function resolve(scope: Construct, obj: T): T { 6 | return Tokenization.resolve(obj, { 7 | scope, 8 | resolver: TOKEN_RESOLVER 9 | }); 10 | } -------------------------------------------------------------------------------- /packages/@terrastack/core/lib/_util.ts: -------------------------------------------------------------------------------- 1 | export function removeEmpty(obj: any): any { 2 | if (obj == null) { 3 | return undefined; 4 | } 5 | 6 | if (typeof(obj) !== 'object') { 7 | return obj; 8 | } 9 | 10 | if (Array.isArray(obj)) { 11 | if (obj.length === 0) { 12 | return undefined; 13 | } 14 | 15 | return obj.map(x => { 16 | // do not remove "{}" and "[]" if they are array elements. 17 | if (Array.isArray(x) && x.length === 0) { return x; } 18 | if (x != null && typeof(x) === 'object' && Object.keys(x).length === 0) { return x; } 19 | return removeEmpty(x) 20 | }); 21 | } 22 | 23 | if (obj.constructor.name !== 'Object') { 24 | throw new Error(`can't render non-simple object of type '${obj.constructor.name}'`); 25 | } 26 | 27 | if (Object.keys(obj).length === 0) { 28 | return undefined; 29 | } 30 | 31 | const newObj: { [key: string]: any } = { }; 32 | 33 | for (const [key, value] of Object.entries(obj)) { 34 | const newValue = removeEmpty(value); 35 | if (newValue != null) { 36 | newObj[key] = newValue; 37 | } 38 | } 39 | 40 | return newObj; 41 | } 42 | 43 | export function snakeCase(str: string): string { 44 | if (!str) return ''; 45 | 46 | return String(str) 47 | .replace(/^[^A-Za-z0-9]*|[^A-Za-z0-9]*$/g, '') 48 | .replace(/([a-z])([A-Z])/g, (_m, a, b) => a + '_' + b.toLowerCase()) 49 | .replace(/[^A-Za-z0-9]+|_+/g, '_') 50 | .toLowerCase(); 51 | } -------------------------------------------------------------------------------- /packages/@terrastack/core/lib/app.ts: -------------------------------------------------------------------------------- 1 | import { Construct, ConstructNode } from '@aws-cdk/core'; 2 | import * as fs from 'fs'; 3 | import * as path from 'path'; 4 | 5 | export interface AppOptions { 6 | /** 7 | * The directory to output Terraform files. 8 | * 9 | * @default "dist" 10 | */ 11 | readonly outdir?: string; 12 | } 13 | 14 | /** 15 | * Represents a Terrastack application. 16 | */ 17 | export class App extends Construct { 18 | /** 19 | * The output directory into which manifests will be synthesized. 20 | */ 21 | public readonly outdir: string; 22 | 23 | /** 24 | * Defines an app 25 | * @param options configuration options 26 | */ 27 | constructor(options: AppOptions = { }) { 28 | super(undefined as any, ''); 29 | this.outdir = options.outdir ?? 'dist'; 30 | } 31 | 32 | /** 33 | * Synthesizes all manifests to the output directory 34 | */ 35 | public synth(): void { 36 | ConstructNode.synth(this.node, { 37 | outdir: this.outdir 38 | }); 39 | 40 | // remove manifest.json and cdk.out 41 | rm(path.join(this.outdir, 'manifest.json')); 42 | rm(path.join(this.outdir, 'cdk.out')); 43 | } 44 | } 45 | 46 | function rm(filePath: string) { 47 | if (fs.existsSync(filePath)) { 48 | fs.unlinkSync(filePath); 49 | } 50 | } -------------------------------------------------------------------------------- /packages/@terrastack/core/lib/index.ts: -------------------------------------------------------------------------------- 1 | export * from './resource-object'; 2 | export * from './app'; 3 | export * from './stack'; 4 | export * from './tf-reference'; 5 | -------------------------------------------------------------------------------- /packages/@terrastack/core/lib/names.ts: -------------------------------------------------------------------------------- 1 | import * as crypto from 'crypto'; 2 | 3 | const MAX_DNS_NAME_LEN = 63; 4 | const VALIDATE = /^[0-9a-z-]+$/; 5 | const HASH_LEN = 8; 6 | 7 | /** 8 | * Utilities for generating unique and stable names. 9 | */ 10 | export class Names { 11 | /** 12 | * Generates a unique and stable name compatible DNS_LABEL from RFC-1123 from 13 | * a path. 14 | * 15 | * The generated name will: 16 | * - contain at most 63 characters 17 | * - contain only lowercase alphanumeric characters or ‘-’ 18 | * - start with an alphanumeric character 19 | * - end with an alphanumeric character 20 | * 21 | * The generated name will have the form: 22 | * --..-- 23 | * 24 | * Where are the path components (assuming they are is separated by 25 | * "/"). 26 | * 27 | * Note that if the total length is longer than 63 characters, we will trim 28 | * the first components since the last components usually encode more meaning. 29 | * 30 | * @link https://tools.ietf.org/html/rfc1123 31 | * 32 | * @param path a path to a node (components separated by "/") 33 | * @param maxLen maximum allowed length for name 34 | * @throws if any of the components do not adhere to naming constraints or 35 | * length. 36 | */ 37 | public static toDnsLabel(path: string, maxLen = MAX_DNS_NAME_LEN) { 38 | if (maxLen < HASH_LEN) { 39 | throw new Error(`minimum max length for object names is ${HASH_LEN} (required for hash)`); 40 | } 41 | 42 | const components = path.split('/'); 43 | 44 | // verify components only use allowed chars. 45 | for (const comp of components) { 46 | if (!VALIDATE.test(comp)) { 47 | throw new Error(`"${comp}" is not a valid object name. The characters allowed in names are: digits (0-9), lower case letters (a-z), -, and .`); 48 | } 49 | 50 | if (comp.length > maxLen) { 51 | throw new Error(`Object name "${comp}" is too long. Maximum allowed length is ${maxLen}`); 52 | } 53 | } 54 | 55 | // special case: if we only have one component in our path, we don't decorate it 56 | if (components.length === 1) { 57 | return components[0]; 58 | } 59 | 60 | components.push(calcHash(path, HASH_LEN)); 61 | 62 | return components 63 | .reverse() 64 | .join('/') 65 | .slice(0, maxLen) 66 | .split('/') 67 | .reverse() 68 | .filter(x => x) 69 | .join('-'); 70 | } 71 | 72 | /* istanbul ignore next */ 73 | private constructor() { 74 | return; 75 | } 76 | } 77 | 78 | function calcHash(path: string, maxLen: number) { 79 | const hash = crypto.createHash('sha256'); 80 | hash.update(path); 81 | return hash.digest('hex').slice(0, maxLen); 82 | } -------------------------------------------------------------------------------- /packages/@terrastack/core/lib/resource-object.ts: -------------------------------------------------------------------------------- 1 | import { Construct, TagManager } from '@aws-cdk/core'; 2 | import { 3 | snakeCase 4 | } from "./_util"; 5 | 6 | 7 | /** 8 | * Metadata associated with this object. 9 | */ 10 | export interface ResourceObjectMetadata { 11 | /** 12 | * The unique, namespace-global, name of this object inside the Terraform 13 | * stack. 14 | * 15 | * Normally, you shouldn't specify names for objects and let the CDK generate 16 | * a name for you that is application-unique. The names CDK generates are 17 | * composed from the construct path components, separated by dots and a suffix 18 | * that is based on a hash of the entire path, to ensure uniqueness. 19 | * 20 | * You can supply custom name allocation logic by overriding the 21 | * `chart.generateObjectName` method. 22 | * 23 | * If you use an explicit name here, bear in mind that this reduces the 24 | * composability of your construct because it won't be possible to include 25 | * more than one instance in any app. Therefore it is highly recommended to 26 | * leave this unspecified. 27 | * 28 | * @default - an app-unique name generated by the chart 29 | */ 30 | readonly name?: string; 31 | 32 | /** 33 | * Arbitrary key/value metadata. 34 | */ 35 | readonly [key: string]: any; 36 | } 37 | 38 | export enum TerraformSchemaType { 39 | PROVIDER = 'provider', 40 | RESOURCE = 'resource', 41 | DATA = 'data' 42 | } 43 | 44 | export interface TerraformMetadata { 45 | readonly name: string; 46 | readonly schemaType: TerraformSchemaType; 47 | readonly providerVersion: string; 48 | readonly providerName: string; 49 | readonly rawSchema: string; 50 | } 51 | 52 | export interface IReferencableResource { 53 | logicalId(): string; 54 | } 55 | /** 56 | * Options for defining API objects. 57 | */ 58 | export interface ResourceObjectProps { 59 | /** 60 | * Data associated with the resource. 61 | */ 62 | readonly data?: any; 63 | 64 | /** 65 | * Object metadata. 66 | * 67 | * If `name` is not specified, an app-unique name will be allocated by the 68 | * framework based on the path of the construct within thes construct tree. 69 | */ 70 | readonly metadata?: ResourceObjectMetadata; 71 | 72 | /** 73 | * Additional attributes for this API object. 74 | */ 75 | readonly [key: string]: any; 76 | } 77 | 78 | interface TerraformMeta { 79 | readonly terraform: TerraformMetadata; 80 | } 81 | 82 | export class ResourceObject extends Construct implements TerraformMeta, IReferencableResource { 83 | /** 84 | * The app-unique name of the object. 85 | * 86 | * The name is allocated based on the path of the object construct within the 87 | * construct tree. 88 | * 89 | * @internal 90 | */ 91 | public readonly _name: string; 92 | 93 | /** 94 | * Defines an API object. 95 | * 96 | * @param scope the construct scope 97 | * @param ns namespace 98 | * @param props options 99 | */ 100 | constructor(scope: Construct, ns: string, private readonly props: ResourceObjectProps, readonly terraform: TerraformMetadata) { 101 | super(scope, ns); 102 | this._name = ns; 103 | } 104 | 105 | public logicalId(): string { 106 | if (this.terraform.schemaType === TerraformSchemaType.DATA) { 107 | return `data.${snakeCase(this.terraform.name)}.${snakeCase(this._name)}` 108 | } else { 109 | return `${snakeCase(this.terraform.name)}.${snakeCase(this._name)}` 110 | } 111 | } 112 | 113 | protected get tfProperties(): { [key: string]: any } { 114 | const props = this.props || {}; 115 | if (TagManager.isTaggable(this)) { 116 | const tagsProp: { [key: string]: any } = {}; 117 | tagsProp[this.tags.tagPropertyName] = this.tags.renderTags(); 118 | return deepMerge(props, tagsProp); 119 | } 120 | return props; 121 | } 122 | 123 | /** 124 | * Renders the object to Terraform config. 125 | * @internal 126 | */ 127 | public _render(): any { 128 | const obj: {[k: string]: any} = {}; 129 | obj[this._name] = this.tfProperties 130 | return obj 131 | } 132 | } 133 | 134 | /** 135 | * Merges `source` into `target`, overriding any existing values. 136 | * `null`s will cause a value to be deleted. 137 | */ 138 | function deepMerge(target: any, ...sources: any[]) { 139 | for (const source of sources) { 140 | if (typeof(source) !== 'object' || typeof(target) !== 'object') { 141 | throw new Error(`Invalid usage. Both source (${JSON.stringify(source)}) and target (${JSON.stringify(target)}) must be objects`); 142 | } 143 | 144 | for (const key of Object.keys(source)) { 145 | const value = source[key]; 146 | if (typeof(value) === 'object' && value != null && !Array.isArray(value)) { 147 | // if the value at the target is not an object, override it with an 148 | // object so we can continue the recursion 149 | if (typeof(target[key]) !== 'object') { 150 | target[key] = {}; 151 | } 152 | 153 | deepMerge(target[key], value); 154 | 155 | // if the result of the merge is an empty object, it's because the 156 | // eventual value we assigned is `undefined`, and there are no 157 | // sibling concrete values alongside, so we can delete this tree. 158 | const output = target[key]; 159 | if (typeof(output) === 'object' && Object.keys(output).length === 0) { 160 | delete target[key]; 161 | } 162 | } else if (value === undefined) { 163 | delete target[key]; 164 | } else { 165 | target[key] = value; 166 | } 167 | } 168 | } 169 | 170 | return target; 171 | } 172 | 173 | 174 | -------------------------------------------------------------------------------- /packages/@terrastack/core/lib/stack.ts: -------------------------------------------------------------------------------- 1 | import { Construct, ISynthesisSession } from '@aws-cdk/core'; 2 | import * as fs from 'fs'; 3 | import * as path from 'path'; 4 | import { ResourceObject } from './resource-object'; 5 | import { resolve } from './_tokens'; 6 | import { removeEmpty } from './_util'; 7 | import { Names } from './names'; 8 | import { 9 | snakeCase, 10 | } from "./_util"; 11 | 12 | 13 | export class Stack extends Construct { 14 | 15 | /** 16 | * Finds the stack in which a node is defined. 17 | * @param node a construct node 18 | */ 19 | public static of(node: Construct): Stack { 20 | if (node instanceof Stack) { 21 | return node; 22 | } 23 | 24 | const parent = node.node.scope as Construct; 25 | if (!parent) { 26 | throw new Error(`cannot find a parent chart (directly or indirectly)`); 27 | } 28 | 29 | return Stack.of(parent); 30 | } 31 | 32 | /** 33 | * The name of the stack's YAML file as emitted into the cloud assembly 34 | * directory during synthesis. 35 | */ 36 | public readonly manifestFile: string; 37 | 38 | constructor(scope: Construct, ns: string) { 39 | super(scope, ns); 40 | this.manifestFile = `${this.node.uniqueId}.tf.json`; 41 | } 42 | 43 | /** 44 | * Generates a app-unique name for an object given it's construct node path. 45 | * 46 | * @param resourceObject The API object to generate a name for. 47 | */ 48 | public generateObjectName(resourceObject: ResourceObject) { 49 | return Names.toDnsLabel(resourceObject.node.path); 50 | } 51 | 52 | protected synthesize(session: ISynthesisSession) { 53 | const doc: {[k: string]: {[k: string]: any}} = {}; 54 | 55 | for (const resource of this.node.findAll()) { 56 | if (!(resource instanceof ResourceObject)) { 57 | continue; 58 | } 59 | const resourceName = snakeCase(resource.terraform.name) 60 | const type = snakeCase(resource.terraform.schemaType) 61 | 62 | 63 | if (!doc[type]) { 64 | const obj: {[k: string]: any} = {}; 65 | doc[type] = obj 66 | } 67 | 68 | if (type === 'provider') { 69 | const manifest = removeEmpty(resolve(this, resource._render())); 70 | const merged = {...doc[type], ...manifest} 71 | doc[type] = merged 72 | } else { 73 | if (!doc[type][resourceName]) { 74 | const obj: {[k: string]: any} = {}; 75 | doc[type][resourceName] = obj 76 | } 77 | 78 | const manifest = removeEmpty(resolve(this, resource._render())); 79 | const merged = {...doc[type][resourceName], ...manifest} 80 | doc[type][resourceName] = merged 81 | } 82 | } 83 | fs.writeFileSync(path.join(session.assembly.outdir, this.manifestFile), JSON.stringify(doc, null, 2)); 84 | } 85 | } -------------------------------------------------------------------------------- /packages/@terrastack/core/lib/tf-reference.ts: -------------------------------------------------------------------------------- 1 | import { IReferencableResource } from "./resource-object" 2 | import { 3 | snakeCase 4 | } from "./_util"; 5 | /** 6 | * A Token that represents a CloudFormation reference to another resource 7 | * 8 | * If these references are used in a different stack from where they are 9 | * defined, appropriate CloudFormation `Export`s and `Fn::ImportValue`s will be 10 | * synthesized automatically instead of the regular CloudFormation references. 11 | * 12 | * Additionally, the dependency between the stacks will be recorded, and the toolkit 13 | * will make sure to deploy producing stack before the consuming stack. 14 | * 15 | * This magic happens in the prepare() phase, where consuming stacks will call 16 | * `consumeFromStack` on these Tokens and if they happen to be exported by a different 17 | * Stack, we'll register the dependency. 18 | */ 19 | export class TfReference { 20 | /** 21 | * Return the TfReference for the indicated target 22 | * 23 | * Will make sure that multiple invocations for the same target and intrinsic 24 | * return the same TfReference. Because TfReferences accumulate state in 25 | * the prepare() phase (for the purpose of cross-stack references), it's 26 | * important that the state isn't lost if it's lazily created, like so: 27 | * 28 | * Lazy.stringValue({ produce: () => new TfReference(...) }) 29 | */ 30 | public static for(target: IReferencableResource, attribute: string, value: any) { 31 | if (value) { 32 | return value; 33 | } else { 34 | return `\$\{${target.logicalId()}.${snakeCase(attribute)}\}` 35 | } 36 | } 37 | 38 | // /** 39 | // * Return a TfReference that references a pseudo referencd 40 | // */ 41 | // public static forPseudo(pseudoName: string, scope: Construct) { 42 | // return TfReference.singletonReference(scope, `Pseudo:${pseudoName}`, () => { 43 | // const cfnIntrinsic = { Ref: pseudoName }; 44 | // return new TfReference(cfnIntrinsic, pseudoName, scope); 45 | // }); 46 | // } 47 | 48 | // /** 49 | // * Static table where we keep singleton TfReference instances 50 | // */ 51 | // private static referenceTable = new Map>(); 52 | 53 | // /** 54 | // * Get or create the table 55 | // */ 56 | // private static singletonReference(target: Construct, attribKey: string, fresh: () => TfReference) { 57 | // let attribs = TfReference.referenceTable.get(target); 58 | // if (!attribs) { 59 | // attribs = new Map(); 60 | // TfReference.referenceTable.set(target, attribs); 61 | // } 62 | // let ref = attribs.get(attribKey); 63 | // if (!ref) { 64 | // ref = fresh(); 65 | // attribs.set(attribKey, ref); 66 | // } 67 | // return ref; 68 | // } 69 | 70 | // /** 71 | // * The Tokens that should be returned for each consuming stack (as decided by the producing Stack) 72 | // */ 73 | // private readonly replacementTokens: Map; 74 | 75 | // protected constructor(value: any, displayName: string, target: IConstruct) { 76 | // // prepend scope path to display name 77 | // super(value, target, displayName); 78 | 79 | // this.replacementTokens = new Map(); 80 | 81 | // Object.defineProperty(this, CFN_REFERENCE_SYMBOL, { value: true }); 82 | // } 83 | 84 | // public resolve(context: IResolveContext): any { 85 | // // If we have a special token for this consuming stack, resolve that. Otherwise resolve as if 86 | // // we are in the same stack. 87 | // const consumingStack = Stack.of(context.scope); 88 | // const token = this.replacementTokens.get(consumingStack); 89 | 90 | // // if (!token && this.isCrossStackReference(consumingStack) && !context.preparing) { 91 | // // tslint:disable-next-line:max-line-length 92 | // // throw new Error(`Cross-stack reference (${context.scope.node.path} -> ${this.target.node.path}) has not been assigned a value--call prepare() first`); 93 | // // } 94 | 95 | // if (token) { 96 | // return token.resolve(context); 97 | // } else { 98 | // return super.resolve(context); 99 | // } 100 | // } 101 | 102 | // public hasValueForStack(stack: Stack) { 103 | // return this.replacementTokens.has(stack); 104 | // } 105 | 106 | // public assignValueForStack(stack: Stack, value: IResolvable) { 107 | // if (this.hasValueForStack(stack)) { 108 | // throw new Error(`Cannot assign a reference value twice to the same stack. Use hasValueForStack to check first`); 109 | // } 110 | 111 | // this.replacementTokens.set(stack, value); 112 | // } 113 | // /** 114 | // * Implementation of toString() that will use the display name 115 | // */ 116 | // public toString(): string { 117 | // return Token.asString(this, { 118 | // displayHint: `${this.target.node.id}.${this.displayName}` 119 | // }); 120 | // } 121 | } 122 | -------------------------------------------------------------------------------- /packages/@terrastack/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@terrastack/core", 3 | "version": "0.15.0", 4 | "description": "Polyglot Terraform supercharged by the Cloud Development Kit (CDK)", 5 | "main": "lib/index.js", 6 | "types": "lib/index.d.ts", 7 | "scripts": { 8 | "build": "jsii", 9 | "watch": "jsii -w", 10 | "package": "jsii-pacmak", 11 | "lint": "eslint . --ext .ts", 12 | "test": "jest && yarn lint" 13 | }, 14 | "author": "Sebastian Korfmann", 15 | "license": "Apache-2.0", 16 | "homepage": "https://terrastack.io", 17 | "keywords": [ 18 | "terrastack", 19 | "terraform", 20 | "cdk", 21 | "cloud", 22 | "aws", 23 | "azure", 24 | "gcp" 25 | ], 26 | "repository": { 27 | "type": "git", 28 | "url": "https://github.com/TerraStackIO/terrastack.git", 29 | "directory": "packages/terrastack" 30 | }, 31 | "jsii": { 32 | "outdir": "dist", 33 | "versionFormat": "short", 34 | "targets": { 35 | "java": { 36 | "package": "org.terrastack", 37 | "maven": { 38 | "groupId": "org.terrastack", 39 | "artifactId": "terrastack" 40 | } 41 | }, 42 | "python": { 43 | "distName": "terrastack", 44 | "module": "terrastack" 45 | }, 46 | "dotnet": { 47 | "namespace": "Org.terrastack", 48 | "packageId": "Org.terrastack" 49 | } 50 | } 51 | }, 52 | "eslintConfig": { 53 | "parser": "@typescript-eslint/parser", 54 | "plugins": [ 55 | "@typescript-eslint" 56 | ], 57 | "extends": [ 58 | "eslint:recommended", 59 | "plugin:@typescript-eslint/eslint-recommended", 60 | "plugin:@typescript-eslint/recommended" 61 | ], 62 | "rules": { 63 | "@typescript-eslint/no-explicit-any": 0, 64 | "@typescript-eslint/explicit-function-return-type": 0, 65 | "@typescript-eslint/no-use-before-define": 0, 66 | "@typescript-eslint/interface-name-prefix": 0 67 | }, 68 | "ignorePatterns": [ 69 | "node_modules", 70 | "dist", 71 | "coverage" 72 | ] 73 | }, 74 | "devDependencies": { 75 | "@aws-cdk/core": "^1.27.0", 76 | "@types/json-schema": "^7.0.4", 77 | "@types/node": "^13.7.7", 78 | "@types/yaml": "^1.2.0", 79 | "@types/yargs": "^15.0.4", 80 | "codemaker": "^1.0.0", 81 | "crypto": "^1.0.1", 82 | "eslint": "^6.8.0", 83 | "fs": "^0.0.1-security", 84 | "jest": "^25.1.0", 85 | "jsii": "^0.22.0", 86 | "jsii-pacmak": "^0.22.0", 87 | "json-schema": "^0.2.5", 88 | "json-schema-to-typescript": "^8.0.1", 89 | "path": "^0.12.7", 90 | "typescript": "^3.8.3", 91 | "yaml": "^1.7.2", 92 | "yargs": "^15.1.0" 93 | }, 94 | "jest": { 95 | "collectCoverage": true, 96 | "moduleFileExtensions": [ 97 | "js" 98 | ] 99 | }, 100 | "bundledDependencies": [ 101 | "yaml" 102 | ], 103 | "dependencies": { 104 | "yaml": "^1.7.2" 105 | }, 106 | "peerDependencies": { 107 | "@aws-cdk/core": "^1.24.0", 108 | "@aws-cdk/cx-api": "^1.24.0" 109 | }, 110 | "stability": "experimental", 111 | "publishConfig": { 112 | "access": "public" 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /packages/@terrastack/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "alwaysStrict": true, 4 | "charset": "utf8", 5 | "declaration": true, 6 | "experimentalDecorators": true, 7 | "inlineSourceMap": true, 8 | "inlineSources": true, 9 | "lib": [ 10 | "es2018" 11 | ], 12 | "module": "CommonJS", 13 | "noEmitOnError": true, 14 | "noFallthroughCasesInSwitch": true, 15 | "noImplicitAny": true, 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "resolveJsonModule": true, 21 | "strict": true, 22 | "strictNullChecks": true, 23 | "strictPropertyInitialization": true, 24 | "stripInternal": true, 25 | "target": "ES2018" 26 | }, 27 | "include": [ 28 | "**/*.ts" 29 | ], 30 | "exclude": [ 31 | "node_modules" 32 | ], 33 | "_generated_by_jsii_": "Generated by jsii - safe to delete, and ideally should be in .gitignore" 34 | } 35 | -------------------------------------------------------------------------------- /packages/terrastack-cli/bin/cmds/generate.ts: -------------------------------------------------------------------------------- 1 | import * as yargs from 'yargs'; 2 | import { generateAllApiObjects } from '../../lib/import'; 3 | 4 | class Command implements yargs.CommandModule { 5 | public readonly command = 'generate'; 6 | public readonly describe = 'Generates typed constructs for Terraform resources'; 7 | public readonly aliases = [ 'gen', 'import' ]; 8 | 9 | public readonly builder = (args: yargs.Argv) => args 10 | .option('output', { type: 'string' as 'string', desc: 'output directory', default: '.generated', alias: 'o' }) 11 | .option('input', { type: 'string' as 'string', desc: 'schema input file', alias: 'i' }); 12 | 13 | public async handler(argv: any) { 14 | await generateAllApiObjects(argv.output, argv.input); 15 | } 16 | } 17 | 18 | module.exports = new Command(); 19 | -------------------------------------------------------------------------------- /packages/terrastack-cli/bin/terrastack: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('./terrastack.js'); -------------------------------------------------------------------------------- /packages/terrastack-cli/bin/terrastack.ts: -------------------------------------------------------------------------------- 1 | import * as yargs from 'yargs'; 2 | 3 | yargs 4 | .commandDir('cmds') 5 | .demandCommand() 6 | .help() 7 | .argv; 8 | -------------------------------------------------------------------------------- /packages/terrastack-cli/lib/codegen-constructs.ts: -------------------------------------------------------------------------------- 1 | import { JSONSchema4 } from "json-schema"; 2 | import { CodeMaker } from 'codemaker'; 3 | import { TypeGenerator } from "./codegen-types"; 4 | import { 5 | pascalCase, 6 | camelCase, 7 | constantCase 8 | } from "change-case"; 9 | import * as path from "path" 10 | 11 | /** 12 | * Generates a construct for an API object defined by `def`. 13 | */ 14 | 15 | function getType(namespace: string) { 16 | switch (namespace) { 17 | case 'data_source_schemas': return 'data'; 18 | case 'resource_schemas': return 'resource'; 19 | case 'provider': return 'provider'; 20 | default: { 21 | throw new Error(`unexpected Namespace ${namespace}`); 22 | } 23 | } 24 | } 25 | 26 | function propertyName(property: string): string { 27 | // `self` doesn't work in as property name in Python 28 | if (property === 'self') { 29 | return camelCase(`${property}Property`) 30 | } 31 | // jsii can't handle `getFoo` properties, since it's incompatible with Java 32 | if (property.split('_')[0] === 'get') { 33 | return camelCase(property.replace('get', 'fetch')) 34 | } 35 | return camelCase(property) 36 | } 37 | 38 | export function emitConstructForApiObject(code: CodeMaker, def: JSONSchema4, provider: string, namespace: string, version: string) { 39 | const objectName = def.name; 40 | if (!objectName) { 41 | throw new Error(`no object name`); 42 | } 43 | 44 | const type = getType(namespace) 45 | const scope = type === 'data' ? 'data_' : ''; 46 | const baseName = `${scope}${objectName.toLocaleLowerCase()}`; 47 | const filePath = path.join(provider, namespace.replace('_schemas', ''), baseName) 48 | const sourceFile = `${filePath}.ts`; 49 | const optionsStructName = `${pascalCase(baseName)}Props`; 50 | const typeGenerator = new TypeGenerator(type); 51 | 52 | emitFile(); 53 | return filePath; 54 | 55 | function emitFile() { 56 | code.openFile(sourceFile); 57 | code.line(`// generated by terrastack.io`); 58 | code.line(); 59 | 60 | code.line(`import * as tst from '@terrastack/core';`); 61 | code.line(`import * as cdk from '@aws-cdk/core';`); 62 | code.line(); 63 | 64 | emitOptionsStruct(); 65 | 66 | code.line(); 67 | 68 | emitConstruct(); 69 | code.line(); 70 | 71 | typeGenerator.generate(code); 72 | 73 | code.closeFile(sourceFile); 74 | } 75 | 76 | function emitOptionsStruct() { 77 | const copy: JSONSchema4 = { ...def }; 78 | copy.properties = copy.properties || {}; 79 | typeGenerator.addType(optionsStructName, copy); 80 | } 81 | 82 | function emitConstruct() { 83 | code.line('/**'); 84 | code.line(` * ${ def?.description }`); 85 | code.line(` */`); 86 | code.openBlock(`export class ${pascalCase(baseName)} extends tst.ResourceObject`); 87 | 88 | emitProperties(); 89 | 90 | code.line(); 91 | 92 | emitInitializer(); 93 | 94 | code.closeBlock(); 95 | } 96 | 97 | function emitInitializer() { 98 | code.openBlock(`public constructor(scope: cdk.Construct, ns: string, options: ${optionsStructName})`); 99 | emitInitializerSuper(); 100 | 101 | emitPropsAssignment(); 102 | code.closeBlock(); 103 | } 104 | 105 | function emitInitializerSuper() { 106 | code.open(`super(scope, ns, { ...options }, {`); 107 | if (type === 'provider') { 108 | code.line(`name: "${provider}",`); 109 | } else { 110 | code.line(`name: "${objectName.toLocaleLowerCase()}",`); 111 | } 112 | code.line(`schemaType: tst.TerraformSchemaType.${constantCase(type)},`); 113 | code.line(`providerVersion: '${version}',`); 114 | code.line(`providerName: '${provider}',`); 115 | code.line(`rawSchema: \`${JSON.stringify(def).replace(new RegExp('`', 'g'), '\\`')}\``); 116 | code.close(`});`); 117 | } 118 | 119 | function emitPropsAssignment() { 120 | const propType = typeGenerator.types[`${pascalCase(baseName)}Props`] 121 | for (const [ propName, propSpec ] of Object.entries(propType || {})) { 122 | if (propSpec.referencable) { 123 | if ((type !== 'data' && propName === 'id') || (type === 'data') || !propSpec.assignable) { 124 | code.line(`this.${propertyName(propName)} = tst.TfReference.for(this, '${propertyName(propName)}', null);`); 125 | } else { 126 | code.line(`this.${propertyName(propName)} = tst.TfReference.for(this, '${propertyName(propName)}', options.${propertyName(propName)});`); 127 | } 128 | } else { 129 | if (propName === 'tags') { 130 | code.line(`this.${propertyName(propName)} = new cdk.TagManager(cdk.TagType.MAP, '${baseName}', options.tags, { tagPropertyName: 'tags' });`); 131 | } else { 132 | code.line(`this.${propertyName(propName)} = options.${propertyName(propName)};`); 133 | } 134 | } 135 | } 136 | } 137 | 138 | function emitProperties() { 139 | const type = typeGenerator.types[`${pascalCase(baseName)}Props`] 140 | for (const [ propName, propSpec ] of Object.entries(type || {})) { 141 | const propType = (propName === 'tags') ? 'cdk.TagManager' : propSpec.type 142 | code.line(`${propertyName(propName)}${propSpec.referencable ? '' : '?'}: ${propType};`); 143 | code.line(); 144 | } 145 | } 146 | } 147 | 148 | export function findApiObjectDefinitions(schema: JSONSchema4) { 149 | const result: {[k: string]: {[k: string]: Array}} = {}; 150 | // const result = new Array(); 151 | const providers = Object.keys(schema.provider_schemas) 152 | 153 | for (const provider of providers) { 154 | const providerDef = schema.provider_schemas[provider] 155 | for (const type of Object.keys(providerDef)) { 156 | if (type === 'provider') { 157 | if (!result[provider]) result[provider] = {}; 158 | if (!result[provider][type]) result[provider][type] = []; 159 | const def = Object.assign({}, providerDef[type], {name: `${provider}_${type}`}) 160 | result[provider][type] = [def] 161 | } else { 162 | for (const object of Object.keys(providerDef[type])) { 163 | const def = Object.assign({}, providerDef[type][object], {name: object}) 164 | if (!result[provider]) result[provider] = {}; 165 | if (!result[provider][type]) result[provider][type] = []; 166 | result[provider][type].push(def) 167 | } 168 | } 169 | } 170 | } 171 | return result; 172 | } -------------------------------------------------------------------------------- /packages/terrastack-cli/lib/codegen-types.ts: -------------------------------------------------------------------------------- 1 | import { JSONSchema4 } from "json-schema"; 2 | import { CodeMaker } from "codemaker"; 3 | import { 4 | camelCase, 5 | pascalCase 6 | } from "change-case"; 7 | 8 | export interface ResolvedTypes { 9 | type: string; 10 | assignable: boolean; 11 | optional: boolean; 12 | referencable: boolean; 13 | } 14 | 15 | function generatePropertyName(property: string): string { 16 | // `self` doesn't work in as property name in Python 17 | if (property === 'self') { 18 | return camelCase(`${property}Property`) 19 | } 20 | // jsii can't handle `getFoo` properties, since it's incompatible with Java 21 | if (property.split('_')[0] === 'get') { 22 | return camelCase(property.replace('get', 'fetch')) 23 | } 24 | return camelCase(property) 25 | } 26 | 27 | 28 | export class TypeGenerator { 29 | private readonly emitted = new Set(); 30 | public readonly types: {[key: string]: {[key: string]: ResolvedTypes}} = { }; 31 | 32 | constructor(private readonly type: string) { 33 | } 34 | 35 | public addType(typeName: string, def: JSONSchema4) { 36 | if (this.emitted.has(typeName)) { 37 | console.log('hier') 38 | return; 39 | } 40 | this.resolveTypes(typeName, def.block || def); 41 | } 42 | 43 | public generate(code: CodeMaker) { 44 | for (const type of Object.keys(this.types)) { 45 | const spec = this.types[type]; 46 | this.emitStruct(code, type, spec) 47 | code.line(); 48 | this.emitted.add(type); 49 | } 50 | } 51 | 52 | private resolveTypes(typeName: string, def: JSONSchema4) { 53 | const self = this; 54 | if (def.attributes || def.block_types) { 55 | for (const [ propName, propSpec ] of Object.entries(def.attributes || {})) { 56 | resolveProperty(propName, propSpec as JSONSchema4); 57 | } 58 | 59 | for (const [ blockName, blockSpec ] of Object.entries(def.block_types || {})) { 60 | const newTypeName = `${typeName}${pascalCase(blockName)}Props` 61 | self.addType(newTypeName, blockSpec as JSONSchema4) 62 | const emptyType = (Object.keys((blockSpec as JSONSchema4).block.attributes || {}).length === 0) 63 | addProperty(blockName, emptyType ? 'any' : newTypeName, true, true, false) 64 | } 65 | } else { 66 | for (const [ propName, propSpec ] of Object.entries(def || {})) { 67 | resolveProperty(propName, propSpec as JSONSchema4); 68 | } 69 | } 70 | 71 | function resolveProperty(name: string, def: JSONSchema4) { 72 | let assignable = true 73 | if ((def.computed && !def.optional) || (self.type !== 'data' && name === 'id')) { 74 | assignable = false 75 | } 76 | const propertyType = self.typeForProperty(def, name, typeName); 77 | const optional = (def.optional || def.computed) 78 | addProperty(name, propertyType, assignable, optional, def.computed) 79 | } 80 | 81 | function addProperty(name: string, propertyType: string, assignable: boolean, optional: boolean, referencable: boolean) { 82 | if (!self.types[typeName]) { 83 | self.types[typeName] = {} 84 | } 85 | 86 | self.types[typeName][name] = { 87 | type: propertyType, 88 | assignable, 89 | optional, 90 | referencable 91 | } 92 | } 93 | } 94 | 95 | private emitStruct(code: CodeMaker, typeName: string, def: {[key: string]: ResolvedTypes}) { 96 | code.openBlock(`export interface ${typeName}`); 97 | for (const propertyName of Object.keys(def)) { 98 | const property = def[propertyName] 99 | if (!property.assignable) continue; 100 | code.line(`readonly ${generatePropertyName(propertyName)}${property.optional ? '?' : ''}: ${property.type};`); 101 | code.line(); 102 | } 103 | 104 | code.closeBlock(); 105 | } 106 | 107 | private typeForProperty(def: JSONSchema4, name: string, typeName: string): string { 108 | const comparable = def.type || def 109 | switch (true) { 110 | case (comparable === "string"): return 'string'; 111 | case (comparable === "number"): return 'number'; 112 | case (comparable === "integer"): return 'number'; 113 | case (comparable?.toString() === "bool"): return 'boolean'; 114 | case (Array.isArray(comparable)): return this.handleArrayType(comparable as JSONSchema4, name, typeName); 115 | default: 116 | console.log({typeForProperty: def, name, typeName, comparable}) 117 | return 'any'; 118 | } 119 | } 120 | 121 | private handleArrayType(def: JSONSchema4, name: string, typeName: string): string { 122 | const element = ((def.type || def) as Array) 123 | if (Array.isArray(element[element.length - 1])) { 124 | const type = element[0] 125 | const obj = element[element.length - 1] 126 | let newTypeName = undefined; 127 | if (this.jsonEqual(obj, ["map", "string"])) { 128 | newTypeName = `{[key: string]: string}` 129 | } else { 130 | newTypeName = `${typeName}${pascalCase(name)}Props` 131 | this.addType(newTypeName, obj[obj.length - 1] as JSONSchema4) 132 | } 133 | switch(type) { 134 | case 'list': return `${newTypeName}[]`; 135 | case 'set': return `${newTypeName}[]`; 136 | } 137 | } 138 | switch (true) { 139 | case (this.jsonEqual(element, ["map", "string"])): return '{[k:string]: string}'; 140 | case (this.jsonEqual(element, ["map", "bool"])): return '{[k:string]: boolean}'; 141 | case (this.jsonEqual(element, ["map", "number"])): return '{[k:string]: number}'; 142 | case (this.jsonEqual(element, ["set", "number"])): return 'number[]'; 143 | case (this.jsonEqual(element, ["set", "string"])): return 'string[]'; 144 | case (this.jsonEqual(element, ["list", "string"])): return 'string[]'; 145 | default: 146 | console.log({handleArrayType: def, name, typeName}) 147 | return 'any'; 148 | } 149 | } 150 | 151 | private jsonEqual(a: any, b: any): boolean { 152 | return JSON.stringify(a) === JSON.stringify(b); 153 | } 154 | } -------------------------------------------------------------------------------- /packages/terrastack-cli/lib/import.ts: -------------------------------------------------------------------------------- 1 | import { promises, readFileSync, writeFileSync } from 'fs'; 2 | import { emitConstructForApiObject, findApiObjectDefinitions } from './codegen-constructs'; 3 | import { CodeMaker } from 'codemaker'; 4 | import { JSONSchema4 } from 'json-schema'; 5 | import * as path from "path" 6 | 7 | export interface Options { 8 | readonly apiVersion: string; 9 | } 10 | 11 | export async function generateAllApiObjects(outdir: string, inputFile: string) { 12 | const version = '2.52.0' 13 | const code = new CodeMaker(); 14 | code.indentation = 2; 15 | 16 | const schema = await downloadSchema(inputFile); 17 | 18 | const topLevelObjects = findApiObjectDefinitions(schema); 19 | 20 | for (const provider of Object.keys(topLevelObjects)) { 21 | const files = [] 22 | for (const type of Object.keys(topLevelObjects[provider])) { 23 | for (const o of topLevelObjects[provider][type]) { 24 | files.push(emitConstructForApiObject(code, o, provider, type, version)); 25 | } 26 | } 27 | writeIndex(code, files, provider) 28 | } 29 | await promises.mkdir(outdir, { recursive: true }); 30 | await code.save(outdir); 31 | 32 | for (const provider of Object.keys(topLevelObjects)) { 33 | writePackageJson(path.join(outdir, provider), provider) 34 | } 35 | } 36 | 37 | async function downloadSchema(inputFile: string) { 38 | const output = readFileSync(path.join(process.cwd(), inputFile)) 39 | return JSON.parse(output.toString()) as JSONSchema4; 40 | } 41 | 42 | async function writeIndex(code: CodeMaker, files: Array, filePath: string) { 43 | const sourceFile = path.join(filePath, 'index.ts') 44 | code.openFile(sourceFile); 45 | code.line(`// generated by terrastack.io`); 46 | code.line(); 47 | files.forEach(file => { 48 | code.line(`export * from './${file.replace(`${filePath}/`, '')}';`); 49 | }) 50 | code.line(); 51 | code.closeFile(sourceFile); 52 | } 53 | 54 | async function writePackageJson(workdir: string, provider: string) { 55 | const main = 'index' 56 | const pkg = { 57 | name: `@terrastack/${provider}-provider`, 58 | version: '0.0.0', 59 | author: "sebastian@korfmann.net", 60 | main: `${main}.js`, 61 | types: `${main}.d.ts`, 62 | license: 'Apache-2.0', 63 | scripts: { 64 | build: "jsii", 65 | watch: "jsii -w", 66 | package: "jsii-pacmak", 67 | }, 68 | repository: { url: 'https://github.com/terrastackio/terrastack', type: 'git' }, 69 | jsii: { 70 | outdir: "dist", 71 | targets: { 72 | python: { 73 | distName: `terrastack_${provider}_provider`, 74 | module: `terrastack_${provider}_provider` 75 | } 76 | } 77 | }, 78 | dependencies: { 79 | "@aws-cdk/core": "^1.27.0", 80 | "@terrastack/core": "^0.15.0", 81 | }, 82 | devDependencies: { 83 | "@types/node": "^13.7.7", 84 | jsii: "^0.22.0", 85 | "jsii-pacmak": "^0.22.0" 86 | }, 87 | peerDependencies: { 88 | "@aws-cdk/core": "^1.27.0", 89 | "@terrastack/core": "^0.15.0", 90 | } 91 | 92 | }; 93 | writeFileSync(path.join(workdir, 'package.json'), JSON.stringify(pkg, undefined, 2)); 94 | } -------------------------------------------------------------------------------- /packages/terrastack-cli/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "terrastack-cli", 3 | "version": "0.15.0", 4 | "description": "Terrastack - A Polyglot Terraform CLI", 5 | "bin": { 6 | "terrastack": "bin/terrastack" 7 | }, 8 | "scripts": { 9 | "build": "tsc", 10 | "watch": "tsc -w", 11 | "lint": "eslint . --ext .ts", 12 | "test": "jest && yarn lint", 13 | "package": "./package.sh" 14 | }, 15 | "keywords": [ 16 | "terrastack", 17 | "terraform", 18 | "cdk", 19 | "cloud", 20 | "aws", 21 | "azure", 22 | "gcp" 23 | ], 24 | "author": "Sebastian Korfmann", 25 | "license": "Apache-2.0", 26 | "jest": { 27 | "collectCoverage": true, 28 | "moduleFileExtensions": [ 29 | "js" 30 | ] 31 | }, 32 | "eslintConfig": { 33 | "parser": "@typescript-eslint/parser", 34 | "plugins": [ 35 | "@typescript-eslint" 36 | ], 37 | "extends": [ 38 | "eslint:recommended", 39 | "plugin:@typescript-eslint/eslint-recommended", 40 | "plugin:@typescript-eslint/recommended" 41 | ], 42 | "rules": { 43 | "@typescript-eslint/no-explicit-any": 0, 44 | "@typescript-eslint/explicit-function-return-type": 0, 45 | "@typescript-eslint/no-use-before-define": 0, 46 | "@typescript-eslint/camelcase": 0, 47 | "@typescript-eslint/no-this-alias": 0 48 | }, 49 | "ignorePatterns": [ 50 | "node_modules", 51 | "dist", 52 | "coverage" 53 | ] 54 | }, 55 | "dependencies": { 56 | "@aws-cdk/core": "^1.27.0", 57 | "@aws-cdk/cx-api": "^1.24.0", 58 | "@types/json-schema": "^7.0.4", 59 | "@types/node": "^13.7.7", 60 | "@types/yaml": "^1.2.0", 61 | "@types/yargs": "^15.0.4", 62 | "change-case": "^4.1.1", 63 | "codemaker": "^1.0.0", 64 | "crypto": "^1.0.1", 65 | "fs": "^0.0.1-security", 66 | "fs-extra": "^8.1.0", 67 | "jsii": "^1.0.0", 68 | "jsii-pacmak": "^1.0.0", 69 | "json-schema": "^0.2.5", 70 | "path": "^0.12.7", 71 | "sscaff": "^1.2.0", 72 | "typescript": "^3.8.3", 73 | "yaml": "^1.7.2", 74 | "yargs": "^15.1.0" 75 | }, 76 | "devDependencies": { 77 | "@types/fs-extra": "^8.1.0", 78 | "@types/jest": "^25.1.4", 79 | "@typescript-eslint/eslint-plugin": "^2.22.0", 80 | "@typescript-eslint/parser": "^2.22.0", 81 | "jest": "^25.1.0" 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /packages/terrastack-cli/package.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | bundle=$(npm pack) 4 | rm -fr dist 5 | mkdir -p dist/js 6 | mv ${bundle} dist/js -------------------------------------------------------------------------------- /packages/terrastack-cli/spec/fixtures/foo.json: -------------------------------------------------------------------------------- 1 | { 2 | "provider": { 3 | "aws": { 4 | "region": "eu-central-1", 5 | "version": "~\u003e 2.0" 6 | } 7 | }, 8 | "resource": { 9 | "aws_s3_bucket": { 10 | "b": { 11 | "acl": "private", 12 | "bucket": "my-tf-test-bucket", 13 | "logging": { 14 | "target_bucket": "${aws_s3_bucket.log_bucket.id}", 15 | "target_prefix": "log/" 16 | }, 17 | "tags": { 18 | "Foo": "bar" 19 | } 20 | }, 21 | "log_bucket": { 22 | "acl": "log-delivery-write", 23 | "bucket": "my-tf-log-bucket" 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /packages/terrastack-cli/spec/fixtures/main.tf: -------------------------------------------------------------------------------- 1 | provider "aws" { 2 | version = "~> 2.0" 3 | region = "eu-central-1" 4 | } 5 | 6 | resource "aws_s3_bucket" "log_bucket" { 7 | bucket = "my-tf-log-bucket" 8 | acl = "log-delivery-write" 9 | } 10 | 11 | resource "aws_s3_bucket" "b" { 12 | bucket = "my-tf-test-bucket" 13 | acl = "private" 14 | 15 | logging { 16 | target_bucket = aws_s3_bucket.log_bucket.id 17 | target_prefix = "log/" 18 | } 19 | 20 | tags { 21 | Foo = "bar" 22 | } 23 | } -------------------------------------------------------------------------------- /packages/terrastack-cli/test/__snapshots__/codegen-construct.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`construct generate complex construct 1`] = ` 4 | "// generated by terrastack.io 5 | 6 | import * as tst from '@terrastack/core'; 7 | import * as cdk from '@aws-cdk/core'; 8 | 9 | 10 | /** 11 | * undefined 12 | */ 13 | export class AwsFooResource extends tst.ResourceObject { 14 | arn: string; 15 | 16 | tags?: cdk.TagManager; 17 | 18 | s3Import?: AwsFooResourcePropsS3ImportProps; 19 | 20 | 21 | public constructor(scope: cdk.Construct, ns: string, options: AwsFooResourceProps) { 22 | super(scope, ns, { ...options }, { 23 | name: \\"aws_foo_resource\\", 24 | schemaType: tst.TerraformSchemaType.RESOURCE, 25 | providerVersion: '2.5.2', 26 | providerName: 'aws', 27 | rawSchema: \`{\\"name\\":\\"aws_foo_resource\\",\\"version\\":0,\\"block\\":{\\"attributes\\":{\\"arn\\":{\\"type\\":\\"string\\",\\"computed\\":true},\\"tags\\":{\\"type\\":[\\"map\\",\\"string\\"],\\"optional\\":true}},\\"block_types\\":{\\"s3_import\\":{\\"nesting_mode\\":\\"single\\",\\"block\\":{\\"block_types\\":{\\"foo\\":{\\"nesting_mode\\":\\"single\\",\\"block\\":{\\"attributes\\":{\\"bar\\":{\\"type\\":\\"string\\",\\"optional\\":true}}}}},\\"attributes\\":{\\"create\\":{\\"type\\":\\"string\\",\\"optional\\":true}}}}}}}\` 28 | }); 29 | this.arn = tst.TfReference.for(this, 'arn', null); 30 | this.tags = new cdk.TagManager(cdk.TagType.MAP, 'aws_foo_resource', options.tags, { tagPropertyName: 'tags' }); 31 | this.s3Import = options.s3Import; 32 | } 33 | } 34 | 35 | export interface AwsFooResourceProps { 36 | readonly tags?: {[k:string]: string}; 37 | 38 | readonly s3Import?: AwsFooResourcePropsS3ImportProps; 39 | 40 | } 41 | 42 | export interface AwsFooResourcePropsS3ImportProps { 43 | readonly create?: string; 44 | 45 | readonly foo?: AwsFooResourcePropsS3ImportPropsFooProps; 46 | 47 | } 48 | 49 | export interface AwsFooResourcePropsS3ImportPropsFooProps { 50 | readonly bar?: string; 51 | 52 | } 53 | 54 | " 55 | `; 56 | -------------------------------------------------------------------------------- /packages/terrastack-cli/test/__snapshots__/codegen-types.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`structs has an optional map of strings 1`] = ` 4 | "export interface TestType { 5 | readonly tags?: {[k:string]: string}; 6 | 7 | readonly bool: {[k:string]: boolean}; 8 | 9 | readonly number: {[k:string]: number}; 10 | 11 | readonly numberSet: number[]; 12 | 13 | } 14 | 15 | " 16 | `; 17 | 18 | exports[`structs has block type nested as single 1`] = ` 19 | "export interface TestType { 20 | readonly tags?: {[k:string]: string}; 21 | 22 | readonly s3Import?: TestTypeS3ImportProps; 23 | 24 | } 25 | 26 | export interface TestTypeS3ImportProps { 27 | readonly create?: string; 28 | 29 | } 30 | 31 | " 32 | `; 33 | 34 | exports[`structs has block type nested in block type 1`] = ` 35 | "export interface TestType { 36 | readonly tags?: {[k:string]: string}; 37 | 38 | readonly s3Import?: TestTypeS3ImportProps; 39 | 40 | } 41 | 42 | export interface TestTypeS3ImportProps { 43 | readonly create?: string; 44 | 45 | readonly foo?: TestTypeS3ImportPropsFooProps; 46 | 47 | } 48 | 49 | export interface TestTypeS3ImportPropsFooProps { 50 | readonly bar?: string; 51 | 52 | } 53 | 54 | " 55 | `; 56 | 57 | exports[`structs has empty block type nested as single 1`] = ` 58 | "export interface TestType { 59 | readonly tags?: {[k:string]: string}; 60 | 61 | readonly s3Import?: any; 62 | 63 | } 64 | 65 | " 66 | `; 67 | 68 | exports[`structs has list of complex nested objects 1`] = ` 69 | "export interface TestTypeInstanceMarketOptionsProps { 70 | readonly marketType: string; 71 | 72 | readonly spotOptions: TestTypeInstanceMarketOptionsPropsSpotOptionsProps[]; 73 | 74 | } 75 | 76 | export interface TestTypeInstanceMarketOptionsPropsSpotOptionsProps { 77 | readonly blockDurationMinutes: number; 78 | 79 | } 80 | 81 | export interface TestType { 82 | readonly instanceMarketOptions?: TestTypeInstanceMarketOptionsProps[]; 83 | 84 | } 85 | 86 | " 87 | `; 88 | 89 | exports[`structs has primitive computed type 1`] = ` 90 | "export interface TestType { 91 | } 92 | 93 | " 94 | `; 95 | 96 | exports[`structs has primitive optional computed type 1`] = ` 97 | "export interface TestType { 98 | readonly architecture?: string; 99 | 100 | } 101 | 102 | " 103 | `; 104 | 105 | exports[`structs has primitive optional types 1`] = ` 106 | "export interface TestType { 107 | readonly architecture?: string; 108 | 109 | } 110 | 111 | " 112 | `; 113 | 114 | exports[`structs has primitive types 1`] = ` 115 | "export interface TestType { 116 | readonly string: string; 117 | 118 | readonly bool: boolean; 119 | 120 | readonly number: number; 121 | 122 | } 123 | 124 | " 125 | `; 126 | -------------------------------------------------------------------------------- /packages/terrastack-cli/test/codegen-construct.test.ts: -------------------------------------------------------------------------------- 1 | import { emitConstructForApiObject } from "../lib/codegen-constructs"; 2 | import { promises as fs } from 'fs'; 3 | import * as fse from 'fs-extra'; 4 | import * as path from 'path'; 5 | import * as os from 'os'; 6 | import { CodeMaker } from "codemaker"; 7 | import { JSONSchema4 } from "json-schema"; 8 | 9 | jest.setTimeout(60000); // 1min 10 | 11 | describe('construct', () => { 12 | construct('generate complex construct', { 13 | name: 'aws_foo_resource', 14 | version: 0, 15 | block: { 16 | attributes: { 17 | arn: { 18 | type: "string", 19 | computed: true 20 | }, 21 | tags: { 22 | type: ["map", "string"], 23 | optional: true 24 | } 25 | }, 26 | block_types: { 27 | s3_import: { 28 | nesting_mode: "single", 29 | block: { 30 | block_types: { 31 | foo: { 32 | nesting_mode: "single", 33 | block: { 34 | attributes: { 35 | bar: { 36 | type: "string", 37 | optional: true 38 | } 39 | } 40 | } 41 | } 42 | }, 43 | attributes: { 44 | create: { 45 | type: "string", 46 | optional: true 47 | } 48 | } 49 | } 50 | } 51 | } 52 | } 53 | }); 54 | }); 55 | 56 | function construct(name: string, schema: JSONSchema4) { 57 | which('resource_schemas', name, schema) 58 | } 59 | 60 | 61 | function which(type: string, name: string, schema: JSONSchema4) { 62 | test(name, async () => { 63 | const code = new CodeMaker(); 64 | const filePath = emitConstructForApiObject(code, schema, 'aws', type, '2.5.2') 65 | await withTempDir('test', async () => { 66 | expect(await generate(code, filePath)).toMatchSnapshot(); 67 | }); 68 | }); 69 | } 70 | 71 | async function generate(code: CodeMaker, filePath: string) { 72 | await code.save('.'); 73 | const source = await fs.readFile(path.join('.', `${filePath}.ts`), 'utf-8'); 74 | return source; 75 | } 76 | 77 | async function withTempDir(dirname: string, closure: () => Promise) { 78 | const prevdir = process.cwd(); 79 | const parent = await fs.mkdtemp(path.join(os.tmpdir(), 'terrastack.')); 80 | const workdir = path.join(parent, dirname); 81 | await fse.mkdirp(workdir); 82 | try { 83 | process.chdir(workdir); 84 | await closure(); 85 | } finally { 86 | process.chdir(prevdir); 87 | await fse.remove(parent); 88 | } 89 | } -------------------------------------------------------------------------------- /packages/terrastack-cli/test/codegen-types.test.ts: -------------------------------------------------------------------------------- 1 | import { TypeGenerator } from "../lib/codegen-types"; 2 | import { promises as fs } from 'fs'; 3 | import * as fse from 'fs-extra'; 4 | import * as path from 'path'; 5 | import * as os from 'os'; 6 | import { CodeMaker } from "codemaker"; 7 | import { JSONSchema4 } from "json-schema"; 8 | 9 | jest.setTimeout(60000); // 1min 10 | 11 | describe('structs', () => { 12 | resource('has primitive types', { 13 | version: 0, 14 | block: { 15 | attributes: { 16 | string: { 17 | type: "string" 18 | }, 19 | bool: { 20 | type: "bool" 21 | }, 22 | number: { 23 | type: "number" 24 | }, 25 | } 26 | } 27 | }); 28 | 29 | resource('has primitive optional types', { 30 | version: 0, 31 | block: { 32 | attributes: { 33 | architecture: { 34 | type: "string", 35 | optional: true 36 | } 37 | } 38 | } 39 | }); 40 | 41 | resource('has primitive optional computed type', { 42 | version: 0, 43 | block: { 44 | attributes: { 45 | architecture: { 46 | type: "string", 47 | optional: true, 48 | computed: true 49 | } 50 | } 51 | } 52 | }); 53 | 54 | resource('has primitive computed type', { 55 | version: 0, 56 | block: { 57 | attributes: { 58 | architecture: { 59 | type: "string", 60 | computed: true 61 | } 62 | } 63 | } 64 | }); 65 | 66 | resource('has an optional map of strings', { 67 | version: 0, 68 | block: { 69 | attributes: { 70 | tags: { 71 | type: ["map", "string"], 72 | optional: true 73 | }, 74 | bool: { 75 | type: ["map", "bool"] 76 | }, 77 | number: { 78 | type: ["map", "number"] 79 | }, 80 | numberSet: { 81 | type: ["set", "number"] 82 | } 83 | } 84 | } 85 | }); 86 | 87 | resource('has list of complex nested objects', { 88 | version: 0, 89 | block: { 90 | attributes: { 91 | instance_market_options: { 92 | type: ["list", ["object", { 93 | market_type: "string", 94 | spot_options: ["set", ["object", { 95 | block_duration_minutes: "number" 96 | }]] 97 | }]], 98 | optional: true 99 | } 100 | } 101 | } 102 | }); 103 | 104 | resource('has block type nested as single', { 105 | version: 0, 106 | block: { 107 | attributes: { 108 | tags: { 109 | type: ["map", "string"], 110 | optional: true 111 | } 112 | }, 113 | block_types: { 114 | s3_import: { 115 | nesting_mode: "single", 116 | block: { 117 | attributes: { 118 | create: { 119 | type: "string", 120 | optional: true 121 | } 122 | } 123 | } 124 | } 125 | } 126 | } 127 | }); 128 | 129 | resource('has empty block type nested as single', { 130 | version: 0, 131 | block: { 132 | attributes: { 133 | tags: { 134 | type: ["map", "string"], 135 | optional: true 136 | } 137 | }, 138 | block_types: { 139 | s3_import: { 140 | nesting_mode: "single", 141 | block: { 142 | attributes: {} 143 | } 144 | } 145 | } 146 | } 147 | }); 148 | 149 | resource('has block type nested in block type', { 150 | version: 0, 151 | block: { 152 | attributes: { 153 | tags: { 154 | type: ["map", "string"], 155 | optional: true 156 | } 157 | }, 158 | block_types: { 159 | s3_import: { 160 | nesting_mode: "single", 161 | block: { 162 | block_types: { 163 | foo: { 164 | nesting_mode: "single", 165 | block: { 166 | attributes: { 167 | bar: { 168 | type: "string", 169 | optional: true 170 | } 171 | } 172 | } 173 | } 174 | }, 175 | attributes: { 176 | create: { 177 | type: "string", 178 | optional: true 179 | } 180 | } 181 | } 182 | } 183 | } 184 | } 185 | }); 186 | }); 187 | 188 | function resource(name: string, schema: JSONSchema4) { 189 | which('resource', name, schema) 190 | } 191 | 192 | 193 | function which(type: string, name: string, schema: JSONSchema4) { 194 | test(name, async () => { 195 | const gen = new TypeGenerator(type); 196 | gen.addType('TestType', schema); 197 | 198 | await withTempDir('test', async () => { 199 | expect(await generate(gen)).toMatchSnapshot(); 200 | }); 201 | }); 202 | } 203 | 204 | async function generate(gen: TypeGenerator) { 205 | const code = new CodeMaker(); 206 | 207 | const entrypoint = 'index.ts'; 208 | 209 | code.openFile(entrypoint); 210 | gen.generate(code); 211 | code.closeFile(entrypoint) 212 | await code.save('.'); 213 | 214 | const source = await fs.readFile(path.join('.', entrypoint), 'utf-8'); 215 | 216 | return source; 217 | } 218 | 219 | async function withTempDir(dirname: string, closure: () => Promise) { 220 | const prevdir = process.cwd(); 221 | const parent = await fs.mkdtemp(path.join(os.tmpdir(), 'terrastack.')); 222 | const workdir = path.join(parent, dirname); 223 | await fse.mkdirp(workdir); 224 | try { 225 | process.chdir(workdir); 226 | await closure(); 227 | } finally { 228 | process.chdir(prevdir); 229 | await fse.remove(parent); 230 | } 231 | } -------------------------------------------------------------------------------- /packages/terrastack-cli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "alwaysStrict": true, 4 | "charset": "utf8", 5 | "declaration": true, 6 | "experimentalDecorators": true, 7 | "inlineSourceMap": true, 8 | "inlineSources": true, 9 | "lib": [ 10 | "es2018" 11 | ], 12 | "module": "CommonJS", 13 | "noEmitOnError": true, 14 | "noFallthroughCasesInSwitch": true, 15 | "noImplicitAny": true, 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "resolveJsonModule": true, 21 | "strict": true, 22 | "strictNullChecks": true, 23 | "strictPropertyInitialization": true, 24 | "stripInternal": true, 25 | "target": "ES2018" 26 | }, 27 | "include": [ 28 | "**/*.ts" 29 | ], 30 | "exclude": [ 31 | "node_modules" 32 | ], 33 | } 34 | -------------------------------------------------------------------------------- /tools/align-version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # usage: align-version.sh [SUFFIX] 4 | # 5 | # aligns lerna version to package.json 6 | # this is executed in CI builds so artifacts include the actual version instead of 0.0.0.0 7 | # 8 | # if SUFFIX is provided, appends this to the version as-is 9 | # 10 | 11 | set -euo pipefail 12 | scriptdir="$(cd $(dirname $0) && pwd)" 13 | cd ${scriptdir}/.. 14 | 15 | suffix="${1:-}" 16 | version="$(node -p "require('./package.json').version")${suffix}" 17 | npx lerna version ${version} --yes --exact --force-publish=* --no-git-tag-version --no-push 18 | -------------------------------------------------------------------------------- /tools/bump.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | repo_root=$(cd $(dirname $0)/.. && pwd) 4 | cd ${repo_root} 5 | 6 | # default to minor releases 7 | version="${1:-minor}" 8 | 9 | yarn install 10 | npx standard-version --release-as ${version} --skip.tag --bumpFiles package.json -------------------------------------------------------------------------------- /tools/collect-dist.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | 4 | # always run from repo root 5 | cd $(dirname $0)/.. 6 | 7 | rm -fr dist 8 | mkdir -p dist 9 | 10 | for dir in $(lerna ls -p); do 11 | src="${dir}/dist" 12 | echo "collecting from ${src}" 13 | rsync -aL ${src}/ dist/ 14 | done 15 | 16 | find dist/ -------------------------------------------------------------------------------- /tools/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -euo pipefail 3 | scriptdir=$(cd $(dirname $0) && pwd) 4 | 5 | # release to all package managers 6 | npx jsii-release 7 | 8 | # release to github releases (still not supported by jsii-release) 9 | ${scriptdir}/release-github.sh -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "alwaysStrict": true, 4 | "charset": "utf8", 5 | "declaration": true, 6 | "experimentalDecorators": true, 7 | "inlineSourceMap": true, 8 | "inlineSources": true, 9 | "lib": [ 10 | "es2018" 11 | ], 12 | "module": "CommonJS", 13 | "noEmitOnError": false, 14 | "noFallthroughCasesInSwitch": true, 15 | "noImplicitAny": true, 16 | "noImplicitReturns": true, 17 | "noImplicitThis": true, 18 | "noUnusedLocals": true, 19 | "noUnusedParameters": true, 20 | "resolveJsonModule": true, 21 | "strict": true, 22 | "strictNullChecks": true, 23 | "strictPropertyInitialization": true, 24 | "stripInternal": true, 25 | "target": "ES2018" 26 | }, 27 | "include": [ 28 | "**/*.ts" 29 | ], 30 | "exclude": [ 31 | "node_modules" 32 | ] 33 | } 34 | --------------------------------------------------------------------------------