diff --git a/packages/@aws-cdk/mixins-preview/.eslintrc.js b/packages/@aws-cdk/mixins-preview/.eslintrc.js new file mode 100644 index 0000000000000..73d2505a85a7f --- /dev/null +++ b/packages/@aws-cdk/mixins-preview/.eslintrc.js @@ -0,0 +1,4 @@ +const baseConfig = require('@aws-cdk/cdk-build-tools/config/eslintrc'); +baseConfig.parserOptions.project = __dirname + '/tsconfig.json'; +baseConfig.rules['import/order'] = 'off'; +module.exports = baseConfig; diff --git a/packages/@aws-cdk/mixins-preview/.gitignore b/packages/@aws-cdk/mixins-preview/.gitignore new file mode 100644 index 0000000000000..60f323886f8db --- /dev/null +++ b/packages/@aws-cdk/mixins-preview/.gitignore @@ -0,0 +1,22 @@ +*.js +*.js.map +*.d.ts +node_modules +*.generated.ts +dist +.jsii + +.LAST_BUILD +.nyc_output +coverage +nyc.config.js +.LAST_PACKAGE +*.snk +!.eslintrc.js +!jest.config.js + +junit.xml +!**/*.snapshot/**/asset.*/*.js +!**/*.snapshot/**/asset.*/*.d.ts + +!**/*.snapshot/**/asset.*/** diff --git a/packages/@aws-cdk/mixins-preview/.npmignore b/packages/@aws-cdk/mixins-preview/.npmignore new file mode 100644 index 0000000000000..b94897de6fcce --- /dev/null +++ b/packages/@aws-cdk/mixins-preview/.npmignore @@ -0,0 +1,28 @@ +# Don't include original .ts files when doing `npm pack` +*.ts +!*.d.ts +coverage +.nyc_output +*.tgz + +dist +.LAST_PACKAGE +.LAST_BUILD +!*.js + +# Include .jsii +!.jsii + +*.snk + +*.tsbuildinfo + +tsconfig.json +.eslintrc.js +jest.config.js + +# exclude cdk artifacts +**/cdk.out +junit.xml +!*.lit.ts +**/*.snapshot diff --git a/packages/@aws-cdk/mixins-preview/LICENSE b/packages/@aws-cdk/mixins-preview/LICENSE new file mode 100644 index 0000000000000..5ccf0c6780bab --- /dev/null +++ b/packages/@aws-cdk/mixins-preview/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018-2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/@aws-cdk/mixins-preview/NOTICE b/packages/@aws-cdk/mixins-preview/NOTICE new file mode 100644 index 0000000000000..cd0946c1cf193 --- /dev/null +++ b/packages/@aws-cdk/mixins-preview/NOTICE @@ -0,0 +1,2 @@ +AWS Cloud Development Kit (AWS CDK) +Copyright 2018-2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. diff --git a/packages/@aws-cdk/mixins-preview/README.md b/packages/@aws-cdk/mixins-preview/README.md new file mode 100644 index 0000000000000..6547394e744fd --- /dev/null +++ b/packages/@aws-cdk/mixins-preview/README.md @@ -0,0 +1,16 @@ +# CDK Mixins: Composable Abstractions for AWS Resources + + + +--- + +![cdk-constructs: Experimental](https://img.shields.io/badge/cdk--constructs-experimental-important.svg?style=for-the-badge) + +> The APIs of higher level constructs in this module are experimental and under active development. They are subject to non-backward compatible changes or removal in any future version. These are not subject to the [Semantic Versioning](https://semver.org/) model and breaking changes will be announced in the release notes. This means that while you may use them, you may need to update your source code when upgrading to a newer version of this package. + +--- + + + +Implementation of the CDK Mixins proposal. +See for details. diff --git a/packages/@aws-cdk/mixins-preview/jest.config.js b/packages/@aws-cdk/mixins-preview/jest.config.js new file mode 100644 index 0000000000000..3a2fd93a1228a --- /dev/null +++ b/packages/@aws-cdk/mixins-preview/jest.config.js @@ -0,0 +1,2 @@ +const baseConfig = require('@aws-cdk/cdk-build-tools/config/jest.config'); +module.exports = baseConfig; diff --git a/packages/@aws-cdk/mixins-preview/lib/index.ts b/packages/@aws-cdk/mixins-preview/lib/index.ts new file mode 100644 index 0000000000000..2b3d8cffd6894 --- /dev/null +++ b/packages/@aws-cdk/mixins-preview/lib/index.ts @@ -0,0 +1 @@ +export * from './mixins'; diff --git a/packages/@aws-cdk/mixins-preview/lib/mixins/index.ts b/packages/@aws-cdk/mixins-preview/lib/mixins/index.ts new file mode 100644 index 0000000000000..314ebca47ddfe --- /dev/null +++ b/packages/@aws-cdk/mixins-preview/lib/mixins/index.ts @@ -0,0 +1,73 @@ +import { IConstruct } from 'constructs'; + +/** + * A mixin is a reusable piece of functionality that can be applied to constructs + * to add behavior, properties, or modify existing functionality without inheritance. + * + * Mixins follow a three-phase pattern: + * 1. Check if the mixin supports the target construct (supports) + * 2. Optionally validate the construct before applying (validate) + * 3. Apply the mixin functionality to the construct (applyTo) + */ +export interface IMixin { + /** + * Determines whether this mixin can be applied to the given construct. + * + * This method should perform type checking and compatibility validation + * to ensure the mixin can safely operate on the construct. + * + * @param construct - The construct to check for compatibility + * @returns true if the mixin supports this construct type, false otherwise + */ + supports(construct: IConstruct): boolean; + + /** + * Validates the construct before applying the mixin. + * + * This optional method allows the mixin to perform additional validation + * beyond basic type compatibility. It can check for required properties, + * configuration constraints, or other preconditions. + * + * @param construct - The construct to validate + * @returns An array of validation error messages, or empty array if valid + */ + validate?(construct: IConstruct): string[]; + + /** + * Applies the mixin functionality to the target construct. + * + * This method performs the actual work of the mixin, such as: + * - Adding new properties or methods + * - Modifying existing behavior + * - Setting up additional resources or configurations + * - Establishing relationships with other constructs + * + * @param construct - The construct to apply the mixin to + * @returns The modified construct (may be the same instance or a wrapper) + */ + applyTo(construct: IConstruct): IConstruct; +} + +/** + * Abstract base class for mixins that provides default implementations + * and simplifies mixin creation. + */ +export abstract class Mixin implements IMixin { + /** + * Default implementation that supports any construct. + * Override this method to add type-specific support logic. + */ + public supports(_construct: IConstruct): boolean { + return true; + } + + /** + * Default validation implementation that returns no errors. + * Override this method to add custom validation logic. + */ + public validate(_construct: IConstruct): string[] { + return []; + } + + abstract applyTo(construct: IConstruct): IConstruct; +} diff --git a/packages/@aws-cdk/mixins-preview/package.json b/packages/@aws-cdk/mixins-preview/package.json new file mode 100644 index 0000000000000..eec9bce9108ad --- /dev/null +++ b/packages/@aws-cdk/mixins-preview/package.json @@ -0,0 +1,108 @@ +{ + "name": "@aws-cdk/mixins-preview", + "private": true, + "version": "0.0.0", + "description": "Preview of CDK Mixins - composable, reusable abstractions that can be applied to any construct (L1, L2 or custom).", + "main": "lib/mixins/index.js", + "types": "lib/mixins/index.d.ts", + "exports": { + "./.jsii": "./.jsii", + "./.warnings.jsii.js": "./.warnings.jsii.js", + "./mixins": "./index.js" + }, + "jsii": { + "outdir": "dist", + "targets": { + "java": { + "package": "software.amazon.awscdk.mixins.preview", + "maven": { + "groupId": "software.amazon.awscdk", + "artifactId": "cdk-mixins-preview" + } + }, + "dotnet": { + "namespace": "Amazon.CDK.Mixins.Preview", + "packageId": "Amazon.CDK.Mixins.Preview", + "iconUrl": "https://raw.githubusercontent.com/aws/aws-cdk/main/logo/default-256-dark.png" + }, + "python": { + "distName": "aws-cdk.mixins-preview", + "module": "aws_cdk.mixins_preview", + "classifiers": [ + "Framework :: AWS CDK", + "Framework :: AWS CDK :: 2" + ] + }, + "go": { + "moduleName": "github.com/aws/aws-cdk-go", + "packageName": "awscdkmixinspreview" + } + }, + "metadata": { + "jsii": { + "rosetta": { + "strict": true + } + } + }, + "tsconfig": "tsconfig.json", + "validateTsconfig": "minimal" + }, + "repository": { + "type": "git", + "url": "https://github.com/aws/aws-cdk.git", + "directory": "packages/@aws-cdk/mixins-preview" + }, + "scripts": { + "build": "cdk-build", + "watch": "cdk-watch", + "lint": "cdk-lint", + "test": "cdk-test", + "pkglint": "pkglint -f", + "package": "cdk-package", + "awslint": "cdk-awslint", + "build+test": "yarn build && yarn test", + "build+test+package": "yarn build+test && yarn package", + "compat": "cdk-compat", + "rosetta:extract": "yarn --silent jsii-rosetta extract", + "build+extract": "yarn build && yarn rosetta:extract", + "build+test+extract": "yarn build+test && yarn rosetta:extract" + }, + "keywords": [ + "aws", + "cdk", + "construct", + "library", + "mixins", + "preview" + ], + "author": { + "name": "Amazon Web Services", + "url": "https://aws.amazon.com", + "organization": true + }, + "license": "Apache-2.0", + "devDependencies": { + "aws-cdk-lib": "0.0.0", + "@aws-cdk/cdk-build-tools": "0.0.0", + "@aws-cdk/pkglint": "0.0.0", + "@types/jest": "^29.5.14", + "jest": "^29.7.0" + }, + "homepage": "https://github.com/aws/aws-cdk", + "peerDependencies": { + "aws-cdk-lib": "^0.0.0", + "constructs": "^10.0.0" + }, + "separate-module": false, + "engines": { + "node": ">= 18.0.0" + }, + "stability": "experimental", + "maturity": "experimental", + "awscdkio": { + "announce": false + }, + "cdk-build": { + } +} diff --git a/packages/@aws-cdk/mixins-preview/rosetta/default.ts-fixture b/packages/@aws-cdk/mixins-preview/rosetta/default.ts-fixture new file mode 100644 index 0000000000000..e8deb6060d76d --- /dev/null +++ b/packages/@aws-cdk/mixins-preview/rosetta/default.ts-fixture @@ -0,0 +1,11 @@ +// Fixture with packages imported, but nothing else +import { Construct } from 'constructs'; +import { Stack } from '@aws-cdk/core'; + +class Fixture extends Stack { + constructor(scope: Construct, id: string) { + super(scope, id); + + /// here + } +} diff --git a/packages/@aws-cdk/mixins-preview/test/mixin.test.ts b/packages/@aws-cdk/mixins-preview/test/mixin.test.ts new file mode 100644 index 0000000000000..b9aa16527536c --- /dev/null +++ b/packages/@aws-cdk/mixins-preview/test/mixin.test.ts @@ -0,0 +1,28 @@ +import { Construct } from 'constructs'; +import { Mixin } from '../lib'; + +class TestConstruct extends Construct { + constructor(scope: Construct, id: string) { + super(scope, id); + } +} + +class TestMixin extends Mixin { + applyTo(construct: any): any { + (construct as any).mixinApplied = true; + return construct; + } +} + +describe('IMixin', () => { + test('mixin can be applied to supported construct', () => { + const construct = new TestConstruct(new Construct(undefined as any, 'root'), 'test'); + const mixin = new TestMixin(); + + expect(mixin.supports(construct)).toBe(true); + expect(mixin.validate(construct)).toEqual([]); + + const result = mixin.applyTo(construct); + expect((result as any).mixinApplied).toBe(true); + }); +}); diff --git a/packages/@aws-cdk/mixins-preview/tsconfig.json b/packages/@aws-cdk/mixins-preview/tsconfig.json new file mode 100644 index 0000000000000..59c061c862645 --- /dev/null +++ b/packages/@aws-cdk/mixins-preview/tsconfig.json @@ -0,0 +1,48 @@ +{ + "compilerOptions": { + "declarationMap": false, + "inlineSourceMap": true, + "inlineSources": true, + "alwaysStrict": true, + "declaration": true, + "incremental": true, + "lib": [ + "es2022" + ], + "module": "commonjs", + "noEmitOnError": true, + "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "strict": true, + "strictNullChecks": true, + "strictPropertyInitialization": true, + "stripInternal": false, + "target": "es2022", + "composite": true, + "tsBuildInfoFile": "tsconfig.tsbuildinfo", + "esModuleInterop": false + }, + "include": [ + "**/*.ts" + ], + "exclude": [ + "node_modules" + ], + "references": [ + { + "path": "../../aws-cdk-lib" + }, + { + "path": "../../../tools/@aws-cdk/cdk-build-tools" + }, + { + "path": "../../../tools/@aws-cdk/pkglint" + } + ] +}