@aws/nx-plugin 1.0.0-rc.79 → 1.0.0-rc.80

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 (469) hide show
  1. package/generators.json +10 -0
  2. package/migrations.json +1 -1
  3. package/package.json +1 -1
  4. package/src/agentcore-gateway/agent-connection/generator.d.ts +1 -1
  5. package/src/agentcore-gateway/agent-connection/generator.js.map +1 -1
  6. package/src/agentcore-gateway/agent-connection/schema.d.js.map +1 -1
  7. package/src/agentcore-gateway/agent-connection/schema.d.ts +1 -1
  8. package/src/agentcore-gateway/attach-upstream.js.map +1 -1
  9. package/src/agentcore-gateway/gateway-connection/generator.d.ts +1 -1
  10. package/src/agentcore-gateway/gateway-connection/generator.js.map +1 -1
  11. package/src/agentcore-gateway/gateway-connection/schema.d.js.map +1 -1
  12. package/src/agentcore-gateway/gateway-connection/schema.d.ts +1 -1
  13. package/src/agentcore-gateway/generator.d.ts +3 -3
  14. package/src/agentcore-gateway/generator.js.map +1 -1
  15. package/src/agentcore-gateway/mcp-connection/generator.d.ts +1 -1
  16. package/src/agentcore-gateway/mcp-connection/generator.js.map +1 -1
  17. package/src/agentcore-gateway/mcp-connection/schema.d.js.map +1 -1
  18. package/src/agentcore-gateway/mcp-connection/schema.d.ts +1 -1
  19. package/src/agentcore-gateway/react-connection/generator.d.ts +1 -1
  20. package/src/agentcore-gateway/react-connection/generator.js.map +1 -1
  21. package/src/agentcore-gateway/react-connection/schema.d.js.map +1 -1
  22. package/src/agentcore-gateway/react-connection/schema.d.ts +1 -1
  23. package/src/agentcore-gateway/schema.d.js.map +1 -1
  24. package/src/agentcore-gateway/schema.d.ts +1 -1
  25. package/src/agentcore-harness/generator.d.ts +4 -4
  26. package/src/agentcore-harness/generator.js.map +1 -1
  27. package/src/agentcore-harness/schema.d.js.map +1 -1
  28. package/src/agentcore-harness/schema.d.ts +1 -1
  29. package/src/connection/agent-local-dev.d.ts +1 -1
  30. package/src/connection/agent-local-dev.js.map +1 -1
  31. package/src/connection/agent-runtime-config.js.map +1 -1
  32. package/src/connection/gateway-runtime-config.js.map +1 -1
  33. package/src/connection/generator.d.ts +2 -2
  34. package/src/connection/generator.js.map +1 -1
  35. package/src/connection/local-dev.js.map +1 -1
  36. package/src/connection/scaffold-catalog.d.ts +1 -1
  37. package/src/connection/scaffold-catalog.js +6 -6
  38. package/src/connection/scaffold-catalog.js.map +1 -1
  39. package/src/connection/schema-resolver.d.ts +1 -1
  40. package/src/connection/schema-resolver.js.map +1 -1
  41. package/src/index.d.ts +1 -1
  42. package/src/index.js.map +1 -1
  43. package/src/infra/app/generator.d.ts +7 -7
  44. package/src/infra/app/generator.js.map +1 -1
  45. package/src/init/generator.d.ts +3 -3
  46. package/src/init/generator.js.map +1 -1
  47. package/src/init/schema.d.js.map +1 -1
  48. package/src/init/schema.d.ts +2 -2
  49. package/src/internal/test-matrix/generator.js.map +1 -1
  50. package/src/license/config-types.d.ts +2 -2
  51. package/src/license/config-types.js.map +1 -1
  52. package/src/license/config.d.ts +2 -2
  53. package/src/license/config.js.map +1 -1
  54. package/src/license/dependency-check/check.d.ts +2 -2
  55. package/src/license/dependency-check/check.js.map +1 -1
  56. package/src/license/dependency-check/collectors/collector.js.map +1 -1
  57. package/src/license/dependency-check/collectors/python-collector.js.map +1 -1
  58. package/src/license/dependency-check/evaluator.d.ts +1 -1
  59. package/src/license/dependency-check/evaluator.js.map +1 -1
  60. package/src/license/dependency-check/executor.js.map +1 -1
  61. package/src/license/dependency-check/pre-approved.d.ts +1 -1
  62. package/src/license/dependency-check/pre-approved.js.map +1 -1
  63. package/src/license/dependency-check/types.d.ts +1 -1
  64. package/src/license/dependency-check/types.js.map +1 -1
  65. package/src/license/generator.d.ts +1 -1
  66. package/src/license/generator.js.map +1 -1
  67. package/src/license/index.d.ts +4 -4
  68. package/src/license/index.js.map +1 -1
  69. package/src/license/known-exceptions.d.ts +1 -1
  70. package/src/license/known-exceptions.js.map +1 -1
  71. package/src/license/sync/generator.js.map +1 -1
  72. package/src/license/sync/project-file-sync.d.ts +1 -1
  73. package/src/license/sync/project-file-sync.js.map +1 -1
  74. package/src/mcp-server/dev-server.js.map +1 -1
  75. package/src/mcp-server/generator-info.d.ts +3 -3
  76. package/src/mcp-server/generator-info.js.map +1 -1
  77. package/src/mcp-server/guide-pipeline.d.ts +1 -1
  78. package/src/mcp-server/guide-pipeline.js.map +1 -1
  79. package/src/mcp-server/guide-render.js.map +1 -1
  80. package/src/mcp-server/index.js.map +1 -1
  81. package/src/mcp-server/mdx-ast.js.map +1 -1
  82. package/src/mcp-server/server.js.map +1 -1
  83. package/src/mcp-server/tools/add-to-existing-project.d.ts +1 -1
  84. package/src/mcp-server/tools/add-to-existing-project.js.map +1 -1
  85. package/src/mcp-server/tools/create-workspace-command.js.map +1 -1
  86. package/src/mcp-server/tools/general-guidance.d.ts +1 -1
  87. package/src/mcp-server/tools/general-guidance.js +1 -1
  88. package/src/mcp-server/tools/general-guidance.js.map +1 -1
  89. package/src/mcp-server/tools/generator-guide.d.ts +1 -1
  90. package/src/mcp-server/tools/generator-guide.js.map +1 -1
  91. package/src/mcp-server/tools/list-generators.d.ts +1 -1
  92. package/src/mcp-server/tools/list-generators.js.map +1 -1
  93. package/src/mcp-server/tools/upgrade-workspace.d.ts +1 -1
  94. package/src/mcp-server/tools/upgrade-workspace.js.map +1 -1
  95. package/src/migrations/latest/apt-security-updates-in-container-images/migration.js.map +1 -1
  96. package/src/migrations/latest/dynamodb-configurable-encryption/migration.js.map +1 -1
  97. package/src/migrations/latest/py-agent-ag-ui-hooks/migration.js.map +1 -1
  98. package/src/migrations/latest/py-agent-session-id-middleware-extraction/migration.js.map +1 -1
  99. package/src/migrations/latest/remove-mcp-inspector-license-exceptions/migration.js.map +1 -1
  100. package/src/migrations/latest/static-website-configurable-waf-encryption/migration.js.map +1 -1
  101. package/src/migrations/latest/ts-agent-session-id-middleware-extraction/migration.js.map +1 -1
  102. package/src/migrations/latest/user-identity-configurable-mfa/migration.js.map +1 -1
  103. package/src/migrations/v1.0.0-rc.50/0001-modernize-function-props-cast/migration.js.map +1 -1
  104. package/src/migrations/v1.0.0-rc.50/0002-restrict-cors-to-custom-domains/migration.js.map +1 -1
  105. package/src/migrations/v1.0.0-rc.50/0003-terraform-bootstrap-adopt-existing-bucket/migration.js.map +1 -1
  106. package/src/migrations/v1.0.0-rc.52/0001-user-identity-waf-allow-localhost-callback/migration.js.map +1 -1
  107. package/src/migrations/v1.0.0-rc.54/0001-order-access-log-delivery-after-bucket-policy/migration.js.map +1 -1
  108. package/src/migrations/v1.0.0-rc.55/0001-backfill-generator-metadata/migration.d.ts +1 -1
  109. package/src/migrations/v1.0.0-rc.55/0001-backfill-generator-metadata/migration.js.map +1 -1
  110. package/src/migrations/v1.0.0-rc.56/0001-dynamodb-local-remove-optimize-flag/migration.js.map +1 -1
  111. package/src/migrations/v1.0.0-rc.57/0001-ts-agent-session-management-support/migration.js.map +1 -1
  112. package/src/migrations/v1.0.0-rc.58/0001-smithy-ssdk-bundle-pins/migration.js.map +1 -1
  113. package/src/migrations/v1.0.0-rc.59/0001-gateway-agent-targets/migration.js.map +1 -1
  114. package/src/migrations/v1.0.0-rc.60/0001-gateway-cognito-passthrough/migration.js.map +1 -1
  115. package/src/migrations/v1.0.0-rc.60/0002-waf-log-group-removal/migration.js.map +1 -1
  116. package/src/migrations/v1.0.0-rc.60/0003-strip-pip-from-python-images/migration.js.map +1 -1
  117. package/src/migrations/v1.0.0-rc.61/0001-py-agent-lifespan-construction/migration.js.map +1 -1
  118. package/src/migrations/v1.0.0-rc.62/0001-rename-load-runtime-config-target/migration.js.map +1 -1
  119. package/src/migrations/v1.0.0-rc.63/0001-add-deploy-sandbox-target/migration.js.map +1 -1
  120. package/src/migrations/v1.0.0-rc.64/0001-smithy-build-without-docker/migration.js.map +1 -1
  121. package/src/migrations/v1.0.0-rc.65/0001-vite-config-import-meta-dirname/migration.js.map +1 -1
  122. package/src/migrations/v1.0.0-rc.65/0002-rolldown-code-splitting/migration.js.map +1 -1
  123. package/src/migrations/v1.0.0-rc.67/0001-smithy-server-package-rename/migration.js.map +1 -1
  124. package/src/migrations/v1.0.0-rc.68/0001-translate-script-whole-file-writes/migration.js.map +1 -1
  125. package/src/migrations/v1.0.0-rc.70/0001-py-agent-a2a-lifespan-construction/migration.js.map +1 -1
  126. package/src/migrations/v1.0.0-rc.71/0001-py-agent-a2a-httpx-client-per-call/migration.js.map +1 -1
  127. package/src/migrations/v1.0.0-rc.72/0001-py-agent-session-management-support/migration.js.map +1 -1
  128. package/src/open-api/ts-client/generator.d.ts +1 -1
  129. package/src/open-api/ts-client/generator.js.map +1 -1
  130. package/src/open-api/ts-hooks/generator.d.ts +1 -1
  131. package/src/open-api/ts-hooks/generator.js.map +1 -1
  132. package/src/open-api/ts-hooks/generator.spec.tsx +6 -6
  133. package/src/open-api/ts-metadata/generator.d.ts +1 -1
  134. package/src/open-api/ts-metadata/generator.js.map +1 -1
  135. package/src/open-api/utils/codegen-data/languages.d.ts +1 -1
  136. package/src/open-api/utils/codegen-data/languages.js.map +1 -1
  137. package/src/open-api/utils/codegen-data.d.ts +2 -2
  138. package/src/open-api/utils/codegen-data.js.map +1 -1
  139. package/src/open-api/utils/normalise.d.ts +1 -1
  140. package/src/open-api/utils/normalise.js.map +1 -1
  141. package/src/open-api/utils/parse.d.ts +1 -1
  142. package/src/open-api/utils/parse.js.map +1 -1
  143. package/src/open-api/utils/parser.d.ts +2 -2
  144. package/src/open-api/utils/parser.js.map +1 -1
  145. package/src/open-api/utils/refs.d.ts +1 -1
  146. package/src/open-api/utils/refs.js.map +1 -1
  147. package/src/preset/generator.d.ts +3 -3
  148. package/src/preset/generator.js.map +1 -1
  149. package/src/preset/schema.d.js.map +1 -1
  150. package/src/preset/schema.d.ts +3 -3
  151. package/src/py/agent/a2a-connection/generator.d.ts +3 -3
  152. package/src/py/agent/a2a-connection/generator.js.map +1 -1
  153. package/src/py/agent/a2a-connection/schema.d.js.map +1 -1
  154. package/src/py/agent/a2a-connection/schema.d.ts +1 -1
  155. package/src/py/agent/gateway-connection/generator.d.ts +3 -3
  156. package/src/py/agent/gateway-connection/generator.js.map +1 -1
  157. package/src/py/agent/gateway-connection/schema.d.js.map +1 -1
  158. package/src/py/agent/gateway-connection/schema.d.ts +1 -1
  159. package/src/py/agent/generator.d.ts +3 -3
  160. package/src/py/agent/generator.js.map +1 -1
  161. package/src/py/agent/mcp-connection/generator.d.ts +3 -3
  162. package/src/py/agent/mcp-connection/generator.js.map +1 -1
  163. package/src/py/agent/mcp-connection/schema.d.js.map +1 -1
  164. package/src/py/agent/mcp-connection/schema.d.ts +1 -1
  165. package/src/py/agent/react-connection/generator.d.ts +12 -12
  166. package/src/py/agent/react-connection/generator.js.map +1 -1
  167. package/src/py/agent/react-connection/local-dev.d.ts +1 -1
  168. package/src/py/agent/react-connection/local-dev.js.map +1 -1
  169. package/src/py/agent/react-connection/schema.d.js.map +1 -1
  170. package/src/py/agent/react-connection/schema.d.ts +1 -1
  171. package/src/py/agent/schema.d.js.map +1 -1
  172. package/src/py/agent/schema.d.ts +1 -1
  173. package/src/py/api/generator.d.ts +1 -1
  174. package/src/py/api/generator.js.map +1 -1
  175. package/src/py/api/schema.d.js.map +1 -1
  176. package/src/py/api/schema.d.ts +1 -1
  177. package/src/py/dynamodb/agent-connection/generator.d.ts +1 -1
  178. package/src/py/dynamodb/agent-connection/generator.js.map +1 -1
  179. package/src/py/dynamodb/agent-connection/schema.d.js.map +1 -1
  180. package/src/py/dynamodb/agent-connection/schema.d.ts +1 -1
  181. package/src/py/dynamodb/fast-api-connection/generator.d.ts +1 -1
  182. package/src/py/dynamodb/fast-api-connection/generator.js.map +1 -1
  183. package/src/py/dynamodb/generator.d.ts +4 -4
  184. package/src/py/dynamodb/generator.js.map +1 -1
  185. package/src/py/dynamodb/mcp-server-connection/generator.d.ts +1 -1
  186. package/src/py/dynamodb/mcp-server-connection/generator.js.map +1 -1
  187. package/src/py/dynamodb/mcp-server-connection/schema.d.js.map +1 -1
  188. package/src/py/dynamodb/mcp-server-connection/schema.d.ts +1 -1
  189. package/src/py/dynamodb/schema.d.js.map +1 -1
  190. package/src/py/dynamodb/schema.d.ts +1 -1
  191. package/src/py/fast-api/generator.d.ts +3 -3
  192. package/src/py/fast-api/generator.js.map +1 -1
  193. package/src/py/fast-api/react/generator.d.ts +3 -3
  194. package/src/py/fast-api/react/generator.js.map +1 -1
  195. package/src/py/fast-api/react/open-api.js.map +1 -1
  196. package/src/py/fast-api/schema.d.js.map +1 -1
  197. package/src/py/fast-api/schema.d.ts +1 -1
  198. package/src/py/lambda-function/files/handler/__lambdaFunctionSnakeCase__.py.template +1 -1
  199. package/src/py/lambda-function/generator.d.ts +10 -4
  200. package/src/py/lambda-function/generator.js +8 -1
  201. package/src/py/lambda-function/generator.js.map +1 -1
  202. package/src/py/lambda-function/schema.d.js.map +1 -1
  203. package/src/py/lambda-function/schema.d.ts +2 -2
  204. package/src/py/lambda-function/schema.json +1 -1
  205. package/src/py/mcp-server/generator.d.ts +3 -3
  206. package/src/py/mcp-server/generator.js.map +1 -1
  207. package/src/py/mcp-server/schema.d.js.map +1 -1
  208. package/src/py/mcp-server/schema.d.ts +1 -1
  209. package/src/py/project/generator.d.ts +2 -2
  210. package/src/py/project/generator.js.map +1 -1
  211. package/src/py/project/schema.json +1 -0
  212. package/src/py/rdb/agent-connection/generator.d.ts +1 -1
  213. package/src/py/rdb/agent-connection/generator.js.map +1 -1
  214. package/src/py/rdb/agent-connection/schema.d.js.map +1 -1
  215. package/src/py/rdb/agent-connection/schema.d.ts +1 -1
  216. package/src/py/rdb/fast-api-connection/generator.d.ts +1 -1
  217. package/src/py/rdb/fast-api-connection/generator.js.map +1 -1
  218. package/src/py/rdb/generator.d.ts +3 -3
  219. package/src/py/rdb/generator.js.map +1 -1
  220. package/src/py/rdb/mcp-server-connection/generator.d.ts +1 -1
  221. package/src/py/rdb/mcp-server-connection/generator.js.map +1 -1
  222. package/src/py/rdb/mcp-server-connection/schema.d.js.map +1 -1
  223. package/src/py/rdb/mcp-server-connection/schema.d.ts +1 -1
  224. package/src/sdk/agentcore-gateway.d.ts +1 -1
  225. package/src/sdk/agentcore-gateway.js.map +1 -1
  226. package/src/sdk/agentcore-harness.d.ts +1 -1
  227. package/src/sdk/agentcore-harness.js.map +1 -1
  228. package/src/sdk/connection.d.ts +1 -1
  229. package/src/sdk/connection.js.map +1 -1
  230. package/src/sdk/license.d.ts +3 -3
  231. package/src/sdk/license.js.map +1 -1
  232. package/src/sdk/open-api.d.ts +3 -3
  233. package/src/sdk/open-api.js.map +1 -1
  234. package/src/sdk/py.d.ts +7 -7
  235. package/src/sdk/py.js.map +1 -1
  236. package/src/sdk/smithy.d.ts +1 -1
  237. package/src/sdk/smithy.js.map +1 -1
  238. package/src/sdk/terraform.d.ts +1 -1
  239. package/src/sdk/terraform.js.map +1 -1
  240. package/src/sdk/ts.d.ts +18 -18
  241. package/src/sdk/ts.js.map +1 -1
  242. package/src/sdk/utils/ast.d.ts +1 -1
  243. package/src/sdk/utils/ast.js.map +1 -1
  244. package/src/sdk/utils/format.d.ts +1 -1
  245. package/src/sdk/utils/format.js.map +1 -1
  246. package/src/sdk/utils/test.d.ts +1 -1
  247. package/src/sdk/utils/test.js.map +1 -1
  248. package/src/smithy/project/generator.d.ts +4 -4
  249. package/src/smithy/project/generator.js.map +1 -1
  250. package/src/smithy/react-connection/generator.d.ts +3 -3
  251. package/src/smithy/react-connection/generator.js.map +1 -1
  252. package/src/smithy/ts/api/generator.d.ts +3 -3
  253. package/src/smithy/ts/api/generator.js.map +1 -1
  254. package/src/smithy/ts/api/schema.d.js.map +1 -1
  255. package/src/smithy/ts/api/schema.d.ts +1 -1
  256. package/src/terraform/project/generator.d.ts +5 -5
  257. package/src/terraform/project/generator.js.map +1 -1
  258. package/src/trpc/backend/generator.d.ts +3 -3
  259. package/src/trpc/backend/generator.js.map +1 -1
  260. package/src/trpc/backend/schema.d.js.map +1 -1
  261. package/src/trpc/backend/schema.d.ts +1 -1
  262. package/src/trpc/react/generator.d.ts +3 -3
  263. package/src/trpc/react/generator.js.map +1 -1
  264. package/src/ts/agent/a2a-connection/generator.d.ts +3 -3
  265. package/src/ts/agent/a2a-connection/generator.js.map +1 -1
  266. package/src/ts/agent/a2a-connection/schema.d.js.map +1 -1
  267. package/src/ts/agent/a2a-connection/schema.d.ts +1 -1
  268. package/src/ts/agent/gateway-connection/generator.d.ts +3 -3
  269. package/src/ts/agent/gateway-connection/generator.js.map +1 -1
  270. package/src/ts/agent/gateway-connection/schema.d.js.map +1 -1
  271. package/src/ts/agent/gateway-connection/schema.d.ts +1 -1
  272. package/src/ts/agent/generator.d.ts +3 -3
  273. package/src/ts/agent/generator.js.map +1 -1
  274. package/src/ts/agent/mcp-connection/generator.d.ts +3 -3
  275. package/src/ts/agent/mcp-connection/generator.js.map +1 -1
  276. package/src/ts/agent/mcp-connection/schema.d.js.map +1 -1
  277. package/src/ts/agent/mcp-connection/schema.d.ts +1 -1
  278. package/src/ts/agent/react-connection/generator.d.ts +11 -11
  279. package/src/ts/agent/react-connection/generator.js.map +1 -1
  280. package/src/ts/agent/react-connection/local-dev.d.ts +1 -1
  281. package/src/ts/agent/react-connection/local-dev.js.map +1 -1
  282. package/src/ts/agent/react-connection/schema.d.js.map +1 -1
  283. package/src/ts/agent/react-connection/schema.d.ts +1 -1
  284. package/src/ts/agent/schema.d.js.map +1 -1
  285. package/src/ts/agent/schema.d.ts +1 -1
  286. package/src/ts/api/generator.d.ts +1 -1
  287. package/src/ts/api/generator.js.map +1 -1
  288. package/src/ts/api/schema.d.js.map +1 -1
  289. package/src/ts/api/schema.d.ts +1 -1
  290. package/src/ts/astro-docs/generator.d.ts +3 -3
  291. package/src/ts/astro-docs/generator.js.map +1 -1
  292. package/src/ts/dcr-proxy/generator.d.ts +5 -5
  293. package/src/ts/dcr-proxy/generator.js.map +1 -1
  294. package/src/ts/dcr-proxy/schema.d.js.map +1 -1
  295. package/src/ts/dcr-proxy/schema.d.ts +1 -1
  296. package/src/ts/docs/generator.d.ts +1 -1
  297. package/src/ts/docs/generator.js.map +1 -1
  298. package/src/ts/dynamodb/agent-connection/generator.d.ts +1 -1
  299. package/src/ts/dynamodb/agent-connection/generator.js.map +1 -1
  300. package/src/ts/dynamodb/agent-connection/schema.d.js.map +1 -1
  301. package/src/ts/dynamodb/agent-connection/schema.d.ts +1 -1
  302. package/src/ts/dynamodb/generator.d.ts +5 -5
  303. package/src/ts/dynamodb/generator.js +1 -0
  304. package/src/ts/dynamodb/generator.js.map +1 -1
  305. package/src/ts/dynamodb/mcp-server-connection/generator.d.ts +1 -1
  306. package/src/ts/dynamodb/mcp-server-connection/generator.js.map +1 -1
  307. package/src/ts/dynamodb/mcp-server-connection/schema.d.js.map +1 -1
  308. package/src/ts/dynamodb/mcp-server-connection/schema.d.ts +1 -1
  309. package/src/ts/dynamodb/schema.d.js.map +1 -1
  310. package/src/ts/dynamodb/schema.d.ts +1 -1
  311. package/src/ts/dynamodb/smithy-connection/generator.d.ts +1 -1
  312. package/src/ts/dynamodb/smithy-connection/generator.js.map +1 -1
  313. package/src/ts/dynamodb/trpc-connection/generator.d.ts +1 -1
  314. package/src/ts/dynamodb/trpc-connection/generator.js.map +1 -1
  315. package/src/ts/lambda-function/generator.d.ts +5 -5
  316. package/src/ts/lambda-function/generator.js.map +1 -1
  317. package/src/ts/lambda-function/schema.d.js.map +1 -1
  318. package/src/ts/lambda-function/schema.d.ts +1 -1
  319. package/src/ts/lib/biome.d.ts +1 -1
  320. package/src/ts/lib/biome.js.map +1 -1
  321. package/src/ts/lib/generator.d.ts +3 -3
  322. package/src/ts/lib/generator.js.map +1 -1
  323. package/src/ts/lib/ts-project-utils.d.ts +3 -3
  324. package/src/ts/lib/ts-project-utils.js.map +1 -1
  325. package/src/ts/lib/vitest.d.ts +2 -2
  326. package/src/ts/lib/vitest.js.map +1 -1
  327. package/src/ts/mcp-server/generator.d.ts +3 -3
  328. package/src/ts/mcp-server/generator.js.map +1 -1
  329. package/src/ts/mcp-server/schema.d.js.map +1 -1
  330. package/src/ts/mcp-server/schema.d.ts +1 -1
  331. package/src/ts/nx-generator/generator.d.ts +2 -2
  332. package/src/ts/nx-generator/generator.js.map +1 -1
  333. package/src/ts/nx-migration/generator.d.ts +3 -3
  334. package/src/ts/nx-migration/generator.js.map +1 -1
  335. package/src/ts/nx-plugin/generator.d.ts +3 -3
  336. package/src/ts/nx-plugin/generator.js.map +1 -1
  337. package/src/ts/nx-plugin/utils.d.ts +1 -1
  338. package/src/ts/nx-plugin/utils.js.map +1 -1
  339. package/src/ts/rdb/agent-connection/generator.d.ts +1 -1
  340. package/src/ts/rdb/agent-connection/generator.js.map +1 -1
  341. package/src/ts/rdb/agent-connection/schema.d.js.map +1 -1
  342. package/src/ts/rdb/agent-connection/schema.d.ts +1 -1
  343. package/src/ts/rdb/generator.d.ts +4 -4
  344. package/src/ts/rdb/generator.js +1 -0
  345. package/src/ts/rdb/generator.js.map +1 -1
  346. package/src/ts/rdb/mcp-server-connection/generator.d.ts +1 -1
  347. package/src/ts/rdb/mcp-server-connection/generator.js.map +1 -1
  348. package/src/ts/rdb/mcp-server-connection/schema.d.js.map +1 -1
  349. package/src/ts/rdb/mcp-server-connection/schema.d.ts +1 -1
  350. package/src/ts/rdb/schema.d.js.map +1 -1
  351. package/src/ts/rdb/schema.d.ts +1 -1
  352. package/src/ts/rdb/smithy-connection/generator.d.ts +1 -1
  353. package/src/ts/rdb/smithy-connection/generator.js.map +1 -1
  354. package/src/ts/rdb/trpc-connection/generator.d.ts +1 -1
  355. package/src/ts/rdb/trpc-connection/generator.js.map +1 -1
  356. package/src/ts/react-website/agui/generator.d.ts +2 -2
  357. package/src/ts/react-website/agui/generator.js.map +1 -1
  358. package/src/ts/react-website/app/generator.d.ts +3 -3
  359. package/src/ts/react-website/app/generator.js.map +1 -1
  360. package/src/ts/react-website/app/schema.d.js.map +1 -1
  361. package/src/ts/react-website/app/schema.d.ts +2 -2
  362. package/src/ts/react-website/cognito-auth/generator.d.ts +5 -5
  363. package/src/ts/react-website/cognito-auth/generator.js.map +1 -1
  364. package/src/ts/react-website/cognito-auth/schema.d.js.map +1 -1
  365. package/src/ts/react-website/cognito-auth/schema.d.ts +1 -1
  366. package/src/ts/react-website/cognito-auth/utils.js.map +1 -1
  367. package/src/ts/react-website/runtime-config/generator.d.ts +1 -1
  368. package/src/ts/react-website/runtime-config/generator.js.map +1 -1
  369. package/src/ts/sync/generator.d.ts +1 -1
  370. package/src/ts/sync/generator.js.map +1 -1
  371. package/src/ts/website/app/generator.d.ts +1 -1
  372. package/src/ts/website/app/generator.js.map +1 -1
  373. package/src/ts/website/app/schema.d.js.map +1 -1
  374. package/src/ts/website/app/schema.d.ts +2 -2
  375. package/src/ts/website/auth/generator.d.ts +1 -1
  376. package/src/ts/website/auth/generator.js.map +1 -1
  377. package/src/ts/website/auth/schema.d.js.map +1 -1
  378. package/src/ts/website/auth/schema.d.ts +1 -1
  379. package/src/utils/add-dependencies.d.ts +1 -1
  380. package/src/utils/add-dependencies.js.map +1 -1
  381. package/src/utils/agent-chat/agent-chat.js.map +1 -1
  382. package/src/utils/agent-connection/agent-connection.d.ts +2 -2
  383. package/src/utils/agent-connection/agent-connection.js.map +1 -1
  384. package/src/utils/agent-core-constructs/agent-core-constructs.d.ts +4 -4
  385. package/src/utils/agent-core-constructs/agent-core-constructs.js.map +1 -1
  386. package/src/utils/api-constructs/api-constructs.d.ts +3 -3
  387. package/src/utils/api-constructs/api-constructs.js.map +1 -1
  388. package/src/utils/api-constructs/open-api-metadata.d.ts +1 -1
  389. package/src/utils/api-constructs/open-api-metadata.js.map +1 -1
  390. package/src/utils/ast/website.js.map +1 -1
  391. package/src/utils/ast.js +6 -0
  392. package/src/utils/ast.js.map +1 -1
  393. package/src/utils/bundle/bundle.d.ts +1 -1
  394. package/src/utils/bundle/bundle.js.map +1 -1
  395. package/src/utils/commands.d.ts +1 -1
  396. package/src/utils/commands.js.map +1 -1
  397. package/src/utils/config/index.d.ts +6 -6
  398. package/src/utils/config/index.js.map +1 -1
  399. package/src/utils/config/utils.d.ts +1 -1
  400. package/src/utils/config/utils.js.map +1 -1
  401. package/src/utils/connection/open-api/react.d.ts +1 -1
  402. package/src/utils/connection/open-api/react.js.map +1 -1
  403. package/src/utils/containers.js.map +1 -1
  404. package/src/utils/dcr-proxy-constructs/dcr-proxy-constructs.d.ts +1 -1
  405. package/src/utils/dcr-proxy-constructs/dcr-proxy-constructs.js.map +1 -1
  406. package/src/utils/declared-dependencies.d.ts +1 -1
  407. package/src/utils/declared-dependencies.js.map +1 -1
  408. package/src/utils/dependencies.js.map +1 -1
  409. package/src/utils/docker.d.ts +1 -1
  410. package/src/utils/docker.js.map +1 -1
  411. package/src/utils/dynamodb-constructs/dynamodb-constructs.d.ts +1 -1
  412. package/src/utils/dynamodb-constructs/dynamodb-constructs.js.map +1 -1
  413. package/src/utils/format.js.map +1 -1
  414. package/src/utils/fs.d.ts +1 -1
  415. package/src/utils/fs.js.map +1 -1
  416. package/src/utils/function-constructs/function-constructs.d.ts +1 -1
  417. package/src/utils/function-constructs/function-constructs.js.map +1 -1
  418. package/src/utils/iac.d.ts +2 -2
  419. package/src/utils/iac.js.map +1 -1
  420. package/src/utils/identity-constructs/identity-constructs.d.ts +2 -2
  421. package/src/utils/identity-constructs/identity-constructs.js.map +1 -1
  422. package/src/utils/init.d.ts +3 -3
  423. package/src/utils/init.js.map +1 -1
  424. package/src/utils/install.d.ts +1 -1
  425. package/src/utils/install.js.map +1 -1
  426. package/src/utils/mcp.js.map +1 -1
  427. package/src/utils/metrics.d.ts +1 -1
  428. package/src/utils/metrics.js.map +1 -1
  429. package/src/utils/migration-manifest.d.ts +1 -1
  430. package/src/utils/migration-manifest.js.map +1 -1
  431. package/src/utils/nx.d.ts +3 -3
  432. package/src/utils/nx.js.map +1 -1
  433. package/src/utils/paths.js.map +1 -1
  434. package/src/utils/pkg-manager.d.ts +1 -1
  435. package/src/utils/pkg-manager.js.map +1 -1
  436. package/src/utils/pnpm-workspace.js.map +1 -1
  437. package/src/utils/project-package-json.js.map +1 -1
  438. package/src/utils/py.d.ts +2 -2
  439. package/src/utils/py.js.map +1 -1
  440. package/src/utils/rdb-constructs/rdb-constructs.d.ts +2 -2
  441. package/src/utils/rdb-constructs/rdb-constructs.js.map +1 -1
  442. package/src/utils/shared-constructs-constants.js.map +1 -1
  443. package/src/utils/shared-constructs.d.ts +3 -3
  444. package/src/utils/shared-constructs.js.map +1 -1
  445. package/src/utils/shared-dynamodb-scripts.d.ts +1 -1
  446. package/src/utils/shared-dynamodb-scripts.js.map +1 -1
  447. package/src/utils/shared-infra-config.js.map +1 -1
  448. package/src/utils/shared-infra-scripts.d.ts +2 -2
  449. package/src/utils/shared-infra-scripts.js.map +1 -1
  450. package/src/utils/shared-rdb-scripts.d.ts +1 -1
  451. package/src/utils/shared-rdb-scripts.js.map +1 -1
  452. package/src/utils/shared-scripts.js.map +1 -1
  453. package/src/utils/shared-shadcn.d.ts +1 -1
  454. package/src/utils/shared-shadcn.js.map +1 -1
  455. package/src/utils/smithy.js.map +1 -1
  456. package/src/utils/test.js.map +1 -1
  457. package/src/utils/version-upgrade-migration/migration.js.map +1 -1
  458. package/src/utils/version-upgrade-migration/nx-package-updates.js.map +1 -1
  459. package/src/utils/version-upgrade-migration/owned-dependencies.d.ts +1 -1
  460. package/src/utils/version-upgrade-migration/owned-dependencies.js.map +1 -1
  461. package/src/utils/version-upgrade-migration/register.js.map +1 -1
  462. package/src/utils/version-upgrade-migration/sync-embedded-versions.d.ts +1 -1
  463. package/src/utils/version-upgrade-migration/sync-embedded-versions.js.map +1 -1
  464. package/src/utils/version-upgrade-migration/sync-metrics-version.js.map +1 -1
  465. package/src/utils/version-upgrade-migration/sync-vended-versions.js.map +1 -1
  466. package/src/utils/versions.d.ts +1 -1
  467. package/src/utils/versions.js.map +1 -1
  468. package/src/utils/website-constructs/website-constructs.d.ts +2 -2
  469. package/src/utils/website-constructs/website-constructs.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/latest/py-agent-session-id-middleware-extraction/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport {\n getProjects,\n joinPathFragments,\n type MigrationReturnObject,\n type Tree,\n} from '@nx/devkit';\nimport { PY_AGENT_GENERATOR_INFO } from '../../../py/agent/generator';\nimport {\n addPythonDestructuredImport,\n applyGritQL,\n captureGritQLVariable,\n matchGritQL,\n} from '../../../utils/ast';\nimport { formatFilesInSubtree } from '../../../utils/format';\nimport type { ComponentMetadata } from '../../../utils/nx';\n\n/**\n * Extract the `_SessionIdMiddleware` class - previously duplicated verbatim\n * in every py#agent `main.py` (one copy per framework/protocol combination) -\n * into a shared `middleware/session_id_middleware.py` module sitting beside\n * `main.py`, mirroring the http#/a2a#/ag-ui#'s own `common/agent.py` and\n * `common/session.py` sharing pattern.\n *\n * The transform is protocol/framework-agnostic: every generated `main.py`\n * duplicated the exact same `dispatch` body (only the docstring differs\n * between AG-UI and HTTP/A2A), so one rewrite handles all six shapes. Imports\n * that only existed to support the inlined class (`uuid`, `Request`,\n * `BaseHTTPMiddleware`, `session_id_context`) are left for the trailing\n * `formatFilesInSubtree` ruff pass to prune if they've gone unused - AG-UI's\n * `main.py` still uses several of them directly, so nothing here can safely\n * assume any one of them is dead.\n */\nconst pyMatch = (snippet: string) => `language python\\n\\`${snippet}\\``;\n\nconst pyDelete = (snippet: string) => `${pyMatch(snippet)} => .`;\n\nconst pyRewrite = (from: string, to: string): string => {\n const replacement = to.includes('\\n') ? `raw\\`${to}\\`` : `\\`${to}\\``;\n return `${pyMatch(from)} => ${replacement}`;\n};\n\n// Both frameworks import `session_id_context` from the shared agent-connection\n// module alongside other names, so this both confirms the old shape is\n// present and recovers the module name to import the new middleware from.\nconst SESSION_ID_CONTEXT_MOD_CAPTURE =\n 'language python\\n`from $mod import $names` where { $names <: contains `session_id_context` }';\n\nconst SESSION_ID_HEADER_LINE =\n 'SESSION_ID_HEADER = \"x-amzn-bedrock-agentcore-runtime-session-id\"';\n\nconst ADD_MIDDLEWARE_OLD = 'app.add_middleware(_SessionIdMiddleware)';\nconst ADD_MIDDLEWARE_NEW = 'app.add_middleware(SessionIdMiddleware)';\n\n// HTTP and A2A share this docstring verbatim; AG-UI's differs (see below).\nconst OLD_CLASS_INBOUND = `class _SessionIdMiddleware(BaseHTTPMiddleware):\n \"\"\"Bind the inbound session (or a fresh UUID) to async context.\"\"\"\n\n async def dispatch(self, request: Request, call_next):\n session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())\n with session_id_context(session_id):\n return await call_next(request)`;\n\nconst OLD_CLASS_DOWNSTREAM = `class _SessionIdMiddleware(BaseHTTPMiddleware):\n \"\"\"Bind the session ID for this request so downstream MCP / A2A clients forward it on outbound calls.\"\"\"\n\n async def dispatch(self, request: Request, call_next):\n session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())\n with session_id_context(session_id):\n return await call_next(request)`;\n\n// Both framework common/ dirs vend byte-identical middleware content (the\n// LangChain copy would work equally well); this one is read as the single\n// source of truth rather than hand-duplicating it here.\nconst SESSION_ID_MIDDLEWARE_TEMPLATE = readFileSync(\n join(\n import.meta.dirname,\n '../../../py/agent/files/strands/common/middleware/session_id_middleware.py.template',\n ),\n 'utf-8',\n);\n\nconst sessionIdMiddlewareContent = (agentConnectionModule: string): string =>\n SESSION_ID_MIDDLEWARE_TEMPLATE.replace(\n '<%- agentConnectionModuleName %>',\n agentConnectionModule,\n );\n\nconst findAgentComponents = (\n components: ComponentMetadata[] | undefined,\n): ComponentMetadata[] =>\n (components ?? []).filter(\n (component) => component.generator === PY_AGENT_GENERATOR_INFO.id,\n );\n\nconst migrateMain = async (\n tree: Tree,\n mainPath: string,\n component: ComponentMetadata,\n nextSteps: string[],\n): Promise<void> => {\n if (!tree.exists(mainPath)) return;\n\n const contents = tree.read(mainPath, 'utf-8') ?? '';\n if (!contents.includes('class _SessionIdMiddleware(')) return;\n\n const oldClass = contents.includes(\n 'Bind the session ID for this request so downstream MCP / A2A clients forward it on outbound calls.',\n )\n ? OLD_CLASS_DOWNSTREAM\n : OLD_CLASS_INBOUND;\n\n const ready =\n (await matchGritQL(tree, mainPath, pyMatch(oldClass))) &&\n (await matchGritQL(tree, mainPath, pyMatch(SESSION_ID_HEADER_LINE))) &&\n (await matchGritQL(tree, mainPath, pyMatch(ADD_MIDDLEWARE_OLD))) &&\n (await matchGritQL(tree, mainPath, SESSION_ID_CONTEXT_MOD_CAPTURE));\n\n const diverged = () => {\n const shape = [component.framework, component.protocol]\n .filter(Boolean)\n .join('/');\n nextSteps.push(\n `${mainPath}: diverged from the generated ${shape || 'py#agent'} shape - left untouched. Manually move \\`_SessionIdMiddleware\\` into a sibling \\`middleware/session_id_middleware.py\\` module and import it from there (see the py#agent generator's template).`,\n );\n };\n\n if (!ready) {\n diverged();\n return;\n }\n\n const mod = await captureGritQLVariable(\n tree,\n mainPath,\n SESSION_ID_CONTEXT_MOD_CAPTURE,\n 'mod',\n );\n if (!mod) {\n diverged();\n return;\n }\n\n const dir = mainPath.split('/').slice(0, -1).join('/');\n const middlewareInitPath = joinPathFragments(\n dir,\n 'middleware',\n '__init__.py',\n );\n const middlewarePath = joinPathFragments(\n dir,\n 'middleware',\n 'session_id_middleware.py',\n );\n\n if (!tree.exists(middlewareInitPath)) {\n tree.write(middlewareInitPath, '');\n }\n if (!tree.exists(middlewarePath)) {\n tree.write(middlewarePath, sessionIdMiddlewareContent(mod));\n }\n\n await applyGritQL(tree, mainPath, pyDelete(SESSION_ID_HEADER_LINE));\n await applyGritQL(tree, mainPath, pyDelete(oldClass));\n await applyGritQL(\n tree,\n mainPath,\n pyRewrite(ADD_MIDDLEWARE_OLD, ADD_MIDDLEWARE_NEW),\n );\n // Imported unconditionally - SESSION_ID_HEADER is only still referenced by\n // AG-UI's main.py after the class is gone; the ruff pass below prunes it\n // from HTTP/A2A's now-unused import list.\n await addPythonDestructuredImport(\n tree,\n mainPath,\n ['SESSION_ID_HEADER', 'SessionIdMiddleware'],\n '.middleware.session_id_middleware',\n );\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n for (const project of getProjects(tree).values()) {\n const components = findAgentComponents(\n (project.metadata as { components?: ComponentMetadata[] })?.components,\n );\n\n for (const component of components) {\n if (!component.path) continue;\n\n const componentDir = component.path.endsWith('/agent.py')\n ? component.path.slice(0, -'/agent.py'.length)\n : component.path;\n const mainPath = joinPathFragments(project.root, componentDir, 'main.py');\n\n await migrateMain(tree, mainPath, component, nextSteps);\n }\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["readFileSync","join","getProjects","joinPathFragments","PY_AGENT_GENERATOR_INFO","addPythonDestructuredImport","applyGritQL","captureGritQLVariable","matchGritQL","formatFilesInSubtree","pyMatch","snippet","pyDelete","pyRewrite","from","to","replacement","includes","SESSION_ID_CONTEXT_MOD_CAPTURE","SESSION_ID_HEADER_LINE","ADD_MIDDLEWARE_OLD","ADD_MIDDLEWARE_NEW","OLD_CLASS_INBOUND","OLD_CLASS_DOWNSTREAM","SESSION_ID_MIDDLEWARE_TEMPLATE","dirname","sessionIdMiddlewareContent","agentConnectionModule","replace","findAgentComponents","components","filter","component","generator","id","migrateMain","tree","mainPath","nextSteps","exists","contents","read","oldClass","ready","diverged","shape","framework","protocol","Boolean","push","mod","dir","split","slice","middlewareInitPath","middlewarePath","write","migration","project","values","metadata","path","componentDir","endsWith","length","root"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/latest/py-agent-session-id-middleware-extraction/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport {\n getProjects,\n joinPathFragments,\n type MigrationReturnObject,\n type Tree,\n} from '@nx/devkit';\nimport { PY_AGENT_GENERATOR_INFO } from '../../../py/agent/generator.js';\nimport {\n addPythonDestructuredImport,\n applyGritQL,\n captureGritQLVariable,\n matchGritQL,\n} from '../../../utils/ast.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\nimport type { ComponentMetadata } from '../../../utils/nx.js';\n\n/**\n * Extract the `_SessionIdMiddleware` class - previously duplicated verbatim\n * in every py#agent `main.py` (one copy per framework/protocol combination) -\n * into a shared `middleware/session_id_middleware.py` module sitting beside\n * `main.py`, mirroring the http#/a2a#/ag-ui#'s own `common/agent.py` and\n * `common/session.py` sharing pattern.\n *\n * The transform is protocol/framework-agnostic: every generated `main.py`\n * duplicated the exact same `dispatch` body (only the docstring differs\n * between AG-UI and HTTP/A2A), so one rewrite handles all six shapes. Imports\n * that only existed to support the inlined class (`uuid`, `Request`,\n * `BaseHTTPMiddleware`, `session_id_context`) are left for the trailing\n * `formatFilesInSubtree` ruff pass to prune if they've gone unused - AG-UI's\n * `main.py` still uses several of them directly, so nothing here can safely\n * assume any one of them is dead.\n */\nconst pyMatch = (snippet: string) => `language python\\n\\`${snippet}\\``;\n\nconst pyDelete = (snippet: string) => `${pyMatch(snippet)} => .`;\n\nconst pyRewrite = (from: string, to: string): string => {\n const replacement = to.includes('\\n') ? `raw\\`${to}\\`` : `\\`${to}\\``;\n return `${pyMatch(from)} => ${replacement}`;\n};\n\n// Both frameworks import `session_id_context` from the shared agent-connection\n// module alongside other names, so this both confirms the old shape is\n// present and recovers the module name to import the new middleware from.\nconst SESSION_ID_CONTEXT_MOD_CAPTURE =\n 'language python\\n`from $mod import $names` where { $names <: contains `session_id_context` }';\n\nconst SESSION_ID_HEADER_LINE =\n 'SESSION_ID_HEADER = \"x-amzn-bedrock-agentcore-runtime-session-id\"';\n\nconst ADD_MIDDLEWARE_OLD = 'app.add_middleware(_SessionIdMiddleware)';\nconst ADD_MIDDLEWARE_NEW = 'app.add_middleware(SessionIdMiddleware)';\n\n// HTTP and A2A share this docstring verbatim; AG-UI's differs (see below).\nconst OLD_CLASS_INBOUND = `class _SessionIdMiddleware(BaseHTTPMiddleware):\n \"\"\"Bind the inbound session (or a fresh UUID) to async context.\"\"\"\n\n async def dispatch(self, request: Request, call_next):\n session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())\n with session_id_context(session_id):\n return await call_next(request)`;\n\nconst OLD_CLASS_DOWNSTREAM = `class _SessionIdMiddleware(BaseHTTPMiddleware):\n \"\"\"Bind the session ID for this request so downstream MCP / A2A clients forward it on outbound calls.\"\"\"\n\n async def dispatch(self, request: Request, call_next):\n session_id = request.headers.get(SESSION_ID_HEADER) or str(uuid.uuid4())\n with session_id_context(session_id):\n return await call_next(request)`;\n\n// Both framework common/ dirs vend byte-identical middleware content (the\n// LangChain copy would work equally well); this one is read as the single\n// source of truth rather than hand-duplicating it here.\nconst SESSION_ID_MIDDLEWARE_TEMPLATE = readFileSync(\n join(\n import.meta.dirname,\n '../../../py/agent/files/strands/common/middleware/session_id_middleware.py.template',\n ),\n 'utf-8',\n);\n\nconst sessionIdMiddlewareContent = (agentConnectionModule: string): string =>\n SESSION_ID_MIDDLEWARE_TEMPLATE.replace(\n '<%- agentConnectionModuleName %>',\n agentConnectionModule,\n );\n\nconst findAgentComponents = (\n components: ComponentMetadata[] | undefined,\n): ComponentMetadata[] =>\n (components ?? []).filter(\n (component) => component.generator === PY_AGENT_GENERATOR_INFO.id,\n );\n\nconst migrateMain = async (\n tree: Tree,\n mainPath: string,\n component: ComponentMetadata,\n nextSteps: string[],\n): Promise<void> => {\n if (!tree.exists(mainPath)) return;\n\n const contents = tree.read(mainPath, 'utf-8') ?? '';\n if (!contents.includes('class _SessionIdMiddleware(')) return;\n\n const oldClass = contents.includes(\n 'Bind the session ID for this request so downstream MCP / A2A clients forward it on outbound calls.',\n )\n ? OLD_CLASS_DOWNSTREAM\n : OLD_CLASS_INBOUND;\n\n const ready =\n (await matchGritQL(tree, mainPath, pyMatch(oldClass))) &&\n (await matchGritQL(tree, mainPath, pyMatch(SESSION_ID_HEADER_LINE))) &&\n (await matchGritQL(tree, mainPath, pyMatch(ADD_MIDDLEWARE_OLD))) &&\n (await matchGritQL(tree, mainPath, SESSION_ID_CONTEXT_MOD_CAPTURE));\n\n const diverged = () => {\n const shape = [component.framework, component.protocol]\n .filter(Boolean)\n .join('/');\n nextSteps.push(\n `${mainPath}: diverged from the generated ${shape || 'py#agent'} shape - left untouched. Manually move \\`_SessionIdMiddleware\\` into a sibling \\`middleware/session_id_middleware.py\\` module and import it from there (see the py#agent generator's template).`,\n );\n };\n\n if (!ready) {\n diverged();\n return;\n }\n\n const mod = await captureGritQLVariable(\n tree,\n mainPath,\n SESSION_ID_CONTEXT_MOD_CAPTURE,\n 'mod',\n );\n if (!mod) {\n diverged();\n return;\n }\n\n const dir = mainPath.split('/').slice(0, -1).join('/');\n const middlewareInitPath = joinPathFragments(\n dir,\n 'middleware',\n '__init__.py',\n );\n const middlewarePath = joinPathFragments(\n dir,\n 'middleware',\n 'session_id_middleware.py',\n );\n\n if (!tree.exists(middlewareInitPath)) {\n tree.write(middlewareInitPath, '');\n }\n if (!tree.exists(middlewarePath)) {\n tree.write(middlewarePath, sessionIdMiddlewareContent(mod));\n }\n\n await applyGritQL(tree, mainPath, pyDelete(SESSION_ID_HEADER_LINE));\n await applyGritQL(tree, mainPath, pyDelete(oldClass));\n await applyGritQL(\n tree,\n mainPath,\n pyRewrite(ADD_MIDDLEWARE_OLD, ADD_MIDDLEWARE_NEW),\n );\n // Imported unconditionally - SESSION_ID_HEADER is only still referenced by\n // AG-UI's main.py after the class is gone; the ruff pass below prunes it\n // from HTTP/A2A's now-unused import list.\n await addPythonDestructuredImport(\n tree,\n mainPath,\n ['SESSION_ID_HEADER', 'SessionIdMiddleware'],\n '.middleware.session_id_middleware',\n );\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n for (const project of getProjects(tree).values()) {\n const components = findAgentComponents(\n (project.metadata as { components?: ComponentMetadata[] })?.components,\n );\n\n for (const component of components) {\n if (!component.path) continue;\n\n const componentDir = component.path.endsWith('/agent.py')\n ? component.path.slice(0, -'/agent.py'.length)\n : component.path;\n const mainPath = joinPathFragments(project.root, componentDir, 'main.py');\n\n await migrateMain(tree, mainPath, component, nextSteps);\n }\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["readFileSync","join","getProjects","joinPathFragments","PY_AGENT_GENERATOR_INFO","addPythonDestructuredImport","applyGritQL","captureGritQLVariable","matchGritQL","formatFilesInSubtree","pyMatch","snippet","pyDelete","pyRewrite","from","to","replacement","includes","SESSION_ID_CONTEXT_MOD_CAPTURE","SESSION_ID_HEADER_LINE","ADD_MIDDLEWARE_OLD","ADD_MIDDLEWARE_NEW","OLD_CLASS_INBOUND","OLD_CLASS_DOWNSTREAM","SESSION_ID_MIDDLEWARE_TEMPLATE","dirname","sessionIdMiddlewareContent","agentConnectionModule","replace","findAgentComponents","components","filter","component","generator","id","migrateMain","tree","mainPath","nextSteps","exists","contents","read","oldClass","ready","diverged","shape","framework","protocol","Boolean","push","mod","dir","split","slice","middlewareInitPath","middlewarePath","write","migration","project","values","metadata","path","componentDir","endsWith","length","root"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/latest/remove-mcp-inspector-license-exceptions/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { readJson, type Tree } from '@nx/devkit';\nimport { removeLicenseExceptions } from '../../../license/config';\nimport { registerPnpmBuiltDependencies } from '../../../utils/pnpm-workspace';\n\nconst INSPECTOR = '@modelcontextprotocol/inspector';\n\n/**\n * MCP Inspector v1 declared `SEE LICENSE IN LICENSE`, which resolves to no SPDX\n * identifier, so the license check needed an exception per published package.\n * v2 ships as a single package declaring `MIT`, which the allowlist already\n * covers, and the stale exceptions would override that with `Apache-2.0`.\n */\nconst OBSOLETE_EXCEPTIONS = [\n INSPECTOR,\n `${INSPECTOR}-cli`,\n `${INSPECTOR}-server`,\n `${INSPECTOR}-client`,\n];\n\n/** Whether the workspace root declares the inspector as a devDependency. */\nconst hasInspector = (tree: Tree): boolean => {\n if (!tree.exists('package.json')) {\n return false;\n }\n const packageJson = readJson(tree, 'package.json');\n return Boolean(\n packageJson.devDependencies?.[INSPECTOR] ??\n packageJson.dependencies?.[INSPECTOR],\n );\n};\n\nexport default async function migration(tree: Tree): Promise<void> {\n await removeLicenseExceptions(tree, OBSOLETE_EXCEPTIONS);\n\n // The version sync bumps the inspector to v2, which added a postinstall\n // script. Register it as an explicitly-rejected build so the upgrade's\n // install isn't failed by pnpm 11's default `strictDepBuilds=true`.\n if (hasInspector(tree)) {\n registerPnpmBuiltDependencies(tree, { [INSPECTOR]: false });\n }\n}\n"],"names":["readJson","removeLicenseExceptions","registerPnpmBuiltDependencies","INSPECTOR","OBSOLETE_EXCEPTIONS","hasInspector","tree","exists","packageJson","Boolean","devDependencies","dependencies","migration"],"mappings":"AAAA;;;CAGC,GACD,SAASA,QAAQ,QAAmB,aAAa;AACjD,SAASC,uBAAuB,QAAQ,6BAA0B;AAClE,SAASC,6BAA6B,QAAQ,mCAAgC;AAE9E,MAAMC,YAAY;AAElB;;;;;CAKC,GACD,MAAMC,sBAAsB;IAC1BD;IACA,GAAGA,UAAU,IAAI,CAAC;IAClB,GAAGA,UAAU,OAAO,CAAC;IACrB,GAAGA,UAAU,OAAO,CAAC;CACtB;AAED,0EAA0E,GAC1E,MAAME,eAAe,CAACC;IACpB,IAAI,CAACA,KAAKC,MAAM,CAAC,iBAAiB;QAChC,OAAO;IACT;IACA,MAAMC,cAAcR,SAASM,MAAM;IACnC,OAAOG,QACLD,YAAYE,eAAe,EAAE,CAACP,UAAU,IACtCK,YAAYG,YAAY,EAAE,CAACR,UAAU;AAE3C;AAEA,eAAe,eAAeS,UAAUN,IAAU;IAChD,MAAML,wBAAwBK,MAAMF;IAEpC,wEAAwE;IACxE,uEAAuE;IACvE,oEAAoE;IACpE,IAAIC,aAAaC,OAAO;QACtBJ,8BAA8BI,MAAM;YAAE,CAACH,UAAU,EAAE;QAAM;IAC3D;AACF"}
1
+ {"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/latest/remove-mcp-inspector-license-exceptions/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { readJson, type Tree } from '@nx/devkit';\nimport { removeLicenseExceptions } from '../../../license/config.js';\nimport { registerPnpmBuiltDependencies } from '../../../utils/pnpm-workspace.js';\n\nconst INSPECTOR = '@modelcontextprotocol/inspector';\n\n/**\n * MCP Inspector v1 declared `SEE LICENSE IN LICENSE`, which resolves to no SPDX\n * identifier, so the license check needed an exception per published package.\n * v2 ships as a single package declaring `MIT`, which the allowlist already\n * covers, and the stale exceptions would override that with `Apache-2.0`.\n */\nconst OBSOLETE_EXCEPTIONS = [\n INSPECTOR,\n `${INSPECTOR}-cli`,\n `${INSPECTOR}-server`,\n `${INSPECTOR}-client`,\n];\n\n/** Whether the workspace root declares the inspector as a devDependency. */\nconst hasInspector = (tree: Tree): boolean => {\n if (!tree.exists('package.json')) {\n return false;\n }\n const packageJson = readJson(tree, 'package.json');\n return Boolean(\n packageJson.devDependencies?.[INSPECTOR] ??\n packageJson.dependencies?.[INSPECTOR],\n );\n};\n\nexport default async function migration(tree: Tree): Promise<void> {\n await removeLicenseExceptions(tree, OBSOLETE_EXCEPTIONS);\n\n // The version sync bumps the inspector to v2, which added a postinstall\n // script. Register it as an explicitly-rejected build so the upgrade's\n // install isn't failed by pnpm 11's default `strictDepBuilds=true`.\n if (hasInspector(tree)) {\n registerPnpmBuiltDependencies(tree, { [INSPECTOR]: false });\n }\n}\n"],"names":["readJson","removeLicenseExceptions","registerPnpmBuiltDependencies","INSPECTOR","OBSOLETE_EXCEPTIONS","hasInspector","tree","exists","packageJson","Boolean","devDependencies","dependencies","migration"],"mappings":"AAAA;;;CAGC,GACD,SAASA,QAAQ,QAAmB,aAAa;AACjD,SAASC,uBAAuB,QAAQ,6BAA6B;AACrE,SAASC,6BAA6B,QAAQ,mCAAmC;AAEjF,MAAMC,YAAY;AAElB;;;;;CAKC,GACD,MAAMC,sBAAsB;IAC1BD;IACA,GAAGA,UAAU,IAAI,CAAC;IAClB,GAAGA,UAAU,OAAO,CAAC;IACrB,GAAGA,UAAU,OAAO,CAAC;CACtB;AAED,0EAA0E,GAC1E,MAAME,eAAe,CAACC;IACpB,IAAI,CAACA,KAAKC,MAAM,CAAC,iBAAiB;QAChC,OAAO;IACT;IACA,MAAMC,cAAcR,SAASM,MAAM;IACnC,OAAOG,QACLD,YAAYE,eAAe,EAAE,CAACP,UAAU,IACtCK,YAAYG,YAAY,EAAE,CAACR,UAAU;AAE3C;AAEA,eAAe,eAAeS,UAAUN,IAAU;IAChD,MAAML,wBAAwBK,MAAMF;IAEpC,wEAAwE;IACxE,uEAAuE;IACvE,oEAAoE;IACpE,IAAIC,aAAaC,OAAO;QACtBJ,8BAA8BI,MAAM;YAAE,CAACH,UAAU,EAAE;QAAM;IAC3D;AACF"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/latest/static-website-configurable-waf-encryption/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 {\n addDestructuredImport,\n applyGritQL,\n captureGritQLVariable,\n GRIT_INSERT_PLACEHOLDER,\n insertViaGritQL,\n matchGritQL,\n} from '../../../utils/ast';\nimport { formatFilesInSubtree } from '../../../utils/format';\nimport {\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n SHARED_TERRAFORM_DIR,\n} from '../../../utils/shared-constructs-constants';\n\n/**\n * Bring existing workspaces up to the generators' current StaticWebsite\n * configurability:\n *\n * - The vended `StaticWebsite` CDK construct gains `enableWaf`, `encryption`,\n * `encryptionKey` and `enableKeyRotation` props. The WAF Web ACL is now\n * optional, the KMS key used to encrypt the website and distribution log\n * buckets is now overridable (or skippable in favour of another\n * `BucketEncryption`), and the auto-created key's rotation is configurable.\n * - Each vended per-website app construct (`app/static-websites/*.ts`) gains\n * an optional `props` constructor parameter so these can be configured per\n * app, rather than only by hand-editing the vended construct.\n * - The vended Terraform static-website core module gains the equivalent\n * `enable_waf`, `encryption`, `kms_key_arn`, `create_kms_key` and\n * `enable_key_rotation` variables. The CloudFront distribution's\n * `lifecycle.replace_triggered_by` on the WAF ACL is also dropped - it\n * can't resolve once the ACL has a count of 0 and no prior state to\n * reference, which broke `terraform plan` on any greenfield deployment\n * with `enable_waf = false`.\n * - Each vended per-website Terraform app module (`app/static-websites/<name>/<name>.tf`)\n * gains pass-through `custom_domain_names`, `acm_certificate_arn`,\n * `enable_waf`, `encryption`, `kms_key_arn`, `create_kms_key` and\n * `enable_key_rotation` variables forwarded to the core module call,\n * matching the pass-through convention every other app-wraps-core\n * Terraform module in this plugin already follows (`rdb`, `agent-core`,\n * `dcr-proxies`, the REST/HTTP API).\n *\n * These files are generated with `KeepExisting`, so without this an upgraded\n * workspace has generators that support this configuration but vended files\n * that don't. Diverged files are left untouched and reported via `nextSteps`.\n */\n\nconst CDK_STATIC_WEBSITE_FILE = `${PACKAGES_DIR}/${SHARED_CONSTRUCTS_DIR}/src/core/static-website.ts`;\nconst CDK_STATIC_WEBSITES_APP_DIR = `${PACKAGES_DIR}/${SHARED_CONSTRUCTS_DIR}/src/app/static-websites`;\nconst TERRAFORM_STATIC_WEBSITE_FILE = `${PACKAGES_DIR}/${SHARED_TERRAFORM_DIR}/src/core/static-website/static-website.tf`;\nconst TERRAFORM_STATIC_WEBSITES_APP_DIR = `${PACKAGES_DIR}/${SHARED_TERRAFORM_DIR}/src/app/static-websites`;\n\nconst CDK_DIVERGED_MESSAGE = `${CDK_STATIC_WEBSITE_FILE}: has diverged from the generated shape - left untouched. To pick up the enableWaf, encryption, encryptionKey and enableKeyRotation props, manually port them from the vended core/static-website.ts template (see the ts#react-website generator's static-website construct).`;\n\nconst TERRAFORM_DIVERGED_MESSAGE = `${TERRAFORM_STATIC_WEBSITE_FILE}: has diverged from the generated shape - left untouched. To pick up the enable_waf, encryption, kms_key_arn, create_kms_key and enable_key_rotation variables, manually port them from the vended static-website.tf template (see the ts#react-website generator's static-website module).`;\n\nconst terraformAppDivergedMessage = (filePath: string) =>\n `${filePath}: has diverged from the generated shape - left untouched. To make custom_domain_names, acm_certificate_arn, enable_waf, encryption, kms_key_arn, create_kms_key and enable_key_rotation configurable from your root Terraform configuration, add pass-through variables here and forward them to the static_website module call (see the ts#react-website generator's static-websites app template).`;\n\n// The doc comments below carry backticked markdown, which must stay out of\n// the GritQL pattern that inserts this text (see insertViaGritQL). It's routed\n// in as plain text via the placeholder instead.\nconst STATIC_WEBSITE_PROPS_TEXT = `/**\n * Whether to protect the CloudFront distribution with an AWS WAF Web ACL.\n *\n * @default true\n */\n readonly enableWaf?: boolean;\n /**\n * Server-side encryption for the website and distribution log buckets.\n *\n * @default BucketEncryption.KMS\n */\n readonly encryption?: BucketEncryption;\n /**\n * KMS key used to encrypt the website and distribution log buckets. Only used when \\`encryption\\` is\n * \\`BucketEncryption.KMS\\`. When not provided, a new key is created. Note that a key imported via\n * \\`Key.fromKeyArn\\` must already grant the CloudWatch Logs, S3 and CloudFront service principals the\n * necessary permissions in its own key policy - \\`addToResourcePolicy\\` is a no-op on an imported key,\n * so this construct cannot grant them on your behalf.\n */\n readonly encryptionKey?: IKey;\n /**\n * Whether the automatically created KMS key has rotation enabled. Only applies when \\`encryption\\` is\n * \\`BucketEncryption.KMS\\` and no \\`encryptionKey\\` is supplied.\n *\n * @default true\n */\n readonly enableKeyRotation?: boolean`;\n\n/**\n * Surface enableWaf/encryption/encryptionKey/enableKeyRotation on the vended\n * StaticWebsite CDK construct.\n */\nconst migrateCdkConstruct = async (\n tree: Tree,\n nextSteps: string[],\n): Promise<void> => {\n if (!tree.exists(CDK_STATIC_WEBSITE_FILE)) {\n return; // This workspace has no CDK static website construct.\n }\n\n if (\n await matchGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n '`readonly enableWaf?: boolean`',\n )\n ) {\n return; // Already migrated.\n }\n\n const CERTIFICATE_FIELD = '`readonly certificate?: ICertificate`';\n const DESTRUCTURE_OLD =\n '`{ websiteFilePath, websiteName, domainNames, certificate }: StaticWebsiteProps`';\n const KEY_CREATION_OLD =\n \"`const websiteKey = new Key(this, 'WebsiteKey', { enableKeyRotation: true })`\";\n const ADD_TO_RESOURCE_POLICY_OLD = '`websiteKey.addToResourcePolicy($args)`';\n const WEBSITE_BUCKET_ENCRYPTION_OLD =\n \"`encryption: BucketEncryption.KMS` as $prop where { $prop <: within `new Bucket($_, 'WebsiteBucket', $_)` }\";\n const DISTRIBUTION_LOGS_BUCKET_ENCRYPTION_OLD =\n \"`encryption: BucketEncryption.KMS` as $prop where { $prop <: within `new Bucket($_, 'DistributionLogBucket', $_)` }\";\n const WAF_STACK_OLD = \"`const wafStack = new CloudfrontWebAcl(this, 'waf')`\";\n const WEB_ACL_ID_OLD = '`webAclId: wafStack.wafArn`';\n\n const anchors = [\n CERTIFICATE_FIELD,\n DESTRUCTURE_OLD,\n KEY_CREATION_OLD,\n ADD_TO_RESOURCE_POLICY_OLD,\n WEBSITE_BUCKET_ENCRYPTION_OLD,\n DISTRIBUTION_LOGS_BUCKET_ENCRYPTION_OLD,\n WAF_STACK_OLD,\n WEB_ACL_ID_OLD,\n ];\n\n const allPresent = (\n await Promise.all(\n anchors.map((pattern) =>\n matchGritQL(tree, CDK_STATIC_WEBSITE_FILE, pattern),\n ),\n )\n ).every(Boolean);\n\n if (!allPresent) {\n nextSteps.push(CDK_DIVERGED_MESSAGE);\n return;\n }\n\n await addDestructuredImport(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n ['IKey'],\n 'aws-cdk-lib/aws-kms',\n );\n\n await insertViaGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n `${CERTIFICATE_FIELD} as $field => \\`$field;\n ${GRIT_INSERT_PLACEHOLDER}\\``,\n STATIC_WEBSITE_PROPS_TEXT,\n );\n\n await applyGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n `${DESTRUCTURE_OLD} => \\`{\n websiteFilePath,\n websiteName,\n domainNames,\n certificate,\n enableWaf = true,\n encryption = BucketEncryption.KMS,\n encryptionKey,\n enableKeyRotation = true,\n }: StaticWebsiteProps\\``,\n );\n\n await applyGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n `${KEY_CREATION_OLD} => \\`const websiteKey: IKey | undefined =\n encryption === BucketEncryption.KMS\n ? (encryptionKey ?? new Key(this, 'WebsiteKey', { enableKeyRotation }))\n : undefined;\\``,\n );\n\n await applyGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n `${ADD_TO_RESOURCE_POLICY_OLD} => \\`websiteKey?.addToResourcePolicy($args)\\``,\n );\n\n await applyGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n `${WEBSITE_BUCKET_ENCRYPTION_OLD} => \\`encryption\\``,\n );\n\n await applyGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n `${DISTRIBUTION_LOGS_BUCKET_ENCRYPTION_OLD} => \\`encryption\\``,\n );\n\n await applyGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n `${WAF_STACK_OLD} => \\`const wafStack = enableWaf ? new CloudfrontWebAcl(this, 'waf') : undefined;\\``,\n );\n\n await applyGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n `${WEB_ACL_ID_OLD} => \\`webAclId: wafStack?.wafArn\\``,\n );\n};\n\n/**\n * Surface the same props on each vended per-website app construct, so they\n * can be configured per app rather than only by editing the shared construct.\n */\nconst migrateCdkAppConstructs = async (\n tree: Tree,\n nextSteps: string[],\n): Promise<void> => {\n if (!tree.exists(CDK_STATIC_WEBSITES_APP_DIR)) {\n return;\n }\n\n for (const fileName of tree.children(CDK_STATIC_WEBSITES_APP_DIR)) {\n if (fileName === 'index.ts' || !fileName.endsWith('.ts')) {\n continue;\n }\n const filePath = joinPathFragments(CDK_STATIC_WEBSITES_APP_DIR, fileName);\n\n const contents = tree.read(filePath, 'utf-8') ?? '';\n if (contents.includes('StaticWebsiteProps')) {\n continue; // Already migrated.\n }\n\n const name = await captureGritQLVariable(\n tree,\n filePath,\n '`export class $name extends StaticWebsite { $_ }`',\n 'name',\n );\n if (!name) {\n continue; // Not a StaticWebsite subclass, not ours to touch.\n }\n\n const importPattern = '`import { StaticWebsite } from $path`';\n const ctorPattern = '`constructor(scope: Construct, id: string) { $body }`';\n // Captures the whole property list rather than anchoring on websiteName\n // being followed by exactly one more property ($rest binds a single\n // trailing node, not a variadic list). This must still match when a\n // workspace already customised the super() call with e.g. domainNames\n // and certificate (the pre-existing documented customization point).\n const superPattern = `\\`super(scope, id, { $props })\\` as $call where {\n $props <: contains \\`websiteName: '${name}'\\`\n}`;\n\n const ready = (\n await Promise.all(\n [importPattern, ctorPattern, superPattern].map((pattern) =>\n matchGritQL(tree, filePath, pattern),\n ),\n )\n ).every(Boolean);\n\n if (!ready) {\n nextSteps.push(\n `${filePath}: has diverged from the generated shape - left untouched. To make enableWaf, encryption, encryptionKey and enableKeyRotation configurable here, add an optional \\`props?: ${name}Props\\` constructor parameter (\\`Omit<StaticWebsiteProps, 'websiteName' | 'websiteFilePath'>\\`) and spread it into the super() call (see the ts#react-website generator's static-websites app template).`,\n );\n continue;\n }\n\n await applyGritQL(\n tree,\n filePath,\n `${importPattern} => \\`import { StaticWebsite, StaticWebsiteProps } from $path\\``,\n );\n\n await insertViaGritQL(\n tree,\n filePath,\n `\\`export class ${name} extends StaticWebsite { $body }\\` as $cls => \\`${GRIT_INSERT_PLACEHOLDER}\n\n$cls\\``,\n `export type ${name}Props = Omit<\\n StaticWebsiteProps,\\n 'websiteName' | 'websiteFilePath'\\n>;`,\n );\n\n await applyGritQL(\n tree,\n filePath,\n `${ctorPattern} => \\`constructor(scope: Construct, id: string, props?: ${name}Props) { $body }\\``,\n );\n\n await applyGritQL(\n tree,\n filePath,\n `${superPattern} => \\`super(scope, id, { ...props, $props })\\``,\n );\n }\n};\n\n// Terraform (HCL) patterns are built from plain strings rather than JS\n// template literals, since the HCL text itself is full of `${...}`\n// interpolations that would otherwise be parsed as JS interpolation.\nconst hcl = (pattern: string) => 'language hcl\\n' + pattern;\n\nconst withinResource = (\n linePattern: string,\n resourceType: string,\n resourceLabel: string,\n) =>\n hcl(\n '`' +\n linePattern +\n '` as $line where {\\n' +\n ' $line <: within `resource \"' +\n resourceType +\n '\" \"' +\n resourceLabel +\n '\" { $_ }`\\n' +\n '}',\n );\n\nconst NEW_VARIABLES_TEXT = [\n 'variable \"enable_waf\" {',\n ' description = \"Whether to protect the CloudFront distribution with an AWS WAF Web ACL.\"',\n ' type = bool',\n ' default = true',\n '}',\n '',\n 'variable \"encryption\" {',\n ' description = \"Server-side encryption for the website and distribution log buckets. One of KMS or S3_MANAGED.\"',\n ' type = string',\n ' default = \"KMS\"',\n '',\n ' validation {',\n ' condition = contains([\"KMS\", \"S3_MANAGED\"], var.encryption)',\n ' error_message = \"encryption must be one of KMS or S3_MANAGED.\"',\n ' }',\n '}',\n '',\n 'variable \"kms_key_arn\" {',\n ' description = \"ARN of an existing KMS key used to encrypt the website and distribution log buckets when encryption is KMS. When not provided and create_kms_key is true, a new key is created. Note that a customer-supplied key must already grant the CloudWatch Logs, S3 and CloudFront service principals the necessary permissions in its own key policy.\"',\n ' type = string',\n ' default = null',\n '}',\n '',\n 'variable \"create_kms_key\" {',\n ' description = \"Whether to create a KMS key for the website. Only applies when encryption is KMS. Set to false when supplying kms_key_arn.\"',\n ' type = bool',\n ' default = true',\n '}',\n '',\n 'variable \"enable_key_rotation\" {',\n ' description = \"Whether the automatically created KMS key has rotation enabled. Only applies when encryption is KMS and create_kms_key is true.\"',\n ' type = bool',\n ' default = true',\n '}',\n].join('\\n');\n\nconst NEW_LOCALS_TEXT = [\n '',\n ' create_website_key = var.encryption == \"KMS\" && var.create_kms_key',\n ' website_kms_key_arn = (',\n ' var.encryption != \"KMS\" ? null :',\n ' local.create_website_key ? aws_kms_key.website_key[0].arn :',\n ' var.kms_key_arn',\n ' )',\n].join('\\n');\n\n/**\n * Surface the same configuration on the vended Terraform static-website\n * module.\n */\nconst migrateTerraformModule = async (\n tree: Tree,\n nextSteps: string[],\n): Promise<void> => {\n if (!tree.exists(TERRAFORM_STATIC_WEBSITE_FILE)) {\n return; // This workspace has no Terraform static website module.\n }\n\n if (\n await matchGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n hcl('`variable \"enable_waf\" { $_ }`'),\n )\n ) {\n return; // Already migrated.\n }\n\n const ACM_CERT_VARIABLE = hcl('`variable \"acm_certificate_arn\" { $_ }`');\n const ACCESS_LOGS_LOCAL = hcl(\n '`access_logs_name_prefix = substr(lower(var.website_name), 0, 16)`',\n );\n const KMS_KEY_BLOCK = hcl('`resource \"aws_kms_key\" \"website_key\" { $_ }`');\n const KMS_KEY_ROTATION_LINE = withinResource(\n 'enable_key_rotation = true',\n 'aws_kms_key',\n 'website_key',\n );\n const KMS_ALIAS_BLOCK = hcl(\n '`resource \"aws_kms_alias\" \"website_key_alias\" { $_ }`',\n );\n const KMS_ALIAS_TARGET_LINE = hcl(\n '`target_key_id = aws_kms_key.website_key.key_id`',\n );\n const LOG_GROUP_KMS_LINE = hcl('`kms_key_id = aws_kms_key.website_key.arn`');\n const WEBSITE_ENCRYPTION_KEY_LINE = withinResource(\n 'kms_master_key_id = aws_kms_key.website_key.arn',\n 'aws_s3_bucket_server_side_encryption_configuration',\n 'website_encryption',\n );\n const WEBSITE_ENCRYPTION_ALGO_LINE = withinResource(\n 'sse_algorithm = \"aws:kms\"',\n 'aws_s3_bucket_server_side_encryption_configuration',\n 'website_encryption',\n );\n const DISTRIBUTION_LOGS_ENCRYPTION_KEY_LINE = withinResource(\n 'kms_master_key_id = aws_kms_key.website_key.arn',\n 'aws_s3_bucket_server_side_encryption_configuration',\n 'distribution_logs_encryption',\n );\n const DISTRIBUTION_LOGS_ENCRYPTION_ALGO_LINE = withinResource(\n 'sse_algorithm = \"aws:kms\"',\n 'aws_s3_bucket_server_side_encryption_configuration',\n 'distribution_logs_encryption',\n );\n const WAF_BLOCK = hcl(\n '`resource \"aws_wafv2_web_acl\" \"cloudfront_waf\" { $_ }`',\n );\n const WEB_ACL_ID_LINE = hcl(\n '`web_acl_id = aws_wafv2_web_acl.cloudfront_waf.arn`',\n );\n const WAF_OUTPUT_VALUE_LINE = hcl(\n '`value = aws_wafv2_web_acl.cloudfront_waf.arn` as $line where {\\n' +\n ' $line <: within `output \"waf_web_acl_arn\" { $_ }`\\n' +\n '}',\n );\n // Forces distribution replacement whenever the WAF ACL changes. Once the\n // ACL gains `count`, this can't be resolved on a greenfield apply where\n // count is 0 and there's no prior state to reference - see\n // https://github.com/awslabs/nx-plugin-for-aws/pull/1107. It's also\n // redundant: web_acl_id already references the ACL ARN directly, so a\n // replaced ACL still drives a plain distribution update.\n const LIFECYCLE_REPLACE_TRIGGERED_BY_BLOCK = hcl(\n '`lifecycle {\\n replace_triggered_by = [\\n aws_wafv2_web_acl.cloudfront_waf\\n ]\\n }`',\n );\n\n const anchors = [\n ACM_CERT_VARIABLE,\n ACCESS_LOGS_LOCAL,\n KMS_KEY_BLOCK,\n KMS_KEY_ROTATION_LINE,\n KMS_ALIAS_BLOCK,\n KMS_ALIAS_TARGET_LINE,\n LOG_GROUP_KMS_LINE,\n WEBSITE_ENCRYPTION_KEY_LINE,\n WEBSITE_ENCRYPTION_ALGO_LINE,\n DISTRIBUTION_LOGS_ENCRYPTION_KEY_LINE,\n DISTRIBUTION_LOGS_ENCRYPTION_ALGO_LINE,\n WAF_BLOCK,\n WEB_ACL_ID_LINE,\n WAF_OUTPUT_VALUE_LINE,\n LIFECYCLE_REPLACE_TRIGGERED_BY_BLOCK,\n ];\n\n const allPresent = (\n await Promise.all(\n anchors.map((pattern) =>\n matchGritQL(tree, TERRAFORM_STATIC_WEBSITE_FILE, pattern),\n ),\n )\n ).every(Boolean);\n\n if (!allPresent) {\n nextSteps.push(TERRAFORM_DIVERGED_MESSAGE);\n return;\n }\n\n // 1. New variables, after acm_certificate_arn. Routed through the\n // placeholder since the variable descriptions contain double-quoted\n // HCL string values.\n await insertViaGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n hcl(\n '`variable \"acm_certificate_arn\" { $body }` as $var => `$var\\n\\n' +\n GRIT_INSERT_PLACEHOLDER +\n '`',\n ),\n NEW_VARIABLES_TEXT,\n );\n\n // 2. New locals, after access_logs_name_prefix.\n await insertViaGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n hcl(\n '`access_logs_name_prefix = substr(lower(var.website_name), 0, 16)` as $line => `$line\\n' +\n GRIT_INSERT_PLACEHOLDER +\n '`',\n ),\n NEW_LOCALS_TEXT,\n );\n\n // 3. Make the KMS key and alias conditional on encryption/kms_key_arn.\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n hcl(\n '`resource \"aws_kms_key\" \"website_key\" { $body }` => `resource \"aws_kms_key\" \"website_key\" {\\n' +\n ' count = local.create_website_key ? 1 : 0\\n\\n' +\n ' $body\\n' +\n '}`',\n ),\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n KMS_KEY_ROTATION_LINE +\n ' => `enable_key_rotation = var.enable_key_rotation`',\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n hcl(\n '`resource \"aws_kms_alias\" \"website_key_alias\" { $body }` => `resource \"aws_kms_alias\" \"website_key_alias\" {\\n' +\n ' count = local.create_website_key ? 1 : 0\\n\\n' +\n ' $body\\n' +\n '}`',\n ),\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n KMS_ALIAS_TARGET_LINE +\n ' => `target_key_id = aws_kms_key.website_key[0].key_id`',\n );\n\n // 4. Resolve the key ARN through the new local everywhere it's consumed.\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n LOG_GROUP_KMS_LINE + ' => `kms_key_id = local.website_kms_key_arn`',\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n WEBSITE_ENCRYPTION_KEY_LINE +\n ' => `kms_master_key_id = var.encryption == \"KMS\" ? local.website_kms_key_arn : null`',\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n WEBSITE_ENCRYPTION_ALGO_LINE +\n ' => `sse_algorithm = var.encryption == \"KMS\" ? \"aws:kms\" : \"AES256\"`',\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n DISTRIBUTION_LOGS_ENCRYPTION_KEY_LINE +\n ' => `kms_master_key_id = var.encryption == \"KMS\" ? local.website_kms_key_arn : null`',\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n DISTRIBUTION_LOGS_ENCRYPTION_ALGO_LINE +\n ' => `sse_algorithm = var.encryption == \"KMS\" ? \"aws:kms\" : \"AES256\"`',\n );\n\n // 5. Make the WAF Web ACL conditional on enable_waf.\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n hcl(\n '`resource \"aws_wafv2_web_acl\" \"cloudfront_waf\" { $body }` => `resource \"aws_wafv2_web_acl\" \"cloudfront_waf\" {\\n' +\n ' count = var.enable_waf ? 1 : 0\\n\\n' +\n ' $body\\n' +\n '}`',\n ),\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n WEB_ACL_ID_LINE +\n ' => `web_acl_id = var.enable_waf ? aws_wafv2_web_acl.cloudfront_waf[0].arn : null`',\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n WAF_OUTPUT_VALUE_LINE +\n ' => `value = var.enable_waf ? aws_wafv2_web_acl.cloudfront_waf[0].arn : null`',\n );\n\n // 6. Drop the lifecycle block that can no longer resolve once the WAF ACL\n // has a count of 0 and no prior state to reference.\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n LIFECYCLE_REPLACE_TRIGGERED_BY_BLOCK + ' => ``',\n );\n};\n\nconst NEW_APP_MODULE_VARIABLES_TEXT = [\n 'variable \"custom_domain_names\" {',\n ' description = \"Custom domain names (aliases) for the CloudFront distribution. Requires acm_certificate_arn.\"',\n ' type = list(string)',\n ' default = []',\n '}',\n '',\n 'variable \"acm_certificate_arn\" {',\n ' description = \"ARN of an ACM certificate (in us-east-1) for the custom domain names. When set, viewers are required to use TLS 1.2 or later.\"',\n ' type = string',\n ' default = null',\n '}',\n '',\n 'variable \"enable_waf\" {',\n ' description = \"Whether to protect the CloudFront distribution with an AWS WAF Web ACL.\"',\n ' type = bool',\n ' default = true',\n '}',\n '',\n 'variable \"encryption\" {',\n ' description = \"Server-side encryption for the website and distribution log buckets. One of KMS or S3_MANAGED.\"',\n ' type = string',\n ' default = \"KMS\"',\n '',\n ' validation {',\n ' condition = contains([\"KMS\", \"S3_MANAGED\"], var.encryption)',\n ' error_message = \"encryption must be one of KMS or S3_MANAGED.\"',\n ' }',\n '}',\n '',\n 'variable \"kms_key_arn\" {',\n ' description = \"ARN of an existing KMS key used to encrypt the website and distribution log buckets when encryption is KMS. When not provided and create_kms_key is true, a new key is created. Note that a customer-supplied key must already grant the CloudWatch Logs, S3 and CloudFront service principals the necessary permissions in its own key policy.\"',\n ' type = string',\n ' default = null',\n '}',\n '',\n 'variable \"create_kms_key\" {',\n ' description = \"Whether to create a KMS key for the website. Only applies when encryption is KMS. Set to false when supplying kms_key_arn.\"',\n ' type = bool',\n ' default = true',\n '}',\n '',\n 'variable \"enable_key_rotation\" {',\n ' description = \"Whether the automatically created KMS key has rotation enabled. Only applies when encryption is KMS and create_kms_key is true.\"',\n ' type = bool',\n ' default = true',\n '}',\n].join('\\n');\n\nconst NEW_APP_MODULE_FORWARD_TEXT = [\n 'custom_domain_names = var.custom_domain_names',\n 'acm_certificate_arn = var.acm_certificate_arn',\n 'enable_waf = var.enable_waf',\n 'encryption = var.encryption',\n 'kms_key_arn = var.kms_key_arn',\n 'create_kms_key = var.create_kms_key',\n 'enable_key_rotation = var.enable_key_rotation',\n].join('\\n ');\n\n/**\n * Surface the same configuration as pass-through variables on each vended\n * per-website Terraform app module, matching the pass-through convention\n * every other app-wraps-core Terraform module already follows.\n */\nconst migrateTerraformAppModules = async (\n tree: Tree,\n nextSteps: string[],\n): Promise<void> => {\n if (!tree.exists(TERRAFORM_STATIC_WEBSITES_APP_DIR)) {\n return;\n }\n\n for (const dirName of tree.children(TERRAFORM_STATIC_WEBSITES_APP_DIR)) {\n const filePath = joinPathFragments(\n TERRAFORM_STATIC_WEBSITES_APP_DIR,\n dirName,\n `${dirName}.tf`,\n );\n if (!tree.exists(filePath)) {\n continue; // Not a website app module directory.\n }\n\n if (\n await matchGritQL(tree, filePath, hcl('`variable \"enable_waf\" { $_ }`'))\n ) {\n continue; // Already migrated.\n }\n\n const TERRAFORM_BLOCK = hcl('`terraform { $_ }`');\n const MODULE_BLOCK = hcl('`module \"static_website\" { $_ }`');\n const FILE_PATH_LINE = hcl(\n '`website_file_path = $val` as $line where {\\n' +\n ' $line <: within `module \"static_website\" { $_ }`\\n' +\n '}',\n );\n // custom_domain_names/acm_certificate_arn predate this migration as a\n // documented customization point (hand-editing the module block). If\n // either is already present as a literal argument, blindly forwarding\n // our own `= var.x` line would produce a duplicate argument, which is\n // invalid HCL. Treat that as diverged instead of silently corrupting the file.\n const hasExistingCustomDomainArg = async (name: string) =>\n matchGritQL(\n tree,\n filePath,\n hcl(\n `\\`${name} = $_\\` as $line where {\\n` +\n ' $line <: within `module \"static_website\" { $_ }`\\n' +\n '}',\n ),\n );\n\n const ready =\n (\n await Promise.all(\n [TERRAFORM_BLOCK, MODULE_BLOCK, FILE_PATH_LINE].map((pattern) =>\n matchGritQL(tree, filePath, pattern),\n ),\n )\n ).every(Boolean) &&\n !(await hasExistingCustomDomainArg('custom_domain_names')) &&\n !(await hasExistingCustomDomainArg('acm_certificate_arn'));\n\n if (!ready) {\n nextSteps.push(terraformAppDivergedMessage(filePath));\n continue;\n }\n\n // Inserted right after the terraform/required_providers block (and\n // before the \"Static website module\" comment), matching where the\n // vended template puts these variables.\n await insertViaGritQL(\n tree,\n filePath,\n hcl('`terraform { $body }` as $tf') +\n ` => \\`$tf\\n\\n${GRIT_INSERT_PLACEHOLDER}\\``,\n NEW_APP_MODULE_VARIABLES_TEXT,\n );\n\n await insertViaGritQL(\n tree,\n filePath,\n FILE_PATH_LINE + ` => \\`$line\\n\\n ${GRIT_INSERT_PLACEHOLDER}\\``,\n NEW_APP_MODULE_FORWARD_TEXT,\n );\n }\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n await migrateCdkConstruct(tree, nextSteps);\n await migrateCdkAppConstructs(tree, nextSteps);\n await migrateTerraformModule(tree, nextSteps);\n await migrateTerraformAppModules(tree, nextSteps);\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["joinPathFragments","addDestructuredImport","applyGritQL","captureGritQLVariable","GRIT_INSERT_PLACEHOLDER","insertViaGritQL","matchGritQL","formatFilesInSubtree","PACKAGES_DIR","SHARED_CONSTRUCTS_DIR","SHARED_TERRAFORM_DIR","CDK_STATIC_WEBSITE_FILE","CDK_STATIC_WEBSITES_APP_DIR","TERRAFORM_STATIC_WEBSITE_FILE","TERRAFORM_STATIC_WEBSITES_APP_DIR","CDK_DIVERGED_MESSAGE","TERRAFORM_DIVERGED_MESSAGE","terraformAppDivergedMessage","filePath","STATIC_WEBSITE_PROPS_TEXT","migrateCdkConstruct","tree","nextSteps","exists","CERTIFICATE_FIELD","DESTRUCTURE_OLD","KEY_CREATION_OLD","ADD_TO_RESOURCE_POLICY_OLD","WEBSITE_BUCKET_ENCRYPTION_OLD","DISTRIBUTION_LOGS_BUCKET_ENCRYPTION_OLD","WAF_STACK_OLD","WEB_ACL_ID_OLD","anchors","allPresent","Promise","all","map","pattern","every","Boolean","push","migrateCdkAppConstructs","fileName","children","endsWith","contents","read","includes","name","importPattern","ctorPattern","superPattern","ready","hcl","withinResource","linePattern","resourceType","resourceLabel","NEW_VARIABLES_TEXT","join","NEW_LOCALS_TEXT","migrateTerraformModule","ACM_CERT_VARIABLE","ACCESS_LOGS_LOCAL","KMS_KEY_BLOCK","KMS_KEY_ROTATION_LINE","KMS_ALIAS_BLOCK","KMS_ALIAS_TARGET_LINE","LOG_GROUP_KMS_LINE","WEBSITE_ENCRYPTION_KEY_LINE","WEBSITE_ENCRYPTION_ALGO_LINE","DISTRIBUTION_LOGS_ENCRYPTION_KEY_LINE","DISTRIBUTION_LOGS_ENCRYPTION_ALGO_LINE","WAF_BLOCK","WEB_ACL_ID_LINE","WAF_OUTPUT_VALUE_LINE","LIFECYCLE_REPLACE_TRIGGERED_BY_BLOCK","NEW_APP_MODULE_VARIABLES_TEXT","NEW_APP_MODULE_FORWARD_TEXT","migrateTerraformAppModules","dirName","TERRAFORM_BLOCK","MODULE_BLOCK","FILE_PATH_LINE","hasExistingCustomDomainArg","migration"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/latest/static-website-configurable-waf-encryption/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 {\n addDestructuredImport,\n applyGritQL,\n captureGritQLVariable,\n GRIT_INSERT_PLACEHOLDER,\n insertViaGritQL,\n matchGritQL,\n} from '../../../utils/ast.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\nimport {\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n SHARED_TERRAFORM_DIR,\n} from '../../../utils/shared-constructs-constants.js';\n\n/**\n * Bring existing workspaces up to the generators' current StaticWebsite\n * configurability:\n *\n * - The vended `StaticWebsite` CDK construct gains `enableWaf`, `encryption`,\n * `encryptionKey` and `enableKeyRotation` props. The WAF Web ACL is now\n * optional, the KMS key used to encrypt the website and distribution log\n * buckets is now overridable (or skippable in favour of another\n * `BucketEncryption`), and the auto-created key's rotation is configurable.\n * - Each vended per-website app construct (`app/static-websites/*.ts`) gains\n * an optional `props` constructor parameter so these can be configured per\n * app, rather than only by hand-editing the vended construct.\n * - The vended Terraform static-website core module gains the equivalent\n * `enable_waf`, `encryption`, `kms_key_arn`, `create_kms_key` and\n * `enable_key_rotation` variables. The CloudFront distribution's\n * `lifecycle.replace_triggered_by` on the WAF ACL is also dropped - it\n * can't resolve once the ACL has a count of 0 and no prior state to\n * reference, which broke `terraform plan` on any greenfield deployment\n * with `enable_waf = false`.\n * - Each vended per-website Terraform app module (`app/static-websites/<name>/<name>.tf`)\n * gains pass-through `custom_domain_names`, `acm_certificate_arn`,\n * `enable_waf`, `encryption`, `kms_key_arn`, `create_kms_key` and\n * `enable_key_rotation` variables forwarded to the core module call,\n * matching the pass-through convention every other app-wraps-core\n * Terraform module in this plugin already follows (`rdb`, `agent-core`,\n * `dcr-proxies`, the REST/HTTP API).\n *\n * These files are generated with `KeepExisting`, so without this an upgraded\n * workspace has generators that support this configuration but vended files\n * that don't. Diverged files are left untouched and reported via `nextSteps`.\n */\n\nconst CDK_STATIC_WEBSITE_FILE = `${PACKAGES_DIR}/${SHARED_CONSTRUCTS_DIR}/src/core/static-website.ts`;\nconst CDK_STATIC_WEBSITES_APP_DIR = `${PACKAGES_DIR}/${SHARED_CONSTRUCTS_DIR}/src/app/static-websites`;\nconst TERRAFORM_STATIC_WEBSITE_FILE = `${PACKAGES_DIR}/${SHARED_TERRAFORM_DIR}/src/core/static-website/static-website.tf`;\nconst TERRAFORM_STATIC_WEBSITES_APP_DIR = `${PACKAGES_DIR}/${SHARED_TERRAFORM_DIR}/src/app/static-websites`;\n\nconst CDK_DIVERGED_MESSAGE = `${CDK_STATIC_WEBSITE_FILE}: has diverged from the generated shape - left untouched. To pick up the enableWaf, encryption, encryptionKey and enableKeyRotation props, manually port them from the vended core/static-website.ts template (see the ts#react-website generator's static-website construct).`;\n\nconst TERRAFORM_DIVERGED_MESSAGE = `${TERRAFORM_STATIC_WEBSITE_FILE}: has diverged from the generated shape - left untouched. To pick up the enable_waf, encryption, kms_key_arn, create_kms_key and enable_key_rotation variables, manually port them from the vended static-website.tf template (see the ts#react-website generator's static-website module).`;\n\nconst terraformAppDivergedMessage = (filePath: string) =>\n `${filePath}: has diverged from the generated shape - left untouched. To make custom_domain_names, acm_certificate_arn, enable_waf, encryption, kms_key_arn, create_kms_key and enable_key_rotation configurable from your root Terraform configuration, add pass-through variables here and forward them to the static_website module call (see the ts#react-website generator's static-websites app template).`;\n\n// The doc comments below carry backticked markdown, which must stay out of\n// the GritQL pattern that inserts this text (see insertViaGritQL). It's routed\n// in as plain text via the placeholder instead.\nconst STATIC_WEBSITE_PROPS_TEXT = `/**\n * Whether to protect the CloudFront distribution with an AWS WAF Web ACL.\n *\n * @default true\n */\n readonly enableWaf?: boolean;\n /**\n * Server-side encryption for the website and distribution log buckets.\n *\n * @default BucketEncryption.KMS\n */\n readonly encryption?: BucketEncryption;\n /**\n * KMS key used to encrypt the website and distribution log buckets. Only used when \\`encryption\\` is\n * \\`BucketEncryption.KMS\\`. When not provided, a new key is created. Note that a key imported via\n * \\`Key.fromKeyArn\\` must already grant the CloudWatch Logs, S3 and CloudFront service principals the\n * necessary permissions in its own key policy - \\`addToResourcePolicy\\` is a no-op on an imported key,\n * so this construct cannot grant them on your behalf.\n */\n readonly encryptionKey?: IKey;\n /**\n * Whether the automatically created KMS key has rotation enabled. Only applies when \\`encryption\\` is\n * \\`BucketEncryption.KMS\\` and no \\`encryptionKey\\` is supplied.\n *\n * @default true\n */\n readonly enableKeyRotation?: boolean`;\n\n/**\n * Surface enableWaf/encryption/encryptionKey/enableKeyRotation on the vended\n * StaticWebsite CDK construct.\n */\nconst migrateCdkConstruct = async (\n tree: Tree,\n nextSteps: string[],\n): Promise<void> => {\n if (!tree.exists(CDK_STATIC_WEBSITE_FILE)) {\n return; // This workspace has no CDK static website construct.\n }\n\n if (\n await matchGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n '`readonly enableWaf?: boolean`',\n )\n ) {\n return; // Already migrated.\n }\n\n const CERTIFICATE_FIELD = '`readonly certificate?: ICertificate`';\n const DESTRUCTURE_OLD =\n '`{ websiteFilePath, websiteName, domainNames, certificate }: StaticWebsiteProps`';\n const KEY_CREATION_OLD =\n \"`const websiteKey = new Key(this, 'WebsiteKey', { enableKeyRotation: true })`\";\n const ADD_TO_RESOURCE_POLICY_OLD = '`websiteKey.addToResourcePolicy($args)`';\n const WEBSITE_BUCKET_ENCRYPTION_OLD =\n \"`encryption: BucketEncryption.KMS` as $prop where { $prop <: within `new Bucket($_, 'WebsiteBucket', $_)` }\";\n const DISTRIBUTION_LOGS_BUCKET_ENCRYPTION_OLD =\n \"`encryption: BucketEncryption.KMS` as $prop where { $prop <: within `new Bucket($_, 'DistributionLogBucket', $_)` }\";\n const WAF_STACK_OLD = \"`const wafStack = new CloudfrontWebAcl(this, 'waf')`\";\n const WEB_ACL_ID_OLD = '`webAclId: wafStack.wafArn`';\n\n const anchors = [\n CERTIFICATE_FIELD,\n DESTRUCTURE_OLD,\n KEY_CREATION_OLD,\n ADD_TO_RESOURCE_POLICY_OLD,\n WEBSITE_BUCKET_ENCRYPTION_OLD,\n DISTRIBUTION_LOGS_BUCKET_ENCRYPTION_OLD,\n WAF_STACK_OLD,\n WEB_ACL_ID_OLD,\n ];\n\n const allPresent = (\n await Promise.all(\n anchors.map((pattern) =>\n matchGritQL(tree, CDK_STATIC_WEBSITE_FILE, pattern),\n ),\n )\n ).every(Boolean);\n\n if (!allPresent) {\n nextSteps.push(CDK_DIVERGED_MESSAGE);\n return;\n }\n\n await addDestructuredImport(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n ['IKey'],\n 'aws-cdk-lib/aws-kms',\n );\n\n await insertViaGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n `${CERTIFICATE_FIELD} as $field => \\`$field;\n ${GRIT_INSERT_PLACEHOLDER}\\``,\n STATIC_WEBSITE_PROPS_TEXT,\n );\n\n await applyGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n `${DESTRUCTURE_OLD} => \\`{\n websiteFilePath,\n websiteName,\n domainNames,\n certificate,\n enableWaf = true,\n encryption = BucketEncryption.KMS,\n encryptionKey,\n enableKeyRotation = true,\n }: StaticWebsiteProps\\``,\n );\n\n await applyGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n `${KEY_CREATION_OLD} => \\`const websiteKey: IKey | undefined =\n encryption === BucketEncryption.KMS\n ? (encryptionKey ?? new Key(this, 'WebsiteKey', { enableKeyRotation }))\n : undefined;\\``,\n );\n\n await applyGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n `${ADD_TO_RESOURCE_POLICY_OLD} => \\`websiteKey?.addToResourcePolicy($args)\\``,\n );\n\n await applyGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n `${WEBSITE_BUCKET_ENCRYPTION_OLD} => \\`encryption\\``,\n );\n\n await applyGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n `${DISTRIBUTION_LOGS_BUCKET_ENCRYPTION_OLD} => \\`encryption\\``,\n );\n\n await applyGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n `${WAF_STACK_OLD} => \\`const wafStack = enableWaf ? new CloudfrontWebAcl(this, 'waf') : undefined;\\``,\n );\n\n await applyGritQL(\n tree,\n CDK_STATIC_WEBSITE_FILE,\n `${WEB_ACL_ID_OLD} => \\`webAclId: wafStack?.wafArn\\``,\n );\n};\n\n/**\n * Surface the same props on each vended per-website app construct, so they\n * can be configured per app rather than only by editing the shared construct.\n */\nconst migrateCdkAppConstructs = async (\n tree: Tree,\n nextSteps: string[],\n): Promise<void> => {\n if (!tree.exists(CDK_STATIC_WEBSITES_APP_DIR)) {\n return;\n }\n\n for (const fileName of tree.children(CDK_STATIC_WEBSITES_APP_DIR)) {\n if (fileName === 'index.ts' || !fileName.endsWith('.ts')) {\n continue;\n }\n const filePath = joinPathFragments(CDK_STATIC_WEBSITES_APP_DIR, fileName);\n\n const contents = tree.read(filePath, 'utf-8') ?? '';\n if (contents.includes('StaticWebsiteProps')) {\n continue; // Already migrated.\n }\n\n const name = await captureGritQLVariable(\n tree,\n filePath,\n '`export class $name extends StaticWebsite { $_ }`',\n 'name',\n );\n if (!name) {\n continue; // Not a StaticWebsite subclass, not ours to touch.\n }\n\n const importPattern = '`import { StaticWebsite } from $path`';\n const ctorPattern = '`constructor(scope: Construct, id: string) { $body }`';\n // Captures the whole property list rather than anchoring on websiteName\n // being followed by exactly one more property ($rest binds a single\n // trailing node, not a variadic list). This must still match when a\n // workspace already customised the super() call with e.g. domainNames\n // and certificate (the pre-existing documented customization point).\n const superPattern = `\\`super(scope, id, { $props })\\` as $call where {\n $props <: contains \\`websiteName: '${name}'\\`\n}`;\n\n const ready = (\n await Promise.all(\n [importPattern, ctorPattern, superPattern].map((pattern) =>\n matchGritQL(tree, filePath, pattern),\n ),\n )\n ).every(Boolean);\n\n if (!ready) {\n nextSteps.push(\n `${filePath}: has diverged from the generated shape - left untouched. To make enableWaf, encryption, encryptionKey and enableKeyRotation configurable here, add an optional \\`props?: ${name}Props\\` constructor parameter (\\`Omit<StaticWebsiteProps, 'websiteName' | 'websiteFilePath'>\\`) and spread it into the super() call (see the ts#react-website generator's static-websites app template).`,\n );\n continue;\n }\n\n await applyGritQL(\n tree,\n filePath,\n `${importPattern} => \\`import { StaticWebsite, StaticWebsiteProps } from $path\\``,\n );\n\n await insertViaGritQL(\n tree,\n filePath,\n `\\`export class ${name} extends StaticWebsite { $body }\\` as $cls => \\`${GRIT_INSERT_PLACEHOLDER}\n\n$cls\\``,\n `export type ${name}Props = Omit<\\n StaticWebsiteProps,\\n 'websiteName' | 'websiteFilePath'\\n>;`,\n );\n\n await applyGritQL(\n tree,\n filePath,\n `${ctorPattern} => \\`constructor(scope: Construct, id: string, props?: ${name}Props) { $body }\\``,\n );\n\n await applyGritQL(\n tree,\n filePath,\n `${superPattern} => \\`super(scope, id, { ...props, $props })\\``,\n );\n }\n};\n\n// Terraform (HCL) patterns are built from plain strings rather than JS\n// template literals, since the HCL text itself is full of `${...}`\n// interpolations that would otherwise be parsed as JS interpolation.\nconst hcl = (pattern: string) => 'language hcl\\n' + pattern;\n\nconst withinResource = (\n linePattern: string,\n resourceType: string,\n resourceLabel: string,\n) =>\n hcl(\n '`' +\n linePattern +\n '` as $line where {\\n' +\n ' $line <: within `resource \"' +\n resourceType +\n '\" \"' +\n resourceLabel +\n '\" { $_ }`\\n' +\n '}',\n );\n\nconst NEW_VARIABLES_TEXT = [\n 'variable \"enable_waf\" {',\n ' description = \"Whether to protect the CloudFront distribution with an AWS WAF Web ACL.\"',\n ' type = bool',\n ' default = true',\n '}',\n '',\n 'variable \"encryption\" {',\n ' description = \"Server-side encryption for the website and distribution log buckets. One of KMS or S3_MANAGED.\"',\n ' type = string',\n ' default = \"KMS\"',\n '',\n ' validation {',\n ' condition = contains([\"KMS\", \"S3_MANAGED\"], var.encryption)',\n ' error_message = \"encryption must be one of KMS or S3_MANAGED.\"',\n ' }',\n '}',\n '',\n 'variable \"kms_key_arn\" {',\n ' description = \"ARN of an existing KMS key used to encrypt the website and distribution log buckets when encryption is KMS. When not provided and create_kms_key is true, a new key is created. Note that a customer-supplied key must already grant the CloudWatch Logs, S3 and CloudFront service principals the necessary permissions in its own key policy.\"',\n ' type = string',\n ' default = null',\n '}',\n '',\n 'variable \"create_kms_key\" {',\n ' description = \"Whether to create a KMS key for the website. Only applies when encryption is KMS. Set to false when supplying kms_key_arn.\"',\n ' type = bool',\n ' default = true',\n '}',\n '',\n 'variable \"enable_key_rotation\" {',\n ' description = \"Whether the automatically created KMS key has rotation enabled. Only applies when encryption is KMS and create_kms_key is true.\"',\n ' type = bool',\n ' default = true',\n '}',\n].join('\\n');\n\nconst NEW_LOCALS_TEXT = [\n '',\n ' create_website_key = var.encryption == \"KMS\" && var.create_kms_key',\n ' website_kms_key_arn = (',\n ' var.encryption != \"KMS\" ? null :',\n ' local.create_website_key ? aws_kms_key.website_key[0].arn :',\n ' var.kms_key_arn',\n ' )',\n].join('\\n');\n\n/**\n * Surface the same configuration on the vended Terraform static-website\n * module.\n */\nconst migrateTerraformModule = async (\n tree: Tree,\n nextSteps: string[],\n): Promise<void> => {\n if (!tree.exists(TERRAFORM_STATIC_WEBSITE_FILE)) {\n return; // This workspace has no Terraform static website module.\n }\n\n if (\n await matchGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n hcl('`variable \"enable_waf\" { $_ }`'),\n )\n ) {\n return; // Already migrated.\n }\n\n const ACM_CERT_VARIABLE = hcl('`variable \"acm_certificate_arn\" { $_ }`');\n const ACCESS_LOGS_LOCAL = hcl(\n '`access_logs_name_prefix = substr(lower(var.website_name), 0, 16)`',\n );\n const KMS_KEY_BLOCK = hcl('`resource \"aws_kms_key\" \"website_key\" { $_ }`');\n const KMS_KEY_ROTATION_LINE = withinResource(\n 'enable_key_rotation = true',\n 'aws_kms_key',\n 'website_key',\n );\n const KMS_ALIAS_BLOCK = hcl(\n '`resource \"aws_kms_alias\" \"website_key_alias\" { $_ }`',\n );\n const KMS_ALIAS_TARGET_LINE = hcl(\n '`target_key_id = aws_kms_key.website_key.key_id`',\n );\n const LOG_GROUP_KMS_LINE = hcl('`kms_key_id = aws_kms_key.website_key.arn`');\n const WEBSITE_ENCRYPTION_KEY_LINE = withinResource(\n 'kms_master_key_id = aws_kms_key.website_key.arn',\n 'aws_s3_bucket_server_side_encryption_configuration',\n 'website_encryption',\n );\n const WEBSITE_ENCRYPTION_ALGO_LINE = withinResource(\n 'sse_algorithm = \"aws:kms\"',\n 'aws_s3_bucket_server_side_encryption_configuration',\n 'website_encryption',\n );\n const DISTRIBUTION_LOGS_ENCRYPTION_KEY_LINE = withinResource(\n 'kms_master_key_id = aws_kms_key.website_key.arn',\n 'aws_s3_bucket_server_side_encryption_configuration',\n 'distribution_logs_encryption',\n );\n const DISTRIBUTION_LOGS_ENCRYPTION_ALGO_LINE = withinResource(\n 'sse_algorithm = \"aws:kms\"',\n 'aws_s3_bucket_server_side_encryption_configuration',\n 'distribution_logs_encryption',\n );\n const WAF_BLOCK = hcl(\n '`resource \"aws_wafv2_web_acl\" \"cloudfront_waf\" { $_ }`',\n );\n const WEB_ACL_ID_LINE = hcl(\n '`web_acl_id = aws_wafv2_web_acl.cloudfront_waf.arn`',\n );\n const WAF_OUTPUT_VALUE_LINE = hcl(\n '`value = aws_wafv2_web_acl.cloudfront_waf.arn` as $line where {\\n' +\n ' $line <: within `output \"waf_web_acl_arn\" { $_ }`\\n' +\n '}',\n );\n // Forces distribution replacement whenever the WAF ACL changes. Once the\n // ACL gains `count`, this can't be resolved on a greenfield apply where\n // count is 0 and there's no prior state to reference - see\n // https://github.com/awslabs/nx-plugin-for-aws/pull/1107. It's also\n // redundant: web_acl_id already references the ACL ARN directly, so a\n // replaced ACL still drives a plain distribution update.\n const LIFECYCLE_REPLACE_TRIGGERED_BY_BLOCK = hcl(\n '`lifecycle {\\n replace_triggered_by = [\\n aws_wafv2_web_acl.cloudfront_waf\\n ]\\n }`',\n );\n\n const anchors = [\n ACM_CERT_VARIABLE,\n ACCESS_LOGS_LOCAL,\n KMS_KEY_BLOCK,\n KMS_KEY_ROTATION_LINE,\n KMS_ALIAS_BLOCK,\n KMS_ALIAS_TARGET_LINE,\n LOG_GROUP_KMS_LINE,\n WEBSITE_ENCRYPTION_KEY_LINE,\n WEBSITE_ENCRYPTION_ALGO_LINE,\n DISTRIBUTION_LOGS_ENCRYPTION_KEY_LINE,\n DISTRIBUTION_LOGS_ENCRYPTION_ALGO_LINE,\n WAF_BLOCK,\n WEB_ACL_ID_LINE,\n WAF_OUTPUT_VALUE_LINE,\n LIFECYCLE_REPLACE_TRIGGERED_BY_BLOCK,\n ];\n\n const allPresent = (\n await Promise.all(\n anchors.map((pattern) =>\n matchGritQL(tree, TERRAFORM_STATIC_WEBSITE_FILE, pattern),\n ),\n )\n ).every(Boolean);\n\n if (!allPresent) {\n nextSteps.push(TERRAFORM_DIVERGED_MESSAGE);\n return;\n }\n\n // 1. New variables, after acm_certificate_arn. Routed through the\n // placeholder since the variable descriptions contain double-quoted\n // HCL string values.\n await insertViaGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n hcl(\n '`variable \"acm_certificate_arn\" { $body }` as $var => `$var\\n\\n' +\n GRIT_INSERT_PLACEHOLDER +\n '`',\n ),\n NEW_VARIABLES_TEXT,\n );\n\n // 2. New locals, after access_logs_name_prefix.\n await insertViaGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n hcl(\n '`access_logs_name_prefix = substr(lower(var.website_name), 0, 16)` as $line => `$line\\n' +\n GRIT_INSERT_PLACEHOLDER +\n '`',\n ),\n NEW_LOCALS_TEXT,\n );\n\n // 3. Make the KMS key and alias conditional on encryption/kms_key_arn.\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n hcl(\n '`resource \"aws_kms_key\" \"website_key\" { $body }` => `resource \"aws_kms_key\" \"website_key\" {\\n' +\n ' count = local.create_website_key ? 1 : 0\\n\\n' +\n ' $body\\n' +\n '}`',\n ),\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n KMS_KEY_ROTATION_LINE +\n ' => `enable_key_rotation = var.enable_key_rotation`',\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n hcl(\n '`resource \"aws_kms_alias\" \"website_key_alias\" { $body }` => `resource \"aws_kms_alias\" \"website_key_alias\" {\\n' +\n ' count = local.create_website_key ? 1 : 0\\n\\n' +\n ' $body\\n' +\n '}`',\n ),\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n KMS_ALIAS_TARGET_LINE +\n ' => `target_key_id = aws_kms_key.website_key[0].key_id`',\n );\n\n // 4. Resolve the key ARN through the new local everywhere it's consumed.\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n LOG_GROUP_KMS_LINE + ' => `kms_key_id = local.website_kms_key_arn`',\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n WEBSITE_ENCRYPTION_KEY_LINE +\n ' => `kms_master_key_id = var.encryption == \"KMS\" ? local.website_kms_key_arn : null`',\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n WEBSITE_ENCRYPTION_ALGO_LINE +\n ' => `sse_algorithm = var.encryption == \"KMS\" ? \"aws:kms\" : \"AES256\"`',\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n DISTRIBUTION_LOGS_ENCRYPTION_KEY_LINE +\n ' => `kms_master_key_id = var.encryption == \"KMS\" ? local.website_kms_key_arn : null`',\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n DISTRIBUTION_LOGS_ENCRYPTION_ALGO_LINE +\n ' => `sse_algorithm = var.encryption == \"KMS\" ? \"aws:kms\" : \"AES256\"`',\n );\n\n // 5. Make the WAF Web ACL conditional on enable_waf.\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n hcl(\n '`resource \"aws_wafv2_web_acl\" \"cloudfront_waf\" { $body }` => `resource \"aws_wafv2_web_acl\" \"cloudfront_waf\" {\\n' +\n ' count = var.enable_waf ? 1 : 0\\n\\n' +\n ' $body\\n' +\n '}`',\n ),\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n WEB_ACL_ID_LINE +\n ' => `web_acl_id = var.enable_waf ? aws_wafv2_web_acl.cloudfront_waf[0].arn : null`',\n );\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n WAF_OUTPUT_VALUE_LINE +\n ' => `value = var.enable_waf ? aws_wafv2_web_acl.cloudfront_waf[0].arn : null`',\n );\n\n // 6. Drop the lifecycle block that can no longer resolve once the WAF ACL\n // has a count of 0 and no prior state to reference.\n await applyGritQL(\n tree,\n TERRAFORM_STATIC_WEBSITE_FILE,\n LIFECYCLE_REPLACE_TRIGGERED_BY_BLOCK + ' => ``',\n );\n};\n\nconst NEW_APP_MODULE_VARIABLES_TEXT = [\n 'variable \"custom_domain_names\" {',\n ' description = \"Custom domain names (aliases) for the CloudFront distribution. Requires acm_certificate_arn.\"',\n ' type = list(string)',\n ' default = []',\n '}',\n '',\n 'variable \"acm_certificate_arn\" {',\n ' description = \"ARN of an ACM certificate (in us-east-1) for the custom domain names. When set, viewers are required to use TLS 1.2 or later.\"',\n ' type = string',\n ' default = null',\n '}',\n '',\n 'variable \"enable_waf\" {',\n ' description = \"Whether to protect the CloudFront distribution with an AWS WAF Web ACL.\"',\n ' type = bool',\n ' default = true',\n '}',\n '',\n 'variable \"encryption\" {',\n ' description = \"Server-side encryption for the website and distribution log buckets. One of KMS or S3_MANAGED.\"',\n ' type = string',\n ' default = \"KMS\"',\n '',\n ' validation {',\n ' condition = contains([\"KMS\", \"S3_MANAGED\"], var.encryption)',\n ' error_message = \"encryption must be one of KMS or S3_MANAGED.\"',\n ' }',\n '}',\n '',\n 'variable \"kms_key_arn\" {',\n ' description = \"ARN of an existing KMS key used to encrypt the website and distribution log buckets when encryption is KMS. When not provided and create_kms_key is true, a new key is created. Note that a customer-supplied key must already grant the CloudWatch Logs, S3 and CloudFront service principals the necessary permissions in its own key policy.\"',\n ' type = string',\n ' default = null',\n '}',\n '',\n 'variable \"create_kms_key\" {',\n ' description = \"Whether to create a KMS key for the website. Only applies when encryption is KMS. Set to false when supplying kms_key_arn.\"',\n ' type = bool',\n ' default = true',\n '}',\n '',\n 'variable \"enable_key_rotation\" {',\n ' description = \"Whether the automatically created KMS key has rotation enabled. Only applies when encryption is KMS and create_kms_key is true.\"',\n ' type = bool',\n ' default = true',\n '}',\n].join('\\n');\n\nconst NEW_APP_MODULE_FORWARD_TEXT = [\n 'custom_domain_names = var.custom_domain_names',\n 'acm_certificate_arn = var.acm_certificate_arn',\n 'enable_waf = var.enable_waf',\n 'encryption = var.encryption',\n 'kms_key_arn = var.kms_key_arn',\n 'create_kms_key = var.create_kms_key',\n 'enable_key_rotation = var.enable_key_rotation',\n].join('\\n ');\n\n/**\n * Surface the same configuration as pass-through variables on each vended\n * per-website Terraform app module, matching the pass-through convention\n * every other app-wraps-core Terraform module already follows.\n */\nconst migrateTerraformAppModules = async (\n tree: Tree,\n nextSteps: string[],\n): Promise<void> => {\n if (!tree.exists(TERRAFORM_STATIC_WEBSITES_APP_DIR)) {\n return;\n }\n\n for (const dirName of tree.children(TERRAFORM_STATIC_WEBSITES_APP_DIR)) {\n const filePath = joinPathFragments(\n TERRAFORM_STATIC_WEBSITES_APP_DIR,\n dirName,\n `${dirName}.tf`,\n );\n if (!tree.exists(filePath)) {\n continue; // Not a website app module directory.\n }\n\n if (\n await matchGritQL(tree, filePath, hcl('`variable \"enable_waf\" { $_ }`'))\n ) {\n continue; // Already migrated.\n }\n\n const TERRAFORM_BLOCK = hcl('`terraform { $_ }`');\n const MODULE_BLOCK = hcl('`module \"static_website\" { $_ }`');\n const FILE_PATH_LINE = hcl(\n '`website_file_path = $val` as $line where {\\n' +\n ' $line <: within `module \"static_website\" { $_ }`\\n' +\n '}',\n );\n // custom_domain_names/acm_certificate_arn predate this migration as a\n // documented customization point (hand-editing the module block). If\n // either is already present as a literal argument, blindly forwarding\n // our own `= var.x` line would produce a duplicate argument, which is\n // invalid HCL. Treat that as diverged instead of silently corrupting the file.\n const hasExistingCustomDomainArg = async (name: string) =>\n matchGritQL(\n tree,\n filePath,\n hcl(\n `\\`${name} = $_\\` as $line where {\\n` +\n ' $line <: within `module \"static_website\" { $_ }`\\n' +\n '}',\n ),\n );\n\n const ready =\n (\n await Promise.all(\n [TERRAFORM_BLOCK, MODULE_BLOCK, FILE_PATH_LINE].map((pattern) =>\n matchGritQL(tree, filePath, pattern),\n ),\n )\n ).every(Boolean) &&\n !(await hasExistingCustomDomainArg('custom_domain_names')) &&\n !(await hasExistingCustomDomainArg('acm_certificate_arn'));\n\n if (!ready) {\n nextSteps.push(terraformAppDivergedMessage(filePath));\n continue;\n }\n\n // Inserted right after the terraform/required_providers block (and\n // before the \"Static website module\" comment), matching where the\n // vended template puts these variables.\n await insertViaGritQL(\n tree,\n filePath,\n hcl('`terraform { $body }` as $tf') +\n ` => \\`$tf\\n\\n${GRIT_INSERT_PLACEHOLDER}\\``,\n NEW_APP_MODULE_VARIABLES_TEXT,\n );\n\n await insertViaGritQL(\n tree,\n filePath,\n FILE_PATH_LINE + ` => \\`$line\\n\\n ${GRIT_INSERT_PLACEHOLDER}\\``,\n NEW_APP_MODULE_FORWARD_TEXT,\n );\n }\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n await migrateCdkConstruct(tree, nextSteps);\n await migrateCdkAppConstructs(tree, nextSteps);\n await migrateTerraformModule(tree, nextSteps);\n await migrateTerraformAppModules(tree, nextSteps);\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["joinPathFragments","addDestructuredImport","applyGritQL","captureGritQLVariable","GRIT_INSERT_PLACEHOLDER","insertViaGritQL","matchGritQL","formatFilesInSubtree","PACKAGES_DIR","SHARED_CONSTRUCTS_DIR","SHARED_TERRAFORM_DIR","CDK_STATIC_WEBSITE_FILE","CDK_STATIC_WEBSITES_APP_DIR","TERRAFORM_STATIC_WEBSITE_FILE","TERRAFORM_STATIC_WEBSITES_APP_DIR","CDK_DIVERGED_MESSAGE","TERRAFORM_DIVERGED_MESSAGE","terraformAppDivergedMessage","filePath","STATIC_WEBSITE_PROPS_TEXT","migrateCdkConstruct","tree","nextSteps","exists","CERTIFICATE_FIELD","DESTRUCTURE_OLD","KEY_CREATION_OLD","ADD_TO_RESOURCE_POLICY_OLD","WEBSITE_BUCKET_ENCRYPTION_OLD","DISTRIBUTION_LOGS_BUCKET_ENCRYPTION_OLD","WAF_STACK_OLD","WEB_ACL_ID_OLD","anchors","allPresent","Promise","all","map","pattern","every","Boolean","push","migrateCdkAppConstructs","fileName","children","endsWith","contents","read","includes","name","importPattern","ctorPattern","superPattern","ready","hcl","withinResource","linePattern","resourceType","resourceLabel","NEW_VARIABLES_TEXT","join","NEW_LOCALS_TEXT","migrateTerraformModule","ACM_CERT_VARIABLE","ACCESS_LOGS_LOCAL","KMS_KEY_BLOCK","KMS_KEY_ROTATION_LINE","KMS_ALIAS_BLOCK","KMS_ALIAS_TARGET_LINE","LOG_GROUP_KMS_LINE","WEBSITE_ENCRYPTION_KEY_LINE","WEBSITE_ENCRYPTION_ALGO_LINE","DISTRIBUTION_LOGS_ENCRYPTION_KEY_LINE","DISTRIBUTION_LOGS_ENCRYPTION_ALGO_LINE","WAF_BLOCK","WEB_ACL_ID_LINE","WAF_OUTPUT_VALUE_LINE","LIFECYCLE_REPLACE_TRIGGERED_BY_BLOCK","NEW_APP_MODULE_VARIABLES_TEXT","NEW_APP_MODULE_FORWARD_TEXT","migrateTerraformAppModules","dirName","TERRAFORM_BLOCK","MODULE_BLOCK","FILE_PATH_LINE","hasExistingCustomDomainArg","migration"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/latest/ts-agent-session-id-middleware-extraction/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport {\n getProjects,\n joinPathFragments,\n type MigrationReturnObject,\n type Tree,\n} from '@nx/devkit';\nimport { TS_AGENT_GENERATOR_INFO } from '../../../ts/agent/generator';\nimport {\n addDestructuredImport,\n applyGritQL,\n captureGritQLVariable,\n matchGritQL,\n} from '../../../utils/ast';\nimport { formatFilesInSubtree } from '../../../utils/format';\nimport type { ComponentMetadata } from '../../../utils/nx';\n\n/**\n * Extract the inline session-id Express middleware - previously duplicated\n * verbatim in every ts#agent AG-UI/A2A `index.ts` - into a shared\n * `middleware/session-id-middleware.ts` module sitting beside `index.ts`,\n * mirroring how `agent.ts`/`session.ts` are already shared siblings.\n *\n * HTTP is untouched: its one procedure is a tRPC subscription served over\n * WebSocket, which bypasses Express entirely, so it never had this middleware\n * to begin with (see `enterSessionContext` in its `router.ts` instead).\n *\n * AG-UI and A2A wire the same logic up differently - AG-UI binds it to a\n * named `sessionIdMiddleware` const before use, while A2A inlines it directly\n * at the `app.use` call site - so each gets its own old-shape pattern below,\n * even though both resolve to the same shared middleware content.\n *\n * Guardrails:\n * - Pattern-match before writing: skip files that have diverged from the\n * generated shape and report them via `nextSteps`, rather than clobbering\n * the user's changes.\n * - Idempotent: re-running is a no-op once migrated.\n */\n\n// The consuming repo's formatter/linter (e.g. Biome, ESLint import/order) may\n// alphabetize these three named type imports, so match/rewrite any of their\n// orderings rather than only the literal order the generator emits. Fixed-\n// arity destructuring (`[$a, $b, $c]`) pins the specifier count to exactly\n// three - so a 4th merged-in specifier fails to match - while constraining\n// each slot independently makes the check order-independent.\nconst EXPRESS_IMPORT_OLD_WHERE = `{\n $clause <: import_clause(default=\\`express\\`, name=named_imports(imports=[$a, $b, $c])),\n $a <: or { \\`type Request\\`, \\`type Response\\`, \\`type NextFunction\\` },\n $b <: or { \\`type Request\\`, \\`type Response\\`, \\`type NextFunction\\` },\n $c <: or { \\`type Request\\`, \\`type Response\\`, \\`type NextFunction\\` }\n}`;\nconst EXPRESS_IMPORT_OLD_MATCH = `\\`import $clause from 'express';\\` where ${EXPRESS_IMPORT_OLD_WHERE}`;\nconst EXPRESS_IMPORT_OLD_REWRITE = `\\`import $clause from 'express';\\` => \\`import express from 'express';\\` where ${EXPRESS_IMPORT_OLD_WHERE}`;\n\nconst RANDOM_UUID_IMPORT = \"import { randomUUID } from 'node:crypto';\";\n\nconst SESSION_ID_HEADER_LINE =\n \"const SESSION_ID_HEADER = 'x-amzn-bedrock-agentcore-runtime-session-id';\";\n\n// The leading comment sits directly above both shapes below. GritQL can only\n// match it as its own comment node - folding it into the same backtick\n// snippet as the statement that follows fails to parse as one construct - so\n// it's matched/deleted as a separate step from the statement/expression.\nconst MIDDLEWARE_LEADING_COMMENT =\n '// Bind the inbound session (or a fresh UUID) for downstream MCP / A2A calls.';\n\n// AG-UI binds the middleware to a named const, used later via `app.use(sessionIdMiddleware)`.\nconst AGUI_MIDDLEWARE_DEFINITION_OLD = `const sessionIdMiddleware = (req: Request, _res: Response, next: NextFunction) => {\n const header = req.headers[SESSION_ID_HEADER];\n const sessionId = (Array.isArray(header) ? header[0] : header) ?? randomUUID();\n runWithSessionId(sessionId, () => next());\n};`;\n\n// A2A inlines the same logic directly at the `app.use` call site instead.\nconst A2A_MIDDLEWARE_USE_OLD = `app.use((req: Request, _res: Response, next: NextFunction) => {\n const header = req.headers[SESSION_ID_HEADER];\n const sessionId = (Array.isArray(header) ? header[0] : header) ?? randomUUID();\n runWithSessionId(sessionId, () => next());\n });`;\nconst A2A_MIDDLEWARE_USE_NEW = 'app.use(sessionIdMiddleware);';\n\n// Captures the agent-connection package specifier so the new middleware\n// module targets the same one. Generic over `$names` since a connection\n// generator may have merged its own import into this same statement.\nconst RUNWITHSESSIONID_IMPORT_CAPTURE =\n \"`import { $names } from '$mod';` where { $names <: contains `runWithSessionId` }\";\n\n// Removes `runWithSessionId` from the named import, preserving any other\n// merged specifiers (or the whole statement if it was the only one).\n// Decomposed via `import_clause(name=named_imports($imports))` since a naive\n// `$rest`-based rewrite silently fails once other specifiers are involved.\nconst REMOVE_RUNWITHSESSIONID_IMPORT_PATTERN = `\\`import $clause from '$mod';\\` as $import where {\n $clause <: import_clause(name=named_imports($imports)),\n $imports <: contains \\`runWithSessionId\\`,\n if ($imports <: [\\`runWithSessionId\\`]) { $import => . } else { $imports <: some import_specifier(name=\\`runWithSessionId\\`) => . }\n}`;\n\n// Both protocols' `common-express/` dir vends byte-identical middleware\n// content; read as the single source of truth rather than hand-duplicating\n// it here.\nconst SESSION_ID_MIDDLEWARE_TEMPLATE = readFileSync(\n join(\n import.meta.dirname,\n '../../../ts/agent/files/common-express/middleware/session-id-middleware.ts.template',\n ),\n 'utf-8',\n);\n\nconst sessionIdMiddlewareContent = (agentConnectionImport: string): string =>\n SESSION_ID_MIDDLEWARE_TEMPLATE.replace(\n '<%- agentConnectionImport %>',\n agentConnectionImport,\n );\n\nconst findAgentComponents = (\n components: ComponentMetadata[] | undefined,\n): ComponentMetadata[] =>\n (components ?? []).filter(\n (component) =>\n component.generator === TS_AGENT_GENERATOR_INFO.id &&\n (component.protocol === 'ag-ui' || component.protocol === 'a2a'),\n );\n\nconst migrateIndex = async (\n tree: Tree,\n indexPath: string,\n protocol: 'ag-ui' | 'a2a',\n nextSteps: string[],\n): Promise<void> => {\n if (!tree.exists(indexPath)) return;\n\n const contents = tree.read(indexPath, 'utf-8') ?? '';\n if (!contents.includes(SESSION_ID_HEADER_LINE)) return;\n\n const middlewarePattern =\n protocol === 'ag-ui'\n ? AGUI_MIDDLEWARE_DEFINITION_OLD\n : A2A_MIDDLEWARE_USE_OLD;\n\n const diverged = () => {\n nextSteps.push(\n `${indexPath}: diverged from the generated ts#agent ${protocol} shape - left untouched. Manually move the inline session-id middleware into a sibling \\`middleware/session-id-middleware.ts\\` module and import \\`sessionIdMiddleware\\` from there (see the ts#agent generator's template).`,\n );\n };\n\n const ready =\n (await matchGritQL(tree, indexPath, EXPRESS_IMPORT_OLD_MATCH)) &&\n (await matchGritQL(tree, indexPath, `\\`${RANDOM_UUID_IMPORT}\\``)) &&\n (await matchGritQL(tree, indexPath, `\\`${SESSION_ID_HEADER_LINE}\\``)) &&\n (await matchGritQL(tree, indexPath, `\\`${MIDDLEWARE_LEADING_COMMENT}\\``)) &&\n (await matchGritQL(tree, indexPath, `\\`${middlewarePattern}\\``)) &&\n (await matchGritQL(tree, indexPath, RUNWITHSESSIONID_IMPORT_CAPTURE));\n\n if (!ready) {\n diverged();\n return;\n }\n\n const mod = await captureGritQLVariable(\n tree,\n indexPath,\n RUNWITHSESSIONID_IMPORT_CAPTURE,\n 'mod',\n );\n if (!mod) {\n diverged();\n return;\n }\n\n const dir = indexPath.split('/').slice(0, -1).join('/');\n const middlewarePath = joinPathFragments(\n dir,\n 'middleware',\n 'session-id-middleware.ts',\n );\n\n if (!tree.exists(middlewarePath)) {\n tree.write(middlewarePath, sessionIdMiddlewareContent(mod));\n }\n\n await applyGritQL(tree, indexPath, EXPRESS_IMPORT_OLD_REWRITE);\n await applyGritQL(tree, indexPath, `\\`${RANDOM_UUID_IMPORT}\\` => .`);\n await applyGritQL(tree, indexPath, `\\`${SESSION_ID_HEADER_LINE}\\` => .`);\n await applyGritQL(tree, indexPath, REMOVE_RUNWITHSESSIONID_IMPORT_PATTERN);\n await applyGritQL(tree, indexPath, `\\`${MIDDLEWARE_LEADING_COMMENT}\\` => .`);\n\n if (protocol === 'ag-ui') {\n await applyGritQL(\n tree,\n indexPath,\n `\\`${AGUI_MIDDLEWARE_DEFINITION_OLD}\\` => .`,\n );\n } else {\n await applyGritQL(\n tree,\n indexPath,\n `\\`${A2A_MIDDLEWARE_USE_OLD}\\` => \\`${A2A_MIDDLEWARE_USE_NEW}\\``,\n );\n }\n\n await addDestructuredImport(\n tree,\n indexPath,\n ['sessionIdMiddleware'],\n './middleware/session-id-middleware.js',\n );\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n for (const project of getProjects(tree).values()) {\n const components = findAgentComponents(\n (project.metadata as { components?: ComponentMetadata[] })?.components,\n );\n\n for (const component of components) {\n if (!component.path) continue;\n\n const indexPath = joinPathFragments(\n project.root,\n component.path,\n 'index.ts',\n );\n\n await migrateIndex(\n tree,\n indexPath,\n component.protocol as 'ag-ui' | 'a2a',\n nextSteps,\n );\n }\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["readFileSync","join","getProjects","joinPathFragments","TS_AGENT_GENERATOR_INFO","addDestructuredImport","applyGritQL","captureGritQLVariable","matchGritQL","formatFilesInSubtree","EXPRESS_IMPORT_OLD_WHERE","EXPRESS_IMPORT_OLD_MATCH","EXPRESS_IMPORT_OLD_REWRITE","RANDOM_UUID_IMPORT","SESSION_ID_HEADER_LINE","MIDDLEWARE_LEADING_COMMENT","AGUI_MIDDLEWARE_DEFINITION_OLD","A2A_MIDDLEWARE_USE_OLD","A2A_MIDDLEWARE_USE_NEW","RUNWITHSESSIONID_IMPORT_CAPTURE","REMOVE_RUNWITHSESSIONID_IMPORT_PATTERN","SESSION_ID_MIDDLEWARE_TEMPLATE","dirname","sessionIdMiddlewareContent","agentConnectionImport","replace","findAgentComponents","components","filter","component","generator","id","protocol","migrateIndex","tree","indexPath","nextSteps","exists","contents","read","includes","middlewarePattern","diverged","push","ready","mod","dir","split","slice","middlewarePath","write","migration","project","values","metadata","path","root"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/latest/ts-agent-session-id-middleware-extraction/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport { readFileSync } from 'node:fs';\nimport { join } from 'node:path';\nimport {\n getProjects,\n joinPathFragments,\n type MigrationReturnObject,\n type Tree,\n} from '@nx/devkit';\nimport { TS_AGENT_GENERATOR_INFO } from '../../../ts/agent/generator.js';\nimport {\n addDestructuredImport,\n applyGritQL,\n captureGritQLVariable,\n matchGritQL,\n} from '../../../utils/ast.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\nimport type { ComponentMetadata } from '../../../utils/nx.js';\n\n/**\n * Extract the inline session-id Express middleware - previously duplicated\n * verbatim in every ts#agent AG-UI/A2A `index.ts` - into a shared\n * `middleware/session-id-middleware.ts` module sitting beside `index.ts`,\n * mirroring how `agent.ts`/`session.ts` are already shared siblings.\n *\n * HTTP is untouched: its one procedure is a tRPC subscription served over\n * WebSocket, which bypasses Express entirely, so it never had this middleware\n * to begin with (see `enterSessionContext` in its `router.ts` instead).\n *\n * AG-UI and A2A wire the same logic up differently - AG-UI binds it to a\n * named `sessionIdMiddleware` const before use, while A2A inlines it directly\n * at the `app.use` call site - so each gets its own old-shape pattern below,\n * even though both resolve to the same shared middleware content.\n *\n * Guardrails:\n * - Pattern-match before writing: skip files that have diverged from the\n * generated shape and report them via `nextSteps`, rather than clobbering\n * the user's changes.\n * - Idempotent: re-running is a no-op once migrated.\n */\n\n// The consuming repo's formatter/linter (e.g. Biome, ESLint import/order) may\n// alphabetize these three named type imports, so match/rewrite any of their\n// orderings rather than only the literal order the generator emits. Fixed-\n// arity destructuring (`[$a, $b, $c]`) pins the specifier count to exactly\n// three - so a 4th merged-in specifier fails to match - while constraining\n// each slot independently makes the check order-independent.\nconst EXPRESS_IMPORT_OLD_WHERE = `{\n $clause <: import_clause(default=\\`express\\`, name=named_imports(imports=[$a, $b, $c])),\n $a <: or { \\`type Request\\`, \\`type Response\\`, \\`type NextFunction\\` },\n $b <: or { \\`type Request\\`, \\`type Response\\`, \\`type NextFunction\\` },\n $c <: or { \\`type Request\\`, \\`type Response\\`, \\`type NextFunction\\` }\n}`;\nconst EXPRESS_IMPORT_OLD_MATCH = `\\`import $clause from 'express';\\` where ${EXPRESS_IMPORT_OLD_WHERE}`;\nconst EXPRESS_IMPORT_OLD_REWRITE = `\\`import $clause from 'express';\\` => \\`import express from 'express';\\` where ${EXPRESS_IMPORT_OLD_WHERE}`;\n\nconst RANDOM_UUID_IMPORT = \"import { randomUUID } from 'node:crypto';\";\n\nconst SESSION_ID_HEADER_LINE =\n \"const SESSION_ID_HEADER = 'x-amzn-bedrock-agentcore-runtime-session-id';\";\n\n// The leading comment sits directly above both shapes below. GritQL can only\n// match it as its own comment node - folding it into the same backtick\n// snippet as the statement that follows fails to parse as one construct - so\n// it's matched/deleted as a separate step from the statement/expression.\nconst MIDDLEWARE_LEADING_COMMENT =\n '// Bind the inbound session (or a fresh UUID) for downstream MCP / A2A calls.';\n\n// AG-UI binds the middleware to a named const, used later via `app.use(sessionIdMiddleware)`.\nconst AGUI_MIDDLEWARE_DEFINITION_OLD = `const sessionIdMiddleware = (req: Request, _res: Response, next: NextFunction) => {\n const header = req.headers[SESSION_ID_HEADER];\n const sessionId = (Array.isArray(header) ? header[0] : header) ?? randomUUID();\n runWithSessionId(sessionId, () => next());\n};`;\n\n// A2A inlines the same logic directly at the `app.use` call site instead.\nconst A2A_MIDDLEWARE_USE_OLD = `app.use((req: Request, _res: Response, next: NextFunction) => {\n const header = req.headers[SESSION_ID_HEADER];\n const sessionId = (Array.isArray(header) ? header[0] : header) ?? randomUUID();\n runWithSessionId(sessionId, () => next());\n });`;\nconst A2A_MIDDLEWARE_USE_NEW = 'app.use(sessionIdMiddleware);';\n\n// Captures the agent-connection package specifier so the new middleware\n// module targets the same one. Generic over `$names` since a connection\n// generator may have merged its own import into this same statement.\nconst RUNWITHSESSIONID_IMPORT_CAPTURE =\n \"`import { $names } from '$mod';` where { $names <: contains `runWithSessionId` }\";\n\n// Removes `runWithSessionId` from the named import, preserving any other\n// merged specifiers (or the whole statement if it was the only one).\n// Decomposed via `import_clause(name=named_imports($imports))` since a naive\n// `$rest`-based rewrite silently fails once other specifiers are involved.\nconst REMOVE_RUNWITHSESSIONID_IMPORT_PATTERN = `\\`import $clause from '$mod';\\` as $import where {\n $clause <: import_clause(name=named_imports($imports)),\n $imports <: contains \\`runWithSessionId\\`,\n if ($imports <: [\\`runWithSessionId\\`]) { $import => . } else { $imports <: some import_specifier(name=\\`runWithSessionId\\`) => . }\n}`;\n\n// Both protocols' `common-express/` dir vends byte-identical middleware\n// content; read as the single source of truth rather than hand-duplicating\n// it here.\nconst SESSION_ID_MIDDLEWARE_TEMPLATE = readFileSync(\n join(\n import.meta.dirname,\n '../../../ts/agent/files/common-express/middleware/session-id-middleware.ts.template',\n ),\n 'utf-8',\n);\n\nconst sessionIdMiddlewareContent = (agentConnectionImport: string): string =>\n SESSION_ID_MIDDLEWARE_TEMPLATE.replace(\n '<%- agentConnectionImport %>',\n agentConnectionImport,\n );\n\nconst findAgentComponents = (\n components: ComponentMetadata[] | undefined,\n): ComponentMetadata[] =>\n (components ?? []).filter(\n (component) =>\n component.generator === TS_AGENT_GENERATOR_INFO.id &&\n (component.protocol === 'ag-ui' || component.protocol === 'a2a'),\n );\n\nconst migrateIndex = async (\n tree: Tree,\n indexPath: string,\n protocol: 'ag-ui' | 'a2a',\n nextSteps: string[],\n): Promise<void> => {\n if (!tree.exists(indexPath)) return;\n\n const contents = tree.read(indexPath, 'utf-8') ?? '';\n if (!contents.includes(SESSION_ID_HEADER_LINE)) return;\n\n const middlewarePattern =\n protocol === 'ag-ui'\n ? AGUI_MIDDLEWARE_DEFINITION_OLD\n : A2A_MIDDLEWARE_USE_OLD;\n\n const diverged = () => {\n nextSteps.push(\n `${indexPath}: diverged from the generated ts#agent ${protocol} shape - left untouched. Manually move the inline session-id middleware into a sibling \\`middleware/session-id-middleware.ts\\` module and import \\`sessionIdMiddleware\\` from there (see the ts#agent generator's template).`,\n );\n };\n\n const ready =\n (await matchGritQL(tree, indexPath, EXPRESS_IMPORT_OLD_MATCH)) &&\n (await matchGritQL(tree, indexPath, `\\`${RANDOM_UUID_IMPORT}\\``)) &&\n (await matchGritQL(tree, indexPath, `\\`${SESSION_ID_HEADER_LINE}\\``)) &&\n (await matchGritQL(tree, indexPath, `\\`${MIDDLEWARE_LEADING_COMMENT}\\``)) &&\n (await matchGritQL(tree, indexPath, `\\`${middlewarePattern}\\``)) &&\n (await matchGritQL(tree, indexPath, RUNWITHSESSIONID_IMPORT_CAPTURE));\n\n if (!ready) {\n diverged();\n return;\n }\n\n const mod = await captureGritQLVariable(\n tree,\n indexPath,\n RUNWITHSESSIONID_IMPORT_CAPTURE,\n 'mod',\n );\n if (!mod) {\n diverged();\n return;\n }\n\n const dir = indexPath.split('/').slice(0, -1).join('/');\n const middlewarePath = joinPathFragments(\n dir,\n 'middleware',\n 'session-id-middleware.ts',\n );\n\n if (!tree.exists(middlewarePath)) {\n tree.write(middlewarePath, sessionIdMiddlewareContent(mod));\n }\n\n await applyGritQL(tree, indexPath, EXPRESS_IMPORT_OLD_REWRITE);\n await applyGritQL(tree, indexPath, `\\`${RANDOM_UUID_IMPORT}\\` => .`);\n await applyGritQL(tree, indexPath, `\\`${SESSION_ID_HEADER_LINE}\\` => .`);\n await applyGritQL(tree, indexPath, REMOVE_RUNWITHSESSIONID_IMPORT_PATTERN);\n await applyGritQL(tree, indexPath, `\\`${MIDDLEWARE_LEADING_COMMENT}\\` => .`);\n\n if (protocol === 'ag-ui') {\n await applyGritQL(\n tree,\n indexPath,\n `\\`${AGUI_MIDDLEWARE_DEFINITION_OLD}\\` => .`,\n );\n } else {\n await applyGritQL(\n tree,\n indexPath,\n `\\`${A2A_MIDDLEWARE_USE_OLD}\\` => \\`${A2A_MIDDLEWARE_USE_NEW}\\``,\n );\n }\n\n await addDestructuredImport(\n tree,\n indexPath,\n ['sessionIdMiddleware'],\n './middleware/session-id-middleware.js',\n );\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n for (const project of getProjects(tree).values()) {\n const components = findAgentComponents(\n (project.metadata as { components?: ComponentMetadata[] })?.components,\n );\n\n for (const component of components) {\n if (!component.path) continue;\n\n const indexPath = joinPathFragments(\n project.root,\n component.path,\n 'index.ts',\n );\n\n await migrateIndex(\n tree,\n indexPath,\n component.protocol as 'ag-ui' | 'a2a',\n nextSteps,\n );\n }\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["readFileSync","join","getProjects","joinPathFragments","TS_AGENT_GENERATOR_INFO","addDestructuredImport","applyGritQL","captureGritQLVariable","matchGritQL","formatFilesInSubtree","EXPRESS_IMPORT_OLD_WHERE","EXPRESS_IMPORT_OLD_MATCH","EXPRESS_IMPORT_OLD_REWRITE","RANDOM_UUID_IMPORT","SESSION_ID_HEADER_LINE","MIDDLEWARE_LEADING_COMMENT","AGUI_MIDDLEWARE_DEFINITION_OLD","A2A_MIDDLEWARE_USE_OLD","A2A_MIDDLEWARE_USE_NEW","RUNWITHSESSIONID_IMPORT_CAPTURE","REMOVE_RUNWITHSESSIONID_IMPORT_PATTERN","SESSION_ID_MIDDLEWARE_TEMPLATE","dirname","sessionIdMiddlewareContent","agentConnectionImport","replace","findAgentComponents","components","filter","component","generator","id","protocol","migrateIndex","tree","indexPath","nextSteps","exists","contents","read","includes","middlewarePattern","diverged","push","ready","mod","dir","split","slice","middlewarePath","write","migration","project","values","metadata","path","root"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/latest/user-identity-configurable-mfa/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport type { MigrationReturnObject, Tree } from '@nx/devkit';\nimport {\n addDestructuredImport,\n applyGritQL,\n matchGritQL,\n} from '../../../utils/ast';\nimport { formatFilesInSubtree } from '../../../utils/format';\nimport {\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n SHARED_TERRAFORM_DIR,\n} from '../../../utils/shared-constructs-constants';\n\n/**\n * Surface mfa and mfaSecondFactor on the vended UserIdentity construct/module\n *\n * CDK's `mfa` / `mfaSecondFactor` props default to what the construct already\n * hard-coded (`Mfa.REQUIRED` / `{ sms: true, otp: true }`), so this just makes\n * them overridable, matching the existing `enableWaf` prop pattern.\n *\n * Terraform has no equivalent of CloudFormation's `EnabledMfas` - SMS MFA is\n * inseparable from the `sms_configuration` block, which phone number\n * verification also relies on. So `mfa_second_factor_sms` gates\n * `sms_configuration` (and, with it, SMS phone verification) rather than only\n * MFA, unlike the CDK prop.\n *\n * CDK has the same underlying coupling for any non-OFF `mfa`: Cognito rejects\n * `SmsConfiguration` (which CDK emits whenever phone is auto-verified) unless\n * SMS is also an enabled MFA method. So `phoneVerificationEnabled` gates\n * `autoVerify`/`keepOriginal`'s `phone` flag on whether SMS is actually usable\n * as a second factor, and a `mfa: REQUIRED` with neither `sms` nor `otp`\n * enabled - a pool nobody could ever sign into - is rejected at synth time.\n *\n * How to write a migration:\n * - https://nx.dev/docs/kb/migration-generators\n * - What `nextSteps` means: https://nx.dev/docs/reference/devkit/MigrationReturnObject\n *\n * Guardrails:\n * - Pattern-match before writing: skip files that have diverged from the shape\n * your generators produce and report them via `nextSteps`, rather than\n * clobbering the user's changes.\n * - Idempotent: re-running must be a no-op.\n * - Format what you write: finish with `formatFilesInSubtree` so the files your\n * migration wrote are formatted correctly.\n */\n\nconst CDK_USER_IDENTITY_FILE = `${PACKAGES_DIR}/${SHARED_CONSTRUCTS_DIR}/src/core/user-identity.ts`;\nconst TERRAFORM_IDENTITY_FILE = `${PACKAGES_DIR}/${SHARED_TERRAFORM_DIR}/src/core/user-identity/identity/identity.tf`;\nconst TERRAFORM_MAIN_FILE = `${PACKAGES_DIR}/${SHARED_TERRAFORM_DIR}/src/core/user-identity/main.tf`;\n\nconst NEW_TERRAFORM_VARIABLES = `variable \"mfa\" {\n description = \"Whether users must configure MFA, may optionally configure MFA, or cannot use MFA at all\"\n type = string\n default = \"ON\"\n\n validation {\n condition = contains([\"OFF\", \"OPTIONAL\", \"ON\"], var.mfa)\n error_message = \"mfa must be one of OFF, OPTIONAL, ON\"\n }\n}\n\nvariable \"mfa_second_factor_otp\" {\n description = \"Whether users can use a time-based one time password (TOTP), generated by a hardware or software token, as an MFA method\"\n type = bool\n default = true\n}\n\nvariable \"mfa_second_factor_sms\" {\n description = \"Whether users can use SMS to their verified phone numbers as an MFA method. Due to a Terraform AWS provider limitation, disabling this also disables SMS-based phone number verification, unlike the equivalent CDK construct prop\"\n type = bool\n default = true\n}`;\n\n// Guards each file: present only once it has been migrated.\nconst CDK_MIGRATED_PATTERN = '`mfa === Mfa.OFF || mfaSecondFactor.sms`';\nconst TERRAFORM_IDENTITY_MIGRATED_PATTERN =\n 'language hcl\\n`mfa_configuration = var.mfa`';\nconst TERRAFORM_MAIN_MIGRATED_PATTERN = 'language hcl\\n`mfa = var.mfa`';\n\n// Interface members don't parse as standalone snippets, so this anchors on the\n// whole interface body (preserved via $body) rather than the enableWaf member.\nconst CDK_INTERFACE_BODY_PATTERN =\n '`export interface UserIdentityProps { $body }`';\n// A parameter's type annotation blocks matching the destructure sub-pattern in\n// isolation, so this anchors on the full constructor signature instead\n// (literal params so a customised signature is correctly seen as diverged).\nconst CDK_CONSTRUCTOR_PATTERN =\n '`constructor(scope: Construct, id: string, { enableWaf = true }: UserIdentityProps = {}) { $body }`';\nconst CDK_CREATE_CALL_PATTERN = '`this.createUserPool()`';\nconst CDK_CREATE_SIGNATURE_PATTERN = '`private createUserPool = () => $body`';\n// Trailing commas block matching a bare object property in isolation; the\n// separator is outside the matched span either way, so it survives untouched.\nconst CDK_MFA_FIELD_PATTERN = '`mfa: Mfa.REQUIRED`';\nconst CDK_MFA_SECOND_FACTOR_FIELD_PATTERN =\n '`mfaSecondFactor: { sms: true, otp: true }`';\nconst CDK_SUPER_CALL_PATTERN = '`super(scope, id);`';\n// $props preserves the whole (large) UserPool props object untouched while\n// inserting the new const declaration immediately before it.\nconst CDK_NEW_USER_POOL_PATTERN =\n \"`const userPool = new UserPool(this, 'UserPool', { $props })`\";\n// Matches both the autoVerify.phone and keepOriginal.phone occurrences in one pass.\nconst CDK_PHONE_TRUE_PATTERN = '`phone: true`';\n\nconst CDK_EDITS: Array<[string, string]> = [\n [\n CDK_INTERFACE_BODY_PATTERN,\n `${CDK_INTERFACE_BODY_PATTERN} => \\`export interface UserIdentityProps {\n $body\n\n /**\n * Whether users must configure MFA, may optionally configure MFA, or cannot use MFA at all.\n *\n * @default Mfa.REQUIRED\n */\n readonly mfa?: Mfa;\n\n /**\n * The MFA methods available to users when MFA is not off.\n *\n * @default { sms: true, otp: true }\n */\n readonly mfaSecondFactor?: MfaSecondFactor;\n}\\``,\n ],\n [\n CDK_CONSTRUCTOR_PATTERN,\n `${CDK_CONSTRUCTOR_PATTERN} => \\`constructor(\n scope: Construct,\n id: string,\n {\n enableWaf = true,\n mfa = Mfa.REQUIRED,\n mfaSecondFactor = { sms: true, otp: true },\n }: UserIdentityProps = {},\n ) { $body }\\``,\n ],\n [\n CDK_CREATE_CALL_PATTERN,\n `${CDK_CREATE_CALL_PATTERN} => \\`this.createUserPool(mfa, mfaSecondFactor)\\``,\n ],\n [\n CDK_CREATE_SIGNATURE_PATTERN,\n `${CDK_CREATE_SIGNATURE_PATTERN} => \\`private createUserPool = (mfa: Mfa, mfaSecondFactor: MfaSecondFactor) => $body\\``,\n ],\n [CDK_MFA_FIELD_PATTERN, `${CDK_MFA_FIELD_PATTERN} => \\`mfa\\``],\n [\n CDK_MFA_SECOND_FACTOR_FIELD_PATTERN,\n `${CDK_MFA_SECOND_FACTOR_FIELD_PATTERN} => \\`mfaSecondFactor\\``,\n ],\n [\n CDK_SUPER_CALL_PATTERN,\n `${CDK_SUPER_CALL_PATTERN} => \\`super(scope, id);\n\n if (mfa === Mfa.REQUIRED && !mfaSecondFactor.sms && !mfaSecondFactor.otp) {\n throw new Error(\n 'UserIdentity: mfa is REQUIRED but mfaSecondFactor has no methods enabled (sms and otp are both false)',\n );\n }\\``,\n ],\n [\n CDK_NEW_USER_POOL_PATTERN,\n `${CDK_NEW_USER_POOL_PATTERN} => \\`const phoneVerificationEnabled = mfa === Mfa.OFF || mfaSecondFactor.sms;\n\n const userPool = new UserPool(this, 'UserPool', { $props })\\``,\n ],\n [\n CDK_PHONE_TRUE_PATTERN,\n `${CDK_PHONE_TRUE_PATTERN} => \\`phone: phoneVerificationEnabled\\``,\n ],\n];\n\nconst TERRAFORM_IDENTITY_ENABLE_WAF_VARIABLE_ANCHOR = [\n 'language hcl',\n '`variable \"enable_waf\" {',\n ' description = \"Whether to enable AWS WAFv2 with the default managed ruleset (AWSManagedRulesCommonRuleSet and AWSManagedRulesKnownBadInputsRuleSet) and associate it with the user pool\"',\n ' type = bool',\n ' default = true',\n '}`',\n].join('\\n');\n\nconst TERRAFORM_MFA_CONFIGURATION_PATTERN = [\n 'language hcl',\n '`mfa_configuration = \"ON\"`',\n].join('\\n');\n\nconst TERRAFORM_SOFTWARE_TOKEN_MFA_PATTERN = [\n 'language hcl',\n '`software_token_mfa_configuration {',\n ' enabled = true',\n ' }`',\n].join('\\n');\n\nconst TERRAFORM_SMS_CONFIGURATION_PATTERN = [\n 'language hcl',\n '`sms_configuration {',\n ' external_id = random_uuid.sms_external_id.result',\n ' sns_caller_arn = aws_iam_role.cognito_sms_role.arn',\n ' sns_region = data.aws_region.current.region',\n ' }`',\n].join('\\n');\n\nconst TERRAFORM_RANDOM_UUID_PATTERN = [\n 'language hcl',\n '`resource \"random_uuid\" \"sms_external_id\" {}`',\n].join('\\n');\n\nconst TERRAFORM_SMS_ROLE_PATTERN = [\n 'language hcl',\n '`resource \"aws_iam_role\" \"cognito_sms_role\" { $body }`',\n].join('\\n');\n\nconst TERRAFORM_SMS_POLICY_PATTERN = [\n 'language hcl',\n '`resource \"aws_iam_role_policy\" \"cognito_sms_policy\" { $body }`',\n].join('\\n');\n\nconst TERRAFORM_SMS_ROLE_ID_REF_PATTERN = [\n 'language hcl',\n '`aws_iam_role.cognito_sms_role.id`',\n].join('\\n');\n\nconst TERRAFORM_EXTERNAL_ID_REF_PATTERN = [\n 'language hcl',\n '`random_uuid.sms_external_id.result`',\n].join('\\n');\n\nconst TERRAFORM_AUTO_VERIFIED_ATTRIBUTES_PATTERN = [\n 'language hcl',\n '`auto_verified_attributes = [\"email\", \"phone_number\"]`',\n].join('\\n');\n\nconst TERRAFORM_ATTRIBUTES_REQUIRE_VERIFICATION_PATTERN = [\n 'language hcl',\n '`attributes_require_verification_before_update = [\"email\", \"phone_number\"]`',\n].join('\\n');\n\nconst TERRAFORM_IDENTITY_EDITS: Array<[string, string]> = [\n [\n TERRAFORM_IDENTITY_ENABLE_WAF_VARIABLE_ANCHOR,\n `${TERRAFORM_IDENTITY_ENABLE_WAF_VARIABLE_ANCHOR} => \\`variable \"enable_waf\" {\n description = \"Whether to enable AWS WAFv2 with the default managed ruleset (AWSManagedRulesCommonRuleSet and AWSManagedRulesKnownBadInputsRuleSet) and associate it with the user pool\"\n type = bool\n default = true\n}\n\n${NEW_TERRAFORM_VARIABLES}\\``,\n ],\n [\n TERRAFORM_RANDOM_UUID_PATTERN,\n `${TERRAFORM_RANDOM_UUID_PATTERN} => \\`resource \"random_uuid\" \"sms_external_id\" {\n count = var.mfa_second_factor_sms ? 1 : 0\n}\\``,\n ],\n [\n TERRAFORM_SMS_ROLE_PATTERN,\n `${TERRAFORM_SMS_ROLE_PATTERN} => \\`resource \"aws_iam_role\" \"cognito_sms_role\" {\n count = var.mfa_second_factor_sms ? 1 : 0\n\n $body\n}\\``,\n ],\n [\n TERRAFORM_SMS_POLICY_PATTERN,\n `${TERRAFORM_SMS_POLICY_PATTERN} => \\`resource \"aws_iam_role_policy\" \"cognito_sms_policy\" {\n count = var.mfa_second_factor_sms ? 1 : 0\n\n $body\n}\\``,\n ],\n [\n TERRAFORM_MFA_CONFIGURATION_PATTERN,\n `${TERRAFORM_MFA_CONFIGURATION_PATTERN} => \\`mfa_configuration = var.mfa\\``,\n ],\n [\n TERRAFORM_SOFTWARE_TOKEN_MFA_PATTERN,\n `${TERRAFORM_SOFTWARE_TOKEN_MFA_PATTERN} => \\`software_token_mfa_configuration {\n enabled = var.mfa_second_factor_otp\n }\\``,\n ],\n [\n TERRAFORM_SMS_CONFIGURATION_PATTERN,\n `${TERRAFORM_SMS_CONFIGURATION_PATTERN} => \\`# SMS MFA configuration. Due to a Terraform AWS provider limitation, this also gates\n # SMS-based phone number verification below - see the mfa_second_factor_sms variable.\n dynamic \"sms_configuration\" {\n for_each = var.mfa_second_factor_sms ? [1] : []\n\n content {\n external_id = random_uuid.sms_external_id[0].result\n sns_caller_arn = aws_iam_role.cognito_sms_role[0].arn\n sns_region = data.aws_region.current.region\n }\n }\\``,\n ],\n [\n TERRAFORM_SMS_ROLE_ID_REF_PATTERN,\n `${TERRAFORM_SMS_ROLE_ID_REF_PATTERN} => \\`aws_iam_role.cognito_sms_role[0].id\\``,\n ],\n [\n TERRAFORM_EXTERNAL_ID_REF_PATTERN,\n `${TERRAFORM_EXTERNAL_ID_REF_PATTERN} => \\`random_uuid.sms_external_id[0].result\\``,\n ],\n [\n TERRAFORM_AUTO_VERIFIED_ATTRIBUTES_PATTERN,\n `${TERRAFORM_AUTO_VERIFIED_ATTRIBUTES_PATTERN} => \\`auto_verified_attributes = var.mfa_second_factor_sms ? [\"email\", \"phone_number\"] : [\"email\"]\\``,\n ],\n [\n TERRAFORM_ATTRIBUTES_REQUIRE_VERIFICATION_PATTERN,\n `${TERRAFORM_ATTRIBUTES_REQUIRE_VERIFICATION_PATTERN} => \\`attributes_require_verification_before_update = var.mfa_second_factor_sms ? [\"email\", \"phone_number\"] : [\"email\"]\\``,\n ],\n];\n\nconst TERRAFORM_MAIN_ENABLE_WAF_VARIABLE_ANCHOR = [\n 'language hcl',\n '`variable \"enable_waf\" {',\n ' description = \"Whether to enable AWS WAFv2 with the default managed ruleset and associate it with the user pool\"',\n ' type = bool',\n ' default = true',\n '}`',\n].join('\\n');\n\nconst TERRAFORM_MAIN_MODULE_PATTERN = [\n 'language hcl',\n '`module \"identity\" {',\n ' source = \"./identity\"',\n '',\n ' user_pool_domain_prefix = $prefix',\n ' allow_signup = $allowSignup',\n ' enable_waf = var.enable_waf',\n '}`',\n].join('\\n');\n\nconst TERRAFORM_MAIN_EDITS: Array<[string, string]> = [\n [\n TERRAFORM_MAIN_ENABLE_WAF_VARIABLE_ANCHOR,\n `${TERRAFORM_MAIN_ENABLE_WAF_VARIABLE_ANCHOR} => \\`variable \"enable_waf\" {\n description = \"Whether to enable AWS WAFv2 with the default managed ruleset and associate it with the user pool\"\n type = bool\n default = true\n}\n\n${NEW_TERRAFORM_VARIABLES}\\``,\n ],\n [\n TERRAFORM_MAIN_MODULE_PATTERN,\n `${TERRAFORM_MAIN_MODULE_PATTERN} => \\`module \"identity\" {\n source = \"./identity\"\n\n user_pool_domain_prefix = $prefix\n allow_signup = $allowSignup\n enable_waf = var.enable_waf\n mfa = var.mfa\n mfa_second_factor_otp = var.mfa_second_factor_otp\n mfa_second_factor_sms = var.mfa_second_factor_sms\n}\\``,\n ],\n];\n\nconst divergedNextStep = (filePath: string) =>\n `${filePath}: the UserIdentity construct/module has diverged from the generated shape - left untouched. To make MFA configurable, add mfa and mfaSecondFactor (CDK) / mfa, mfa_second_factor_otp and mfa_second_factor_sms (Terraform) yourself, following the same pattern as the existing enableWaf prop/variable.`;\n\nconst applyEdits = async (\n tree: Tree,\n filePath: string,\n migratedPattern: string,\n edits: Array<[string, string]>,\n nextSteps: string[],\n beforeApply?: () => Promise<void>,\n) => {\n if (!tree.exists(filePath)) {\n // This workspace doesn't use this IaC provider, or has no UserIdentity.\n return;\n }\n\n if (await matchGritQL(tree, filePath, migratedPattern)) {\n // Already migrated - silent skip keeps re-runs a no-op.\n return;\n }\n\n // Confirm every edit site is present before writing any of them, so a file\n // that only partly matches is left whole rather than half-edited.\n const allSitesPresent = (\n await Promise.all(\n edits.map(([match]) => matchGritQL(tree, filePath, match)),\n )\n ).every(Boolean);\n\n if (!allSitesPresent) {\n nextSteps.push(divergedNextStep(filePath));\n return;\n }\n\n await beforeApply?.();\n\n for (const [, rewrite] of edits) {\n await applyGritQL(tree, filePath, rewrite);\n }\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n await applyEdits(\n tree,\n CDK_USER_IDENTITY_FILE,\n CDK_MIGRATED_PATTERN,\n CDK_EDITS,\n nextSteps,\n () =>\n addDestructuredImport(\n tree,\n CDK_USER_IDENTITY_FILE,\n ['type MfaSecondFactor'],\n 'aws-cdk-lib/aws-cognito',\n ),\n );\n\n await applyEdits(\n tree,\n TERRAFORM_IDENTITY_FILE,\n TERRAFORM_IDENTITY_MIGRATED_PATTERN,\n TERRAFORM_IDENTITY_EDITS,\n nextSteps,\n );\n\n await applyEdits(\n tree,\n TERRAFORM_MAIN_FILE,\n TERRAFORM_MAIN_MIGRATED_PATTERN,\n TERRAFORM_MAIN_EDITS,\n nextSteps,\n );\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["addDestructuredImport","applyGritQL","matchGritQL","formatFilesInSubtree","PACKAGES_DIR","SHARED_CONSTRUCTS_DIR","SHARED_TERRAFORM_DIR","CDK_USER_IDENTITY_FILE","TERRAFORM_IDENTITY_FILE","TERRAFORM_MAIN_FILE","NEW_TERRAFORM_VARIABLES","CDK_MIGRATED_PATTERN","TERRAFORM_IDENTITY_MIGRATED_PATTERN","TERRAFORM_MAIN_MIGRATED_PATTERN","CDK_INTERFACE_BODY_PATTERN","CDK_CONSTRUCTOR_PATTERN","CDK_CREATE_CALL_PATTERN","CDK_CREATE_SIGNATURE_PATTERN","CDK_MFA_FIELD_PATTERN","CDK_MFA_SECOND_FACTOR_FIELD_PATTERN","CDK_SUPER_CALL_PATTERN","CDK_NEW_USER_POOL_PATTERN","CDK_PHONE_TRUE_PATTERN","CDK_EDITS","TERRAFORM_IDENTITY_ENABLE_WAF_VARIABLE_ANCHOR","join","TERRAFORM_MFA_CONFIGURATION_PATTERN","TERRAFORM_SOFTWARE_TOKEN_MFA_PATTERN","TERRAFORM_SMS_CONFIGURATION_PATTERN","TERRAFORM_RANDOM_UUID_PATTERN","TERRAFORM_SMS_ROLE_PATTERN","TERRAFORM_SMS_POLICY_PATTERN","TERRAFORM_SMS_ROLE_ID_REF_PATTERN","TERRAFORM_EXTERNAL_ID_REF_PATTERN","TERRAFORM_AUTO_VERIFIED_ATTRIBUTES_PATTERN","TERRAFORM_ATTRIBUTES_REQUIRE_VERIFICATION_PATTERN","TERRAFORM_IDENTITY_EDITS","TERRAFORM_MAIN_ENABLE_WAF_VARIABLE_ANCHOR","TERRAFORM_MAIN_MODULE_PATTERN","TERRAFORM_MAIN_EDITS","divergedNextStep","filePath","applyEdits","tree","migratedPattern","edits","nextSteps","beforeApply","exists","allSitesPresent","Promise","all","map","match","every","Boolean","push","rewrite","migration"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/latest/user-identity-configurable-mfa/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport type { MigrationReturnObject, Tree } from '@nx/devkit';\nimport {\n addDestructuredImport,\n applyGritQL,\n matchGritQL,\n} from '../../../utils/ast.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\nimport {\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n SHARED_TERRAFORM_DIR,\n} from '../../../utils/shared-constructs-constants.js';\n\n/**\n * Surface mfa and mfaSecondFactor on the vended UserIdentity construct/module\n *\n * CDK's `mfa` / `mfaSecondFactor` props default to what the construct already\n * hard-coded (`Mfa.REQUIRED` / `{ sms: true, otp: true }`), so this just makes\n * them overridable, matching the existing `enableWaf` prop pattern.\n *\n * Terraform has no equivalent of CloudFormation's `EnabledMfas` - SMS MFA is\n * inseparable from the `sms_configuration` block, which phone number\n * verification also relies on. So `mfa_second_factor_sms` gates\n * `sms_configuration` (and, with it, SMS phone verification) rather than only\n * MFA, unlike the CDK prop.\n *\n * CDK has the same underlying coupling for any non-OFF `mfa`: Cognito rejects\n * `SmsConfiguration` (which CDK emits whenever phone is auto-verified) unless\n * SMS is also an enabled MFA method. So `phoneVerificationEnabled` gates\n * `autoVerify`/`keepOriginal`'s `phone` flag on whether SMS is actually usable\n * as a second factor, and a `mfa: REQUIRED` with neither `sms` nor `otp`\n * enabled - a pool nobody could ever sign into - is rejected at synth time.\n *\n * How to write a migration:\n * - https://nx.dev/docs/kb/migration-generators\n * - What `nextSteps` means: https://nx.dev/docs/reference/devkit/MigrationReturnObject\n *\n * Guardrails:\n * - Pattern-match before writing: skip files that have diverged from the shape\n * your generators produce and report them via `nextSteps`, rather than\n * clobbering the user's changes.\n * - Idempotent: re-running must be a no-op.\n * - Format what you write: finish with `formatFilesInSubtree` so the files your\n * migration wrote are formatted correctly.\n */\n\nconst CDK_USER_IDENTITY_FILE = `${PACKAGES_DIR}/${SHARED_CONSTRUCTS_DIR}/src/core/user-identity.ts`;\nconst TERRAFORM_IDENTITY_FILE = `${PACKAGES_DIR}/${SHARED_TERRAFORM_DIR}/src/core/user-identity/identity/identity.tf`;\nconst TERRAFORM_MAIN_FILE = `${PACKAGES_DIR}/${SHARED_TERRAFORM_DIR}/src/core/user-identity/main.tf`;\n\nconst NEW_TERRAFORM_VARIABLES = `variable \"mfa\" {\n description = \"Whether users must configure MFA, may optionally configure MFA, or cannot use MFA at all\"\n type = string\n default = \"ON\"\n\n validation {\n condition = contains([\"OFF\", \"OPTIONAL\", \"ON\"], var.mfa)\n error_message = \"mfa must be one of OFF, OPTIONAL, ON\"\n }\n}\n\nvariable \"mfa_second_factor_otp\" {\n description = \"Whether users can use a time-based one time password (TOTP), generated by a hardware or software token, as an MFA method\"\n type = bool\n default = true\n}\n\nvariable \"mfa_second_factor_sms\" {\n description = \"Whether users can use SMS to their verified phone numbers as an MFA method. Due to a Terraform AWS provider limitation, disabling this also disables SMS-based phone number verification, unlike the equivalent CDK construct prop\"\n type = bool\n default = true\n}`;\n\n// Guards each file: present only once it has been migrated.\nconst CDK_MIGRATED_PATTERN = '`mfa === Mfa.OFF || mfaSecondFactor.sms`';\nconst TERRAFORM_IDENTITY_MIGRATED_PATTERN =\n 'language hcl\\n`mfa_configuration = var.mfa`';\nconst TERRAFORM_MAIN_MIGRATED_PATTERN = 'language hcl\\n`mfa = var.mfa`';\n\n// Interface members don't parse as standalone snippets, so this anchors on the\n// whole interface body (preserved via $body) rather than the enableWaf member.\nconst CDK_INTERFACE_BODY_PATTERN =\n '`export interface UserIdentityProps { $body }`';\n// A parameter's type annotation blocks matching the destructure sub-pattern in\n// isolation, so this anchors on the full constructor signature instead\n// (literal params so a customised signature is correctly seen as diverged).\nconst CDK_CONSTRUCTOR_PATTERN =\n '`constructor(scope: Construct, id: string, { enableWaf = true }: UserIdentityProps = {}) { $body }`';\nconst CDK_CREATE_CALL_PATTERN = '`this.createUserPool()`';\nconst CDK_CREATE_SIGNATURE_PATTERN = '`private createUserPool = () => $body`';\n// Trailing commas block matching a bare object property in isolation; the\n// separator is outside the matched span either way, so it survives untouched.\nconst CDK_MFA_FIELD_PATTERN = '`mfa: Mfa.REQUIRED`';\nconst CDK_MFA_SECOND_FACTOR_FIELD_PATTERN =\n '`mfaSecondFactor: { sms: true, otp: true }`';\nconst CDK_SUPER_CALL_PATTERN = '`super(scope, id);`';\n// $props preserves the whole (large) UserPool props object untouched while\n// inserting the new const declaration immediately before it.\nconst CDK_NEW_USER_POOL_PATTERN =\n \"`const userPool = new UserPool(this, 'UserPool', { $props })`\";\n// Matches both the autoVerify.phone and keepOriginal.phone occurrences in one pass.\nconst CDK_PHONE_TRUE_PATTERN = '`phone: true`';\n\nconst CDK_EDITS: Array<[string, string]> = [\n [\n CDK_INTERFACE_BODY_PATTERN,\n `${CDK_INTERFACE_BODY_PATTERN} => \\`export interface UserIdentityProps {\n $body\n\n /**\n * Whether users must configure MFA, may optionally configure MFA, or cannot use MFA at all.\n *\n * @default Mfa.REQUIRED\n */\n readonly mfa?: Mfa;\n\n /**\n * The MFA methods available to users when MFA is not off.\n *\n * @default { sms: true, otp: true }\n */\n readonly mfaSecondFactor?: MfaSecondFactor;\n}\\``,\n ],\n [\n CDK_CONSTRUCTOR_PATTERN,\n `${CDK_CONSTRUCTOR_PATTERN} => \\`constructor(\n scope: Construct,\n id: string,\n {\n enableWaf = true,\n mfa = Mfa.REQUIRED,\n mfaSecondFactor = { sms: true, otp: true },\n }: UserIdentityProps = {},\n ) { $body }\\``,\n ],\n [\n CDK_CREATE_CALL_PATTERN,\n `${CDK_CREATE_CALL_PATTERN} => \\`this.createUserPool(mfa, mfaSecondFactor)\\``,\n ],\n [\n CDK_CREATE_SIGNATURE_PATTERN,\n `${CDK_CREATE_SIGNATURE_PATTERN} => \\`private createUserPool = (mfa: Mfa, mfaSecondFactor: MfaSecondFactor) => $body\\``,\n ],\n [CDK_MFA_FIELD_PATTERN, `${CDK_MFA_FIELD_PATTERN} => \\`mfa\\``],\n [\n CDK_MFA_SECOND_FACTOR_FIELD_PATTERN,\n `${CDK_MFA_SECOND_FACTOR_FIELD_PATTERN} => \\`mfaSecondFactor\\``,\n ],\n [\n CDK_SUPER_CALL_PATTERN,\n `${CDK_SUPER_CALL_PATTERN} => \\`super(scope, id);\n\n if (mfa === Mfa.REQUIRED && !mfaSecondFactor.sms && !mfaSecondFactor.otp) {\n throw new Error(\n 'UserIdentity: mfa is REQUIRED but mfaSecondFactor has no methods enabled (sms and otp are both false)',\n );\n }\\``,\n ],\n [\n CDK_NEW_USER_POOL_PATTERN,\n `${CDK_NEW_USER_POOL_PATTERN} => \\`const phoneVerificationEnabled = mfa === Mfa.OFF || mfaSecondFactor.sms;\n\n const userPool = new UserPool(this, 'UserPool', { $props })\\``,\n ],\n [\n CDK_PHONE_TRUE_PATTERN,\n `${CDK_PHONE_TRUE_PATTERN} => \\`phone: phoneVerificationEnabled\\``,\n ],\n];\n\nconst TERRAFORM_IDENTITY_ENABLE_WAF_VARIABLE_ANCHOR = [\n 'language hcl',\n '`variable \"enable_waf\" {',\n ' description = \"Whether to enable AWS WAFv2 with the default managed ruleset (AWSManagedRulesCommonRuleSet and AWSManagedRulesKnownBadInputsRuleSet) and associate it with the user pool\"',\n ' type = bool',\n ' default = true',\n '}`',\n].join('\\n');\n\nconst TERRAFORM_MFA_CONFIGURATION_PATTERN = [\n 'language hcl',\n '`mfa_configuration = \"ON\"`',\n].join('\\n');\n\nconst TERRAFORM_SOFTWARE_TOKEN_MFA_PATTERN = [\n 'language hcl',\n '`software_token_mfa_configuration {',\n ' enabled = true',\n ' }`',\n].join('\\n');\n\nconst TERRAFORM_SMS_CONFIGURATION_PATTERN = [\n 'language hcl',\n '`sms_configuration {',\n ' external_id = random_uuid.sms_external_id.result',\n ' sns_caller_arn = aws_iam_role.cognito_sms_role.arn',\n ' sns_region = data.aws_region.current.region',\n ' }`',\n].join('\\n');\n\nconst TERRAFORM_RANDOM_UUID_PATTERN = [\n 'language hcl',\n '`resource \"random_uuid\" \"sms_external_id\" {}`',\n].join('\\n');\n\nconst TERRAFORM_SMS_ROLE_PATTERN = [\n 'language hcl',\n '`resource \"aws_iam_role\" \"cognito_sms_role\" { $body }`',\n].join('\\n');\n\nconst TERRAFORM_SMS_POLICY_PATTERN = [\n 'language hcl',\n '`resource \"aws_iam_role_policy\" \"cognito_sms_policy\" { $body }`',\n].join('\\n');\n\nconst TERRAFORM_SMS_ROLE_ID_REF_PATTERN = [\n 'language hcl',\n '`aws_iam_role.cognito_sms_role.id`',\n].join('\\n');\n\nconst TERRAFORM_EXTERNAL_ID_REF_PATTERN = [\n 'language hcl',\n '`random_uuid.sms_external_id.result`',\n].join('\\n');\n\nconst TERRAFORM_AUTO_VERIFIED_ATTRIBUTES_PATTERN = [\n 'language hcl',\n '`auto_verified_attributes = [\"email\", \"phone_number\"]`',\n].join('\\n');\n\nconst TERRAFORM_ATTRIBUTES_REQUIRE_VERIFICATION_PATTERN = [\n 'language hcl',\n '`attributes_require_verification_before_update = [\"email\", \"phone_number\"]`',\n].join('\\n');\n\nconst TERRAFORM_IDENTITY_EDITS: Array<[string, string]> = [\n [\n TERRAFORM_IDENTITY_ENABLE_WAF_VARIABLE_ANCHOR,\n `${TERRAFORM_IDENTITY_ENABLE_WAF_VARIABLE_ANCHOR} => \\`variable \"enable_waf\" {\n description = \"Whether to enable AWS WAFv2 with the default managed ruleset (AWSManagedRulesCommonRuleSet and AWSManagedRulesKnownBadInputsRuleSet) and associate it with the user pool\"\n type = bool\n default = true\n}\n\n${NEW_TERRAFORM_VARIABLES}\\``,\n ],\n [\n TERRAFORM_RANDOM_UUID_PATTERN,\n `${TERRAFORM_RANDOM_UUID_PATTERN} => \\`resource \"random_uuid\" \"sms_external_id\" {\n count = var.mfa_second_factor_sms ? 1 : 0\n}\\``,\n ],\n [\n TERRAFORM_SMS_ROLE_PATTERN,\n `${TERRAFORM_SMS_ROLE_PATTERN} => \\`resource \"aws_iam_role\" \"cognito_sms_role\" {\n count = var.mfa_second_factor_sms ? 1 : 0\n\n $body\n}\\``,\n ],\n [\n TERRAFORM_SMS_POLICY_PATTERN,\n `${TERRAFORM_SMS_POLICY_PATTERN} => \\`resource \"aws_iam_role_policy\" \"cognito_sms_policy\" {\n count = var.mfa_second_factor_sms ? 1 : 0\n\n $body\n}\\``,\n ],\n [\n TERRAFORM_MFA_CONFIGURATION_PATTERN,\n `${TERRAFORM_MFA_CONFIGURATION_PATTERN} => \\`mfa_configuration = var.mfa\\``,\n ],\n [\n TERRAFORM_SOFTWARE_TOKEN_MFA_PATTERN,\n `${TERRAFORM_SOFTWARE_TOKEN_MFA_PATTERN} => \\`software_token_mfa_configuration {\n enabled = var.mfa_second_factor_otp\n }\\``,\n ],\n [\n TERRAFORM_SMS_CONFIGURATION_PATTERN,\n `${TERRAFORM_SMS_CONFIGURATION_PATTERN} => \\`# SMS MFA configuration. Due to a Terraform AWS provider limitation, this also gates\n # SMS-based phone number verification below - see the mfa_second_factor_sms variable.\n dynamic \"sms_configuration\" {\n for_each = var.mfa_second_factor_sms ? [1] : []\n\n content {\n external_id = random_uuid.sms_external_id[0].result\n sns_caller_arn = aws_iam_role.cognito_sms_role[0].arn\n sns_region = data.aws_region.current.region\n }\n }\\``,\n ],\n [\n TERRAFORM_SMS_ROLE_ID_REF_PATTERN,\n `${TERRAFORM_SMS_ROLE_ID_REF_PATTERN} => \\`aws_iam_role.cognito_sms_role[0].id\\``,\n ],\n [\n TERRAFORM_EXTERNAL_ID_REF_PATTERN,\n `${TERRAFORM_EXTERNAL_ID_REF_PATTERN} => \\`random_uuid.sms_external_id[0].result\\``,\n ],\n [\n TERRAFORM_AUTO_VERIFIED_ATTRIBUTES_PATTERN,\n `${TERRAFORM_AUTO_VERIFIED_ATTRIBUTES_PATTERN} => \\`auto_verified_attributes = var.mfa_second_factor_sms ? [\"email\", \"phone_number\"] : [\"email\"]\\``,\n ],\n [\n TERRAFORM_ATTRIBUTES_REQUIRE_VERIFICATION_PATTERN,\n `${TERRAFORM_ATTRIBUTES_REQUIRE_VERIFICATION_PATTERN} => \\`attributes_require_verification_before_update = var.mfa_second_factor_sms ? [\"email\", \"phone_number\"] : [\"email\"]\\``,\n ],\n];\n\nconst TERRAFORM_MAIN_ENABLE_WAF_VARIABLE_ANCHOR = [\n 'language hcl',\n '`variable \"enable_waf\" {',\n ' description = \"Whether to enable AWS WAFv2 with the default managed ruleset and associate it with the user pool\"',\n ' type = bool',\n ' default = true',\n '}`',\n].join('\\n');\n\nconst TERRAFORM_MAIN_MODULE_PATTERN = [\n 'language hcl',\n '`module \"identity\" {',\n ' source = \"./identity\"',\n '',\n ' user_pool_domain_prefix = $prefix',\n ' allow_signup = $allowSignup',\n ' enable_waf = var.enable_waf',\n '}`',\n].join('\\n');\n\nconst TERRAFORM_MAIN_EDITS: Array<[string, string]> = [\n [\n TERRAFORM_MAIN_ENABLE_WAF_VARIABLE_ANCHOR,\n `${TERRAFORM_MAIN_ENABLE_WAF_VARIABLE_ANCHOR} => \\`variable \"enable_waf\" {\n description = \"Whether to enable AWS WAFv2 with the default managed ruleset and associate it with the user pool\"\n type = bool\n default = true\n}\n\n${NEW_TERRAFORM_VARIABLES}\\``,\n ],\n [\n TERRAFORM_MAIN_MODULE_PATTERN,\n `${TERRAFORM_MAIN_MODULE_PATTERN} => \\`module \"identity\" {\n source = \"./identity\"\n\n user_pool_domain_prefix = $prefix\n allow_signup = $allowSignup\n enable_waf = var.enable_waf\n mfa = var.mfa\n mfa_second_factor_otp = var.mfa_second_factor_otp\n mfa_second_factor_sms = var.mfa_second_factor_sms\n}\\``,\n ],\n];\n\nconst divergedNextStep = (filePath: string) =>\n `${filePath}: the UserIdentity construct/module has diverged from the generated shape - left untouched. To make MFA configurable, add mfa and mfaSecondFactor (CDK) / mfa, mfa_second_factor_otp and mfa_second_factor_sms (Terraform) yourself, following the same pattern as the existing enableWaf prop/variable.`;\n\nconst applyEdits = async (\n tree: Tree,\n filePath: string,\n migratedPattern: string,\n edits: Array<[string, string]>,\n nextSteps: string[],\n beforeApply?: () => Promise<void>,\n) => {\n if (!tree.exists(filePath)) {\n // This workspace doesn't use this IaC provider, or has no UserIdentity.\n return;\n }\n\n if (await matchGritQL(tree, filePath, migratedPattern)) {\n // Already migrated - silent skip keeps re-runs a no-op.\n return;\n }\n\n // Confirm every edit site is present before writing any of them, so a file\n // that only partly matches is left whole rather than half-edited.\n const allSitesPresent = (\n await Promise.all(\n edits.map(([match]) => matchGritQL(tree, filePath, match)),\n )\n ).every(Boolean);\n\n if (!allSitesPresent) {\n nextSteps.push(divergedNextStep(filePath));\n return;\n }\n\n await beforeApply?.();\n\n for (const [, rewrite] of edits) {\n await applyGritQL(tree, filePath, rewrite);\n }\n};\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n await applyEdits(\n tree,\n CDK_USER_IDENTITY_FILE,\n CDK_MIGRATED_PATTERN,\n CDK_EDITS,\n nextSteps,\n () =>\n addDestructuredImport(\n tree,\n CDK_USER_IDENTITY_FILE,\n ['type MfaSecondFactor'],\n 'aws-cdk-lib/aws-cognito',\n ),\n );\n\n await applyEdits(\n tree,\n TERRAFORM_IDENTITY_FILE,\n TERRAFORM_IDENTITY_MIGRATED_PATTERN,\n TERRAFORM_IDENTITY_EDITS,\n nextSteps,\n );\n\n await applyEdits(\n tree,\n TERRAFORM_MAIN_FILE,\n TERRAFORM_MAIN_MIGRATED_PATTERN,\n TERRAFORM_MAIN_EDITS,\n nextSteps,\n );\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["addDestructuredImport","applyGritQL","matchGritQL","formatFilesInSubtree","PACKAGES_DIR","SHARED_CONSTRUCTS_DIR","SHARED_TERRAFORM_DIR","CDK_USER_IDENTITY_FILE","TERRAFORM_IDENTITY_FILE","TERRAFORM_MAIN_FILE","NEW_TERRAFORM_VARIABLES","CDK_MIGRATED_PATTERN","TERRAFORM_IDENTITY_MIGRATED_PATTERN","TERRAFORM_MAIN_MIGRATED_PATTERN","CDK_INTERFACE_BODY_PATTERN","CDK_CONSTRUCTOR_PATTERN","CDK_CREATE_CALL_PATTERN","CDK_CREATE_SIGNATURE_PATTERN","CDK_MFA_FIELD_PATTERN","CDK_MFA_SECOND_FACTOR_FIELD_PATTERN","CDK_SUPER_CALL_PATTERN","CDK_NEW_USER_POOL_PATTERN","CDK_PHONE_TRUE_PATTERN","CDK_EDITS","TERRAFORM_IDENTITY_ENABLE_WAF_VARIABLE_ANCHOR","join","TERRAFORM_MFA_CONFIGURATION_PATTERN","TERRAFORM_SOFTWARE_TOKEN_MFA_PATTERN","TERRAFORM_SMS_CONFIGURATION_PATTERN","TERRAFORM_RANDOM_UUID_PATTERN","TERRAFORM_SMS_ROLE_PATTERN","TERRAFORM_SMS_POLICY_PATTERN","TERRAFORM_SMS_ROLE_ID_REF_PATTERN","TERRAFORM_EXTERNAL_ID_REF_PATTERN","TERRAFORM_AUTO_VERIFIED_ATTRIBUTES_PATTERN","TERRAFORM_ATTRIBUTES_REQUIRE_VERIFICATION_PATTERN","TERRAFORM_IDENTITY_EDITS","TERRAFORM_MAIN_ENABLE_WAF_VARIABLE_ANCHOR","TERRAFORM_MAIN_MODULE_PATTERN","TERRAFORM_MAIN_EDITS","divergedNextStep","filePath","applyEdits","tree","migratedPattern","edits","nextSteps","beforeApply","exists","allSitesPresent","Promise","all","map","match","every","Boolean","push","rewrite","migration"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/v1.0.0-rc.50/0001-modernize-function-props-cast/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n type MigrationReturnObject,\n type Tree,\n visitNotIgnoredFiles,\n} from '@nx/devkit';\nimport { formatFilesInSubtree } from '../../../utils/format';\nimport {\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n} from '../../../utils/shared-constructs-constants';\n\n/**\n * Replace the legacy angle-bracket FunctionProps type assertion with the modern as syntax in generated API constructs\n *\n * The legacy `<FunctionProps>{ ... }` cast form is not supported by GritQL's\n * TypeScript parser, which silently breaks parsing (and therefore matching)\n * for the entire containing file. Since several other migrations rely on\n * GritQL to update these API construct files, this cast is replaced here with\n * the equivalent, GritQL-compatible `{ ... } as FunctionProps` form. This is\n * matched as an exact literal (rather than via GritQL) since GritQL cannot\n * parse the file while the legacy cast is still present.\n *\n * How to write a migration:\n * - https://nx.dev/docs/kb/migration-generators\n * - What `nextSteps` means: https://nx.dev/docs/reference/devkit/MigrationReturnObject\n *\n * Guardrails:\n * - Pattern-match before writing: skip files that have diverged from the shape\n * your generators produce and report them via `nextSteps`, or consider a\n * hybrid migration, rather than clobbering the user's changes.\n * - Idempotent: re-running must be a no-op.\n * - Format what you write: finish with `formatFilesInSubtree` so the files your\n * migration wrote are formatted correctly.\n */\n\nconst APIS_APP_DIR = `${PACKAGES_DIR}/${SHARED_CONSTRUCTS_DIR}/src/app/apis`;\n\nconst OLD_CAST_OPEN = 'defaultIntegrationOptions: <FunctionProps>{';\nconst NEW_CAST_OPEN = 'defaultIntegrationOptions: {';\nconst OLD_CAST_CLOSE = '\\n },\\n buildDefaultIntegration:';\nconst NEW_CAST_CLOSE =\n '\\n } as FunctionProps,\\n buildDefaultIntegration:';\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n if (!tree.exists(APIS_APP_DIR)) {\n return { nextSteps };\n }\n\n const apiAppFiles: string[] = [];\n visitNotIgnoredFiles(tree, APIS_APP_DIR, (filePath) => {\n apiAppFiles.push(filePath);\n });\n\n for (const filePath of apiAppFiles) {\n if (!filePath.endsWith('.ts') || filePath.endsWith('/index.ts')) {\n continue;\n }\n const contents = tree.read(filePath, 'utf-8') ?? '';\n if (!contents.includes(OLD_CAST_OPEN)) {\n // Already migrated, or doesn't use this shape.\n continue;\n }\n if (!contents.includes(OLD_CAST_CLOSE)) {\n nextSteps.push(\n `${filePath}: the defaultIntegrationOptions cast has diverged from the generated shape - left untouched. Manually replace \\`<FunctionProps>{ ... }\\` with \\`{ ... } as FunctionProps\\`.`,\n );\n continue;\n }\n tree.write(\n filePath,\n contents\n .replace(OLD_CAST_OPEN, NEW_CAST_OPEN)\n .replace(OLD_CAST_CLOSE, NEW_CAST_CLOSE),\n );\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["visitNotIgnoredFiles","formatFilesInSubtree","PACKAGES_DIR","SHARED_CONSTRUCTS_DIR","APIS_APP_DIR","OLD_CAST_OPEN","NEW_CAST_OPEN","OLD_CAST_CLOSE","NEW_CAST_CLOSE","migration","tree","nextSteps","exists","apiAppFiles","filePath","push","endsWith","contents","read","includes","write","replace"],"mappings":"AAAA;;;CAGC,GACD,SAGEA,oBAAoB,QACf,aAAa;AACpB,SAASC,oBAAoB,QAAQ,2BAAwB;AAC7D,SACEC,YAAY,EACZC,qBAAqB,QAChB,gDAA6C;AAEpD;;;;;;;;;;;;;;;;;;;;;;CAsBC,GAED,MAAMC,eAAe,GAAGF,aAAa,CAAC,EAAEC,sBAAsB,aAAa,CAAC;AAE5E,MAAME,gBAAgB;AACtB,MAAMC,gBAAgB;AACtB,MAAMC,iBAAiB;AACvB,MAAMC,iBACJ;AAEF,eAAe,eAAeC,UAC5BC,IAAU;IAEV,MAAMC,YAAsB,EAAE;IAE9B,IAAI,CAACD,KAAKE,MAAM,CAACR,eAAe;QAC9B,OAAO;YAAEO;QAAU;IACrB;IAEA,MAAME,cAAwB,EAAE;IAChCb,qBAAqBU,MAAMN,cAAc,CAACU;QACxCD,YAAYE,IAAI,CAACD;IACnB;IAEA,KAAK,MAAMA,YAAYD,YAAa;QAClC,IAAI,CAACC,SAASE,QAAQ,CAAC,UAAUF,SAASE,QAAQ,CAAC,cAAc;YAC/D;QACF;QACA,MAAMC,WAAWP,KAAKQ,IAAI,CAACJ,UAAU,YAAY;QACjD,IAAI,CAACG,SAASE,QAAQ,CAACd,gBAAgB;YAErC;QACF;QACA,IAAI,CAACY,SAASE,QAAQ,CAACZ,iBAAiB;YACtCI,UAAUI,IAAI,CACZ,GAAGD,SAAS,2KAA2K,CAAC;YAE1L;QACF;QACAJ,KAAKU,KAAK,CACRN,UACAG,SACGI,OAAO,CAAChB,eAAeC,eACvBe,OAAO,CAACd,gBAAgBC;IAE/B;IAEA,MAAMP,qBAAqBS;IAE3B,OAAO;QAAEC;IAAU;AACrB"}
1
+ {"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/v1.0.0-rc.50/0001-modernize-function-props-cast/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n type MigrationReturnObject,\n type Tree,\n visitNotIgnoredFiles,\n} from '@nx/devkit';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\nimport {\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n} from '../../../utils/shared-constructs-constants.js';\n\n/**\n * Replace the legacy angle-bracket FunctionProps type assertion with the modern as syntax in generated API constructs\n *\n * The legacy `<FunctionProps>{ ... }` cast form is not supported by GritQL's\n * TypeScript parser, which silently breaks parsing (and therefore matching)\n * for the entire containing file. Since several other migrations rely on\n * GritQL to update these API construct files, this cast is replaced here with\n * the equivalent, GritQL-compatible `{ ... } as FunctionProps` form. This is\n * matched as an exact literal (rather than via GritQL) since GritQL cannot\n * parse the file while the legacy cast is still present.\n *\n * How to write a migration:\n * - https://nx.dev/docs/kb/migration-generators\n * - What `nextSteps` means: https://nx.dev/docs/reference/devkit/MigrationReturnObject\n *\n * Guardrails:\n * - Pattern-match before writing: skip files that have diverged from the shape\n * your generators produce and report them via `nextSteps`, or consider a\n * hybrid migration, rather than clobbering the user's changes.\n * - Idempotent: re-running must be a no-op.\n * - Format what you write: finish with `formatFilesInSubtree` so the files your\n * migration wrote are formatted correctly.\n */\n\nconst APIS_APP_DIR = `${PACKAGES_DIR}/${SHARED_CONSTRUCTS_DIR}/src/app/apis`;\n\nconst OLD_CAST_OPEN = 'defaultIntegrationOptions: <FunctionProps>{';\nconst NEW_CAST_OPEN = 'defaultIntegrationOptions: {';\nconst OLD_CAST_CLOSE = '\\n },\\n buildDefaultIntegration:';\nconst NEW_CAST_CLOSE =\n '\\n } as FunctionProps,\\n buildDefaultIntegration:';\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n if (!tree.exists(APIS_APP_DIR)) {\n return { nextSteps };\n }\n\n const apiAppFiles: string[] = [];\n visitNotIgnoredFiles(tree, APIS_APP_DIR, (filePath) => {\n apiAppFiles.push(filePath);\n });\n\n for (const filePath of apiAppFiles) {\n if (!filePath.endsWith('.ts') || filePath.endsWith('/index.ts')) {\n continue;\n }\n const contents = tree.read(filePath, 'utf-8') ?? '';\n if (!contents.includes(OLD_CAST_OPEN)) {\n // Already migrated, or doesn't use this shape.\n continue;\n }\n if (!contents.includes(OLD_CAST_CLOSE)) {\n nextSteps.push(\n `${filePath}: the defaultIntegrationOptions cast has diverged from the generated shape - left untouched. Manually replace \\`<FunctionProps>{ ... }\\` with \\`{ ... } as FunctionProps\\`.`,\n );\n continue;\n }\n tree.write(\n filePath,\n contents\n .replace(OLD_CAST_OPEN, NEW_CAST_OPEN)\n .replace(OLD_CAST_CLOSE, NEW_CAST_CLOSE),\n );\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["visitNotIgnoredFiles","formatFilesInSubtree","PACKAGES_DIR","SHARED_CONSTRUCTS_DIR","APIS_APP_DIR","OLD_CAST_OPEN","NEW_CAST_OPEN","OLD_CAST_CLOSE","NEW_CAST_CLOSE","migration","tree","nextSteps","exists","apiAppFiles","filePath","push","endsWith","contents","read","includes","write","replace"],"mappings":"AAAA;;;CAGC,GACD,SAGEA,oBAAoB,QACf,aAAa;AACpB,SAASC,oBAAoB,QAAQ,2BAA2B;AAChE,SACEC,YAAY,EACZC,qBAAqB,QAChB,gDAAgD;AAEvD;;;;;;;;;;;;;;;;;;;;;;CAsBC,GAED,MAAMC,eAAe,GAAGF,aAAa,CAAC,EAAEC,sBAAsB,aAAa,CAAC;AAE5E,MAAME,gBAAgB;AACtB,MAAMC,gBAAgB;AACtB,MAAMC,iBAAiB;AACvB,MAAMC,iBACJ;AAEF,eAAe,eAAeC,UAC5BC,IAAU;IAEV,MAAMC,YAAsB,EAAE;IAE9B,IAAI,CAACD,KAAKE,MAAM,CAACR,eAAe;QAC9B,OAAO;YAAEO;QAAU;IACrB;IAEA,MAAME,cAAwB,EAAE;IAChCb,qBAAqBU,MAAMN,cAAc,CAACU;QACxCD,YAAYE,IAAI,CAACD;IACnB;IAEA,KAAK,MAAMA,YAAYD,YAAa;QAClC,IAAI,CAACC,SAASE,QAAQ,CAAC,UAAUF,SAASE,QAAQ,CAAC,cAAc;YAC/D;QACF;QACA,MAAMC,WAAWP,KAAKQ,IAAI,CAACJ,UAAU,YAAY;QACjD,IAAI,CAACG,SAASE,QAAQ,CAACd,gBAAgB;YAErC;QACF;QACA,IAAI,CAACY,SAASE,QAAQ,CAACZ,iBAAiB;YACtCI,UAAUI,IAAI,CACZ,GAAGD,SAAS,2KAA2K,CAAC;YAE1L;QACF;QACAJ,KAAKU,KAAK,CACRN,UACAG,SACGI,OAAO,CAAChB,eAAeC,eACvBe,OAAO,CAACd,gBAAgBC;IAE/B;IAEA,MAAMP,qBAAqBS;IAE3B,OAAO;QAAEC;IAAU;AACrB"}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/v1.0.0-rc.50/0002-restrict-cors-to-custom-domains/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n type MigrationReturnObject,\n type Tree,\n visitNotIgnoredFiles,\n} from '@nx/devkit';\nimport {\n addDestructuredImport,\n addStarExport,\n applyGritQL,\n} from '../../../utils/ast';\nimport { formatFilesInSubtree } from '../../../utils/format';\nimport { isEsmWorkspace } from '../../../utils/module-format';\nimport {\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n} from '../../../utils/shared-constructs-constants';\n\n/**\n * Include CloudFront custom domain aliases in restrictCorsTo and UserIdentity callback URLs\n *\n * How to write a migration:\n * - https://nx.dev/docs/kb/migration-generators\n * - What `nextSteps` means: https://nx.dev/docs/reference/devkit/MigrationReturnObject\n *\n * Guardrails:\n * - Pattern-match before writing: skip files that have diverged from the shape\n * your generators produce and report them via `nextSteps`, rather than\n * clobbering the user's changes.\n * - Idempotent: re-running must be a no-op.\n * - Format what you write: finish with `formatFilesInSubtree` so the files your\n * migration wrote are formatted correctly.\n */\n\nconst CORE_DIR = `${PACKAGES_DIR}/${SHARED_CONSTRUCTS_DIR}/src/core`;\nconst APIS_APP_DIR = `${PACKAGES_DIR}/${SHARED_CONSTRUCTS_DIR}/src/app/apis`;\nconst CLOUDFRONT_CORE_FILE = `${CORE_DIR}/cloudfront.ts`;\nconst CORE_INDEX_FILE = `${CORE_DIR}/index.ts`;\nconst USER_IDENTITY_FILE = `${CORE_DIR}/user-identity.ts`;\n\nconst CLOUDFRONT_HELPER_CONTENT = `import { CfnDistribution, Distribution } from 'aws-cdk-lib/aws-cloudfront';\n\n/**\n * Finds the domain names associated with a CloudFront distribution.\n *\n * Includes the distribution's default \\`*.cloudfront.net\\` domain name plus any custom\n * domain names (aliases) configured on it.\n */\nexport const findCloudFrontDomainNames = (\n distribution: Distribution,\n): string[] => {\n const cfnDistribution = distribution.node.defaultChild as CfnDistribution;\n const distributionConfig =\n cfnDistribution.distributionConfig as CfnDistribution.DistributionConfigProperty;\n return [distribution.domainName, ...(distributionConfig.aliases ?? [])];\n};\n`;\n\n// Rewrites the `restrictCorsTo` body as produced by generators prior to this fix.\nconst RESTRICT_CORS_TO_GRITQL_PATTERN =\n \"`origins.map(($o) => typeof $o === 'string' ? $o : 'cloudFrontDistribution' in $o ? $branch1 : $branch2)` where { $branch1 <: contains `distributionDomainName`, $branch2 <: contains `distributionDomainName` } => raw`origins.flatMap(($o) => typeof $o === 'string' ? [$o] : findCloudFrontDomainNames('cloudFrontDistribution' in $o ? $o.cloudFrontDistribution : $o).map((domain) => \\\\`https://${domain}\\\\`))`\";\n\n// Rewrites UserIdentity's callback/logout URL logic as produced by generators\n// prior to this fix.\nconst USER_IDENTITY_CALLBACK_URLS_GRITQL_PATTERN =\n '`this.findCloudFrontDomainNames()` => `Stack.of(this).node.findAll().filter((child): child is Distribution => child instanceof Distribution).flatMap(findCloudFrontDomainNames)`';\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n if (!tree.exists(CORE_INDEX_FILE)) {\n // No common/constructs shared library in this workspace - nothing to migrate.\n return { nextSteps };\n }\n\n const esm = isEsmWorkspace(tree);\n const apiCloudFrontImportSpecifier = esm\n ? '../../core/cloudfront.js'\n : '../../core/cloudfront';\n const coreCloudFrontImportSpecifier = esm\n ? './cloudfront.js'\n : './cloudfront';\n\n if (!tree.exists(CLOUDFRONT_CORE_FILE)) {\n tree.write(CLOUDFRONT_CORE_FILE, CLOUDFRONT_HELPER_CONTENT);\n }\n await addStarExport(tree, CORE_INDEX_FILE, './cloudfront.js');\n\n const apiAppFiles: string[] = [];\n visitNotIgnoredFiles(tree, APIS_APP_DIR, (filePath) => {\n apiAppFiles.push(filePath);\n });\n\n for (const filePath of apiAppFiles) {\n if (!filePath.endsWith('.ts') || filePath.endsWith('/index.ts')) {\n continue;\n }\n const rewrote = await applyGritQL(\n tree,\n filePath,\n RESTRICT_CORS_TO_GRITQL_PATTERN,\n );\n if (rewrote) {\n await addDestructuredImport(\n tree,\n filePath,\n ['findCloudFrontDomainNames'],\n apiCloudFrontImportSpecifier,\n );\n continue;\n }\n const contents = tree.read(filePath, 'utf-8') ?? '';\n if (!contents.includes('findCloudFrontDomainNames(')) {\n nextSteps.push(\n `${filePath}: restrictCorsTo has diverged from the generated shape - left untouched. Manually apply the CloudFront custom domain fix (see findCloudFrontDomainNames in common/constructs/src/core/cloudfront.ts).`,\n );\n }\n // Otherwise already migrated - silent skip.\n }\n\n if (tree.exists(USER_IDENTITY_FILE)) {\n const rewrote = await applyGritQL(\n tree,\n USER_IDENTITY_FILE,\n USER_IDENTITY_CALLBACK_URLS_GRITQL_PATTERN,\n );\n if (rewrote) {\n await applyGritQL(\n tree,\n USER_IDENTITY_FILE,\n \"`import { CfnDistribution, Distribution } from 'aws-cdk-lib/aws-cloudfront'` => `import { Distribution } from 'aws-cdk-lib/aws-cloudfront';`\",\n );\n await applyGritQL(\n tree,\n USER_IDENTITY_FILE,\n \"or { `// Includes each distribution's default domain name plus any custom domain names (aliases) configured on it.` => ., `private findCloudFrontDomainNames = (): string[] => $body` => . }\",\n );\n await addDestructuredImport(\n tree,\n USER_IDENTITY_FILE,\n ['findCloudFrontDomainNames'],\n coreCloudFrontImportSpecifier,\n );\n } else {\n const contents = tree.read(USER_IDENTITY_FILE, 'utf-8') ?? '';\n if (!contents.includes('.flatMap(findCloudFrontDomainNames)')) {\n nextSteps.push(\n `${USER_IDENTITY_FILE}: the callback URL logic has diverged from the generated shape - left untouched. Manually apply the shared findCloudFrontDomainNames helper (see common/constructs/src/core/cloudfront.ts).`,\n );\n }\n // Otherwise already migrated - silent skip.\n }\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["visitNotIgnoredFiles","addDestructuredImport","addStarExport","applyGritQL","formatFilesInSubtree","isEsmWorkspace","PACKAGES_DIR","SHARED_CONSTRUCTS_DIR","CORE_DIR","APIS_APP_DIR","CLOUDFRONT_CORE_FILE","CORE_INDEX_FILE","USER_IDENTITY_FILE","CLOUDFRONT_HELPER_CONTENT","RESTRICT_CORS_TO_GRITQL_PATTERN","USER_IDENTITY_CALLBACK_URLS_GRITQL_PATTERN","migration","tree","nextSteps","exists","esm","apiCloudFrontImportSpecifier","coreCloudFrontImportSpecifier","write","apiAppFiles","filePath","push","endsWith","rewrote","contents","read","includes"],"mappings":"AAAA;;;CAGC,GACD,SAGEA,oBAAoB,QACf,aAAa;AACpB,SACEC,qBAAqB,EACrBC,aAAa,EACbC,WAAW,QACN,wBAAqB;AAC5B,SAASC,oBAAoB,QAAQ,2BAAwB;AAC7D,SAASC,cAAc,QAAQ,kCAA+B;AAC9D,SACEC,YAAY,EACZC,qBAAqB,QAChB,gDAA6C;AAEpD;;;;;;;;;;;;;;CAcC,GAED,MAAMC,WAAW,GAAGF,aAAa,CAAC,EAAEC,sBAAsB,SAAS,CAAC;AACpE,MAAME,eAAe,GAAGH,aAAa,CAAC,EAAEC,sBAAsB,aAAa,CAAC;AAC5E,MAAMG,uBAAuB,GAAGF,SAAS,cAAc,CAAC;AACxD,MAAMG,kBAAkB,GAAGH,SAAS,SAAS,CAAC;AAC9C,MAAMI,qBAAqB,GAAGJ,SAAS,iBAAiB,CAAC;AAEzD,MAAMK,4BAA4B,CAAC;;;;;;;;;;;;;;;;AAgBnC,CAAC;AAED,kFAAkF;AAClF,MAAMC,kCACJ;AAEF,8EAA8E;AAC9E,qBAAqB;AACrB,MAAMC,6CACJ;AAEF,eAAe,eAAeC,UAC5BC,IAAU;IAEV,MAAMC,YAAsB,EAAE;IAE9B,IAAI,CAACD,KAAKE,MAAM,CAACR,kBAAkB;QACjC,8EAA8E;QAC9E,OAAO;YAAEO;QAAU;IACrB;IAEA,MAAME,MAAMf,eAAeY;IAC3B,MAAMI,+BAA+BD,MACjC,6BACA;IACJ,MAAME,gCAAgCF,MAClC,oBACA;IAEJ,IAAI,CAACH,KAAKE,MAAM,CAACT,uBAAuB;QACtCO,KAAKM,KAAK,CAACb,sBAAsBG;IACnC;IACA,MAAMX,cAAce,MAAMN,iBAAiB;IAE3C,MAAMa,cAAwB,EAAE;IAChCxB,qBAAqBiB,MAAMR,cAAc,CAACgB;QACxCD,YAAYE,IAAI,CAACD;IACnB;IAEA,KAAK,MAAMA,YAAYD,YAAa;QAClC,IAAI,CAACC,SAASE,QAAQ,CAAC,UAAUF,SAASE,QAAQ,CAAC,cAAc;YAC/D;QACF;QACA,MAAMC,UAAU,MAAMzB,YACpBc,MACAQ,UACAX;QAEF,IAAIc,SAAS;YACX,MAAM3B,sBACJgB,MACAQ,UACA;gBAAC;aAA4B,EAC7BJ;YAEF;QACF;QACA,MAAMQ,WAAWZ,KAAKa,IAAI,CAACL,UAAU,YAAY;QACjD,IAAI,CAACI,SAASE,QAAQ,CAAC,+BAA+B;YACpDb,UAAUQ,IAAI,CACZ,GAAGD,SAAS,qMAAqM,CAAC;QAEtN;IACA,4CAA4C;IAC9C;IAEA,IAAIR,KAAKE,MAAM,CAACP,qBAAqB;QACnC,MAAMgB,UAAU,MAAMzB,YACpBc,MACAL,oBACAG;QAEF,IAAIa,SAAS;YACX,MAAMzB,YACJc,MACAL,oBACA;YAEF,MAAMT,YACJc,MACAL,oBACA;YAEF,MAAMX,sBACJgB,MACAL,oBACA;gBAAC;aAA4B,EAC7BU;QAEJ,OAAO;YACL,MAAMO,WAAWZ,KAAKa,IAAI,CAAClB,oBAAoB,YAAY;YAC3D,IAAI,CAACiB,SAASE,QAAQ,CAAC,wCAAwC;gBAC7Db,UAAUQ,IAAI,CACZ,GAAGd,mBAAmB,2LAA2L,CAAC;YAEtN;QACA,4CAA4C;QAC9C;IACF;IAEA,MAAMR,qBAAqBa;IAE3B,OAAO;QAAEC;IAAU;AACrB"}
1
+ {"version":3,"sources":["../../../../../../../packages/nx-plugin/src/migrations/v1.0.0-rc.50/0002-restrict-cors-to-custom-domains/migration.ts"],"sourcesContent":["/**\n * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.\n * SPDX-License-Identifier: Apache-2.0\n */\nimport {\n type MigrationReturnObject,\n type Tree,\n visitNotIgnoredFiles,\n} from '@nx/devkit';\nimport {\n addDestructuredImport,\n addStarExport,\n applyGritQL,\n} from '../../../utils/ast.js';\nimport { formatFilesInSubtree } from '../../../utils/format.js';\nimport { isEsmWorkspace } from '../../../utils/module-format.js';\nimport {\n PACKAGES_DIR,\n SHARED_CONSTRUCTS_DIR,\n} from '../../../utils/shared-constructs-constants.js';\n\n/**\n * Include CloudFront custom domain aliases in restrictCorsTo and UserIdentity callback URLs\n *\n * How to write a migration:\n * - https://nx.dev/docs/kb/migration-generators\n * - What `nextSteps` means: https://nx.dev/docs/reference/devkit/MigrationReturnObject\n *\n * Guardrails:\n * - Pattern-match before writing: skip files that have diverged from the shape\n * your generators produce and report them via `nextSteps`, rather than\n * clobbering the user's changes.\n * - Idempotent: re-running must be a no-op.\n * - Format what you write: finish with `formatFilesInSubtree` so the files your\n * migration wrote are formatted correctly.\n */\n\nconst CORE_DIR = `${PACKAGES_DIR}/${SHARED_CONSTRUCTS_DIR}/src/core`;\nconst APIS_APP_DIR = `${PACKAGES_DIR}/${SHARED_CONSTRUCTS_DIR}/src/app/apis`;\nconst CLOUDFRONT_CORE_FILE = `${CORE_DIR}/cloudfront.ts`;\nconst CORE_INDEX_FILE = `${CORE_DIR}/index.ts`;\nconst USER_IDENTITY_FILE = `${CORE_DIR}/user-identity.ts`;\n\nconst CLOUDFRONT_HELPER_CONTENT = `import { CfnDistribution, Distribution } from 'aws-cdk-lib/aws-cloudfront';\n\n/**\n * Finds the domain names associated with a CloudFront distribution.\n *\n * Includes the distribution's default \\`*.cloudfront.net\\` domain name plus any custom\n * domain names (aliases) configured on it.\n */\nexport const findCloudFrontDomainNames = (\n distribution: Distribution,\n): string[] => {\n const cfnDistribution = distribution.node.defaultChild as CfnDistribution;\n const distributionConfig =\n cfnDistribution.distributionConfig as CfnDistribution.DistributionConfigProperty;\n return [distribution.domainName, ...(distributionConfig.aliases ?? [])];\n};\n`;\n\n// Rewrites the `restrictCorsTo` body as produced by generators prior to this fix.\nconst RESTRICT_CORS_TO_GRITQL_PATTERN =\n \"`origins.map(($o) => typeof $o === 'string' ? $o : 'cloudFrontDistribution' in $o ? $branch1 : $branch2)` where { $branch1 <: contains `distributionDomainName`, $branch2 <: contains `distributionDomainName` } => raw`origins.flatMap(($o) => typeof $o === 'string' ? [$o] : findCloudFrontDomainNames('cloudFrontDistribution' in $o ? $o.cloudFrontDistribution : $o).map((domain) => \\\\`https://${domain}\\\\`))`\";\n\n// Rewrites UserIdentity's callback/logout URL logic as produced by generators\n// prior to this fix.\nconst USER_IDENTITY_CALLBACK_URLS_GRITQL_PATTERN =\n '`this.findCloudFrontDomainNames()` => `Stack.of(this).node.findAll().filter((child): child is Distribution => child instanceof Distribution).flatMap(findCloudFrontDomainNames)`';\n\nexport default async function migration(\n tree: Tree,\n): Promise<MigrationReturnObject> {\n const nextSteps: string[] = [];\n\n if (!tree.exists(CORE_INDEX_FILE)) {\n // No common/constructs shared library in this workspace - nothing to migrate.\n return { nextSteps };\n }\n\n const esm = isEsmWorkspace(tree);\n const apiCloudFrontImportSpecifier = esm\n ? '../../core/cloudfront.js'\n : '../../core/cloudfront';\n const coreCloudFrontImportSpecifier = esm\n ? './cloudfront.js'\n : './cloudfront';\n\n if (!tree.exists(CLOUDFRONT_CORE_FILE)) {\n tree.write(CLOUDFRONT_CORE_FILE, CLOUDFRONT_HELPER_CONTENT);\n }\n await addStarExport(tree, CORE_INDEX_FILE, './cloudfront.js');\n\n const apiAppFiles: string[] = [];\n visitNotIgnoredFiles(tree, APIS_APP_DIR, (filePath) => {\n apiAppFiles.push(filePath);\n });\n\n for (const filePath of apiAppFiles) {\n if (!filePath.endsWith('.ts') || filePath.endsWith('/index.ts')) {\n continue;\n }\n const rewrote = await applyGritQL(\n tree,\n filePath,\n RESTRICT_CORS_TO_GRITQL_PATTERN,\n );\n if (rewrote) {\n await addDestructuredImport(\n tree,\n filePath,\n ['findCloudFrontDomainNames'],\n apiCloudFrontImportSpecifier,\n );\n continue;\n }\n const contents = tree.read(filePath, 'utf-8') ?? '';\n if (!contents.includes('findCloudFrontDomainNames(')) {\n nextSteps.push(\n `${filePath}: restrictCorsTo has diverged from the generated shape - left untouched. Manually apply the CloudFront custom domain fix (see findCloudFrontDomainNames in common/constructs/src/core/cloudfront.ts).`,\n );\n }\n // Otherwise already migrated - silent skip.\n }\n\n if (tree.exists(USER_IDENTITY_FILE)) {\n const rewrote = await applyGritQL(\n tree,\n USER_IDENTITY_FILE,\n USER_IDENTITY_CALLBACK_URLS_GRITQL_PATTERN,\n );\n if (rewrote) {\n await applyGritQL(\n tree,\n USER_IDENTITY_FILE,\n \"`import { CfnDistribution, Distribution } from 'aws-cdk-lib/aws-cloudfront'` => `import { Distribution } from 'aws-cdk-lib/aws-cloudfront';`\",\n );\n await applyGritQL(\n tree,\n USER_IDENTITY_FILE,\n \"or { `// Includes each distribution's default domain name plus any custom domain names (aliases) configured on it.` => ., `private findCloudFrontDomainNames = (): string[] => $body` => . }\",\n );\n await addDestructuredImport(\n tree,\n USER_IDENTITY_FILE,\n ['findCloudFrontDomainNames'],\n coreCloudFrontImportSpecifier,\n );\n } else {\n const contents = tree.read(USER_IDENTITY_FILE, 'utf-8') ?? '';\n if (!contents.includes('.flatMap(findCloudFrontDomainNames)')) {\n nextSteps.push(\n `${USER_IDENTITY_FILE}: the callback URL logic has diverged from the generated shape - left untouched. Manually apply the shared findCloudFrontDomainNames helper (see common/constructs/src/core/cloudfront.ts).`,\n );\n }\n // Otherwise already migrated - silent skip.\n }\n }\n\n await formatFilesInSubtree(tree);\n\n return { nextSteps };\n}\n"],"names":["visitNotIgnoredFiles","addDestructuredImport","addStarExport","applyGritQL","formatFilesInSubtree","isEsmWorkspace","PACKAGES_DIR","SHARED_CONSTRUCTS_DIR","CORE_DIR","APIS_APP_DIR","CLOUDFRONT_CORE_FILE","CORE_INDEX_FILE","USER_IDENTITY_FILE","CLOUDFRONT_HELPER_CONTENT","RESTRICT_CORS_TO_GRITQL_PATTERN","USER_IDENTITY_CALLBACK_URLS_GRITQL_PATTERN","migration","tree","nextSteps","exists","esm","apiCloudFrontImportSpecifier","coreCloudFrontImportSpecifier","write","apiAppFiles","filePath","push","endsWith","rewrote","contents","read","includes"],"mappings":"AAAA;;;CAGC,GACD,SAGEA,oBAAoB,QACf,aAAa;AACpB,SACEC,qBAAqB,EACrBC,aAAa,EACbC,WAAW,QACN,wBAAwB;AAC/B,SAASC,oBAAoB,QAAQ,2BAA2B;AAChE,SAASC,cAAc,QAAQ,kCAAkC;AACjE,SACEC,YAAY,EACZC,qBAAqB,QAChB,gDAAgD;AAEvD;;;;;;;;;;;;;;CAcC,GAED,MAAMC,WAAW,GAAGF,aAAa,CAAC,EAAEC,sBAAsB,SAAS,CAAC;AACpE,MAAME,eAAe,GAAGH,aAAa,CAAC,EAAEC,sBAAsB,aAAa,CAAC;AAC5E,MAAMG,uBAAuB,GAAGF,SAAS,cAAc,CAAC;AACxD,MAAMG,kBAAkB,GAAGH,SAAS,SAAS,CAAC;AAC9C,MAAMI,qBAAqB,GAAGJ,SAAS,iBAAiB,CAAC;AAEzD,MAAMK,4BAA4B,CAAC;;;;;;;;;;;;;;;;AAgBnC,CAAC;AAED,kFAAkF;AAClF,MAAMC,kCACJ;AAEF,8EAA8E;AAC9E,qBAAqB;AACrB,MAAMC,6CACJ;AAEF,eAAe,eAAeC,UAC5BC,IAAU;IAEV,MAAMC,YAAsB,EAAE;IAE9B,IAAI,CAACD,KAAKE,MAAM,CAACR,kBAAkB;QACjC,8EAA8E;QAC9E,OAAO;YAAEO;QAAU;IACrB;IAEA,MAAME,MAAMf,eAAeY;IAC3B,MAAMI,+BAA+BD,MACjC,6BACA;IACJ,MAAME,gCAAgCF,MAClC,oBACA;IAEJ,IAAI,CAACH,KAAKE,MAAM,CAACT,uBAAuB;QACtCO,KAAKM,KAAK,CAACb,sBAAsBG;IACnC;IACA,MAAMX,cAAce,MAAMN,iBAAiB;IAE3C,MAAMa,cAAwB,EAAE;IAChCxB,qBAAqBiB,MAAMR,cAAc,CAACgB;QACxCD,YAAYE,IAAI,CAACD;IACnB;IAEA,KAAK,MAAMA,YAAYD,YAAa;QAClC,IAAI,CAACC,SAASE,QAAQ,CAAC,UAAUF,SAASE,QAAQ,CAAC,cAAc;YAC/D;QACF;QACA,MAAMC,UAAU,MAAMzB,YACpBc,MACAQ,UACAX;QAEF,IAAIc,SAAS;YACX,MAAM3B,sBACJgB,MACAQ,UACA;gBAAC;aAA4B,EAC7BJ;YAEF;QACF;QACA,MAAMQ,WAAWZ,KAAKa,IAAI,CAACL,UAAU,YAAY;QACjD,IAAI,CAACI,SAASE,QAAQ,CAAC,+BAA+B;YACpDb,UAAUQ,IAAI,CACZ,GAAGD,SAAS,qMAAqM,CAAC;QAEtN;IACA,4CAA4C;IAC9C;IAEA,IAAIR,KAAKE,MAAM,CAACP,qBAAqB;QACnC,MAAMgB,UAAU,MAAMzB,YACpBc,MACAL,oBACAG;QAEF,IAAIa,SAAS;YACX,MAAMzB,YACJc,MACAL,oBACA;YAEF,MAAMT,YACJc,MACAL,oBACA;YAEF,MAAMX,sBACJgB,MACAL,oBACA;gBAAC;aAA4B,EAC7BU;QAEJ,OAAO;YACL,MAAMO,WAAWZ,KAAKa,IAAI,CAAClB,oBAAoB,YAAY;YAC3D,IAAI,CAACiB,SAASE,QAAQ,CAAC,wCAAwC;gBAC7Db,UAAUQ,IAAI,CACZ,GAAGd,mBAAmB,2LAA2L,CAAC;YAEtN;QACA,4CAA4C;QAC9C;IACF;IAEA,MAAMR,qBAAqBa;IAE3B,OAAO;QAAEC;IAAU;AACrB"}