@aws/nx-plugin 1.0.0-rc.85 → 1.0.0-rc.87

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 (74) hide show
  1. package/migrations.json +11 -1
  2. package/package.json +1 -1
  3. package/src/infra/app/__snapshots__/generator.spec.ts.snap +34 -8
  4. package/src/infra/app/files/app/README.md.template +14 -0
  5. package/src/infra/app/generator.js +15 -2
  6. package/src/infra/app/generator.js.map +1 -1
  7. package/src/mcp-server/tools/general-guidance.js +1 -1
  8. package/src/mcp-server/tools/general-guidance.js.map +1 -1
  9. package/src/migrations/latest/add-destroy-sandbox-target/metadata.json +3 -0
  10. package/src/migrations/latest/add-destroy-sandbox-target/migration.d.ts +6 -0
  11. package/src/migrations/latest/add-destroy-sandbox-target/migration.js +150 -0
  12. package/src/migrations/latest/add-destroy-sandbox-target/migration.js.map +1 -0
  13. package/src/migrations/latest/terraform-rdb-proxy-iam-connect-grant/__snapshots__/migration.spec.ts.snap +23 -0
  14. package/src/migrations/latest/terraform-rdb-proxy-iam-connect-grant/metadata.json +3 -0
  15. package/src/migrations/latest/terraform-rdb-proxy-iam-connect-grant/migration.d.ts +6 -0
  16. package/src/migrations/latest/terraform-rdb-proxy-iam-connect-grant/migration.js +88 -0
  17. package/src/migrations/latest/terraform-rdb-proxy-iam-connect-grant/migration.js.map +1 -0
  18. package/src/open-api/ts-client/generator.js.map +1 -1
  19. package/src/open-api/ts-hooks/generator.spec.tsx +1 -1
  20. package/src/py/project/generator.js +5 -5
  21. package/src/py/project/generator.js.map +1 -1
  22. package/src/py/rdb/__snapshots__/generator.spec.ts.snap +21 -9
  23. package/src/py/rdb/files/__name__/utils.py.template +17 -1
  24. package/src/smithy/ts/api/__snapshots__/generator.spec.ts.snap +8 -8
  25. package/src/trpc/backend/__snapshots__/generator.spec.ts.snap +16 -16
  26. package/src/ts/dcr-proxy/__snapshots__/generator.spec.ts.snap +3 -3
  27. package/src/ts/lambda-function/__snapshots__/generator.spec.ts.snap +3 -3
  28. package/src/ts/rdb/__snapshots__/generator.spec.ts.snap +132 -261
  29. package/src/ts/rdb/files/src/utils.ts.template +28 -1
  30. package/src/ts/react-website/cognito-auth/generator.js +1 -1
  31. package/src/ts/react-website/cognito-auth/generator.js.map +1 -1
  32. package/src/ts/react-website/runtime-config/generator.js +1 -1
  33. package/src/ts/react-website/runtime-config/generator.js.map +1 -1
  34. package/src/utils/agent-core-constructs/agent-core-constructs.js +3 -2
  35. package/src/utils/agent-core-constructs/agent-core-constructs.js.map +1 -1
  36. package/src/utils/agent-core-constructs/files/cdk/core/agentcore-gateway/agentcore-gateway.ts.template +1 -1
  37. package/src/utils/api-constructs/api-constructs.js +7 -4
  38. package/src/utils/api-constructs/api-constructs.js.map +1 -1
  39. package/src/utils/api-constructs/files/cdk/app/apis/http/__apiNameKebabCase__.ts.template +4 -4
  40. package/src/utils/api-constructs/files/cdk/app/apis/rest/__apiNameKebabCase__.ts.template +4 -4
  41. package/src/utils/api-constructs/files/cdk/core/api/rest/api-gateway-account.ts.template +1 -1
  42. package/src/utils/api-constructs/files/terraform/app/apis/http/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +4 -4
  43. package/src/utils/api-constructs/files/terraform/app/apis/rest/__apiNameKebabCase__/__apiNameKebabCase__.tf.template +4 -4
  44. package/src/utils/ast.d.ts +13 -0
  45. package/src/utils/ast.js +36 -0
  46. package/src/utils/ast.js.map +1 -1
  47. package/src/utils/bundle/bundle.js +5 -2
  48. package/src/utils/bundle/bundle.js.map +1 -1
  49. package/src/utils/dcr-proxy-constructs/dcr-proxy-constructs.js +5 -3
  50. package/src/utils/dcr-proxy-constructs/dcr-proxy-constructs.js.map +1 -1
  51. package/src/utils/dcr-proxy-constructs/files/cdk/app/dcr-proxies/__nameKebabCase__/__nameKebabCase__.ts.template +1 -1
  52. package/src/utils/dcr-proxy-constructs/files/terraform/app/dcr-proxies/__nameKebabCase__/__nameKebabCase__.tf.template +2 -2
  53. package/src/utils/files/common/scripts/src/infra/stage-credentials/cdk-command.ts.template +7 -3
  54. package/src/utils/function-constructs/function-constructs.js +3 -3
  55. package/src/utils/function-constructs/function-constructs.js.map +1 -1
  56. package/src/utils/lambda-runtime-resolution.d.ts +74 -0
  57. package/src/utils/lambda-runtime-resolution.js +97 -0
  58. package/src/utils/lambda-runtime-resolution.js.map +1 -0
  59. package/src/utils/rdb-constructs/files/cdk/app/dbs/__nameKebabCase__.ts.template +3 -3
  60. package/src/utils/rdb-constructs/files/terraform/app/dbs/__nameKebabCase__/__nameKebabCase__.tf.template +10 -29
  61. package/src/utils/rdb-constructs/files/terraform/core/rdb/aurora/aurora.tf.template +28 -116
  62. package/src/utils/rdb-constructs/rdb-constructs.js +5 -3
  63. package/src/utils/rdb-constructs/rdb-constructs.js.map +1 -1
  64. package/src/utils/version-upgrade-migration/sync-lambda-runtimes.d.ts +28 -0
  65. package/src/utils/version-upgrade-migration/sync-lambda-runtimes.js +148 -0
  66. package/src/utils/version-upgrade-migration/sync-lambda-runtimes.js.map +1 -0
  67. package/src/utils/version-upgrade-migration/sync-python-language-version.d.ts +31 -0
  68. package/src/utils/version-upgrade-migration/sync-python-language-version.js +110 -0
  69. package/src/utils/version-upgrade-migration/sync-python-language-version.js.map +1 -0
  70. package/src/utils/version-upgrade-migration/sync-vended-versions.js +36 -7
  71. package/src/utils/version-upgrade-migration/sync-vended-versions.js.map +1 -1
  72. package/src/utils/versions.d.ts +60 -0
  73. package/src/utils/versions.js +51 -0
  74. package/src/utils/versions.js.map +1 -1
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */ import { joinPathFragments } from "@nx/devkit";
5
+ import { applyGritQL, matchGritQL } from "../../../utils/ast.js";
6
+ import { formatFilesInSubtree } from "../../../utils/format.js";
7
+ import { PACKAGES_DIR, SHARED_TERRAFORM_DIR } from "../../../utils/shared-constructs-constants.js";
8
+ /**
9
+ * Give the RDS Proxy role in every vended Terraform Aurora app module the
10
+ * `rds-db:connect` grant for the application database user.
11
+ *
12
+ * The proxy is created with `default_auth_scheme = "IAM_AUTH"` and no secrets
13
+ * registered, so `rds-db:connect` on the application user is the only
14
+ * permission it needs to reach the cluster. MySQL modules instead granted the
15
+ * role `secretsmanager:GetSecretValue` on the admin credentials secret plus
16
+ * `kms:Decrypt` — credentials the proxy never uses, and a far wider grant than
17
+ * connecting as the application user.
18
+ *
19
+ * These files are generated with `KeepExisting`, so an upgraded MySQL workspace
20
+ * keeps the admin secret grant until this runs. Diverged files are left
21
+ * untouched and reported via `nextSteps`.
22
+ */ const TERRAFORM_DBS_DIR = `${PACKAGES_DIR}/${SHARED_TERRAFORM_DIR}/src/app/dbs`;
23
+ const hcl = (pattern)=>`language hcl\n${pattern}`;
24
+ const CONNECT_STATEMENT_TEXT = [
25
+ '{',
26
+ ' Effect = "Allow"',
27
+ ' Action = ["rds-db:connect"]',
28
+ ' Resource = [',
29
+ ' "arn:aws:rds-db:${data.aws_region.current.region}:${data.aws_caller_identity.current.account_id}:dbuser:${module.aurora.cluster_resource_id}/${local.database_runtime_user}"',
30
+ ' ]',
31
+ ' }'
32
+ ].join('\n');
33
+ const divergedMessage = (filePath)=>`${filePath}: the aws_iam_role_policy.proxy_db_user_connect policy has diverged from the generated shape - left untouched. The RDS Proxy authenticates to the cluster with IAM, so replace any secretsmanager:GetSecretValue / kms:Decrypt statements on the proxy role with a single rds-db:connect statement for local.database_runtime_user (see the ts#rdb generator's database app template).`;
34
+ const proxyPolicyBlock = (body)=>hcl(`\`resource "aws_iam_role_policy" "proxy_db_user_connect" { ${body} }\``);
35
+ const migrateModule = async (tree, filePath, nextSteps)=>{
36
+ if (!await matchGritQL(tree, filePath, proxyPolicyBlock('$_'))) {
37
+ return; // No proxy policy in this module - nothing this migration owns.
38
+ }
39
+ if (await matchGritQL(tree, filePath, proxyPolicyBlock('$props') + ' where { $props <: contains `"rds-db:connect"` }')) {
40
+ return; // Already granted.
41
+ }
42
+ // The whole statement list is replaced, so the policy must still hold exactly
43
+ // the two statements the generator vended and nothing the user has added.
44
+ const secretStatements = hcl([
45
+ '`Statement = [',
46
+ ' {',
47
+ ' Effect = "Allow"',
48
+ ' Action = [',
49
+ ' "secretsmanager:GetSecretValue"',
50
+ ' ]',
51
+ ' Resource = [module.aurora.secret_arn]',
52
+ ' },',
53
+ ' {',
54
+ ' Effect = "Allow"',
55
+ ' Action = [',
56
+ ' "kms:Decrypt"',
57
+ ' ]',
58
+ ' Resource = [module.aurora.kms_key_arn]',
59
+ ' }',
60
+ ' ]`'
61
+ ].join('\n'));
62
+ if (!await matchGritQL(tree, filePath, secretStatements)) {
63
+ nextSteps.push(divergedMessage(filePath));
64
+ return;
65
+ }
66
+ await applyGritQL(tree, filePath, `${secretStatements} => \`Statement = [\n ${CONNECT_STATEMENT_TEXT}\n ]\``);
67
+ };
68
+ export default async function migration(tree) {
69
+ const nextSteps = [];
70
+ if (!tree.exists(TERRAFORM_DBS_DIR)) {
71
+ return {
72
+ nextSteps
73
+ }; // This workspace has no Terraform database modules.
74
+ }
75
+ for (const dirName of tree.children(TERRAFORM_DBS_DIR)){
76
+ const filePath = joinPathFragments(TERRAFORM_DBS_DIR, dirName, `${dirName}.tf`);
77
+ if (!tree.exists(filePath)) {
78
+ continue; // Not a database app module directory.
79
+ }
80
+ await migrateModule(tree, filePath, nextSteps);
81
+ }
82
+ await formatFilesInSubtree(tree);
83
+ return {
84
+ nextSteps
85
+ };
86
+ }
87
+
88
+ //# sourceMappingURL=migration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/latest/terraform-rdb-proxy-iam-connect-grant/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n joinPathFragments,\n type MigrationReturnObject,\n type Tree,\n} from '@nx/devkit';\nimport { applyGritQL, matchGritQL } from '../../../utils/ast.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\nimport {\n PACKAGES_DIR,\n SHARED_TERRAFORM_DIR,\n} from '../../../utils/shared-constructs-constants.js';\n\n/**\n * Give the RDS Proxy role in every vended Terraform Aurora app module the\n * `rds-db:connect` grant for the application database user.\n *\n * The proxy is created with `default_auth_scheme = \"IAM_AUTH\"` and no secrets\n * registered, so `rds-db:connect` on the application user is the only\n * permission it needs to reach the cluster. MySQL modules instead granted the\n * role `secretsmanager:GetSecretValue` on the admin credentials secret plus\n * `kms:Decrypt` — credentials the proxy never uses, and a far wider grant than\n * connecting as the application user.\n *\n * These files are generated with `KeepExisting`, so an upgraded MySQL workspace\n * keeps the admin secret grant until this runs. Diverged files are left\n * untouched and reported via `nextSteps`.\n */\n\nconst TERRAFORM_DBS_DIR = `${PACKAGES_DIR}/${SHARED_TERRAFORM_DIR}/src/app/dbs`;\n\nconst hcl = (pattern: string) => `language hcl\\n${pattern}`;\n\nconst CONNECT_STATEMENT_TEXT = [\n '{',\n ' Effect = \"Allow\"',\n ' Action = [\"rds-db:connect\"]',\n ' Resource = [',\n ' \"arn:aws:rds-db:${data.aws_region.current.region}:${data.aws_caller_identity.current.account_id}:dbuser:${module.aurora.cluster_resource_id}/${local.database_runtime_user}\"',\n ' ]',\n ' }',\n].join('\\n');\n\nconst divergedMessage = (filePath: string) =>\n `${filePath}: the aws_iam_role_policy.proxy_db_user_connect policy has diverged from the generated shape - left untouched. The RDS Proxy authenticates to the cluster with IAM, so replace any secretsmanager:GetSecretValue / kms:Decrypt statements on the proxy role with a single rds-db:connect statement for local.database_runtime_user (see the ts#rdb generator's database app template).`;\n\nconst proxyPolicyBlock = (body: string) =>\n hcl(`\\`resource \"aws_iam_role_policy\" \"proxy_db_user_connect\" { ${body} }\\``);\n\nconst migrateModule = async (\n tree: Tree,\n filePath: string,\n nextSteps: string[],\n): Promise<void> => {\n if (!(await matchGritQL(tree, filePath, proxyPolicyBlock('$_')))) {\n return; // No proxy policy in this module - nothing this migration owns.\n }\n\n if (\n await matchGritQL(\n tree,\n filePath,\n proxyPolicyBlock('$props') +\n ' where { $props <: contains `\"rds-db:connect\"` }',\n )\n ) {\n return; // Already granted.\n }\n\n // The whole statement list is replaced, so the policy must still hold exactly\n // the two statements the generator vended and nothing the user has added.\n const secretStatements = hcl(\n [\n '`Statement = [',\n ' {',\n ' Effect = \"Allow\"',\n ' Action = [',\n ' \"secretsmanager:GetSecretValue\"',\n ' ]',\n ' Resource = [module.aurora.secret_arn]',\n ' },',\n ' {',\n ' Effect = \"Allow\"',\n ' Action = [',\n ' \"kms:Decrypt\"',\n ' ]',\n ' Resource = [module.aurora.kms_key_arn]',\n ' }',\n ' ]`',\n ].join('\\n'),\n );\n\n if (!(await matchGritQL(tree, filePath, secretStatements))) {\n nextSteps.push(divergedMessage(filePath));\n return;\n }\n\n await applyGritQL(\n tree,\n filePath,\n `${secretStatements} => \\`Statement = [\\n ${CONNECT_STATEMENT_TEXT}\\n ]\\``,\n );\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n if (!tree.exists(TERRAFORM_DBS_DIR)) {\n return { nextSteps }; // This workspace has no Terraform database modules.\n }\n\n for (const dirName of tree.children(TERRAFORM_DBS_DIR)) {\n const filePath = joinPathFragments(\n TERRAFORM_DBS_DIR,\n dirName,\n `${dirName}.tf`,\n );\n if (!tree.exists(filePath)) {\n continue; // Not a database app module directory.\n }\n\n await migrateModule(tree, filePath, nextSteps);\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["joinPathFragments","applyGritQL","matchGritQL","formatFilesInSubtree","PACKAGES_DIR","SHARED_TERRAFORM_DIR","TERRAFORM_DBS_DIR","hcl","pattern","CONNECT_STATEMENT_TEXT","join","divergedMessage","filePath","proxyPolicyBlock","body","migrateModule","tree","nextSteps","secretStatements","push","migration","exists","dirName","children"],"mappings":"AAAA;;;CAGC,GACD,SACEA,iBAAiB,QAGZ,aAAa;AACpB,SAASC,WAAW,EAAEC,WAAW,QAAQ,wBAAwB;AACjE,SAASC,oBAAoB,QAAQ,2BAA2B;AAChE,SACEC,YAAY,EACZC,oBAAoB,QACf,gDAAgD;AAEvD;;;;;;;;;;;;;;CAcC,GAED,MAAMC,oBAAoB,GAAGF,aAAa,CAAC,EAAEC,qBAAqB,YAAY,CAAC;AAE/E,MAAME,MAAM,CAACC,UAAoB,CAAC,cAAc,EAAEA,SAAS;AAE3D,MAAMC,yBAAyB;IAC7B;IACA;IACA;IACA;IACA;IACA;IACA;CACD,CAACC,IAAI,CAAC;AAEP,MAAMC,kBAAkB,CAACC,WACvB,GAAGA,SAAS,sXAAsX,CAAC;AAErY,MAAMC,mBAAmB,CAACC,OACxBP,IAAI,CAAC,2DAA2D,EAAEO,KAAK,IAAI,CAAC;AAE9E,MAAMC,gBAAgB,OACpBC,MACAJ,UACAK;IAEA,IAAI,CAAE,MAAMf,YAAYc,MAAMJ,UAAUC,iBAAiB,QAAS;QAChE,QAAQ,gEAAgE;IAC1E;IAEA,IACE,MAAMX,YACJc,MACAJ,UACAC,iBAAiB,YACf,qDAEJ;QACA,QAAQ,mBAAmB;IAC7B;IAEA,8EAA8E;IAC9E,0EAA0E;IAC1E,MAAMK,mBAAmBX,IACvB;QACE;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;QACA;KACD,CAACG,IAAI,CAAC;IAGT,IAAI,CAAE,MAAMR,YAAYc,MAAMJ,UAAUM,mBAAoB;QAC1DD,UAAUE,IAAI,CAACR,gBAAgBC;QAC/B;IACF;IAEA,MAAMX,YACJe,MACAJ,UACA,GAAGM,iBAAiB,2BAA2B,EAAET,uBAAuB,SAAS,CAAC;AAEtF;AAEA,eAAe,eAAeW,UAC5BJ,IAAU;IAEV,MAAMC,YAAsB,EAAE;IAE9B,IAAI,CAACD,KAAKK,MAAM,CAACf,oBAAoB;QACnC,OAAO;YAAEW;QAAU,GAAG,oDAAoD;IAC5E;IAEA,KAAK,MAAMK,WAAWN,KAAKO,QAAQ,CAACjB,mBAAoB;QACtD,MAAMM,WAAWZ,kBACfM,mBACAgB,SACA,GAAGA,QAAQ,GAAG,CAAC;QAEjB,IAAI,CAACN,KAAKK,MAAM,CAACT,WAAW;YAC1B,UAAU,uCAAuC;QACnD;QAEA,MAAMG,cAAcC,MAAMJ,UAAUK;IACtC;IAEA,MAAMd,qBAAqBa;IAE3B,OAAO;QAAEC;IAAU;AACrB"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../packages/nx-plugin/src/open-api/ts-client/generator.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { generateFiles, logger, type Tree } from '@nx/devkit';\nimport * as path from 'path';\nimport { formatFilesInSubtree } from '../../utils/format.js';\nimport { esmVars } from '../../utils/module-format.js';\nimport { buildOpenApiCodeGenData } from '../utils/codegen-data.js';\nimport type { CodeGenData } from '../utils/codegen-data/types.js';\nimport { parseOpenApiSpec } from '../utils/parse.js';\nimport type { Spec } from '../utils/types.js';\nimport type { OpenApiTsClientGeneratorSchema } from './schema';\n\n/**\n * Generates typescript client from an openapi spec\n */\nexport const openApiTsClientGenerator = async (\n tree: Tree,\n options: OpenApiTsClientGeneratorSchema,\n) => {\n const data = await buildOpenApiCodeGenerationData(\n tree,\n options.openApiSpecPath,\n );\n\n generateOpenApiTsClient(tree, data, options.outputPath);\n\n await formatFilesInSubtree(tree);\n};\n\n/**\n * Build a data structure which can be used to generate code from OpenAPI\n */\nexport const buildOpenApiCodeGenerationData = async (\n tree: Tree,\n specPath: string,\n) => {\n const spec = await parseOpenApiSpec(tree, specPath);\n warnForUnsupportedFeatures(spec);\n return buildOpenApiCodeGenData(spec);\n};\n\n/**\n * Warn for spec features the generated client does not implement, so their\n * omission is never silent.\n */\nconst warnForUnsupportedFeatures = (spec: Spec) => {\n if (Object.keys((spec as { webhooks?: object }).webhooks ?? {}).length > 0) {\n logger.warn(\n 'OpenAPI webhooks are not supported and no client code is generated for them',\n );\n }\n for (const [p, pathItem] of Object.entries(spec.paths ?? {})) {\n for (const [method, op] of Object.entries(pathItem ?? {})) {\n if (typeof op !== 'object' || op === null) continue;\n const operation = op as {\n callbacks?: object;\n servers?: unknown[];\n };\n if (Object.keys(operation.callbacks ?? {}).length > 0) {\n logger.warn(\n `Operation ${method.toUpperCase()} ${p} declares callbacks, which are not supported and are ignored`,\n );\n }\n if ((operation.servers ?? []).length > 0) {\n logger.warn(\n `Operation ${method.toUpperCase()} ${p} declares operation-level servers, which are ignored — the client always uses its configured url`,\n );\n }\n }\n }\n};\n\n/**\n * Generate an OpenAPI typescript client in the target directory\n */\nexport const generateOpenApiTsClient = (\n tree: Tree,\n data: CodeGenData,\n outputPath: string,\n) => {\n generateFiles(tree, path.join(import.meta.dirname, 'files'), outputPath, {\n ...data,\n ...esmVars(tree),\n });\n};\n\nexport default openApiTsClientGenerator;\n"],"names":["generateFiles","logger","path","formatFilesInSubtree","esmVars","buildOpenApiCodeGenData","parseOpenApiSpec","openApiTsClientGenerator","tree","options","data","buildOpenApiCodeGenerationData","openApiSpecPath","generateOpenApiTsClient","outputPath","specPath","spec","warnForUnsupportedFeatures","Object","keys","webhooks","length","warn","p","pathItem","entries","paths","method","op","operation","callbacks","toUpperCase","servers","join","dirname"],"mappings":"AAAA;;;CAGC,GACD,SAASA,aAAa,EAAEC,MAAM,QAAmB,aAAa;AAC9D,YAAYC,UAAU,OAAO;AAC7B,SAASC,oBAAoB,QAAQ,wBAAwB;AAC7D,SAASC,OAAO,QAAQ,+BAA+B;AACvD,SAASC,uBAAuB,QAAQ,2BAA2B;AAEnE,SAASC,gBAAgB,QAAQ,oBAAoB;AAIrD;;CAEC,GACD,OAAO,MAAMC,2BAA2B,OACtCC,MACAC;IAEA,MAAMC,OAAO,MAAMC,+BACjBH,MACAC,QAAQG,eAAe;IAGzBC,wBAAwBL,MAAME,MAAMD,QAAQK,UAAU;IAEtD,MAAMX,qBAAqBK;AAC7B,EAAE;AAEF;;CAEC,GACD,OAAO,MAAMG,iCAAiC,OAC5CH,MACAO;IAEA,MAAMC,OAAO,MAAMV,iBAAiBE,MAAMO;IAC1CE,2BAA2BD;IAC3B,OAAOX,wBAAwBW;AACjC,EAAE;AAEF;;;CAGC,GACD,MAAMC,6BAA6B,CAACD;IAClC,IAAIE,OAAOC,IAAI,CAAC,AAACH,KAA+BI,QAAQ,IAAI,CAAC,GAAGC,MAAM,GAAG,GAAG;QAC1EpB,OAAOqB,IAAI,CACT;IAEJ;IACA,KAAK,MAAM,CAACC,GAAGC,SAAS,IAAIN,OAAOO,OAAO,CAACT,KAAKU,KAAK,IAAI,CAAC,GAAI;QAC5D,KAAK,MAAM,CAACC,QAAQC,GAAG,IAAIV,OAAOO,OAAO,CAACD,YAAY,CAAC,GAAI;YACzD,IAAI,OAAOI,OAAO,YAAYA,OAAO,MAAM;YAC3C,MAAMC,YAAYD;YAIlB,IAAIV,OAAOC,IAAI,CAACU,UAAUC,SAAS,IAAI,CAAC,GAAGT,MAAM,GAAG,GAAG;gBACrDpB,OAAOqB,IAAI,CACT,CAAC,UAAU,EAAEK,OAAOI,WAAW,GAAG,CAAC,EAAER,EAAE,4DAA4D,CAAC;YAExG;YACA,IAAI,AAACM,CAAAA,UAAUG,OAAO,IAAI,EAAE,AAAD,EAAGX,MAAM,GAAG,GAAG;gBACxCpB,OAAOqB,IAAI,CACT,CAAC,UAAU,EAAEK,OAAOI,WAAW,GAAG,CAAC,EAAER,EAAE,gGAAgG,CAAC;YAE5I;QACF;IACF;AACF;AAEA;;CAEC,GACD,OAAO,MAAMV,0BAA0B,CACrCL,MACAE,MACAI;IAEAd,cAAcQ,MAAMN,KAAK+B,IAAI,CAAC,YAAYC,OAAO,EAAE,UAAUpB,YAAY;QACvE,GAAGJ,IAAI;QACP,GAAGN,QAAQI,KAAK;IAClB;AACF,EAAE;AAEF,eAAeD,yBAAyB"}
1
+ {"version":3,"sources":["../../../../../../packages/nx-plugin/src/open-api/ts-client/generator.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { generateFiles, logger, type Tree } from '@nx/devkit';\nimport * as path from 'path';\nimport { formatFilesInSubtree } from '../../utils/format.js';\nimport { esmVars } from '../../utils/module-format.js';\nimport type { CodeGenData } from '../utils/codegen-data/types.js';\nimport { buildOpenApiCodeGenData } from '../utils/codegen-data.js';\nimport { parseOpenApiSpec } from '../utils/parse.js';\nimport type { Spec } from '../utils/types.js';\nimport type { OpenApiTsClientGeneratorSchema } from './schema';\n\n/**\n * Generates typescript client from an openapi spec\n */\nexport const openApiTsClientGenerator = async (\n tree: Tree,\n options: OpenApiTsClientGeneratorSchema,\n) => {\n const data = await buildOpenApiCodeGenerationData(\n tree,\n options.openApiSpecPath,\n );\n\n generateOpenApiTsClient(tree, data, options.outputPath);\n\n await formatFilesInSubtree(tree);\n};\n\n/**\n * Build a data structure which can be used to generate code from OpenAPI\n */\nexport const buildOpenApiCodeGenerationData = async (\n tree: Tree,\n specPath: string,\n) => {\n const spec = await parseOpenApiSpec(tree, specPath);\n warnForUnsupportedFeatures(spec);\n return buildOpenApiCodeGenData(spec);\n};\n\n/**\n * Warn for spec features the generated client does not implement, so their\n * omission is never silent.\n */\nconst warnForUnsupportedFeatures = (spec: Spec) => {\n if (Object.keys((spec as { webhooks?: object }).webhooks ?? {}).length > 0) {\n logger.warn(\n 'OpenAPI webhooks are not supported and no client code is generated for them',\n );\n }\n for (const [p, pathItem] of Object.entries(spec.paths ?? {})) {\n for (const [method, op] of Object.entries(pathItem ?? {})) {\n if (typeof op !== 'object' || op === null) continue;\n const operation = op as {\n callbacks?: object;\n servers?: unknown[];\n };\n if (Object.keys(operation.callbacks ?? {}).length > 0) {\n logger.warn(\n `Operation ${method.toUpperCase()} ${p} declares callbacks, which are not supported and are ignored`,\n );\n }\n if ((operation.servers ?? []).length > 0) {\n logger.warn(\n `Operation ${method.toUpperCase()} ${p} declares operation-level servers, which are ignored — the client always uses its configured url`,\n );\n }\n }\n }\n};\n\n/**\n * Generate an OpenAPI typescript client in the target directory\n */\nexport const generateOpenApiTsClient = (\n tree: Tree,\n data: CodeGenData,\n outputPath: string,\n) => {\n generateFiles(tree, path.join(import.meta.dirname, 'files'), outputPath, {\n ...data,\n ...esmVars(tree),\n });\n};\n\nexport default openApiTsClientGenerator;\n"],"names":["generateFiles","logger","path","formatFilesInSubtree","esmVars","buildOpenApiCodeGenData","parseOpenApiSpec","openApiTsClientGenerator","tree","options","data","buildOpenApiCodeGenerationData","openApiSpecPath","generateOpenApiTsClient","outputPath","specPath","spec","warnForUnsupportedFeatures","Object","keys","webhooks","length","warn","p","pathItem","entries","paths","method","op","operation","callbacks","toUpperCase","servers","join","dirname"],"mappings":"AAAA;;;CAGC,GACD,SAASA,aAAa,EAAEC,MAAM,QAAmB,aAAa;AAC9D,YAAYC,UAAU,OAAO;AAC7B,SAASC,oBAAoB,QAAQ,wBAAwB;AAC7D,SAASC,OAAO,QAAQ,+BAA+B;AAEvD,SAASC,uBAAuB,QAAQ,2BAA2B;AACnE,SAASC,gBAAgB,QAAQ,oBAAoB;AAIrD;;CAEC,GACD,OAAO,MAAMC,2BAA2B,OACtCC,MACAC;IAEA,MAAMC,OAAO,MAAMC,+BACjBH,MACAC,QAAQG,eAAe;IAGzBC,wBAAwBL,MAAME,MAAMD,QAAQK,UAAU;IAEtD,MAAMX,qBAAqBK;AAC7B,EAAE;AAEF;;CAEC,GACD,OAAO,MAAMG,iCAAiC,OAC5CH,MACAO;IAEA,MAAMC,OAAO,MAAMV,iBAAiBE,MAAMO;IAC1CE,2BAA2BD;IAC3B,OAAOX,wBAAwBW;AACjC,EAAE;AAEF;;;CAGC,GACD,MAAMC,6BAA6B,CAACD;IAClC,IAAIE,OAAOC,IAAI,CAAC,AAACH,KAA+BI,QAAQ,IAAI,CAAC,GAAGC,MAAM,GAAG,GAAG;QAC1EpB,OAAOqB,IAAI,CACT;IAEJ;IACA,KAAK,MAAM,CAACC,GAAGC,SAAS,IAAIN,OAAOO,OAAO,CAACT,KAAKU,KAAK,IAAI,CAAC,GAAI;QAC5D,KAAK,MAAM,CAACC,QAAQC,GAAG,IAAIV,OAAOO,OAAO,CAACD,YAAY,CAAC,GAAI;YACzD,IAAI,OAAOI,OAAO,YAAYA,OAAO,MAAM;YAC3C,MAAMC,YAAYD;YAIlB,IAAIV,OAAOC,IAAI,CAACU,UAAUC,SAAS,IAAI,CAAC,GAAGT,MAAM,GAAG,GAAG;gBACrDpB,OAAOqB,IAAI,CACT,CAAC,UAAU,EAAEK,OAAOI,WAAW,GAAG,CAAC,EAAER,EAAE,4DAA4D,CAAC;YAExG;YACA,IAAI,AAACM,CAAAA,UAAUG,OAAO,IAAI,EAAE,AAAD,EAAGX,MAAM,GAAG,GAAG;gBACxCpB,OAAOqB,IAAI,CACT,CAAC,UAAU,EAAEK,OAAOI,WAAW,GAAG,CAAC,EAAER,EAAE,gGAAgG,CAAC;YAE5I;QACF;IACF;AACF;AAEA;;CAEC,GACD,OAAO,MAAMV,0BAA0B,CACrCL,MACAE,MACAI;IAEAd,cAAcQ,MAAMN,KAAK+B,IAAI,CAAC,YAAYC,OAAO,EAAE,UAAUpB,YAAY;QACvE,GAAGJ,IAAI;QACP,GAAGN,QAAQI,KAAK;IAClB;AACF,EAAE;AAEF,eAAeD,yBAAyB"}
@@ -17,8 +17,8 @@ import { fireEvent, render, waitFor } from '@testing-library/react';
17
17
  import type React from 'react';
18
18
  import type { Mock } from 'vitest';
19
19
  import { importTypeScriptModule } from '../../utils/js.js';
20
- import { createTreeUsingTsSolutionSetup } from '../../utils/test.js';
21
20
  import { TypeScriptVerifier } from '../../utils/test/ts.spec.js';
21
+ import { createTreeUsingTsSolutionSetup } from '../../utils/test.js';
22
22
  import { PET_STORE_SPEC } from '../ts-client/generator.petstore.spec.js';
23
23
  import type { Spec } from '../utils/types.js';
24
24
  import { openApiTsHooksGenerator } from './generator.js';
@@ -14,7 +14,7 @@ import { addDependencyToTargetIfNotPresent, addGeneratorMetadata, getGeneratorIn
14
14
  import { migrateToSharedVenvGenerator, uvProjectGenerator } from "../../utils/nxlv-python.js";
15
15
  import { sortObjectKeys } from "../../utils/object.js";
16
16
  import { updateToml } from "../../utils/toml.js";
17
- import { withVersions } from "../../utils/versions.js";
17
+ import { pyenvPythonVersion, pyprojectPythonDependency, withVersions } from "../../utils/versions.js";
18
18
  export const DEPENDENCIES = declareDependencies()({
19
19
  ts: [
20
20
  {
@@ -106,8 +106,8 @@ const PYTHON_TRANSIENT_ARTIFACTS = [
106
106
  autoActivate: true,
107
107
  packageManager: 'uv',
108
108
  moveDevDependencies: false,
109
- pyenvPythonVersion: '3.14.0',
110
- pyprojectPythonDependency: '>=3.14'
109
+ pyenvPythonVersion: pyenvPythonVersion(),
110
+ pyprojectPythonDependency: pyprojectPythonDependency()
111
111
  });
112
112
  }
113
113
  await uvProjectGenerator(tree, {
@@ -124,8 +124,8 @@ const PYTHON_TRANSIENT_ARTIFACTS = [
124
124
  buildBundleLocalDependencies: true,
125
125
  linter: 'ruff',
126
126
  rootPyprojectDependencyGroup: 'main',
127
- pyenvPythonVersion: '3.14.0',
128
- pyprojectPythonDependency: '>=3.14',
127
+ pyenvPythonVersion: pyenvPythonVersion(),
128
+ pyprojectPythonDependency: pyprojectPythonDependency(),
129
129
  projectType: schema.type,
130
130
  projectNameAndRootFormat: 'as-provided',
131
131
  moduleName: normalizedModuleName,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../packages/nx-plugin/src/py/project/generator.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n ensurePackage,\n type GeneratorCallback,\n joinPathFragments,\n readNxJson,\n readProjectConfiguration,\n type Tree,\n updateNxJson,\n updateProjectConfiguration,\n} from '@nx/devkit';\nimport {\n addLicenseCheckToLintTarget,\n ensurePythonLicenseCollector,\n} from '../../license/config.js';\nimport {\n addPyDependencies,\n addTsDependencies,\n} from '../../utils/add-dependencies.js';\nimport { declareDependencies } from '../../utils/declared-dependencies.js';\nimport { updateGitIgnore } from '../../utils/git.js';\nimport { installDependencies } from '../../utils/install.js';\nimport { addGeneratorMetricsIfApplicable } from '../../utils/metrics.js';\nimport { normalizeDistributionName, toSnakeCase } from '../../utils/names.js';\nimport { getNpmScope } from '../../utils/npm-scope.js';\nimport {\n addDependencyToTargetIfNotPresent,\n addGeneratorMetadata,\n getGeneratorInfo,\n type NxGeneratorInfo,\n projectExists,\n} from '../../utils/nx.js';\nimport type { UVPyprojectToml } from '../../utils/nxlv-python.js';\nimport {\n migrateToSharedVenvGenerator,\n uvProjectGenerator,\n} from '../../utils/nxlv-python.js';\nimport { sortObjectKeys } from '../../utils/object.js';\nimport { updateToml } from '../../utils/toml.js';\nimport { withVersions } from '../../utils/versions.js';\nimport type { PyProjectGeneratorSchema } from './schema';\n\nexport const DEPENDENCIES = declareDependencies()({\n ts: [{ name: '@nxlv/python', dev: true, root: true }],\n py: [\n // The linting and type checking tools live in the workspace root pyproject.\n // Pinned to the version generation-time formatting uses, so generated files\n // stay `ruff format --check`-clean across ruff releases.\n { name: 'ruff', group: 'dev', root: true },\n { name: 'ty', group: 'dev', root: true },\n ],\n});\n\nexport const PY_PROJECT_GENERATOR_INFO: NxGeneratorInfo = getGeneratorInfo(\n import.meta.filename,\n);\n\n// Transient artifacts that pytest, coverage and ruff create and remove while\n// the test, typecheck and compile targets run concurrently. `.coverage.*` are\n// the per-process data files pytest-cov writes (`.coverage.<host>.<pid>.<rand>`)\n// before combining them. Both `ty` (which respects .gitignore) and the\n// `@nxlv/python:build` copy (which respects `ignorePaths`) would otherwise fail\n// when they read one of these mid-deletion.\nconst PYTHON_TRANSIENT_ARTIFACTS = [\n '__pycache__',\n '.coverage',\n '.coverage.*',\n '.pytest_cache',\n 'pytest-cache-files-*',\n '.ruff_cache',\n];\n\nexport interface PyProjectDetails {\n /**\n * Fully qualified Nx project id including scope, in dot notation (eg foo.bar).\n * This is the identifier Nx uses (readProjectConfiguration, the project\n * graph, target references) and is intentionally kept dotted.\n */\n readonly fullyQualifiedName: string;\n /**\n * PEP 503 normalised Python distribution name (eg foo-bar). This is the name\n * written to the project's `[project].name`, the root `[tool.uv.sources]`\n * key, and any inter-project dependency string. It is hyphenated (not dotted)\n * so `uv` and `@nxlv/python` can match it: uv writes `uv.lock` keyed by the\n * PEP 503 hyphenated name, and `@nxlv/python`'s dependency inference splits a\n * dotted name on `.` and so would otherwise drop the edge entirely. Keeping\n * the distribution name decoupled from `fullyQualifiedName` is what lets the\n * workspace dependency edge appear in the Nx project graph.\n */\n readonly distributionName: string;\n /**\n * Directory of the library relative to the root\n */\n readonly dir: string;\n /**\n * Module name for the project\n */\n readonly normalizedModuleName: string;\n}\n\n/**\n * Returns details about the Python project to be created\n */\nexport const getPyProjectDetails = (\n tree: Tree,\n schema: {\n name: string;\n directory?: string;\n subDirectory?: string;\n moduleName?: string;\n },\n): PyProjectDetails => {\n const scope = toSnakeCase(getNpmScope(tree));\n const normalizedName = toSnakeCase(schema.name);\n const normalizedModuleName = toSnakeCase(\n schema.moduleName ?? `${scope}_${normalizedName}`,\n );\n const fullyQualifiedName = `${scope}.${normalizedName}`;\n // The Python distribution name is the PEP 503 normalised form of the\n // fully qualified name: hyphenated, never dotted. See PyProjectDetails.\n const distributionName = normalizeDistributionName(fullyQualifiedName);\n // NB: interactive nx generator cli can pass empty string\n const dir = joinPathFragments(\n schema.directory || '.',\n schema.subDirectory || normalizedName,\n );\n return { dir, fullyQualifiedName, distributionName, normalizedModuleName };\n};\n\n/**\n * Generates a Python project\n */\nexport const pyProjectGenerator = async (\n tree: Tree,\n schema: PyProjectGeneratorSchema,\n): Promise<GeneratorCallback> => {\n const { dir, normalizedModuleName, fullyQualifiedName, distributionName } =\n getPyProjectDetails(tree, schema);\n\n addTsDependencies(tree, DEPENDENCIES);\n\n const pythonPlugin = withVersions(DEPENDENCIES, ['@nxlv/python']);\n Object.entries(pythonPlugin).forEach(([name, version]) =>\n ensurePackage(name, version),\n );\n\n const nxJson = readNxJson(tree);\n\n // Only rewrite nx.json when the plugin needs adding, so re-running does not\n // reserialize (and reformat) the file when nothing has changed.\n if (\n !nxJson.plugins?.find((p) =>\n typeof p === 'string'\n ? p === '@nxlv/python'\n : p.plugin === '@nxlv/python',\n )\n ) {\n nxJson.plugins = [\n ...(nxJson.plugins ?? []),\n {\n plugin: '@nxlv/python',\n options: {\n packageManager: 'uv',\n },\n },\n ];\n updateNxJson(tree, nxJson);\n }\n\n // Only scaffold the project when it does not already exist so re-running with\n // the same name does not throw. The rest of the generator still runs to apply\n // any changed options to the existing project.\n if (!projectExists(tree, fullyQualifiedName)) {\n if (!tree.exists('uv.lock')) {\n await migrateToSharedVenvGenerator(tree, {\n autoActivate: true,\n packageManager: 'uv',\n moveDevDependencies: false,\n pyenvPythonVersion: '3.14.0',\n pyprojectPythonDependency: '>=3.14',\n });\n }\n\n await uvProjectGenerator(tree, {\n // The Nx project id (and `uv.workspace.members` etc.) keys off `name`, so\n // keep it dotted. `packageName` is the separate PEP 503 distribution name\n // written to `[project].name` and the root `[tool.uv.sources]` key; it is\n // hyphenated so `@nxlv/python` infers the workspace dependency edge (it\n // splits a dotted name on `.` and would otherwise drop it). This split is\n // the whole fix (see PyProjectDetails.distributionName).\n name: fullyQualifiedName,\n packageName: distributionName,\n publishable: false,\n buildLockedVersions: true,\n buildBundleLocalDependencies: true,\n linter: 'ruff',\n rootPyprojectDependencyGroup: 'main',\n pyenvPythonVersion: '3.14.0',\n pyprojectPythonDependency: '>=3.14',\n projectType: schema.type,\n projectNameAndRootFormat: 'as-provided',\n moduleName: normalizedModuleName,\n directory: dir,\n unitTestRunner: 'pytest',\n codeCoverage: true,\n codeCoverageHtmlReport: true,\n codeCoverageXmlReport: true,\n unitTestHtmlReport: true,\n unitTestJUnitReport: true,\n buildSystem: 'hatch',\n srcDir: false,\n });\n\n // Remove generated hello.py and test_hello.py as they are not needed\n [\n joinPathFragments(dir, normalizedModuleName, 'hello.py'),\n joinPathFragments(dir, 'tests', 'test_hello.py'),\n ].forEach((f) => tree.delete(f));\n\n // Add a placeholder test so pytest doesn't fail with \"no tests collected\"\n tree.write(\n joinPathFragments(dir, 'tests', 'test_noop.py'),\n 'def test_noop():\\n pass\\n',\n );\n }\n\n const outputPath = '{workspaceRoot}/dist/{projectRoot}';\n const buildOutputPath = joinPathFragments(outputPath, 'build');\n const projectConfiguration = readProjectConfiguration(\n tree,\n fullyQualifiedName,\n );\n projectConfiguration.name = fullyQualifiedName;\n // Derive the compile target from the uv build target, and rewrite build to\n // orchestrate the other targets. This only runs on first creation: on re-run\n // build has already been transformed, so re-deriving would corrupt compile\n // and duplicate the build dependencies.\n if (!projectConfiguration.targets.compile) {\n const buildTarget = projectConfiguration.targets.build;\n projectConfiguration.targets.compile = {\n ...buildTarget,\n inputs: ['default', '^production'],\n outputs: [buildOutputPath],\n options: {\n ...buildTarget.options,\n outputPath: buildOutputPath,\n // The build executor copies the project to a temp folder; exclude the\n // executor defaults plus transient artifacts so the copy does not race\n // a concurrent test/typecheck target deleting a file mid-copy.\n ignorePaths: ['.venv', '.tox', 'tests', ...PYTHON_TRANSIENT_ARTIFACTS],\n },\n };\n projectConfiguration.targets.build = {\n inputs: ['default', '^production'],\n dependsOn: [\n 'lint',\n 'compile',\n 'test',\n 'typecheck',\n ...(buildTarget.dependsOn ?? []),\n ],\n options: {\n outputPath,\n },\n };\n }\n projectConfiguration.targets.typecheck = {\n cache: true,\n inputs: ['default', '^production'],\n executor: '@nxlv/python:run-commands',\n options: {\n command: 'uv run ty check',\n cwd: '{projectRoot}',\n },\n };\n\n // Set the default line length to 120, as 88 is a little too strict\n updateToml(\n tree,\n joinPathFragments(dir, 'pyproject.toml'),\n (pyProjectToml: UVPyprojectToml) => {\n if ((pyProjectToml.tool as any)?.ruff) {\n (pyProjectToml.tool as any).ruff['line-length'] = 120;\n }\n return pyProjectToml;\n },\n );\n\n addPyDependencies(tree, DEPENDENCIES);\n\n // Base format target checks rather than writes (so build/lint don't rewrite\n // source); `fix` writes, and `skip-lint` writes without failing so it stays\n // a no-op when propagated through the lint -> format dependency.\n projectConfiguration.targets.format = {\n ...projectConfiguration.targets.format,\n cache: true,\n inputs: ['default', '^production'],\n options: {\n ...projectConfiguration.targets.format?.options,\n check: true,\n },\n configurations: {\n ...projectConfiguration.targets.format?.configurations,\n fix: {\n check: false,\n },\n 'skip-lint': {\n check: false,\n },\n },\n };\n\n // Add a dependency on the format target for lint in order to reduce the number of\n // fixable lint errors (eg line too long)\n projectConfiguration.targets.lint = {\n ...projectConfiguration.targets.lint,\n cache: true,\n inputs: ['default', '^production'],\n configurations: {\n ...projectConfiguration.targets.lint?.configurations,\n fix: {\n fix: true,\n },\n 'skip-lint': {\n exitZero: true,\n },\n },\n };\n // Append `format` without moving an existing entry, so re-running does not\n // reorder lint dependencies (e.g. relative to a later-added license-check).\n addDependencyToTargetIfNotPresent(projectConfiguration, 'lint', 'format');\n\n projectConfiguration.targets = sortObjectKeys(projectConfiguration.targets);\n updateProjectConfiguration(tree, fullyQualifiedName, projectConfiguration);\n\n addGeneratorMetadata(tree, fullyQualifiedName, PY_PROJECT_GENERATOR_INFO);\n\n // Update root .gitignore\n updateGitIgnore(tree, '.', (patterns) => [...patterns, '/reports']);\n\n // Update project level .gitignore. The cache directories are also kept out\n // of type checking: `ty` respects .gitignore, and pytest creates and removes\n // transient `pytest-cache-files-*` directories while the test and typecheck\n // targets run concurrently, which would otherwise make `ty` fail with an I/O\n // error when it scans one mid-deletion.\n updateGitIgnore(tree, dir, (patterns) => [\n ...patterns,\n '**/__pycache__',\n ...PYTHON_TRANSIENT_ARTIFACTS.filter((p) => p !== '__pycache__'),\n ]);\n\n await addGeneratorMetricsIfApplicable(tree, [PY_PROJECT_GENERATOR_INFO]);\n\n await ensurePythonLicenseCollector(tree);\n\n // If license checking is configured, make this project's lint target depend\n // on the root license-check target. No-op if license checking isn't set up;\n // the license generator wires up existing projects itself, so the dependency\n // is added regardless of which generator runs first.\n addLicenseCheckToLintTarget(tree, fullyQualifiedName);\n\n return () =>\n installDependencies(tree, schema.preferInstallDependencies, {\n languages: ['typescript', 'python'],\n });\n};\nexport default pyProjectGenerator;\n"],"names":["ensurePackage","joinPathFragments","readNxJson","readProjectConfiguration","updateNxJson","updateProjectConfiguration","addLicenseCheckToLintTarget","ensurePythonLicenseCollector","addPyDependencies","addTsDependencies","declareDependencies","updateGitIgnore","installDependencies","addGeneratorMetricsIfApplicable","normalizeDistributionName","toSnakeCase","getNpmScope","addDependencyToTargetIfNotPresent","addGeneratorMetadata","getGeneratorInfo","projectExists","migrateToSharedVenvGenerator","uvProjectGenerator","sortObjectKeys","updateToml","withVersions","DEPENDENCIES","ts","name","dev","root","py","group","PY_PROJECT_GENERATOR_INFO","filename","PYTHON_TRANSIENT_ARTIFACTS","getPyProjectDetails","tree","schema","scope","normalizedName","normalizedModuleName","moduleName","fullyQualifiedName","distributionName","dir","directory","subDirectory","pyProjectGenerator","pythonPlugin","Object","entries","forEach","version","nxJson","plugins","find","p","plugin","options","packageManager","exists","autoActivate","moveDevDependencies","pyenvPythonVersion","pyprojectPythonDependency","packageName","publishable","buildLockedVersions","buildBundleLocalDependencies","linter","rootPyprojectDependencyGroup","projectType","type","projectNameAndRootFormat","unitTestRunner","codeCoverage","codeCoverageHtmlReport","codeCoverageXmlReport","unitTestHtmlReport","unitTestJUnitReport","buildSystem","srcDir","f","delete","write","outputPath","buildOutputPath","projectConfiguration","targets","compile","buildTarget","build","inputs","outputs","ignorePaths","dependsOn","typecheck","cache","executor","command","cwd","pyProjectToml","tool","ruff","format","check","configurations","fix","lint","exitZero","patterns","filter","preferInstallDependencies","languages"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../packages/nx-plugin/src/py/project/generator.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n ensurePackage,\n type GeneratorCallback,\n joinPathFragments,\n readNxJson,\n readProjectConfiguration,\n type Tree,\n updateNxJson,\n updateProjectConfiguration,\n} from '@nx/devkit';\nimport {\n addLicenseCheckToLintTarget,\n ensurePythonLicenseCollector,\n} from '../../license/config.js';\nimport {\n addPyDependencies,\n addTsDependencies,\n} from '../../utils/add-dependencies.js';\nimport { declareDependencies } from '../../utils/declared-dependencies.js';\nimport { updateGitIgnore } from '../../utils/git.js';\nimport { installDependencies } from '../../utils/install.js';\nimport { addGeneratorMetricsIfApplicable } from '../../utils/metrics.js';\nimport { normalizeDistributionName, toSnakeCase } from '../../utils/names.js';\nimport { getNpmScope } from '../../utils/npm-scope.js';\nimport {\n addDependencyToTargetIfNotPresent,\n addGeneratorMetadata,\n getGeneratorInfo,\n type NxGeneratorInfo,\n projectExists,\n} from '../../utils/nx.js';\nimport type { UVPyprojectToml } from '../../utils/nxlv-python.js';\nimport {\n migrateToSharedVenvGenerator,\n uvProjectGenerator,\n} from '../../utils/nxlv-python.js';\nimport { sortObjectKeys } from '../../utils/object.js';\nimport { updateToml } from '../../utils/toml.js';\nimport {\n pyenvPythonVersion,\n pyprojectPythonDependency,\n withVersions,\n} from '../../utils/versions.js';\nimport type { PyProjectGeneratorSchema } from './schema';\n\nexport const DEPENDENCIES = declareDependencies()({\n ts: [{ name: '@nxlv/python', dev: true, root: true }],\n py: [\n // The linting and type checking tools live in the workspace root pyproject.\n // Pinned to the version generation-time formatting uses, so generated files\n // stay `ruff format --check`-clean across ruff releases.\n { name: 'ruff', group: 'dev', root: true },\n { name: 'ty', group: 'dev', root: true },\n ],\n});\n\nexport const PY_PROJECT_GENERATOR_INFO: NxGeneratorInfo = getGeneratorInfo(\n import.meta.filename,\n);\n\n// Transient artifacts that pytest, coverage and ruff create and remove while\n// the test, typecheck and compile targets run concurrently. `.coverage.*` are\n// the per-process data files pytest-cov writes (`.coverage.<host>.<pid>.<rand>`)\n// before combining them. Both `ty` (which respects .gitignore) and the\n// `@nxlv/python:build` copy (which respects `ignorePaths`) would otherwise fail\n// when they read one of these mid-deletion.\nconst PYTHON_TRANSIENT_ARTIFACTS = [\n '__pycache__',\n '.coverage',\n '.coverage.*',\n '.pytest_cache',\n 'pytest-cache-files-*',\n '.ruff_cache',\n];\n\nexport interface PyProjectDetails {\n /**\n * Fully qualified Nx project id including scope, in dot notation (eg foo.bar).\n * This is the identifier Nx uses (readProjectConfiguration, the project\n * graph, target references) and is intentionally kept dotted.\n */\n readonly fullyQualifiedName: string;\n /**\n * PEP 503 normalised Python distribution name (eg foo-bar). This is the name\n * written to the project's `[project].name`, the root `[tool.uv.sources]`\n * key, and any inter-project dependency string. It is hyphenated (not dotted)\n * so `uv` and `@nxlv/python` can match it: uv writes `uv.lock` keyed by the\n * PEP 503 hyphenated name, and `@nxlv/python`'s dependency inference splits a\n * dotted name on `.` and so would otherwise drop the edge entirely. Keeping\n * the distribution name decoupled from `fullyQualifiedName` is what lets the\n * workspace dependency edge appear in the Nx project graph.\n */\n readonly distributionName: string;\n /**\n * Directory of the library relative to the root\n */\n readonly dir: string;\n /**\n * Module name for the project\n */\n readonly normalizedModuleName: string;\n}\n\n/**\n * Returns details about the Python project to be created\n */\nexport const getPyProjectDetails = (\n tree: Tree,\n schema: {\n name: string;\n directory?: string;\n subDirectory?: string;\n moduleName?: string;\n },\n): PyProjectDetails => {\n const scope = toSnakeCase(getNpmScope(tree));\n const normalizedName = toSnakeCase(schema.name);\n const normalizedModuleName = toSnakeCase(\n schema.moduleName ?? `${scope}_${normalizedName}`,\n );\n const fullyQualifiedName = `${scope}.${normalizedName}`;\n // The Python distribution name is the PEP 503 normalised form of the\n // fully qualified name: hyphenated, never dotted. See PyProjectDetails.\n const distributionName = normalizeDistributionName(fullyQualifiedName);\n // NB: interactive nx generator cli can pass empty string\n const dir = joinPathFragments(\n schema.directory || '.',\n schema.subDirectory || normalizedName,\n );\n return { dir, fullyQualifiedName, distributionName, normalizedModuleName };\n};\n\n/**\n * Generates a Python project\n */\nexport const pyProjectGenerator = async (\n tree: Tree,\n schema: PyProjectGeneratorSchema,\n): Promise<GeneratorCallback> => {\n const { dir, normalizedModuleName, fullyQualifiedName, distributionName } =\n getPyProjectDetails(tree, schema);\n\n addTsDependencies(tree, DEPENDENCIES);\n\n const pythonPlugin = withVersions(DEPENDENCIES, ['@nxlv/python']);\n Object.entries(pythonPlugin).forEach(([name, version]) =>\n ensurePackage(name, version),\n );\n\n const nxJson = readNxJson(tree);\n\n // Only rewrite nx.json when the plugin needs adding, so re-running does not\n // reserialize (and reformat) the file when nothing has changed.\n if (\n !nxJson.plugins?.find((p) =>\n typeof p === 'string'\n ? p === '@nxlv/python'\n : p.plugin === '@nxlv/python',\n )\n ) {\n nxJson.plugins = [\n ...(nxJson.plugins ?? []),\n {\n plugin: '@nxlv/python',\n options: {\n packageManager: 'uv',\n },\n },\n ];\n updateNxJson(tree, nxJson);\n }\n\n // Only scaffold the project when it does not already exist so re-running with\n // the same name does not throw. The rest of the generator still runs to apply\n // any changed options to the existing project.\n if (!projectExists(tree, fullyQualifiedName)) {\n if (!tree.exists('uv.lock')) {\n await migrateToSharedVenvGenerator(tree, {\n autoActivate: true,\n packageManager: 'uv',\n moveDevDependencies: false,\n pyenvPythonVersion: pyenvPythonVersion(),\n pyprojectPythonDependency: pyprojectPythonDependency(),\n });\n }\n\n await uvProjectGenerator(tree, {\n // The Nx project id (and `uv.workspace.members` etc.) keys off `name`, so\n // keep it dotted. `packageName` is the separate PEP 503 distribution name\n // written to `[project].name` and the root `[tool.uv.sources]` key; it is\n // hyphenated so `@nxlv/python` infers the workspace dependency edge (it\n // splits a dotted name on `.` and would otherwise drop it). This split is\n // the whole fix (see PyProjectDetails.distributionName).\n name: fullyQualifiedName,\n packageName: distributionName,\n publishable: false,\n buildLockedVersions: true,\n buildBundleLocalDependencies: true,\n linter: 'ruff',\n rootPyprojectDependencyGroup: 'main',\n pyenvPythonVersion: pyenvPythonVersion(),\n pyprojectPythonDependency: pyprojectPythonDependency(),\n projectType: schema.type,\n projectNameAndRootFormat: 'as-provided',\n moduleName: normalizedModuleName,\n directory: dir,\n unitTestRunner: 'pytest',\n codeCoverage: true,\n codeCoverageHtmlReport: true,\n codeCoverageXmlReport: true,\n unitTestHtmlReport: true,\n unitTestJUnitReport: true,\n buildSystem: 'hatch',\n srcDir: false,\n });\n\n // Remove generated hello.py and test_hello.py as they are not needed\n [\n joinPathFragments(dir, normalizedModuleName, 'hello.py'),\n joinPathFragments(dir, 'tests', 'test_hello.py'),\n ].forEach((f) => tree.delete(f));\n\n // Add a placeholder test so pytest doesn't fail with \"no tests collected\"\n tree.write(\n joinPathFragments(dir, 'tests', 'test_noop.py'),\n 'def test_noop():\\n pass\\n',\n );\n }\n\n const outputPath = '{workspaceRoot}/dist/{projectRoot}';\n const buildOutputPath = joinPathFragments(outputPath, 'build');\n const projectConfiguration = readProjectConfiguration(\n tree,\n fullyQualifiedName,\n );\n projectConfiguration.name = fullyQualifiedName;\n // Derive the compile target from the uv build target, and rewrite build to\n // orchestrate the other targets. This only runs on first creation: on re-run\n // build has already been transformed, so re-deriving would corrupt compile\n // and duplicate the build dependencies.\n if (!projectConfiguration.targets.compile) {\n const buildTarget = projectConfiguration.targets.build;\n projectConfiguration.targets.compile = {\n ...buildTarget,\n inputs: ['default', '^production'],\n outputs: [buildOutputPath],\n options: {\n ...buildTarget.options,\n outputPath: buildOutputPath,\n // The build executor copies the project to a temp folder; exclude the\n // executor defaults plus transient artifacts so the copy does not race\n // a concurrent test/typecheck target deleting a file mid-copy.\n ignorePaths: ['.venv', '.tox', 'tests', ...PYTHON_TRANSIENT_ARTIFACTS],\n },\n };\n projectConfiguration.targets.build = {\n inputs: ['default', '^production'],\n dependsOn: [\n 'lint',\n 'compile',\n 'test',\n 'typecheck',\n ...(buildTarget.dependsOn ?? []),\n ],\n options: {\n outputPath,\n },\n };\n }\n projectConfiguration.targets.typecheck = {\n cache: true,\n inputs: ['default', '^production'],\n executor: '@nxlv/python:run-commands',\n options: {\n command: 'uv run ty check',\n cwd: '{projectRoot}',\n },\n };\n\n // Set the default line length to 120, as 88 is a little too strict\n updateToml(\n tree,\n joinPathFragments(dir, 'pyproject.toml'),\n (pyProjectToml: UVPyprojectToml) => {\n if ((pyProjectToml.tool as any)?.ruff) {\n (pyProjectToml.tool as any).ruff['line-length'] = 120;\n }\n return pyProjectToml;\n },\n );\n\n addPyDependencies(tree, DEPENDENCIES);\n\n // Base format target checks rather than writes (so build/lint don't rewrite\n // source); `fix` writes, and `skip-lint` writes without failing so it stays\n // a no-op when propagated through the lint -> format dependency.\n projectConfiguration.targets.format = {\n ...projectConfiguration.targets.format,\n cache: true,\n inputs: ['default', '^production'],\n options: {\n ...projectConfiguration.targets.format?.options,\n check: true,\n },\n configurations: {\n ...projectConfiguration.targets.format?.configurations,\n fix: {\n check: false,\n },\n 'skip-lint': {\n check: false,\n },\n },\n };\n\n // Add a dependency on the format target for lint in order to reduce the number of\n // fixable lint errors (eg line too long)\n projectConfiguration.targets.lint = {\n ...projectConfiguration.targets.lint,\n cache: true,\n inputs: ['default', '^production'],\n configurations: {\n ...projectConfiguration.targets.lint?.configurations,\n fix: {\n fix: true,\n },\n 'skip-lint': {\n exitZero: true,\n },\n },\n };\n // Append `format` without moving an existing entry, so re-running does not\n // reorder lint dependencies (e.g. relative to a later-added license-check).\n addDependencyToTargetIfNotPresent(projectConfiguration, 'lint', 'format');\n\n projectConfiguration.targets = sortObjectKeys(projectConfiguration.targets);\n updateProjectConfiguration(tree, fullyQualifiedName, projectConfiguration);\n\n addGeneratorMetadata(tree, fullyQualifiedName, PY_PROJECT_GENERATOR_INFO);\n\n // Update root .gitignore\n updateGitIgnore(tree, '.', (patterns) => [...patterns, '/reports']);\n\n // Update project level .gitignore. The cache directories are also kept out\n // of type checking: `ty` respects .gitignore, and pytest creates and removes\n // transient `pytest-cache-files-*` directories while the test and typecheck\n // targets run concurrently, which would otherwise make `ty` fail with an I/O\n // error when it scans one mid-deletion.\n updateGitIgnore(tree, dir, (patterns) => [\n ...patterns,\n '**/__pycache__',\n ...PYTHON_TRANSIENT_ARTIFACTS.filter((p) => p !== '__pycache__'),\n ]);\n\n await addGeneratorMetricsIfApplicable(tree, [PY_PROJECT_GENERATOR_INFO]);\n\n await ensurePythonLicenseCollector(tree);\n\n // If license checking is configured, make this project's lint target depend\n // on the root license-check target. No-op if license checking isn't set up;\n // the license generator wires up existing projects itself, so the dependency\n // is added regardless of which generator runs first.\n addLicenseCheckToLintTarget(tree, fullyQualifiedName);\n\n return () =>\n installDependencies(tree, schema.preferInstallDependencies, {\n languages: ['typescript', 'python'],\n });\n};\nexport default pyProjectGenerator;\n"],"names":["ensurePackage","joinPathFragments","readNxJson","readProjectConfiguration","updateNxJson","updateProjectConfiguration","addLicenseCheckToLintTarget","ensurePythonLicenseCollector","addPyDependencies","addTsDependencies","declareDependencies","updateGitIgnore","installDependencies","addGeneratorMetricsIfApplicable","normalizeDistributionName","toSnakeCase","getNpmScope","addDependencyToTargetIfNotPresent","addGeneratorMetadata","getGeneratorInfo","projectExists","migrateToSharedVenvGenerator","uvProjectGenerator","sortObjectKeys","updateToml","pyenvPythonVersion","pyprojectPythonDependency","withVersions","DEPENDENCIES","ts","name","dev","root","py","group","PY_PROJECT_GENERATOR_INFO","filename","PYTHON_TRANSIENT_ARTIFACTS","getPyProjectDetails","tree","schema","scope","normalizedName","normalizedModuleName","moduleName","fullyQualifiedName","distributionName","dir","directory","subDirectory","pyProjectGenerator","pythonPlugin","Object","entries","forEach","version","nxJson","plugins","find","p","plugin","options","packageManager","exists","autoActivate","moveDevDependencies","packageName","publishable","buildLockedVersions","buildBundleLocalDependencies","linter","rootPyprojectDependencyGroup","projectType","type","projectNameAndRootFormat","unitTestRunner","codeCoverage","codeCoverageHtmlReport","codeCoverageXmlReport","unitTestHtmlReport","unitTestJUnitReport","buildSystem","srcDir","f","delete","write","outputPath","buildOutputPath","projectConfiguration","targets","compile","buildTarget","build","inputs","outputs","ignorePaths","dependsOn","typecheck","cache","executor","command","cwd","pyProjectToml","tool","ruff","format","check","configurations","fix","lint","exitZero","patterns","filter","preferInstallDependencies","languages"],"mappings":"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"}
@@ -294,12 +294,6 @@ variable "enable_rds_proxy" {
294
294
  default = true
295
295
  }
296
296
 
297
- variable "enable_credential_rotation" {
298
- description = "Whether to enable automatic credential rotation for the admin secret."
299
- type = bool
300
- default = true
301
- }
302
-
303
297
  variable "enable_cloudwatch_logs" {
304
298
  description = "Whether to export Aurora engine logs to CloudWatch. PostgreSQL logs DDL statements only (log_statement=ddl); MySQL enables Advanced Auditing scoped to connections and DDL (server_audit_events=CONNECT,QUERY_DDL). Neither logs statement parameter values, to avoid leaking PII into log data."
305
299
  type = bool
@@ -372,7 +366,6 @@ module "aurora" {
372
366
  name = "db"
373
367
  vpc_id = var.vpc_id
374
368
  subnet_ids = var.database_subnet_ids
375
- lambda_subnet_ids = var.lambda_subnet_ids
376
369
  engine = "aurora-postgresql"
377
370
  engine_version = var.engine_version
378
371
  database_name = "database_name"
@@ -384,7 +377,6 @@ module "aurora" {
384
377
  deletion_protection = var.deletion_protection
385
378
  skip_final_snapshot = var.skip_final_snapshot
386
379
  enable_rds_proxy = var.enable_rds_proxy
387
- enable_credential_rotation = var.enable_credential_rotation
388
380
  enable_cloudwatch_logs = var.enable_cloudwatch_logs
389
381
  enable_performance_insights = var.enable_performance_insights
390
382
  performance_insights_retention_period = var.performance_insights_retention_period
@@ -754,6 +746,9 @@ resource "aws_lambda_function" "create_db_user" {
754
746
  environment {
755
747
  variables = {
756
748
  DATABASE_SECRET_ARN = module.aurora.secret_arn
749
+ DATABASE_HOST = module.aurora.writer_endpoint
750
+ DATABASE_PORT = module.aurora.cluster_port
751
+ DATABASE_NAME = module.aurora.database_name
757
752
  }
758
753
  }
759
754
 
@@ -830,6 +825,9 @@ resource "aws_lambda_function" "migration_handler" {
830
825
  environment {
831
826
  variables = {
832
827
  DATABASE_SECRET_ARN = module.aurora.secret_arn
828
+ DATABASE_HOST = module.aurora.writer_endpoint
829
+ DATABASE_PORT = module.aurora.cluster_port
830
+ DATABASE_NAME = module.aurora.database_name
833
831
  }
834
832
  }
835
833
 
@@ -1309,12 +1307,26 @@ def get_local_dev_config() -> dict:
1309
1307
 
1310
1308
 
1311
1309
  def get_database_secret() -> DatabaseSecret:
1310
+ """Read the admin credentials secret.
1311
+
1312
+ RDS-managed secrets hold only \`username\` and \`password\`, so the host, port
1313
+ and database name are taken from the environment when the secret omits them.
1314
+ """
1312
1315
  secret_arn = os.environ.get("DATABASE_SECRET_ARN")
1313
1316
  if not secret_arn:
1314
1317
  raise ValueError("Missing required environment variable DATABASE_SECRET_ARN.")
1315
1318
  client = boto3.client("secretsmanager")
1316
1319
  response = client.get_secret_value(SecretId=secret_arn)
1317
- return json.loads(response["SecretString"])
1320
+ secret = json.loads(response["SecretString"])
1321
+
1322
+ for key, env_var in (("host", "DATABASE_HOST"), ("port", "DATABASE_PORT"), ("dbname", "DATABASE_NAME")):
1323
+ value = os.environ.get(env_var)
1324
+ if value:
1325
+ secret[key] = value
1326
+ if secret.get(key) is None:
1327
+ raise ValueError(f"Database secret is missing {key} and no override was provided in {env_var}.")
1328
+
1329
+ return secret
1318
1330
 
1319
1331
 
1320
1332
  _database_config: dict[str, DatabaseConfig] = {}
@@ -39,12 +39,28 @@ def get_local_dev_config() -> dict:
39
39
 
40
40
 
41
41
  def get_database_secret() -> DatabaseSecret:
42
+ """Read the admin credentials secret.
43
+
44
+ RDS-managed secrets hold only `username` and `password`, so the host, port
45
+ and database name are taken from the environment when the secret omits them.
46
+ """
42
47
  secret_arn = os.environ.get('DATABASE_SECRET_ARN')
43
48
  if not secret_arn:
44
49
  raise ValueError('Missing required environment variable DATABASE_SECRET_ARN.')
45
50
  client = boto3.client('secretsmanager')
46
51
  response = client.get_secret_value(SecretId=secret_arn)
47
- return json.loads(response['SecretString'])
52
+ secret = json.loads(response['SecretString'])
53
+
54
+ for key, env_var in (('host', 'DATABASE_HOST'), ('port', 'DATABASE_PORT'), ('dbname', 'DATABASE_NAME')):
55
+ value = os.environ.get(env_var)
56
+ if value:
57
+ secret[key] = value
58
+ if secret.get(key) is None:
59
+ raise ValueError(
60
+ f'Database secret is missing {key} and no override was provided in {env_var}.'
61
+ )
62
+
63
+ return secret
48
64
 
49
65
 
50
66
  _database_config: dict[str, DatabaseConfig] = {}
@@ -88,7 +88,7 @@ export class TestApi<
88
88
  pattern: 'isolated',
89
89
  operations: OPERATION_DETAILS,
90
90
  defaultIntegrationOptions: {
91
- runtime: Runtime.NODEJS_LATEST,
91
+ runtime: Runtime.NODEJS_24_X,
92
92
  handler: 'index.handler',
93
93
  code: Code.fromAsset(
94
94
  url.fileURLToPath(
@@ -276,7 +276,7 @@ export class TestApi<
276
276
  pattern: 'isolated',
277
277
  operations: OPERATION_DETAILS,
278
278
  defaultIntegrationOptions: {
279
- runtime: Runtime.NODEJS_LATEST,
279
+ runtime: Runtime.NODEJS_24_X,
280
280
  handler: 'index.handler',
281
281
  code: Code.fromAsset(
282
282
  url.fileURLToPath(
@@ -1158,7 +1158,7 @@ resource "aws_lambda_function" "api_lambda" {
1158
1158
  function_name = local.function_name[each.key]
1159
1159
  role = aws_iam_role.lambda_execution_role[each.key].arn
1160
1160
  handler = "index.handler"
1161
- runtime = "nodejs22.x"
1161
+ runtime = "nodejs24.x"
1162
1162
  timeout = 30
1163
1163
  memory_size = 128
1164
1164
 
@@ -1297,7 +1297,7 @@ resource "aws_lambda_function" "authorizer_lambda" {
1297
1297
  function_name = "TestApiAuthorizer-\${random_string.suffix.result}"
1298
1298
  role = aws_iam_role.authorizer_execution_role.arn
1299
1299
  handler = "index.handler"
1300
- runtime = "nodejs22.x"
1300
+ runtime = "nodejs24.x"
1301
1301
  timeout = 10
1302
1302
  memory_size = 128
1303
1303
 
@@ -2994,7 +2994,7 @@ resource "aws_lambda_function" "api_lambda" {
2994
2994
  function_name = local.function_name[each.key]
2995
2995
  role = aws_iam_role.lambda_execution_role[each.key].arn
2996
2996
  handler = "index.handler"
2997
- runtime = "nodejs22.x"
2997
+ runtime = "nodejs24.x"
2998
2998
  timeout = 30
2999
2999
  memory_size = 128
3000
3000
 
@@ -4297,7 +4297,7 @@ resource "aws_lambda_function" "api_lambda" {
4297
4297
  function_name = local.function_name[each.key]
4298
4298
  role = aws_iam_role.lambda_execution_role[each.key].arn
4299
4299
  handler = "index.handler"
4300
- runtime = "nodejs22.x"
4300
+ runtime = "nodejs24.x"
4301
4301
  timeout = 30
4302
4302
  memory_size = 128
4303
4303
 
@@ -5286,7 +5286,7 @@ resource "aws_lambda_function" "api_lambda" {
5286
5286
  function_name = local.function_name[each.key]
5287
5287
  role = aws_iam_role.lambda_execution_role[each.key].arn
5288
5288
  handler = "index.handler"
5289
- runtime = "nodejs22.x"
5289
+ runtime = "nodejs24.x"
5290
5290
  timeout = 30
5291
5291
  memory_size = 128
5292
5292
 
@@ -5425,7 +5425,7 @@ resource "aws_lambda_function" "authorizer_lambda" {
5425
5425
  function_name = "CustomApiAuthorizer-\${random_string.suffix.result}"
5426
5426
  role = aws_iam_role.authorizer_execution_role.arn
5427
5427
  handler = "index.handler"
5428
- runtime = "nodejs22.x"
5428
+ runtime = "nodejs24.x"
5429
5429
  timeout = 10
5430
5430
  memory_size = 128
5431
5431
 
@@ -437,7 +437,7 @@ export class TestApi<
437
437
  pattern: 'isolated',
438
438
  operations: routerToOperations(appRouter),
439
439
  defaultIntegrationOptions: {
440
- runtime: Runtime.NODEJS_LATEST,
440
+ runtime: Runtime.NODEJS_24_X,
441
441
  handler: 'index.handler',
442
442
  code: Code.fromAsset(
443
443
  url.fileURLToPath(
@@ -647,7 +647,7 @@ export class TestApi<
647
647
  pattern: 'isolated',
648
648
  operations: routerToOperations(appRouter),
649
649
  defaultIntegrationOptions: {
650
- runtime: Runtime.NODEJS_LATEST,
650
+ runtime: Runtime.NODEJS_24_X,
651
651
  handler: 'index.handler',
652
652
  code: Code.fromAsset(
653
653
  url.fileURLToPath(
@@ -865,7 +865,7 @@ export class TestApi<
865
865
  pattern: 'isolated',
866
866
  operations: routerToOperations(appRouter),
867
867
  defaultIntegrationOptions: {
868
- runtime: Runtime.NODEJS_LATEST,
868
+ runtime: Runtime.NODEJS_24_X,
869
869
  handler: 'index.handler',
870
870
  code: Code.fromAsset(
871
871
  url.fileURLToPath(
@@ -904,7 +904,7 @@ export class TestApi<
904
904
  scope,
905
905
  'TestApiAuthorizerFunction',
906
906
  {
907
- runtime: Runtime.NODEJS_LATEST,
907
+ runtime: Runtime.NODEJS_24_X,
908
908
  handler: 'index.handler',
909
909
  code: Code.fromAsset(
910
910
  url.fileURLToPath(
@@ -1082,7 +1082,7 @@ export class TestApi<
1082
1082
  pattern: 'isolated',
1083
1083
  operations: routerToOperations(appRouter),
1084
1084
  defaultIntegrationOptions: {
1085
- runtime: Runtime.NODEJS_LATEST,
1085
+ runtime: Runtime.NODEJS_24_X,
1086
1086
  handler: 'index.handler',
1087
1087
  code: Code.fromAsset(
1088
1088
  url.fileURLToPath(
@@ -1122,7 +1122,7 @@ export class TestApi<
1122
1122
  scope,
1123
1123
  'TestApiAuthorizerFunction',
1124
1124
  {
1125
- runtime: Runtime.NODEJS_LATEST,
1125
+ runtime: Runtime.NODEJS_24_X,
1126
1126
  handler: 'index.handler',
1127
1127
  code: Code.fromAsset(
1128
1128
  url.fileURLToPath(
@@ -1454,7 +1454,7 @@ export class TestApi<
1454
1454
  pattern: 'isolated',
1455
1455
  operations: routerToOperations(appRouter),
1456
1456
  defaultIntegrationOptions: {
1457
- runtime: Runtime.NODEJS_LATEST,
1457
+ runtime: Runtime.NODEJS_24_X,
1458
1458
  handler: 'index.handler',
1459
1459
  code: Code.fromAsset(
1460
1460
  url.fileURLToPath(
@@ -2500,7 +2500,7 @@ export class TestApi<
2500
2500
  pattern: 'isolated',
2501
2501
  operations: routerToOperations(appRouter),
2502
2502
  defaultIntegrationOptions: {
2503
- runtime: Runtime.NODEJS_LATEST,
2503
+ runtime: Runtime.NODEJS_24_X,
2504
2504
  handler: 'index.handler',
2505
2505
  code: Code.fromAsset(
2506
2506
  url.fileURLToPath(
@@ -3669,7 +3669,7 @@ resource "aws_lambda_function" "api_lambda" {
3669
3669
  function_name = local.function_name[each.key]
3670
3670
  role = aws_iam_role.lambda_execution_role[each.key].arn
3671
3671
  handler = "index.handler"
3672
- runtime = "nodejs22.x"
3672
+ runtime = "nodejs24.x"
3673
3673
  timeout = 30
3674
3674
  memory_size = 128
3675
3675
 
@@ -4474,7 +4474,7 @@ resource "aws_lambda_function" "api_lambda" {
4474
4474
  function_name = local.function_name[each.key]
4475
4475
  role = aws_iam_role.lambda_execution_role[each.key].arn
4476
4476
  handler = "index.handler"
4477
- runtime = "nodejs22.x"
4477
+ runtime = "nodejs24.x"
4478
4478
  timeout = 30
4479
4479
  memory_size = 128
4480
4480
 
@@ -4613,7 +4613,7 @@ resource "aws_lambda_function" "authorizer_lambda" {
4613
4613
  function_name = "TestApiAuthorizer-\${random_string.suffix.result}"
4614
4614
  role = aws_iam_role.authorizer_execution_role.arn
4615
4615
  handler = "index.handler"
4616
- runtime = "nodejs22.x"
4616
+ runtime = "nodejs24.x"
4617
4617
  timeout = 10
4618
4618
  memory_size = 128
4619
4619
 
@@ -5362,7 +5362,7 @@ resource "aws_lambda_function" "api_lambda" {
5362
5362
  function_name = local.function_name[each.key]
5363
5363
  role = aws_iam_role.lambda_execution_role[each.key].arn
5364
5364
  handler = "index.handler"
5365
- runtime = "nodejs22.x"
5365
+ runtime = "nodejs24.x"
5366
5366
  timeout = 30
5367
5367
  memory_size = 128
5368
5368
 
@@ -6422,7 +6422,7 @@ resource "aws_lambda_function" "api_lambda" {
6422
6422
  function_name = local.function_name[each.key]
6423
6423
  role = aws_iam_role.lambda_execution_role[each.key].arn
6424
6424
  handler = "index.handler"
6425
- runtime = "nodejs22.x"
6425
+ runtime = "nodejs24.x"
6426
6426
  timeout = 30
6427
6427
  memory_size = 128
6428
6428
 
@@ -7726,7 +7726,7 @@ resource "aws_lambda_function" "api_lambda" {
7726
7726
  function_name = local.function_name[each.key]
7727
7727
  role = aws_iam_role.lambda_execution_role[each.key].arn
7728
7728
  handler = "index.handler"
7729
- runtime = "nodejs22.x"
7729
+ runtime = "nodejs24.x"
7730
7730
  timeout = 30
7731
7731
  memory_size = 128
7732
7732
 
@@ -9029,7 +9029,7 @@ resource "aws_lambda_function" "api_lambda" {
9029
9029
  function_name = local.function_name[each.key]
9030
9030
  role = aws_iam_role.lambda_execution_role[each.key].arn
9031
9031
  handler = "index.handler"
9032
- runtime = "nodejs22.x"
9032
+ runtime = "nodejs24.x"
9033
9033
  timeout = 30
9034
9034
  memory_size = 128
9035
9035
 
@@ -9168,7 +9168,7 @@ resource "aws_lambda_function" "authorizer_lambda" {
9168
9168
  function_name = "TestApiAuthorizer-\${random_string.suffix.result}"
9169
9169
  role = aws_iam_role.authorizer_execution_role.arn
9170
9170
  handler = "index.handler"
9171
- runtime = "nodejs22.x"
9171
+ runtime = "nodejs24.x"
9172
9172
  timeout = 10
9173
9173
  memory_size = 128
9174
9174
 
@@ -214,7 +214,7 @@ export class SnapshotProxy extends Construct {
214
214
  new lambda.Function(this, id_, {
215
215
  code: asset(bundlePathFromRoot),
216
216
  handler: 'index.handler',
217
- runtime: lambda.Runtime.NODEJS_22_X,
217
+ runtime: lambda.Runtime.NODEJS_24_X,
218
218
  architecture: lambda.Architecture.ARM_64,
219
219
  timeout: Duration.seconds(10),
220
220
  memorySize: 256,
@@ -810,7 +810,7 @@ resource "aws_lambda_function" "standard" {
810
810
  function_name = "snapshot-proxy-\${each.key}-\${random_string.suffix.result}"
811
811
  role = each.key == "token" ? aws_iam_role.token.arn : aws_iam_role.proxy.arn
812
812
  handler = "index.handler"
813
- runtime = "nodejs22.x"
813
+ runtime = "nodejs24.x"
814
814
  architectures = ["arm64"]
815
815
  source_code_hash = data.archive_file.handler_zip[each.key].output_base64sha256
816
816
  timeout = 10
@@ -839,7 +839,7 @@ resource "aws_lambda_function" "mcp_proxy" {
839
839
  function_name = "snapshot-proxy-mcp-proxy-\${random_string.suffix.result}"
840
840
  role = aws_iam_role.proxy.arn
841
841
  handler = "index.handler"
842
- runtime = "nodejs22.x"
842
+ runtime = "nodejs24.x"
843
843
  architectures = ["arm64"]
844
844
  source_code_hash = data.archive_file.handler_zip["mcp-proxy"].output_base64sha256
845
845
  timeout = var.mcp_proxy_timeout