@aligent/nx-cdk 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (22) hide show
  1. package/README.md +7 -0
  2. package/package.json +1 -1
  3. package/src/generators/helpers/configs/base-package/package.json +89 -0
  4. package/src/generators/helpers/configs/nxJson.js +3 -1
  5. package/src/generators/helpers/utilities.d.ts +2 -34
  6. package/src/generators/helpers/utilities.js +28 -11
  7. package/src/generators/preset/files/.git-hooks/{pre-push.template → pre-commit.template} +1 -2
  8. package/src/generators/preset/files/.github/CODEOWNERS.template +5 -3
  9. package/src/generators/preset/files/.github/workflows/release.yml.template +1 -1
  10. package/src/generators/preset/files/.github/workflows/stg-deployment.yml.template +1 -1
  11. package/src/generators/preset/files/README.md.template +19 -0
  12. package/src/generators/preset/files/application/README.md.template +34 -2
  13. package/src/generators/preset/files/application/bin/main.ts.template +74 -44
  14. package/src/generators/preset/files/application/cdk.json.template +1 -1
  15. package/src/generators/preset/files/application/lib/service-stacks.ts.template +1 -2
  16. package/src/generators/preset/files/application/package.json.template +21 -7
  17. package/src/generators/preset/files/eslint.config.mjs.template +0 -1
  18. package/src/generators/preset/files/libs/infra/README.md.template +2 -2
  19. package/src/generators/preset/files/libs/infra/src/index.ts.template +26 -22
  20. package/src/generators/service/files/src/index.ts.template +2 -3
  21. package/src/generators/helpers/configs/packageJson.d.ts +0 -83
  22. package/src/generators/helpers/configs/packageJson.js +0 -82
package/README.md CHANGED
@@ -21,6 +21,13 @@ npx create-nx-workspace@latest --preset=@aligent/nx-cdk
21
21
  | `name` | string | Yes | - | The name of the project/application (alphanumeric and dashes only) |
22
22
  | `nodeVersion` | string | No | `24.11.0` | The target Node.js version for the project (must be valid semver, e.g., 22.10.0) |
23
23
 
24
+ #### Post-generation setup
25
+
26
+ After running the preset generator, review these defaults before committing:
27
+
28
+ - **AWS profile** — The `pg:synth`, `pg:deploy`, `pg:diff`, and `pg:destroy` scripts in `package.json` use `--profile playground`. This assumes a profile named `playground` exists in your `~/.aws/config`. Update the profile name in the `pg:*` scripts if yours differs.
29
+ - **`.github/CODEOWNERS`** — The generated file references Aligent's GitHub teams (`@aligent/microservices`, `@aligent/devops`). Replace these with your organisation's team handles.
30
+
24
31
  #### What it creates
25
32
 
26
33
  The preset generator scaffolds:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aligent/nx-cdk",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
4
4
  "type": "commonjs",
5
5
  "main": "./src/index.js",
6
6
  "typings": "./src/index.d.ts",
@@ -0,0 +1,89 @@
1
+ {
2
+ "author": "Aligent",
3
+ "private": true,
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "scripts": {
7
+ "test": "nx affected -t test --coverage",
8
+ "test:all": "nx run-many -t test --coverage",
9
+ "lint": "nx affected -t lint",
10
+ "lint:all": "nx run-many -t lint",
11
+ "typecheck": "nx affected -t typecheck",
12
+ "typecheck:all": "nx run-many -t typecheck",
13
+ "audit": "nx run-many -t lint typecheck test --configuration coverage --skip-nx-cache",
14
+ "pg:synth": "nx run application:pg:synth --bundle-mode=dev --profile playground",
15
+ "pg:deploy": "nx run application:pg:deploy --bundle-mode=dev --method=direct --require-approval never --profile playground",
16
+ "pg:diff": "nx run application:pg:diff --profile playground",
17
+ "pg:destroy": "nx run application:pg:destroy --profile playground",
18
+ "postinstall": "[ -d .git ] && git config core.hooksPath '.git-hooks' && chmod +x .git-hooks/* || true"
19
+ },
20
+ "dependencies": {
21
+ "@aligent/microservice-util-lib": "^1.2.0"
22
+ },
23
+ "devDependencies": {
24
+ "@aligent/cdk-aspects": "^0.5.0",
25
+ "@aligent/cdk-step-function-from-file": "^0.3.2",
26
+ "@aligent/nx-openapi": "^1.0.0",
27
+ "@aligent/ts-code-standards": "^4.1.0",
28
+ "@nx/eslint": "22.1.3",
29
+ "@nx/eslint-plugin": "22.1.3",
30
+ "@nx/js": "22.1.3",
31
+ "@nx/vitest": "22.1.3",
32
+ "@nx/workspace": "22.1.3",
33
+ "@swc-node/register": "^1.10.10",
34
+ "@swc/core": "^1.13.3",
35
+ "@swc/helpers": "^0.5.17",
36
+ "@types/aws-lambda": "^8.10.152",
37
+ "@types/node": "^22.17.0",
38
+ "@typescript-eslint/eslint-plugin": "8.44.0",
39
+ "@typescript-eslint/parser": "8.44.0",
40
+ "@vitest/coverage-v8": "^3.2.4",
41
+ "@vitest/ui": "^3.2.4",
42
+ "aws-cdk": "^2.1033.0",
43
+ "aws-cdk-lib": "^2.235.1",
44
+ "cdk-nag": "^2.37.55",
45
+ "constructs": "^10.4.3",
46
+ "eslint": "^9.32.0",
47
+ "eslint-config-prettier": "^10.1.8",
48
+ "eslint-plugin-import": "^2.32.0",
49
+ "fast-glob": "^3.3.3",
50
+ "jiti": "2.5.1",
51
+ "jsonc-eslint-parser": "^2.4.0",
52
+ "nx": "22.1.3",
53
+ "prettier": "^3.6.2",
54
+ "rolldown": "1.0.0-rc.1",
55
+ "store-parameters": "^1.1.3",
56
+ "tslib": "^2.8.1",
57
+ "tsx": "^4.21.0",
58
+ "typescript": "~5.9.2",
59
+ "vite": "^7.2.6",
60
+ "vitest": "^3.2.4"
61
+ },
62
+ "nx": {
63
+ "includedScripts": [],
64
+ "targets": {
65
+ "parameters": {
66
+ "executor": "nx:run-commands",
67
+ "options": {
68
+ "color": true,
69
+ "cwd": "parameters"
70
+ },
71
+ "configurations": {
72
+ "import": {
73
+ "command": "store-parameters import {args.file} --delimiter=|"
74
+ },
75
+ "export": {
76
+ "command": "store-parameters export {args.file} --path={args.path} --delimiter=|"
77
+ }
78
+ }
79
+ }
80
+ }
81
+ },
82
+ "workspaces": [
83
+ "application",
84
+ "clients",
85
+ "libs/*",
86
+ "services/*"
87
+ ],
88
+ "packageManager": "yarn@4.12.0+sha512.f45ab632439a67f8bc759bf32ead036a1f413287b9042726b7cc4818b7b49e14e9423ba49b18f9e06ea4941c1ad062385b1d8760a8d5091a1a31e5f6219afca8"
89
+ }
@@ -26,7 +26,7 @@ exports.NX_JSON = {
26
26
  cache: true,
27
27
  dependsOn: ['^build'],
28
28
  inputs: ['production', '^production'],
29
- outputs: ['{projectRoot}/dist'],
29
+ outputs: ['{projectRoot}/dist'], // FIXME: It seems that this doesn't cache correctly. Investigate this.
30
30
  },
31
31
  lint: { cache: true, inputs: ['default', '^production'] },
32
32
  test: {
@@ -37,5 +37,7 @@ exports.NX_JSON = {
37
37
  configurations: { coverage: { coverage: true } },
38
38
  },
39
39
  typecheck: { cache: true, inputs: ['default', '^production'] },
40
+ cdk: { dependsOn: [{ target: 'build', params: 'forward', projects: '@services/*' }] },
41
+ pg: { dependsOn: [{ target: 'build', params: 'forward', projects: '@services/*' }] },
40
42
  },
41
43
  };
@@ -11,43 +11,11 @@ interface Service {
11
11
  stack: string;
12
12
  }
13
13
  export declare function constructPackageJsonFile(input: PackageJsonInput): {
14
- [k: string]: string | true | readonly ["application", "clients", "libs/*", "services/*"] | {
15
- readonly test: "nx affected -t test --coverage";
16
- readonly 'test:all': "nx run-many -t test --coverage";
17
- readonly lint: "nx affected -t lint";
18
- readonly 'lint:all': "nx run-many -t lint";
19
- readonly typecheck: "nx affected -t typecheck";
20
- readonly 'typecheck:all': "nx run-many -t typecheck";
21
- readonly postinstall: "[ -d .git ] && git config core.hooksPath '.git-hooks' && chmod +x .git-hooks/* || true";
22
- readonly 'pg:synth': "nx run application:cdk synth 'dev/**' --exclusively --bundle-mode=dev --profile=playground";
23
- readonly 'pg:deploy': "nx run application:cdk deploy 'dev/**' --method=direct --require-approval=never --exclusively --bundle-mode=dev --profile=playground";
24
- readonly 'pg:destroy': "nx run application:cdk destroy 'dev/**' --profile playground";
25
- readonly audit: "nx run-many -t lint typecheck test --configuration coverage --skip-nx-cache";
26
- } | {
27
- readonly '@aligent/microservice-util-lib': "^1.2.0";
28
- } | {
14
+ [k: string]: string | boolean | string[] | Record<string, string> | {
29
15
  [k: string]: string;
30
16
  } | {
31
17
  node: string;
32
18
  } | {
33
- includedScripts: readonly [];
34
- targets: {
35
- readonly parameters: {
36
- readonly executor: "nx:run-commands";
37
- readonly options: {
38
- readonly color: true;
39
- readonly cwd: "parameters";
40
- };
41
- readonly configurations: {
42
- readonly import: {
43
- readonly command: "store-parameters import {args.file} --delimiter=|";
44
- };
45
- readonly export: {
46
- readonly command: "store-parameters export {args.file} --path={args.path} --delimiter=|";
47
- };
48
- };
49
- };
50
- };
51
19
  name: string;
52
20
  node?: never;
53
21
  };
@@ -92,7 +60,7 @@ export declare function getGeneratorVersion(): string;
92
60
  *
93
61
  * @throws {Error} If the ApplicationStage constructor cannot be found in service-stacks.ts
94
62
  */
95
- export declare function addServiceStackToMainApplication(tree: Tree, service: Service, projectName: string): Promise<void>;
63
+ export declare function addServiceStackToMainApplication(tree: Tree, service: Service, projectName: string): void;
96
64
  /**
97
65
  * Splits a kebab-case name into an array of capitalized parts.
98
66
  *
@@ -7,24 +7,35 @@ exports.addServiceStackToMainApplication = addServiceStackToMainApplication;
7
7
  exports.splitInputName = splitInputName;
8
8
  /* v8 ignore start */
9
9
  const devkit_1 = require("@nx/devkit");
10
- const fs_1 = require("fs");
11
10
  const path_1 = require("path");
12
11
  const ts_morph_1 = require("ts-morph");
13
- const packageJson_1 = require("./configs/packageJson");
14
12
  const tsConfigs_1 = require("./configs/tsConfigs");
13
+ /**
14
+ * Reads the base package.json configuration from a JSON file.
15
+ *
16
+ * The configuration is stored as a standalone JSON file rather than a TypeScript constant
17
+ * so that Dependabot can automatically detect and upgrade the dependency versions within it.
18
+ *
19
+ * @returns The parsed package.json configuration.
20
+ */
21
+ function readPackageJsonConfig() {
22
+ const configPath = (0, path_1.join)(__dirname, './configs/base-package/package.json');
23
+ return (0, devkit_1.readJsonFile)(configPath);
24
+ }
15
25
  function constructPackageJsonFile(input) {
26
+ const config = readPackageJsonConfig();
16
27
  const devDependencies = Object.fromEntries(Object.entries({
17
28
  '@aligent/nx-cdk': input.version,
18
- ...packageJson_1.PACKAGE_JSON.devDependencies,
29
+ ...config.devDependencies,
19
30
  }).sort());
20
31
  const packageJson = Object.fromEntries(Object.entries({
21
32
  name: `@${input.name}/integrations`,
22
33
  description: `${input.projectName} integrations mono-repository`,
23
34
  version: input.version,
24
- ...packageJson_1.PACKAGE_JSON,
35
+ ...config,
25
36
  devDependencies,
26
37
  engines: { node: `^${input.nodeVersion}` },
27
- nx: { name: `${input.name}-int`, ...packageJson_1.PACKAGE_JSON.nx },
38
+ nx: { name: `${input.name}-int`, ...config.nx },
28
39
  }));
29
40
  return packageJson;
30
41
  }
@@ -66,18 +77,24 @@ function getGeneratorVersion() {
66
77
  *
67
78
  * @throws {Error} If the ApplicationStage constructor cannot be found in service-stacks.ts
68
79
  */
69
- async function addServiceStackToMainApplication(tree, service, projectName) {
80
+ function addServiceStackToMainApplication(tree, service, projectName) {
70
81
  const application = (0, devkit_1.readProjectConfiguration)(tree, projectName);
71
82
  if (application.root.includes('..')) {
72
83
  throw new Error('Invalid application root path');
73
84
  }
74
- const stacksPath = (0, path_1.join)(tree.root, application.root, 'lib/service-stacks.ts');
75
- if (!(0, fs_1.existsSync)(stacksPath)) {
85
+ const stacksRelativePath = (0, path_1.join)(application.root, 'lib/service-stacks.ts');
86
+ if (!tree.exists(stacksRelativePath)) {
76
87
  console.log('Service Stacks does not exist, skipping service stacks registration.');
77
88
  return;
78
89
  }
79
- const project = new ts_morph_1.Project();
80
- const stackSource = project.addSourceFileAtPath(stacksPath);
90
+ const content = tree.read(stacksRelativePath, 'utf-8');
91
+ if (content === null) {
92
+ throw new Error(`Failed to read file: ${stacksRelativePath}`);
93
+ }
94
+ const fs = new ts_morph_1.InMemoryFileSystemHost();
95
+ fs.writeFileSync(stacksRelativePath, content);
96
+ const project = new ts_morph_1.Project({ fileSystem: fs });
97
+ const stackSource = project.addSourceFileAtPath(stacksRelativePath);
81
98
  const applicationStage = stackSource.getClassOrThrow('ApplicationStage');
82
99
  const stageConstructor = applicationStage.getConstructors()[0];
83
100
  if (!stageConstructor) {
@@ -90,7 +107,7 @@ async function addServiceStackToMainApplication(tree, service, projectName) {
90
107
  const sharedInfra = stageConstructor.getVariableStatement('sharedInfra');
91
108
  const sharedPropsStatement = sharedInfra ? `...sharedInfra.getProps(),` : '';
92
109
  stageConstructor.addStatements(`new ${service.stack}(this, ${service.constant}.NAME, { ...props, ${sharedPropsStatement} description: ${service.constant}.DESCRIPTION });`);
93
- await stackSource.save();
110
+ tree.write(stacksRelativePath, stackSource.getFullText());
94
111
  }
95
112
  /**
96
113
  * Splits a kebab-case name into an array of capitalized parts.
@@ -1,8 +1,7 @@
1
1
  #!/bin/bash
2
2
  # Script adapted from https://github.com/kaczor6418/git-hooks-example/blob/master/git-hooks/pre-commit
3
3
 
4
- # Support using VSCode to commit
5
- # This loads nvm.sh and sets the correct PATH before running hook
4
+ # Support using VSCode to commit. This loads nvm.sh and sets the correct PATH before running hook
6
5
  export NVM_DIR="$HOME/.nvm"
7
6
  [ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
8
7
 
@@ -1,3 +1,5 @@
1
- # Both Microservice and DevOps teams own everything in this repo
2
- # We'll review this in few months time and adjust when needed
3
- * @aligent/microservices @aligent/devops
1
+ # Default owners
2
+ * @aligent/microservices
3
+
4
+ # DevOps owns files inside .github/
5
+ .github/** @aligent/devops
@@ -12,6 +12,6 @@ jobs:
12
12
  with:
13
13
  deploy: true
14
14
  deploy-command: yarn nx run application:cdk deploy
15
- extra-arguments: --exclusively
15
+ environment-target: prd
16
16
  github-environment: production
17
17
  secrets: inherit
@@ -12,6 +12,6 @@ jobs:
12
12
  with:
13
13
  deploy: true
14
14
  deploy-command: yarn nx run application:cdk deploy
15
- extra-arguments: --exclusively
15
+ environment-target: stg
16
16
  github-environment: staging
17
17
  secrets: inherit
@@ -48,6 +48,25 @@ nvm use
48
48
  yarn install
49
49
  ```
50
50
 
51
+ ### Initial Setup Checklist
52
+
53
+ Before committing this repository, review and update the following generated defaults:
54
+
55
+ - **`.github/CODEOWNERS`** — The default file references Aligent's GitHub teams (`@aligent/microservices`, `@aligent/devops`). Replace these with your own organisation's team handles.
56
+ - **`package.json` `pg:*` scripts** — The playground scripts use `--profile playground`. Ensure you have a matching profile in your `~/.aws/config` file:
57
+
58
+ ```ini
59
+ [profile playground]
60
+ source_profile = default
61
+ role_arn = arn:aws:iam::11111111111:role/RoleName
62
+ ```
63
+
64
+ If your profile has a different name, update the `pg:*` scripts in the root `package.json` to match.
65
+
66
+ ### Deployment
67
+
68
+ For deployment instructions (synthesize, deploy, destroy), see the [CDK Application README](./application/README.md).
69
+
51
70
  ## Contributing
52
71
 
53
72
  We follow [trunk-based development framework](https://trunkbaseddevelopment.com/) as a start and will evolve depending on our need.
@@ -6,12 +6,44 @@ This application manages the deployment of microservices and their shared infras
6
6
 
7
7
  ### Using yarn script from workspace root (Recommended)
8
8
 
9
+ ##### Synthesize
10
+
9
11
  ```bash
10
- # Synthesize templates
12
+ # Synthesize for default environment/stage (dev)
11
13
  yarn pg:synth
12
14
 
13
- # Deploy to playground
15
+ # Synthesize for a custom environment/stage
16
+ yarn pg:synth --args="plg/** -c environment=plg"
17
+ ```
18
+
19
+ ##### Deploy
20
+
21
+ ```bash
22
+ # Deploy for default environment/stage (dev)
14
23
  yarn pg:deploy
24
+
25
+ # Deploy to a custom environment/stage
26
+ yarn pg:deploy --args="plg/** -c environment=plg"
27
+ ```
28
+
29
+ ##### Diff
30
+
31
+ ```bash
32
+ # Diff checking for default environment/stage (dev)
33
+ yarn pg:diff
34
+
35
+ # Diff checking for a custom environment/stage
36
+ yarn pg:diff --args="-c environment=plg"
37
+ ```
38
+
39
+ ##### Destroy
40
+
41
+ ```bash
42
+ # Destroy default environment/stage (dev)
43
+ yarn pg:destroy
44
+
45
+ # Destroy a custom environment/stage
46
+ yarn pg:destroy --args="-c environment=plg"
15
47
  ```
16
48
 
17
49
  ### Direct Nx Commands
@@ -1,69 +1,99 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ DynamoDbDefaultsAspect,
3
4
  LambdaAndStepFunctionVersioningAspect,
4
5
  LogGroupDefaultsAspect,
5
6
  MicroserviceChecks,
6
7
  NodeJsFunctionDefaultsAspect,
8
+ ResourcePrefixAspect,
9
+ S3DefaultsAspect,
7
10
  StepFunctionsDefaultsAspect,
8
11
  } from '@aligent/cdk-aspects';
9
- import { App, Aspects, Tags } from 'aws-cdk-lib';
12
+ import { App, AspectOptions, AspectPriority, Aspects, IAspect, Tags } from 'aws-cdk-lib';
10
13
  import { Runtime } from 'aws-cdk-lib/aws-lambda';
11
14
  import { ApplicationStage } from '../lib/service-stacks.js';
12
15
 
13
- const APPLICATION_CONTEXT = { NAME: '<%= name %>-int', OWNER: 'aligent' } as const;
14
- const STAGE_NAMES = { DEV: 'dev', STG: 'stg', PRD: 'prd' } as const;
15
-
16
+ const APPLICATION_CONTEXT = { NAME: '<%= name %>-int', OWNER: 'Aligent' } as const;
16
17
  const app = new App({ context: APPLICATION_CONTEXT });
17
- Tags.of(app).add('OWNER', APPLICATION_CONTEXT.OWNER);
18
18
 
19
19
  /**
20
- * Apply Application-Level Aspects
21
- * These aspects are applied to all resources across all stages
22
- */
23
- Aspects.of(app).add(new NodeJsFunctionDefaultsAspect({ runtime: Runtime.NODEJS_<%= nodeRuntime %> }));
24
- Aspects.of(app).add(new StepFunctionsDefaultsAspect());
25
- Aspects.of(app).add(new MicroserviceChecks());
26
-
27
- /**
28
- * Static Stage Creation Approach
29
- *
30
- * We use static stage creation (explicitly defining dev, stg, prd) for several reasons:
20
+ * Dynamic Stage (Environment) Creation
31
21
  *
32
- * 1. **Predictability**: All stages are known at synthesis time, making the CDK app
33
- * behavior deterministic and easier to reason about.
22
+ * The stage (environment) name is provided via CDK context: `cdk synth -c environment=dev`
34
23
  *
35
- * 2. **Type Safety**: Static definitions provide better IDE support, autocompletion,
36
- * and compile-time type checking for stage-specific configurations.
24
+ * - Stage name must be exactly 3 lowercase alphanumeric characters (e.g., dev (default), stg, prd)
25
+ * - Production-specific aspects (versioning, long log retention) are applied only to `prd`
26
+ * - Non-production stages get shorter log retention and debug-level logging
37
27
  *
38
- * 3. **Explicit Configuration**: Each stage's unique settings (e.g., log retention
39
- * durations, aspects) are clearly visible in the code without needing to trace
40
- * through dynamic logic.
41
- *
42
- * 4. **Simpler Deployment**: CDK can synthesize all stages in a single pass without
43
- * requiring runtime context lookups or conditional logic.
28
+ * @see {@link https://dev.to/aws-heroes/how-to-use-aws-cdk-stage-and-when-to-choose-static-vs-dynamic-stack-creation-35h|Static vs Dynamic Stack Creation}
29
+ */
30
+ const stageName = app.node.tryGetContext('environment') ?? 'dev';
31
+
32
+ if (typeof stageName !== 'string' || !/^[a-z0-9]{3}$/.test(stageName)) {
33
+ throw new Error(
34
+ `Invalid stage (environment) name: "${stageName}". Stage must be exactly 3 lowercase alphanumeric characters (e.g., dev, stg, prd)`
35
+ );
36
+ }
37
+
38
+ const stage = new ApplicationStage(app, stageName);
39
+ Tags.of(stage).add('APPLICATION', APPLICATION_CONTEXT.NAME);
40
+ Tags.of(stage).add('OWNER', APPLICATION_CONTEXT.OWNER);
41
+ Tags.of(stage).add('STAGE', stageName);
42
+
43
+ /**
44
+ * Common aspects applied to every stage.
44
45
  *
45
- * 5. **CI/CD Integration**: Static stages integrate seamlessly with standard CI/CD
46
- * pipelines where environment configuration is managed externally.
46
+ * Aspects must be registered on each `Stage` individually a `Stage` creates a
47
+ * synthesis boundary that prevents `App`-level aspects from traversing into it.
47
48
  *
48
- * @see {@link https://dev.to/aws-heroes/how-to-use-aws-cdk-stage-and-when-to-choose-static-vs-dynamic-stack-creation-35h|Static vs Dynamic Stack Creation}
49
+ * Order matters: `ResourcePrefixAspect` runs first (priority 100) so that
50
+ * resource names are finalised before the versioning aspect reads them.
51
+ */
52
+ const commonAspectsWithOptions: Array<{ aspect: IAspect; options: AspectOptions }> = [
53
+ {
54
+ aspect: new ResourcePrefixAspect({ prefix: `${APPLICATION_CONTEXT.NAME}-${stageName}` }),
55
+ options: { priority: 100 },
56
+ },
57
+ {
58
+ aspect: new NodeJsFunctionDefaultsAspect({ runtime: Runtime.NODEJS_<%= nodeRuntime %> }),
59
+ options: { priority: AspectPriority.MUTATING },
60
+ },
61
+ { aspect: new StepFunctionsDefaultsAspect(), options: { priority: AspectPriority.MUTATING } },
62
+ { aspect: new MicroserviceChecks(), options: { priority: AspectPriority.READONLY } },
63
+ ];
64
+
65
+ /**
66
+ * Apply common aspects to the stage, then add stage-specific aspects.
49
67
  *
50
68
  * @remarks
51
- * Aspects visit all constructs in the tree after synthesis and can modify them
52
- * (e.g., applying log retention policies, enabling tracing).
69
+ * Aspects visit every construct in the tree after synthesis and may mutate them
70
+ * (e.g. setting log retention, enabling X-Ray tracing).
71
+ *
72
+ * Stage-specific behaviour:
73
+ * - `prd`: long log retention (2 years) for compliance; Lambda and Step Function
74
+ * versioning enabled for zero-downtime deployments.
75
+ * - `stg`: medium log retention.
76
+ * - all others: short log retention.
53
77
  */
78
+ commonAspectsWithOptions.forEach(({ aspect, options }) => {
79
+ Aspects.of(stage).add(aspect, options);
80
+ });
54
81
 
55
- const dev = new ApplicationStage(app, STAGE_NAMES.DEV);
56
- Aspects.of(dev).add(new LogGroupDefaultsAspect({ duration: 'SHORT' }));
82
+ const defaultAspectsOptions: AspectOptions = { priority: AspectPriority.MUTATING };
83
+ let duration: 'SHORT' | 'MEDIUM' | 'LONG' = 'SHORT';
57
84
 
58
- const stg = new ApplicationStage(app, STAGE_NAMES.STG);
59
- Aspects.of(stg).add(new LogGroupDefaultsAspect({ duration: 'MEDIUM' }));
85
+ switch (stageName) {
86
+ case 'prd':
87
+ Aspects.of(stage).add(new LambdaAndStepFunctionVersioningAspect(), defaultAspectsOptions);
88
+ duration = 'LONG';
89
+ break;
90
+ case 'stg':
91
+ duration = 'MEDIUM';
92
+ break;
93
+ default:
94
+ duration = 'SHORT';
95
+ }
60
96
 
61
- /**
62
- * Production Stage
63
- * - Long log retention (2 years) for compliance and debugging
64
- * - Resources retained on stack deletion
65
- * - Lambda functions and Step Functions are automatically versioned, enabled for zero-downtime deployments
66
- */
67
- const prd = new ApplicationStage(app, STAGE_NAMES.PRD);
68
- Aspects.of(prd).add(new LogGroupDefaultsAspect({ duration: 'LONG' }));
69
- Aspects.of(prd).add(new LambdaAndStepFunctionVersioningAspect());
97
+ Aspects.of(stage).add(new LogGroupDefaultsAspect({ duration }), defaultAspectsOptions);
98
+ Aspects.of(stage).add(new DynamoDbDefaultsAspect({ duration }), defaultAspectsOptions);
99
+ Aspects.of(stage).add(new S3DefaultsAspect({ duration }), defaultAspectsOptions);
@@ -1,5 +1,5 @@
1
1
  {
2
- "app": "yarn tsx bin/main.ts",
2
+ "app": "npx tsx bin/main.ts",
3
3
  "watch": {
4
4
  "include": ["**"],
5
5
  "exclude": [
@@ -1,5 +1,5 @@
1
1
  import { SharedInfraStack } from '@libs/infra';
2
- import { Stage, Tags, type StageProps } from 'aws-cdk-lib';
2
+ import { Stage, type StageProps } from 'aws-cdk-lib';
3
3
  import type { Construct } from 'constructs';
4
4
 
5
5
  /**
@@ -15,7 +15,6 @@ import type { Construct } from 'constructs';
15
15
  export class ApplicationStage extends Stage {
16
16
  constructor(scope: Construct, id: string, props?: StageProps) {
17
17
  super(scope, id, props);
18
- Tags.of(this).add('STAGE', id);
19
18
 
20
19
  const sharedInfra = new SharedInfraStack(this, 'shared-infra', props);
21
20
 
@@ -14,14 +14,28 @@
14
14
  "color": true,
15
15
  "command": "cdk",
16
16
  "cwd": "{projectRoot}"
17
- },
18
- "dependsOn": [
19
- {
20
- "target": "build",
21
- "params": "forward",
22
- "projects": "@services/*"
17
+ }
18
+ },
19
+ "pg": {
20
+ "executor": "nx:run-commands",
21
+ "configurations": {
22
+ "synth": {
23
+ "command": "cdk synth"
24
+ },
25
+ "deploy": {
26
+ "command": "cdk deploy"
27
+ },
28
+ "diff": {
29
+ "command": "cdk diff"
30
+ },
31
+ "destroy": {
32
+ "command": "cdk destroy"
23
33
  }
24
- ]
34
+ },
35
+ "options": {
36
+ "color": true,
37
+ "cwd": "{projectRoot}"
38
+ }
25
39
  }
26
40
  }
27
41
  }
@@ -24,7 +24,6 @@ const eslintBaseConfig = [
24
24
  'error',
25
25
  {
26
26
  allow: ['^.*/eslint(\\.base)?\\.config\\.[cm]?[jt]s$'],
27
- enforceBuildableLibDependency: true,
28
27
  depConstraints: [
29
28
  {
30
29
  sourceTag: 'scope:application',
@@ -56,6 +56,6 @@ Resources that are used by multiple services are created once in the shared infr
56
56
 
57
57
  The `SharedInfraStack` must be instantiated within a CDK Stage to properly resolve the stage name for resource naming and tagging.
58
58
 
59
- ### Application Context
59
+ ### Resource Naming
60
60
 
61
- Requires the `NAME` context variable to be set for proper resource naming conventions.
61
+ Resource prefixes are automatically applied via a CDK aspect, ensuring consistent naming conventions across all resources without manual configuration.
@@ -1,44 +1,48 @@
1
- import { Stack, Stage, type StackProps } from 'aws-cdk-lib';
1
+ import { Stack, Stage, StackProps, Tags } from 'aws-cdk-lib';
2
2
  import { Secret } from 'aws-cdk-lib/aws-secretsmanager';
3
- import { StringParameter } from 'aws-cdk-lib/aws-ssm';
4
- import type { Construct } from 'constructs';
3
+ import { IStringParameter, StringParameter } from 'aws-cdk-lib/aws-ssm';
4
+ import { Construct } from 'constructs';
5
5
 
6
+ // EXAMPLE ONLY: The following interface, class, and all related resource references
7
+ // (eCommerceBaseUrl, eCommerceCredentials, etc.) are illustrative examples.
8
+ // Replace them with your own resources and props that match your actual infrastructure.
6
9
  export interface SharedInfraProps {
7
- eCommerceBaseUrl: string;
8
- eCommerceCredentials: Secret;
10
+ // eCommerceBaseUrl: IStringParameter;
11
+ // eCommerceCredentials: Secret;
9
12
  }
10
13
 
11
14
  export class SharedInfraStack extends Stack {
12
- readonly eCommerceBaseUrl: string;
13
- readonly eCommerceCredentials: Secret;
15
+ // EXAMPLE: Replace these properties with your own shared resources
16
+ // readonly eCommerceBaseUrl: IStringParameter;
17
+ // readonly eCommerceCredentials: Secret;
14
18
 
15
19
  constructor(scope: Construct, id: string, props?: StackProps) {
16
20
  super(scope, id, props);
17
21
 
18
- const STAGE = Stage.of(this)?.stageName;
19
- if (!STAGE) {
22
+ if (!Stage.of(this)?.stageName) {
20
23
  throw new Error('This construct must be used within a CDK Stage');
21
24
  }
22
25
 
23
- const applicationName = this.node.tryGetContext('NAME');
26
+ Tags.of(this).add('SERVICE', id);
24
27
 
25
- this.eCommerceBaseUrl = StringParameter.fromStringParameterName(
26
- this,
27
- 'ECommerceBaseUrl',
28
- `/${applicationName}/e-commerce/base-url`
29
- ).stringValue;
28
+ // EXAMPLE: Replace with your own SSM parameter lookup
29
+ // this.eCommerceBaseUrl = StringParameter.fromStringParameterName(
30
+ // this,
31
+ // 'ECommerceBaseUrl',
32
+ // `/e-commerce/base-url`
33
+ // );
30
34
 
31
- // Create secrets once in the shared stack
32
- this.eCommerceCredentials = new Secret(this, 'ECommerceCredentials', {
33
- secretName: `${applicationName}/e-commerce-credentials`,
34
- description: 'E-Commerce API credentials shared across services',
35
- });
35
+ // EXAMPLE: Replace with your own secrets/resources
36
+ // this.eCommerceCredentials = new Secret(this, 'ECommerceCredentials', {
37
+ // description: 'E-Commerce API credentials shared across services',
38
+ // });
36
39
  }
37
40
 
41
+ // EXAMPLE: Update getProps() to return your own resource props
38
42
  getProps(): SharedInfraProps {
39
43
  return {
40
- eCommerceBaseUrl: this.eCommerceBaseUrl,
41
- eCommerceCredentials: this.eCommerceCredentials,
44
+ // eCommerceBaseUrl: this.eCommerceBaseUrl,
45
+ // eCommerceCredentials: this.eCommerceCredentials,
42
46
  };
43
47
  }
44
48
  }
@@ -27,11 +27,10 @@ interface Props extends StackProps, SharedInfraProps {
27
27
  * Use 'resolve' helpers below when referencing lambda handlers, step function files etc.
28
28
  */
29
29
  export class <%= stack %> extends Stack {
30
- constructor(scope: Construct, id: Id, props?: Props) {
30
+ constructor(scope: Construct, id: Id, props: Props) {
31
31
  super(scope, id, props);
32
32
 
33
- const STAGE = Stage.of(this)?.stageName;
34
- if (!STAGE) {
33
+ if (!Stage.of(this)?.stageName) {
35
34
  throw new Error('This construct must be used within a CDK Stage');
36
35
  }
37
36
 
@@ -1,83 +0,0 @@
1
- export declare const PACKAGE_JSON: {
2
- readonly author: "Aligent";
3
- readonly private: true;
4
- readonly license: "MIT";
5
- readonly type: "module";
6
- readonly scripts: {
7
- readonly test: "nx affected -t test --coverage";
8
- readonly 'test:all': "nx run-many -t test --coverage";
9
- readonly lint: "nx affected -t lint";
10
- readonly 'lint:all': "nx run-many -t lint";
11
- readonly typecheck: "nx affected -t typecheck";
12
- readonly 'typecheck:all': "nx run-many -t typecheck";
13
- readonly postinstall: "[ -d .git ] && git config core.hooksPath '.git-hooks' && chmod +x .git-hooks/* || true";
14
- readonly 'pg:synth': "nx run application:cdk synth 'dev/**' --exclusively --bundle-mode=dev --profile=playground";
15
- readonly 'pg:deploy': "nx run application:cdk deploy 'dev/**' --method=direct --require-approval=never --exclusively --bundle-mode=dev --profile=playground";
16
- readonly 'pg:destroy': "nx run application:cdk destroy 'dev/**' --profile playground";
17
- readonly audit: "nx run-many -t lint typecheck test --configuration coverage --skip-nx-cache";
18
- };
19
- readonly dependencies: {
20
- readonly '@aligent/microservice-util-lib': "^1.2.0";
21
- };
22
- readonly devDependencies: {
23
- readonly '@aligent/cdk-aspects': "^0.2.0";
24
- readonly '@aligent/cdk-step-function-from-file': "^0.3.2";
25
- readonly '@aligent/nx-openapi': "^1.0.0";
26
- readonly '@aligent/ts-code-standards': "^4.1.0";
27
- readonly '@nx/eslint': "22.1.3";
28
- readonly '@nx/eslint-plugin': "22.1.3";
29
- readonly '@nx/js': "22.1.3";
30
- readonly '@nx/vitest': "22.1.3";
31
- readonly '@nx/workspace': "22.1.3";
32
- readonly '@swc-node/register': "^1.10.10";
33
- readonly '@swc/core': "^1.13.3";
34
- readonly '@swc/helpers': "^0.5.17";
35
- readonly '@types/aws-lambda': "^8.10.152";
36
- readonly '@types/node': "^22.17.0";
37
- readonly '@typescript-eslint/eslint-plugin': "8.44.0";
38
- readonly '@typescript-eslint/parser': "8.44.0";
39
- readonly '@vitest/coverage-v8': "^3.2.4";
40
- readonly '@vitest/ui': "^3.2.4";
41
- readonly 'aws-cdk': "^2.1033.0";
42
- readonly 'aws-cdk-lib': "^2.235.1";
43
- readonly 'cdk-nag': "^2.37.55";
44
- readonly constructs: "^10.4.3";
45
- readonly eslint: "^9.32.0";
46
- readonly 'eslint-config-prettier': "^10.1.8";
47
- readonly 'eslint-plugin-import': "^2.32.0";
48
- readonly 'fast-glob': "^3.3.3";
49
- readonly jiti: "2.5.1";
50
- readonly 'jsonc-eslint-parser': "^2.4.0";
51
- readonly nx: "22.1.3";
52
- readonly prettier: "^3.6.2";
53
- readonly rolldown: "1.0.0-rc.1";
54
- readonly 'store-parameters': "^1.1.3";
55
- readonly tslib: "^2.8.1";
56
- readonly tsx: "^4.21.0";
57
- readonly typescript: "~5.9.2";
58
- readonly vite: "^7.2.6";
59
- readonly vitest: "^3.2.4";
60
- };
61
- readonly nx: {
62
- readonly includedScripts: readonly [];
63
- readonly targets: {
64
- readonly parameters: {
65
- readonly executor: "nx:run-commands";
66
- readonly options: {
67
- readonly color: true;
68
- readonly cwd: "parameters";
69
- };
70
- readonly configurations: {
71
- readonly import: {
72
- readonly command: "store-parameters import {args.file} --delimiter=|";
73
- };
74
- readonly export: {
75
- readonly command: "store-parameters export {args.file} --path={args.path} --delimiter=|";
76
- };
77
- };
78
- };
79
- };
80
- };
81
- readonly workspaces: readonly ["application", "clients", "libs/*", "services/*"];
82
- readonly packageManager: "yarn@4.12.0+sha512.f45ab632439a67f8bc759bf32ead036a1f413287b9042726b7cc4818b7b49e14e9423ba49b18f9e06ea4941c1ad062385b1d8760a8d5091a1a31e5f6219afca8";
83
- };
@@ -1,82 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PACKAGE_JSON = void 0;
4
- exports.PACKAGE_JSON = {
5
- author: 'Aligent',
6
- private: true,
7
- license: 'MIT',
8
- type: 'module',
9
- scripts: {
10
- test: 'nx affected -t test --coverage',
11
- 'test:all': 'nx run-many -t test --coverage',
12
- lint: 'nx affected -t lint',
13
- 'lint:all': 'nx run-many -t lint',
14
- typecheck: 'nx affected -t typecheck',
15
- 'typecheck:all': 'nx run-many -t typecheck',
16
- postinstall: `[ -d .git ] && git config core.hooksPath '.git-hooks' && chmod +x .git-hooks/* || true`,
17
- 'pg:synth': `nx run application:cdk synth 'dev/**' --exclusively --bundle-mode=dev --profile=playground`,
18
- 'pg:deploy': `nx run application:cdk deploy 'dev/**' --method=direct --require-approval=never --exclusively --bundle-mode=dev --profile=playground`,
19
- 'pg:destroy': "nx run application:cdk destroy 'dev/**' --profile playground",
20
- audit: 'nx run-many -t lint typecheck test --configuration coverage --skip-nx-cache',
21
- },
22
- dependencies: {
23
- '@aligent/microservice-util-lib': '^1.2.0',
24
- },
25
- devDependencies: {
26
- '@aligent/cdk-aspects': '^0.2.0',
27
- '@aligent/cdk-step-function-from-file': '^0.3.2',
28
- '@aligent/nx-openapi': '^1.0.0',
29
- '@aligent/ts-code-standards': '^4.1.0',
30
- '@nx/eslint': '22.1.3',
31
- '@nx/eslint-plugin': '22.1.3',
32
- '@nx/js': '22.1.3',
33
- '@nx/vitest': '22.1.3',
34
- '@nx/workspace': '22.1.3',
35
- '@swc-node/register': '^1.10.10',
36
- '@swc/core': '^1.13.3',
37
- '@swc/helpers': '^0.5.17',
38
- '@types/aws-lambda': '^8.10.152',
39
- '@types/node': '^22.17.0',
40
- '@typescript-eslint/eslint-plugin': '8.44.0',
41
- '@typescript-eslint/parser': '8.44.0',
42
- '@vitest/coverage-v8': '^3.2.4',
43
- '@vitest/ui': '^3.2.4',
44
- 'aws-cdk': '^2.1033.0',
45
- 'aws-cdk-lib': '^2.235.1',
46
- 'cdk-nag': '^2.37.55',
47
- constructs: '^10.4.3',
48
- eslint: '^9.32.0',
49
- 'eslint-config-prettier': '^10.1.8',
50
- 'eslint-plugin-import': '^2.32.0',
51
- 'fast-glob': '^3.3.3',
52
- jiti: '2.5.1',
53
- 'jsonc-eslint-parser': '^2.4.0',
54
- nx: '22.1.3',
55
- prettier: '^3.6.2',
56
- // FIXME: [MI-251] Rolldown is still in RC. We pin the version and can upgrade with precaution.
57
- rolldown: '1.0.0-rc.1',
58
- 'store-parameters': '^1.1.3',
59
- tslib: '^2.8.1',
60
- tsx: '^4.21.0',
61
- typescript: '~5.9.2',
62
- vite: '^7.2.6',
63
- vitest: '^3.2.4',
64
- },
65
- nx: {
66
- includedScripts: [],
67
- targets: {
68
- parameters: {
69
- executor: 'nx:run-commands',
70
- options: { color: true, cwd: 'parameters' },
71
- configurations: {
72
- import: { command: 'store-parameters import {args.file} --delimiter=|' },
73
- export: {
74
- command: 'store-parameters export {args.file} --path={args.path} --delimiter=|',
75
- },
76
- },
77
- },
78
- },
79
- },
80
- workspaces: ['application', 'clients', 'libs/*', 'services/*'],
81
- packageManager: 'yarn@4.12.0+sha512.f45ab632439a67f8bc759bf32ead036a1f413287b9042726b7cc4818b7b49e14e9423ba49b18f9e06ea4941c1ad062385b1d8760a8d5091a1a31e5f6219afca8',
82
- };