@friggframework/devtools 2.0.0-next.5 → 2.0.0-next.50

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 (356) hide show
  1. package/frigg-cli/README.md +1290 -0
  2. package/frigg-cli/__tests__/unit/commands/build.test.js +279 -0
  3. package/frigg-cli/__tests__/unit/commands/db-setup.test.js +548 -0
  4. package/frigg-cli/__tests__/unit/commands/deploy.test.js +320 -0
  5. package/frigg-cli/__tests__/unit/commands/doctor.test.js +309 -0
  6. package/frigg-cli/__tests__/unit/commands/install.test.js +400 -0
  7. package/frigg-cli/__tests__/unit/commands/ui.test.js +346 -0
  8. package/frigg-cli/__tests__/unit/dependencies.test.js +74 -0
  9. package/frigg-cli/__tests__/unit/utils/database-validator.test.js +366 -0
  10. package/frigg-cli/__tests__/unit/utils/error-messages.test.js +304 -0
  11. package/frigg-cli/__tests__/unit/version-detection.test.js +171 -0
  12. package/frigg-cli/__tests__/utils/mock-factory.js +270 -0
  13. package/frigg-cli/__tests__/utils/prisma-mock.js +194 -0
  14. package/frigg-cli/__tests__/utils/test-fixtures.js +463 -0
  15. package/frigg-cli/__tests__/utils/test-setup.js +287 -0
  16. package/frigg-cli/build-command/index.js +66 -0
  17. package/frigg-cli/db-setup-command/index.js +193 -0
  18. package/frigg-cli/deploy-command/SPEC-DEPLOY-DRY-RUN.md +981 -0
  19. package/frigg-cli/deploy-command/index.js +302 -0
  20. package/frigg-cli/doctor-command/index.js +335 -0
  21. package/frigg-cli/generate-command/__tests__/generate-command.test.js +301 -0
  22. package/frigg-cli/generate-command/azure-generator.js +43 -0
  23. package/frigg-cli/generate-command/gcp-generator.js +47 -0
  24. package/frigg-cli/generate-command/index.js +332 -0
  25. package/frigg-cli/generate-command/terraform-generator.js +555 -0
  26. package/frigg-cli/generate-iam-command.js +118 -0
  27. package/frigg-cli/index.js +154 -1
  28. package/frigg-cli/index.test.js +1 -4
  29. package/frigg-cli/init-command/backend-first-handler.js +756 -0
  30. package/frigg-cli/init-command/index.js +93 -0
  31. package/frigg-cli/init-command/template-handler.js +143 -0
  32. package/frigg-cli/install-command/index.js +1 -4
  33. package/frigg-cli/jest.config.js +124 -0
  34. package/frigg-cli/package.json +63 -0
  35. package/frigg-cli/repair-command/index.js +564 -0
  36. package/frigg-cli/start-command/index.js +125 -6
  37. package/frigg-cli/start-command/start-command.test.js +297 -0
  38. package/frigg-cli/test/init-command.test.js +180 -0
  39. package/frigg-cli/test/npm-registry.test.js +319 -0
  40. package/frigg-cli/ui-command/index.js +154 -0
  41. package/frigg-cli/utils/app-resolver.js +319 -0
  42. package/frigg-cli/utils/backend-path.js +16 -17
  43. package/frigg-cli/utils/database-validator.js +154 -0
  44. package/frigg-cli/utils/error-messages.js +257 -0
  45. package/frigg-cli/utils/npm-registry.js +167 -0
  46. package/frigg-cli/utils/process-manager.js +199 -0
  47. package/frigg-cli/utils/repo-detection.js +405 -0
  48. package/infrastructure/ARCHITECTURE.md +487 -0
  49. package/infrastructure/CLAUDE.md +481 -0
  50. package/infrastructure/HEALTH.md +468 -0
  51. package/infrastructure/README.md +494 -0
  52. package/infrastructure/__tests__/fixtures/mock-aws-resources.js +391 -0
  53. package/infrastructure/__tests__/helpers/test-utils.js +277 -0
  54. package/infrastructure/__tests__/postgres-config.test.js +914 -0
  55. package/infrastructure/__tests__/template-generation.test.js +687 -0
  56. package/infrastructure/create-frigg-infrastructure.js +129 -20
  57. package/infrastructure/docs/POSTGRES-CONFIGURATION.md +630 -0
  58. package/infrastructure/docs/PRE-DEPLOYMENT-HEALTH-CHECK-SPEC.md +1317 -0
  59. package/infrastructure/docs/WEBSOCKET-CONFIGURATION.md +105 -0
  60. package/infrastructure/docs/deployment-instructions.md +268 -0
  61. package/infrastructure/docs/generate-iam-command.md +278 -0
  62. package/infrastructure/docs/iam-policy-templates.md +193 -0
  63. package/infrastructure/domains/database/aurora-builder.js +809 -0
  64. package/infrastructure/domains/database/aurora-builder.test.js +950 -0
  65. package/infrastructure/domains/database/aurora-discovery.js +87 -0
  66. package/infrastructure/domains/database/aurora-discovery.test.js +188 -0
  67. package/infrastructure/domains/database/aurora-resolver.js +210 -0
  68. package/infrastructure/domains/database/aurora-resolver.test.js +347 -0
  69. package/infrastructure/domains/database/migration-builder.js +695 -0
  70. package/infrastructure/domains/database/migration-builder.test.js +294 -0
  71. package/infrastructure/domains/database/migration-resolver.js +163 -0
  72. package/infrastructure/domains/database/migration-resolver.test.js +337 -0
  73. package/infrastructure/domains/health/application/ports/IPropertyReconciler.js +164 -0
  74. package/infrastructure/domains/health/application/ports/IResourceDetector.js +129 -0
  75. package/infrastructure/domains/health/application/ports/IResourceImporter.js +142 -0
  76. package/infrastructure/domains/health/application/ports/IStackRepository.js +131 -0
  77. package/infrastructure/domains/health/application/ports/index.js +26 -0
  78. package/infrastructure/domains/health/application/use-cases/__tests__/execute-resource-import-use-case.test.js +679 -0
  79. package/infrastructure/domains/health/application/use-cases/__tests__/mismatch-analyzer-method-name.test.js +167 -0
  80. package/infrastructure/domains/health/application/use-cases/__tests__/repair-via-import-use-case.test.js +1130 -0
  81. package/infrastructure/domains/health/application/use-cases/execute-resource-import-use-case.js +221 -0
  82. package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.js +152 -0
  83. package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.test.js +343 -0
  84. package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.js +535 -0
  85. package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.test.js +376 -0
  86. package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.js +213 -0
  87. package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.test.js +441 -0
  88. package/infrastructure/domains/health/docs/ACME-DEV-DRIFT-ANALYSIS.md +267 -0
  89. package/infrastructure/domains/health/docs/BUILD-VS-DEPLOYED-TEMPLATE-ANALYSIS.md +324 -0
  90. package/infrastructure/domains/health/docs/ORPHAN-DETECTION-ANALYSIS.md +386 -0
  91. package/infrastructure/domains/health/docs/SPEC-CLEANUP-COMMAND.md +1419 -0
  92. package/infrastructure/domains/health/docs/TDD-IMPLEMENTATION-SUMMARY.md +391 -0
  93. package/infrastructure/domains/health/docs/TEMPLATE-COMPARISON-IMPLEMENTATION.md +551 -0
  94. package/infrastructure/domains/health/domain/entities/issue.js +299 -0
  95. package/infrastructure/domains/health/domain/entities/issue.test.js +528 -0
  96. package/infrastructure/domains/health/domain/entities/property-mismatch.js +108 -0
  97. package/infrastructure/domains/health/domain/entities/property-mismatch.test.js +275 -0
  98. package/infrastructure/domains/health/domain/entities/resource.js +159 -0
  99. package/infrastructure/domains/health/domain/entities/resource.test.js +432 -0
  100. package/infrastructure/domains/health/domain/entities/stack-health-report.js +306 -0
  101. package/infrastructure/domains/health/domain/entities/stack-health-report.test.js +601 -0
  102. package/infrastructure/domains/health/domain/services/__tests__/health-score-percentage-based.test.js +380 -0
  103. package/infrastructure/domains/health/domain/services/__tests__/import-progress-monitor.test.js +971 -0
  104. package/infrastructure/domains/health/domain/services/__tests__/import-template-generator.test.js +1150 -0
  105. package/infrastructure/domains/health/domain/services/__tests__/logical-id-mapper.test.js +672 -0
  106. package/infrastructure/domains/health/domain/services/__tests__/template-parser.test.js +496 -0
  107. package/infrastructure/domains/health/domain/services/__tests__/update-progress-monitor.test.js +419 -0
  108. package/infrastructure/domains/health/domain/services/health-score-calculator.js +248 -0
  109. package/infrastructure/domains/health/domain/services/health-score-calculator.test.js +504 -0
  110. package/infrastructure/domains/health/domain/services/import-progress-monitor.js +195 -0
  111. package/infrastructure/domains/health/domain/services/import-template-generator.js +435 -0
  112. package/infrastructure/domains/health/domain/services/logical-id-mapper.js +345 -0
  113. package/infrastructure/domains/health/domain/services/mismatch-analyzer.js +234 -0
  114. package/infrastructure/domains/health/domain/services/mismatch-analyzer.test.js +431 -0
  115. package/infrastructure/domains/health/domain/services/property-mutability-config.js +382 -0
  116. package/infrastructure/domains/health/domain/services/template-parser.js +245 -0
  117. package/infrastructure/domains/health/domain/services/update-progress-monitor.js +192 -0
  118. package/infrastructure/domains/health/domain/value-objects/health-score.js +138 -0
  119. package/infrastructure/domains/health/domain/value-objects/health-score.test.js +267 -0
  120. package/infrastructure/domains/health/domain/value-objects/property-mutability.js +161 -0
  121. package/infrastructure/domains/health/domain/value-objects/property-mutability.test.js +198 -0
  122. package/infrastructure/domains/health/domain/value-objects/resource-state.js +167 -0
  123. package/infrastructure/domains/health/domain/value-objects/resource-state.test.js +196 -0
  124. package/infrastructure/domains/health/domain/value-objects/stack-identifier.js +192 -0
  125. package/infrastructure/domains/health/domain/value-objects/stack-identifier.test.js +262 -0
  126. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-cfn-tagged.test.js +312 -0
  127. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-multi-stack.test.js +367 -0
  128. package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-relationship-analysis.test.js +432 -0
  129. package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.js +784 -0
  130. package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.test.js +1133 -0
  131. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.js +565 -0
  132. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.test.js +554 -0
  133. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.js +318 -0
  134. package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.test.js +398 -0
  135. package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.js +777 -0
  136. package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.test.js +580 -0
  137. package/infrastructure/domains/integration/integration-builder.js +397 -0
  138. package/infrastructure/domains/integration/integration-builder.test.js +593 -0
  139. package/infrastructure/domains/integration/integration-resolver.js +170 -0
  140. package/infrastructure/domains/integration/integration-resolver.test.js +369 -0
  141. package/infrastructure/domains/integration/websocket-builder.js +69 -0
  142. package/infrastructure/domains/integration/websocket-builder.test.js +195 -0
  143. package/infrastructure/domains/networking/vpc-builder.js +1829 -0
  144. package/infrastructure/domains/networking/vpc-builder.test.js +1262 -0
  145. package/infrastructure/domains/networking/vpc-discovery.js +177 -0
  146. package/infrastructure/domains/networking/vpc-discovery.test.js +350 -0
  147. package/infrastructure/domains/networking/vpc-resolver.js +324 -0
  148. package/infrastructure/domains/networking/vpc-resolver.test.js +501 -0
  149. package/infrastructure/domains/parameters/ssm-builder.js +79 -0
  150. package/infrastructure/domains/parameters/ssm-builder.test.js +189 -0
  151. package/infrastructure/domains/parameters/ssm-discovery.js +84 -0
  152. package/infrastructure/domains/parameters/ssm-discovery.test.js +210 -0
  153. package/infrastructure/domains/security/iam-generator.js +816 -0
  154. package/infrastructure/domains/security/iam-generator.test.js +204 -0
  155. package/infrastructure/domains/security/kms-builder.js +366 -0
  156. package/infrastructure/domains/security/kms-builder.test.js +374 -0
  157. package/infrastructure/domains/security/kms-discovery.js +80 -0
  158. package/infrastructure/domains/security/kms-discovery.test.js +177 -0
  159. package/infrastructure/domains/security/kms-resolver.js +96 -0
  160. package/infrastructure/domains/security/kms-resolver.test.js +216 -0
  161. package/infrastructure/domains/security/templates/frigg-deployment-iam-stack.yaml +401 -0
  162. package/infrastructure/domains/security/templates/iam-policy-basic.json +218 -0
  163. package/infrastructure/domains/security/templates/iam-policy-full.json +288 -0
  164. package/infrastructure/domains/shared/base-builder.js +112 -0
  165. package/infrastructure/domains/shared/base-resolver.js +186 -0
  166. package/infrastructure/domains/shared/base-resolver.test.js +305 -0
  167. package/infrastructure/domains/shared/builder-orchestrator.js +212 -0
  168. package/infrastructure/domains/shared/builder-orchestrator.test.js +213 -0
  169. package/infrastructure/domains/shared/cloudformation-discovery-v2.js +334 -0
  170. package/infrastructure/domains/shared/cloudformation-discovery.js +375 -0
  171. package/infrastructure/domains/shared/cloudformation-discovery.test.js +590 -0
  172. package/infrastructure/domains/shared/environment-builder.js +119 -0
  173. package/infrastructure/domains/shared/environment-builder.test.js +247 -0
  174. package/infrastructure/domains/shared/providers/aws-provider-adapter.js +544 -0
  175. package/infrastructure/domains/shared/providers/aws-provider-adapter.test.js +377 -0
  176. package/infrastructure/domains/shared/providers/azure-provider-adapter.stub.js +93 -0
  177. package/infrastructure/domains/shared/providers/cloud-provider-adapter.js +136 -0
  178. package/infrastructure/domains/shared/providers/gcp-provider-adapter.stub.js +82 -0
  179. package/infrastructure/domains/shared/providers/provider-factory.js +108 -0
  180. package/infrastructure/domains/shared/providers/provider-factory.test.js +170 -0
  181. package/infrastructure/domains/shared/resource-discovery.enhanced.test.js +306 -0
  182. package/infrastructure/domains/shared/resource-discovery.js +221 -0
  183. package/infrastructure/domains/shared/resource-discovery.test.js +552 -0
  184. package/infrastructure/domains/shared/types/app-definition.js +205 -0
  185. package/infrastructure/domains/shared/types/discovery-result.js +106 -0
  186. package/infrastructure/domains/shared/types/discovery-result.test.js +258 -0
  187. package/infrastructure/domains/shared/types/index.js +46 -0
  188. package/infrastructure/domains/shared/types/resource-ownership.js +108 -0
  189. package/infrastructure/domains/shared/types/resource-ownership.test.js +101 -0
  190. package/infrastructure/domains/shared/utilities/base-definition-factory.js +380 -0
  191. package/infrastructure/domains/shared/utilities/base-definition-factory.js.bak +338 -0
  192. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +248 -0
  193. package/infrastructure/domains/shared/utilities/handler-path-resolver.js +134 -0
  194. package/infrastructure/domains/shared/utilities/handler-path-resolver.test.js +268 -0
  195. package/infrastructure/domains/shared/utilities/prisma-layer-manager.js +159 -0
  196. package/infrastructure/domains/shared/utilities/prisma-layer-manager.test.js +444 -0
  197. package/infrastructure/domains/shared/validation/env-validator.js +78 -0
  198. package/infrastructure/domains/shared/validation/env-validator.test.js +173 -0
  199. package/infrastructure/domains/shared/validation/plugin-validator.js +187 -0
  200. package/infrastructure/domains/shared/validation/plugin-validator.test.js +323 -0
  201. package/infrastructure/esbuild.config.js +53 -0
  202. package/infrastructure/infrastructure-composer.js +109 -0
  203. package/infrastructure/infrastructure-composer.test.js +1895 -0
  204. package/infrastructure/integration.test.js +383 -0
  205. package/infrastructure/scripts/build-prisma-layer.js +553 -0
  206. package/infrastructure/scripts/build-prisma-layer.test.js +102 -0
  207. package/infrastructure/scripts/build-time-discovery.js +238 -0
  208. package/infrastructure/scripts/build-time-discovery.test.js +379 -0
  209. package/infrastructure/scripts/run-discovery.js +110 -0
  210. package/layers/prisma/.build-complete +3 -0
  211. package/layers/prisma/nodejs/package.json +8 -0
  212. package/management-ui/.eslintrc.js +22 -0
  213. package/management-ui/README.md +203 -0
  214. package/management-ui/components.json +21 -0
  215. package/management-ui/docs/phase2-integration-guide.md +320 -0
  216. package/management-ui/index.html +13 -0
  217. package/management-ui/package.json +76 -0
  218. package/management-ui/packages/devtools/frigg-cli/ui-command/index.js +302 -0
  219. package/management-ui/postcss.config.js +6 -0
  220. package/management-ui/server/api/backend.js +256 -0
  221. package/management-ui/server/api/cli.js +315 -0
  222. package/management-ui/server/api/codegen.js +663 -0
  223. package/management-ui/server/api/connections.js +857 -0
  224. package/management-ui/server/api/discovery.js +185 -0
  225. package/management-ui/server/api/environment/index.js +1 -0
  226. package/management-ui/server/api/environment/router.js +378 -0
  227. package/management-ui/server/api/environment.js +328 -0
  228. package/management-ui/server/api/integrations.js +876 -0
  229. package/management-ui/server/api/logs.js +248 -0
  230. package/management-ui/server/api/monitoring.js +282 -0
  231. package/management-ui/server/api/open-ide.js +31 -0
  232. package/management-ui/server/api/project.js +1029 -0
  233. package/management-ui/server/api/users/sessions.js +371 -0
  234. package/management-ui/server/api/users/simulation.js +254 -0
  235. package/management-ui/server/api/users.js +362 -0
  236. package/management-ui/server/api-contract.md +275 -0
  237. package/management-ui/server/index.js +873 -0
  238. package/management-ui/server/middleware/errorHandler.js +93 -0
  239. package/management-ui/server/middleware/security.js +32 -0
  240. package/management-ui/server/processManager.js +296 -0
  241. package/management-ui/server/server.js +346 -0
  242. package/management-ui/server/services/aws-monitor.js +413 -0
  243. package/management-ui/server/services/npm-registry.js +347 -0
  244. package/management-ui/server/services/template-engine.js +538 -0
  245. package/management-ui/server/utils/cliIntegration.js +220 -0
  246. package/management-ui/server/utils/environment/auditLogger.js +471 -0
  247. package/management-ui/server/utils/environment/awsParameterStore.js +275 -0
  248. package/management-ui/server/utils/environment/encryption.js +278 -0
  249. package/management-ui/server/utils/environment/envFileManager.js +286 -0
  250. package/management-ui/server/utils/import-commonjs.js +28 -0
  251. package/management-ui/server/utils/response.js +83 -0
  252. package/management-ui/server/websocket/handler.js +325 -0
  253. package/management-ui/src/App.jsx +25 -0
  254. package/management-ui/src/assets/FriggLogo.svg +1 -0
  255. package/management-ui/src/components/AppRouter.jsx +65 -0
  256. package/management-ui/src/components/Button.jsx +70 -0
  257. package/management-ui/src/components/Card.jsx +97 -0
  258. package/management-ui/src/components/EnvironmentCompare.jsx +400 -0
  259. package/management-ui/src/components/EnvironmentEditor.jsx +372 -0
  260. package/management-ui/src/components/EnvironmentImportExport.jsx +469 -0
  261. package/management-ui/src/components/EnvironmentSchema.jsx +491 -0
  262. package/management-ui/src/components/EnvironmentSecurity.jsx +463 -0
  263. package/management-ui/src/components/ErrorBoundary.jsx +73 -0
  264. package/management-ui/src/components/IntegrationCard.jsx +481 -0
  265. package/management-ui/src/components/IntegrationCardEnhanced.jsx +770 -0
  266. package/management-ui/src/components/IntegrationExplorer.jsx +379 -0
  267. package/management-ui/src/components/IntegrationStatus.jsx +336 -0
  268. package/management-ui/src/components/Layout.jsx +716 -0
  269. package/management-ui/src/components/LoadingSpinner.jsx +113 -0
  270. package/management-ui/src/components/RepositoryPicker.jsx +248 -0
  271. package/management-ui/src/components/SessionMonitor.jsx +350 -0
  272. package/management-ui/src/components/StatusBadge.jsx +208 -0
  273. package/management-ui/src/components/UserContextSwitcher.jsx +212 -0
  274. package/management-ui/src/components/UserSimulation.jsx +327 -0
  275. package/management-ui/src/components/Welcome.jsx +434 -0
  276. package/management-ui/src/components/codegen/APIEndpointGenerator.jsx +637 -0
  277. package/management-ui/src/components/codegen/APIModuleSelector.jsx +227 -0
  278. package/management-ui/src/components/codegen/CodeGenerationWizard.jsx +247 -0
  279. package/management-ui/src/components/codegen/CodePreviewEditor.jsx +316 -0
  280. package/management-ui/src/components/codegen/DynamicModuleForm.jsx +271 -0
  281. package/management-ui/src/components/codegen/FormBuilder.jsx +737 -0
  282. package/management-ui/src/components/codegen/IntegrationGenerator.jsx +855 -0
  283. package/management-ui/src/components/codegen/ProjectScaffoldWizard.jsx +797 -0
  284. package/management-ui/src/components/codegen/SchemaBuilder.jsx +303 -0
  285. package/management-ui/src/components/codegen/TemplateSelector.jsx +586 -0
  286. package/management-ui/src/components/codegen/index.js +10 -0
  287. package/management-ui/src/components/connections/ConnectionConfigForm.jsx +362 -0
  288. package/management-ui/src/components/connections/ConnectionHealthMonitor.jsx +182 -0
  289. package/management-ui/src/components/connections/ConnectionTester.jsx +200 -0
  290. package/management-ui/src/components/connections/EntityRelationshipMapper.jsx +292 -0
  291. package/management-ui/src/components/connections/OAuthFlow.jsx +204 -0
  292. package/management-ui/src/components/connections/index.js +5 -0
  293. package/management-ui/src/components/index.js +21 -0
  294. package/management-ui/src/components/monitoring/APIGatewayMetrics.jsx +222 -0
  295. package/management-ui/src/components/monitoring/LambdaMetrics.jsx +169 -0
  296. package/management-ui/src/components/monitoring/MetricsChart.jsx +197 -0
  297. package/management-ui/src/components/monitoring/MonitoringDashboard.jsx +393 -0
  298. package/management-ui/src/components/monitoring/SQSMetrics.jsx +246 -0
  299. package/management-ui/src/components/monitoring/index.js +6 -0
  300. package/management-ui/src/components/monitoring/monitoring.css +218 -0
  301. package/management-ui/src/components/theme-provider.jsx +52 -0
  302. package/management-ui/src/components/theme-toggle.jsx +39 -0
  303. package/management-ui/src/components/ui/badge.tsx +36 -0
  304. package/management-ui/src/components/ui/button.test.jsx +56 -0
  305. package/management-ui/src/components/ui/button.tsx +57 -0
  306. package/management-ui/src/components/ui/card.tsx +76 -0
  307. package/management-ui/src/components/ui/dropdown-menu.tsx +199 -0
  308. package/management-ui/src/components/ui/select.tsx +157 -0
  309. package/management-ui/src/components/ui/skeleton.jsx +15 -0
  310. package/management-ui/src/hooks/useFrigg.jsx +387 -0
  311. package/management-ui/src/hooks/useSocket.jsx +58 -0
  312. package/management-ui/src/index.css +193 -0
  313. package/management-ui/src/lib/utils.ts +6 -0
  314. package/management-ui/src/main.jsx +10 -0
  315. package/management-ui/src/pages/CodeGeneration.jsx +14 -0
  316. package/management-ui/src/pages/Connections.jsx +252 -0
  317. package/management-ui/src/pages/ConnectionsEnhanced.jsx +633 -0
  318. package/management-ui/src/pages/Dashboard.jsx +311 -0
  319. package/management-ui/src/pages/Environment.jsx +314 -0
  320. package/management-ui/src/pages/IntegrationConfigure.jsx +669 -0
  321. package/management-ui/src/pages/IntegrationDiscovery.jsx +567 -0
  322. package/management-ui/src/pages/IntegrationTest.jsx +742 -0
  323. package/management-ui/src/pages/Integrations.jsx +253 -0
  324. package/management-ui/src/pages/Monitoring.jsx +17 -0
  325. package/management-ui/src/pages/Simulation.jsx +155 -0
  326. package/management-ui/src/pages/Users.jsx +492 -0
  327. package/management-ui/src/services/api.js +41 -0
  328. package/management-ui/src/services/apiModuleService.js +193 -0
  329. package/management-ui/src/services/websocket-handlers.js +120 -0
  330. package/management-ui/src/test/api/project.test.js +273 -0
  331. package/management-ui/src/test/components/Welcome.test.jsx +378 -0
  332. package/management-ui/src/test/mocks/server.js +178 -0
  333. package/management-ui/src/test/setup.js +61 -0
  334. package/management-ui/src/test/utils/test-utils.jsx +134 -0
  335. package/management-ui/src/utils/repository.js +98 -0
  336. package/management-ui/src/utils/repository.test.js +118 -0
  337. package/management-ui/src/workflows/phase2-integration-workflows.js +884 -0
  338. package/management-ui/tailwind.config.js +63 -0
  339. package/management-ui/tsconfig.json +37 -0
  340. package/management-ui/tsconfig.node.json +10 -0
  341. package/management-ui/vite.config.js +26 -0
  342. package/management-ui/vitest.config.js +38 -0
  343. package/package.json +35 -13
  344. package/test/index.js +2 -4
  345. package/test/mock-integration.js +4 -14
  346. package/infrastructure/app-handler-helpers.js +0 -57
  347. package/infrastructure/backend-utils.js +0 -87
  348. package/infrastructure/routers/auth.js +0 -26
  349. package/infrastructure/routers/integration-defined-routers.js +0 -42
  350. package/infrastructure/routers/middleware/loadUser.js +0 -15
  351. package/infrastructure/routers/middleware/requireLoggedInUser.js +0 -12
  352. package/infrastructure/routers/user.js +0 -41
  353. package/infrastructure/routers/websocket.js +0 -55
  354. package/infrastructure/serverless-template.js +0 -291
  355. package/infrastructure/workers/integration-defined-workers.js +0 -24
  356. package/test/auther-definition-tester.js +0 -125
@@ -0,0 +1,204 @@
1
+ const { generateIAMCloudFormation, getFeatureSummary } = require('./iam-generator');
2
+
3
+ describe('IAM Generator', () => {
4
+ describe('getFeatureSummary', () => {
5
+ it('should detect all features when enabled', () => {
6
+ const appDefinition = {
7
+ name: 'test-app',
8
+ integrations: ['Integration1', 'Integration2'],
9
+ vpc: { enable: true },
10
+ encryption: { fieldLevelEncryptionMethod: 'kms' },
11
+ ssm: { enable: true },
12
+ websockets: { enable: true }
13
+ };
14
+
15
+ const summary = getFeatureSummary(appDefinition);
16
+
17
+ expect(summary.appName).toBe('test-app');
18
+ expect(summary.integrationCount).toBe(2);
19
+ expect(summary.features.core).toBe(true);
20
+ expect(summary.features.vpc).toBe(true);
21
+ expect(summary.features.kms).toBe(true);
22
+ expect(summary.features.ssm).toBe(true);
23
+ expect(summary.features.websockets).toBe(true);
24
+ });
25
+
26
+ it('should detect minimal features when disabled', () => {
27
+ const appDefinition = {
28
+ integrations: []
29
+ };
30
+
31
+ const summary = getFeatureSummary(appDefinition);
32
+
33
+ expect(summary.appName).toBe('Unnamed Frigg App');
34
+ expect(summary.integrationCount).toBe(0);
35
+ expect(summary.features.core).toBe(true);
36
+ expect(summary.features.vpc).toBe(false);
37
+ expect(summary.features.kms).toBe(false);
38
+ expect(summary.features.ssm).toBe(false);
39
+ expect(summary.features.websockets).toBe(false);
40
+ });
41
+ });
42
+
43
+ describe('generateIAMCloudFormation', () => {
44
+ it('should generate valid CloudFormation YAML', () => {
45
+ const appDefinition = {
46
+ name: 'test-app',
47
+ integrations: [],
48
+ vpc: { enable: false },
49
+ encryption: { fieldLevelEncryptionMethod: 'aes' },
50
+ ssm: { enable: false },
51
+ websockets: { enable: false }
52
+ };
53
+
54
+ const summary = getFeatureSummary(appDefinition);
55
+ const yaml = generateIAMCloudFormation({
56
+ appName: summary.appName,
57
+ features: summary.features
58
+ });
59
+
60
+ expect(yaml).toContain('AWSTemplateFormatVersion');
61
+ expect(yaml).toContain('FriggDeploymentUser');
62
+ expect(yaml).toContain('FriggCoreDeploymentPolicy');
63
+ expect(yaml).toContain('FriggDiscoveryPolicy');
64
+ });
65
+
66
+ it('should include VPC policy when VPC is enabled', () => {
67
+ const appDefinition = {
68
+ name: 'test-app',
69
+ integrations: [],
70
+ vpc: { enable: true }
71
+ };
72
+
73
+ const summary = getFeatureSummary(appDefinition);
74
+ const yaml = generateIAMCloudFormation({
75
+ appName: summary.appName,
76
+ features: summary.features
77
+ });
78
+
79
+ expect(yaml).toContain('FriggVPCPolicy');
80
+ expect(yaml).toContain('CreateVPCPermissions');
81
+ expect(yaml).toContain('EnableVPCSupport');
82
+ expect(yaml).toContain('ec2:ReplaceRoute');
83
+ });
84
+
85
+ it('should include KMS policy when encryption is enabled', () => {
86
+ const appDefinition = {
87
+ name: 'test-app',
88
+ integrations: [],
89
+ encryption: { fieldLevelEncryptionMethod: 'kms' }
90
+ };
91
+
92
+ const summary = getFeatureSummary(appDefinition);
93
+ const yaml = generateIAMCloudFormation({
94
+ appName: summary.appName,
95
+ features: summary.features
96
+ });
97
+
98
+ expect(yaml).toContain('FriggKMSPolicy');
99
+ expect(yaml).toContain('CreateKMSPermissions');
100
+ expect(yaml).toContain('EnableKMSSupport');
101
+ expect(yaml).toContain('FriggKMSKeyAlias');
102
+ expect(yaml).toContain('kms:CreateAlias');
103
+ });
104
+
105
+ it('should include SSM policy when SSM is enabled', () => {
106
+ const appDefinition = {
107
+ name: 'test-app',
108
+ integrations: [],
109
+ ssm: { enable: true }
110
+ };
111
+
112
+ const summary = getFeatureSummary(appDefinition);
113
+ const yaml = generateIAMCloudFormation({
114
+ appName: summary.appName,
115
+ features: summary.features
116
+ });
117
+
118
+ expect(yaml).toContain('FriggSSMPolicy');
119
+ expect(yaml).toContain('CreateSSMPermissions');
120
+ expect(yaml).toContain('EnableSSMSupport');
121
+ });
122
+
123
+ it('should set correct default parameter values based on features', () => {
124
+ const appDefinition = {
125
+ name: 'test-app',
126
+ integrations: [],
127
+ vpc: { enable: true },
128
+ encryption: { fieldLevelEncryptionMethod: 'aes' },
129
+ ssm: { enable: true }
130
+ };
131
+
132
+ const summary = getFeatureSummary(appDefinition);
133
+ const yaml = generateIAMCloudFormation({
134
+ appName: summary.appName,
135
+ features: summary.features
136
+ });
137
+
138
+ // Check parameter defaults match the enabled features
139
+ expect(yaml).toContain("Default: 'true'"); // VPC enabled
140
+ expect(yaml).toContain("Default: 'false'"); // KMS disabled
141
+ expect(yaml).toContain('alias/frigg-deployment');
142
+ });
143
+
144
+ it('should include all core permissions', () => {
145
+ const appDefinition = {
146
+ name: 'test-app',
147
+ integrations: []
148
+ };
149
+
150
+ const summary = getFeatureSummary(appDefinition);
151
+ const yaml = generateIAMCloudFormation({
152
+ appName: summary.appName,
153
+ features: summary.features
154
+ });
155
+
156
+ // Check for core permissions
157
+ expect(yaml).toContain('cloudformation:CreateStack');
158
+ expect(yaml).toContain('cloudformation:ListStackResources');
159
+ expect(yaml).toContain('lambda:CreateFunction');
160
+ expect(yaml).toContain('iam:CreateRole');
161
+ expect(yaml).toContain('s3:CreateBucket');
162
+ expect(yaml).toContain('sqs:CreateQueue');
163
+ expect(yaml).toContain('sns:CreateTopic');
164
+ expect(yaml).toContain('logs:CreateLogGroup');
165
+ expect(yaml).toContain('apigateway:POST');
166
+ expect(yaml).toContain('lambda:ListVersionsByFunction');
167
+ expect(yaml).toContain('iam:ListPolicyVersions');
168
+ });
169
+
170
+ it('should include internal-error-queue pattern in SQS resources', () => {
171
+ const appDefinition = {
172
+ name: 'test-app',
173
+ integrations: []
174
+ };
175
+
176
+ const summary = getFeatureSummary(appDefinition);
177
+ const yaml = generateIAMCloudFormation({
178
+ appName: summary.appName,
179
+ features: summary.features
180
+ });
181
+
182
+ expect(yaml).toContain('internal-error-queue-*');
183
+ });
184
+
185
+ it('should generate outputs section', () => {
186
+ const appDefinition = {
187
+ name: 'test-app',
188
+ integrations: []
189
+ };
190
+
191
+ const summary = getFeatureSummary(appDefinition);
192
+ const yaml = generateIAMCloudFormation({
193
+ appName: summary.appName,
194
+ features: summary.features
195
+ });
196
+
197
+ expect(yaml).toContain('Outputs:');
198
+ expect(yaml).toContain('DeploymentUserArn:');
199
+ expect(yaml).toContain('AccessKeyId:');
200
+ expect(yaml).toContain('SecretAccessKeyCommand:');
201
+ expect(yaml).toContain('CredentialsSecretArn:');
202
+ });
203
+ });
204
+ });
@@ -0,0 +1,366 @@
1
+ /**
2
+ * KMS (Key Management Service) Builder
3
+ *
4
+ * Domain Layer - Hexagonal Architecture
5
+ *
6
+ * Responsible for:
7
+ * - KMS key creation or discovery
8
+ * - KMS key configuration for field-level encryption
9
+ * - IAM permissions for KMS operations
10
+ * - KMS key policy configuration for Lambda execution role
11
+ */
12
+
13
+ const { InfrastructureBuilder, ValidationResult } = require('../shared/base-builder');
14
+ const { KmsResourceResolver } = require('./kms-resolver');
15
+ const { createEmptyDiscoveryResult, ResourceOwnership } = require('../shared/types');
16
+
17
+ class KmsBuilder extends InfrastructureBuilder {
18
+ constructor() {
19
+ super();
20
+ this.name = 'KmsBuilder';
21
+ }
22
+
23
+ shouldExecute(appDefinition) {
24
+ // Skip KMS in local mode (when FRIGG_SKIP_AWS_DISCOVERY is set)
25
+ // KMS is an AWS-specific service that should only be created in production
26
+ if (process.env.FRIGG_SKIP_AWS_DISCOVERY === 'true') {
27
+ return false;
28
+ }
29
+
30
+ return appDefinition.encryption?.fieldLevelEncryptionMethod === 'kms';
31
+ }
32
+
33
+ validate(appDefinition) {
34
+ const result = new ValidationResult();
35
+
36
+ if (!appDefinition.encryption) {
37
+ result.addError('Encryption configuration is missing');
38
+ return result;
39
+ }
40
+
41
+ const encryption = appDefinition.encryption;
42
+
43
+ if (encryption.fieldLevelEncryptionMethod !== 'kms') {
44
+ // Not an error - just not applicable
45
+ return result;
46
+ }
47
+
48
+ // Validate createResourceIfNoneFound is boolean
49
+ if (encryption.createResourceIfNoneFound !== undefined &&
50
+ typeof encryption.createResourceIfNoneFound !== 'boolean') {
51
+ result.addError('encryption.createResourceIfNoneFound must be a boolean');
52
+ }
53
+
54
+ return result;
55
+ }
56
+
57
+ /**
58
+ * Build KMS infrastructure using ownership-based architecture
59
+ */
60
+ async build(appDefinition, discoveredResources) {
61
+ console.log(`\n[${this.name}] Configuring KMS encryption...`);
62
+
63
+ // Backwards compatibility: Translate old schema to new ownership schema
64
+ appDefinition = this.translateLegacyConfig(appDefinition, discoveredResources);
65
+
66
+ const result = {
67
+ resources: {},
68
+ iamStatements: [],
69
+ environment: {},
70
+ pluginConfig: {},
71
+ plugins: [],
72
+ };
73
+
74
+ // Get structured discovery result
75
+ const discovery = discoveredResources._structured || this.convertFlatDiscoveryToStructured(discoveredResources, appDefinition);
76
+
77
+ // Use KmsResourceResolver to make ownership decisions
78
+ const resolver = new KmsResourceResolver();
79
+ const decisions = resolver.resolveAll(appDefinition, discovery);
80
+
81
+ console.log('\n 📋 Resource Ownership Decisions:');
82
+ console.log(` Key: ${decisions.key.ownership} - ${decisions.key.reason}`);
83
+
84
+ // Build resources based on ownership decisions
85
+ await this.buildFromDecisions(decisions, appDefinition, discoveredResources, result);
86
+
87
+ // Add IAM permissions for Lambda role
88
+ result.iamStatements.push({
89
+ Effect: 'Allow',
90
+ Action: ['kms:GenerateDataKey', 'kms:Decrypt', 'kms:Encrypt', 'kms:DescribeKey'],
91
+ Resource: result.environment.KMS_KEY_ARN,
92
+ });
93
+
94
+ console.log(`[${this.name}] ✅ KMS configuration completed`);
95
+ return result;
96
+ }
97
+
98
+ /**
99
+ * Convert flat discovery to structured discovery
100
+ * Provides backwards compatibility for tests
101
+ */
102
+ convertFlatDiscoveryToStructured(flatDiscovery, appDefinition = {}) {
103
+ const discovery = createEmptyDiscoveryResult();
104
+
105
+ if (!flatDiscovery) {
106
+ return discovery;
107
+ }
108
+
109
+ // Check if resources are from CloudFormation stack
110
+ const isManagedIsolated = appDefinition.managementMode === 'managed' &&
111
+ (appDefinition.vpcIsolation === 'isolated' || !appDefinition.vpcIsolation);
112
+ const hasExistingStackResources = isManagedIsolated && flatDiscovery.defaultKmsKeyId &&
113
+ typeof flatDiscovery.defaultKmsKeyId === 'string';
114
+
115
+ if (flatDiscovery.fromCloudFormationStack || hasExistingStackResources) {
116
+ discovery.fromCloudFormation = true;
117
+ discovery.stackName = flatDiscovery.stackName || 'assumed-stack';
118
+
119
+ // Add stack-managed resources
120
+ let existingLogicalIds = flatDiscovery.existingLogicalIds || [];
121
+
122
+ // Infer logical IDs from physical IDs if needed
123
+ if (hasExistingStackResources && existingLogicalIds.length === 0) {
124
+ if (flatDiscovery.defaultKmsKeyId) {
125
+ existingLogicalIds.push('FriggKMSKey');
126
+ existingLogicalIds.push('FriggKMSKeyAlias');
127
+ }
128
+ }
129
+
130
+ existingLogicalIds.forEach(logicalId => {
131
+ let resourceType = '';
132
+ let physicalId = '';
133
+
134
+ if (logicalId === 'FriggKMSKey') {
135
+ resourceType = 'AWS::KMS::Key';
136
+ physicalId = flatDiscovery.defaultKmsKeyId;
137
+ } else if (logicalId === 'FriggKMSKeyAlias') {
138
+ resourceType = 'AWS::KMS::Alias';
139
+ // Extract alias name from KMS key ARN or use default pattern
140
+ const stackName = flatDiscovery.stackName || 'unknown';
141
+ const stage = appDefinition.stage || 'dev';
142
+ physicalId = `alias/${stackName.replace(`-${stage}`, '')}-${stage}-frigg-kms`;
143
+ }
144
+
145
+ if (physicalId && typeof physicalId === 'string') {
146
+ discovery.stackManaged.push({
147
+ logicalId,
148
+ physicalId,
149
+ resourceType
150
+ });
151
+ }
152
+ });
153
+ } else {
154
+ // Resources discovered from AWS API (external)
155
+ if (flatDiscovery.defaultKmsKeyId && typeof flatDiscovery.defaultKmsKeyId === 'string') {
156
+ discovery.external.push({
157
+ physicalId: flatDiscovery.defaultKmsKeyId,
158
+ resourceType: 'AWS::KMS::Key',
159
+ source: 'aws-discovery'
160
+ });
161
+ }
162
+ }
163
+
164
+ return discovery;
165
+ }
166
+
167
+ /**
168
+ * Translate legacy configuration to ownership-based configuration
169
+ * Provides backwards compatibility
170
+ */
171
+ translateLegacyConfig(appDefinition, discoveredResources) {
172
+ // If already using ownership schema, return as-is
173
+ if (appDefinition.encryption?.ownership) {
174
+ return appDefinition;
175
+ }
176
+
177
+ const translated = JSON.parse(JSON.stringify(appDefinition));
178
+
179
+ // Initialize ownership sections
180
+ if (!translated.encryption) translated.encryption = {};
181
+ if (!translated.encryption.ownership) {
182
+ translated.encryption.ownership = {};
183
+ }
184
+
185
+ // Handle top-level managementMode
186
+ const globalMode = appDefinition.managementMode || 'discover';
187
+ const vpcIsolation = appDefinition.vpcIsolation || 'shared';
188
+
189
+ if (globalMode === 'managed') {
190
+ if (appDefinition.encryption?.createResourceIfNoneFound !== undefined) {
191
+ console.log(` ⚠️ managementMode='managed' ignoring: encryption.createResourceIfNoneFound`);
192
+ }
193
+
194
+ if (vpcIsolation === 'isolated') {
195
+ const hasStackKms = discoveredResources?.defaultKmsKeyId &&
196
+ typeof discoveredResources.defaultKmsKeyId === 'string';
197
+
198
+ if (hasStackKms) {
199
+ translated.encryption.ownership.key = 'auto';
200
+ console.log(` managementMode='managed' + vpcIsolation='isolated' → stack has KMS, reusing`);
201
+ } else {
202
+ translated.encryption.ownership.key = 'stack';
203
+ console.log(` managementMode='managed' + vpcIsolation='isolated' → no stack KMS, creating new`);
204
+ }
205
+ } else {
206
+ translated.encryption.ownership.key = 'auto';
207
+ console.log(` managementMode='managed' + vpcIsolation='shared' → discovering KMS`);
208
+ }
209
+ } else {
210
+ // Handle legacy createResourceIfNoneFound
211
+ const createIfNoneFound = appDefinition.encryption?.createResourceIfNoneFound;
212
+ if (createIfNoneFound === true) {
213
+ translated.encryption.ownership.key = 'stack';
214
+ } else if (createIfNoneFound === false || createIfNoneFound === undefined) {
215
+ // When createResourceIfNoneFound is false or not specified:
216
+ // - If KMS found → use it (auto)
217
+ // - If not found → use environment variable (external)
218
+ // We use 'auto' here; the resolver will decide based on discovery
219
+ // But we need special handling in buildFromDecisions for the env var fallback
220
+ translated.encryption.ownership.key = 'auto';
221
+ translated.encryption._useEnvVarFallback = true; // Flag for env var fallback
222
+ }
223
+ }
224
+
225
+ return translated;
226
+ }
227
+
228
+ /**
229
+ * Build all KMS resources based on ownership decisions
230
+ */
231
+ async buildFromDecisions(decisions, appDefinition, discoveredResources, result) {
232
+ // Check for environment variable fallback flag (legacy behavior)
233
+ const useEnvVarFallback = appDefinition.encryption?._useEnvVarFallback;
234
+
235
+ // CRITICAL FIX: Check if KMS key exists OUTSIDE of stack (orphaned resource)
236
+ // If key exists but not in stack, we should use it as EXTERNAL, not try to create it
237
+ const externalKmsKey = discoveredResources?.defaultKmsKeyId ||
238
+ discoveredResources?.kmsKeyArn ||
239
+ discoveredResources?.kmsKeyId;
240
+
241
+ if (decisions.key.ownership === ResourceOwnership.STACK && decisions.key.physicalId) {
242
+ // Key exists in stack - add definitions (CloudFormation idempotency)
243
+ console.log(' → Adding KMS definitions to template (existing in stack)');
244
+ result.resources = this.createKmsKey(appDefinition);
245
+ result.environment.KMS_KEY_ARN = { 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] };
246
+ console.log(' ✅ KMS key resources created');
247
+ } else if (decisions.key.ownership === ResourceOwnership.STACK && !decisions.key.physicalId && externalKmsKey) {
248
+ // ORPHANED KEY FIX: Key exists externally but not in stack
249
+ // Use it as external instead of trying to create (would fail with "already exists")
250
+ console.log(' ⚠️ KMS key exists externally but not in stack - using as external resource');
251
+ console.log(` → Using external KMS key: ${externalKmsKey}`);
252
+
253
+ // Format as ARN if it's just a key ID
254
+ const kmsArn = externalKmsKey.startsWith('arn:')
255
+ ? externalKmsKey
256
+ : `arn:aws:kms:\${self:provider.region}:\${aws:accountId}:key/${externalKmsKey}`;
257
+
258
+ result.environment.KMS_KEY_ARN = kmsArn;
259
+ } else if (decisions.key.ownership === ResourceOwnership.STACK && !decisions.key.physicalId && !useEnvVarFallback) {
260
+ // Create new KMS key (only if not using env var fallback and no external key found)
261
+ console.log(' → Creating new KMS key in stack');
262
+ result.resources = this.createKmsKey(appDefinition);
263
+ result.environment.KMS_KEY_ARN = { 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] };
264
+ console.log(' ✅ KMS key resources created');
265
+ } else if (decisions.key.ownership === ResourceOwnership.STACK && !decisions.key.physicalId && useEnvVarFallback) {
266
+ // Legacy behavior: fallback to environment variable when createResourceIfNoneFound=false/undefined
267
+ const createIfNoneFound = discoveredResources.defaultKmsKeyId ? true : appDefinition.encryption?.createResourceIfNoneFound;
268
+ const formatAsArn = createIfNoneFound === undefined; // Format as ARN when not specified
269
+
270
+ if (formatAsArn) {
271
+ console.log(' → Using environment variable for KMS key (formatted as ARN)');
272
+ result.environment.KMS_KEY_ARN = 'arn:aws:kms:${self:provider.region}:${aws:accountId}:key/${env:AWS_DISCOVERY_KMS_KEY_ID}';
273
+ } else {
274
+ console.log(' → Using environment variable for KMS key');
275
+ result.environment.KMS_KEY_ARN = '${env:AWS_DISCOVERY_KMS_KEY_ID}';
276
+ }
277
+ } else if (decisions.key.ownership === ResourceOwnership.EXTERNAL) {
278
+ // Use discovered KMS key
279
+ const kmsKeyId = decisions.key.physicalId || '${env:AWS_DISCOVERY_KMS_KEY_ID}';
280
+ console.log(` → Using ${decisions.key.physicalId ? 'discovered' : 'environment variable'} KMS key`);
281
+
282
+ // Format as ARN if it's just a key ID (for IAM policies)
283
+ const kmsArn = kmsKeyId.startsWith('arn:')
284
+ ? kmsKeyId
285
+ : `arn:aws:kms:\${self:provider.region}:\${aws:accountId}:key/${kmsKeyId}`;
286
+
287
+ result.environment.KMS_KEY_ARN = kmsArn;
288
+ } else {
289
+ // Fallback
290
+ console.log(' → Using environment variable for KMS key');
291
+ result.environment.KMS_KEY_ARN = 'arn:aws:kms:${self:provider.region}:${aws:accountId}:key/${env:AWS_DISCOVERY_KMS_KEY_ID}';
292
+ }
293
+ }
294
+
295
+ /**
296
+ * Create KMS key CloudFormation resources
297
+ */
298
+ createKmsKey(appDefinition) {
299
+ return {
300
+ FriggKMSKey: {
301
+ Type: 'AWS::KMS::Key',
302
+ DeletionPolicy: 'Retain',
303
+ UpdateReplacePolicy: 'Retain',
304
+ Properties: {
305
+ Description: 'Frigg Field-Level Encryption Key for ${self:service}-${self:provider.stage}',
306
+ EnableKeyRotation: true,
307
+ KeyPolicy: {
308
+ Version: '2012-10-17',
309
+ Id: 'key-policy-1',
310
+ Statement: [
311
+ {
312
+ Sid: 'AllowRootAccountAdmin',
313
+ Effect: 'Allow',
314
+ Principal: {
315
+ AWS: {
316
+ 'Fn::Sub': 'arn:aws:iam::${AWS::AccountId}:root',
317
+ },
318
+ },
319
+ Action: 'kms:*',
320
+ Resource: '*',
321
+ },
322
+ {
323
+ Sid: 'AllowLambdaService',
324
+ Effect: 'Allow',
325
+ Principal: {
326
+ Service: 'lambda.amazonaws.com',
327
+ },
328
+ Action: [
329
+ 'kms:Decrypt',
330
+ 'kms:GenerateDataKey',
331
+ 'kms:CreateGrant',
332
+ ],
333
+ Resource: '*',
334
+ Condition: {
335
+ StringEquals: {
336
+ 'kms:ViaService': 'lambda.${self:provider.region}.amazonaws.com',
337
+ },
338
+ },
339
+ },
340
+ // NOTE: We do NOT add a statement referencing IamRoleLambdaExecution here
341
+ // because it creates a circular dependency (KMS Key → IAM Role → KMS Key).
342
+ // Instead, IAM policies grant the Lambda execution role permissions to use KMS.
343
+ ],
344
+ },
345
+ Tags: [
346
+ { Key: 'Name', Value: '${self:service}-${self:provider.stage}-kms' },
347
+ { Key: 'ManagedBy', Value: 'Frigg' },
348
+ { Key: 'Service', Value: '${self:service}' },
349
+ { Key: 'Stage', Value: '${self:provider.stage}' },
350
+ ],
351
+ },
352
+ },
353
+ FriggKMSKeyAlias: {
354
+ Type: 'AWS::KMS::Alias',
355
+ DeletionPolicy: 'Retain',
356
+ Properties: {
357
+ AliasName: 'alias/${self:service}-${self:provider.stage}-frigg-kms',
358
+ TargetKeyId: { 'Fn::GetAtt': ['FriggKMSKey', 'Arn'] },
359
+ },
360
+ },
361
+ };
362
+ }
363
+ }
364
+
365
+ module.exports = { KmsBuilder };
366
+