@friggframework/devtools 2.0.0-next.45 → 2.0.0-next.46
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.
- package/infrastructure/ARCHITECTURE.md +487 -0
- package/infrastructure/HEALTH.md +468 -0
- package/infrastructure/README.md +51 -0
- package/infrastructure/__tests__/postgres-config.test.js +914 -0
- package/infrastructure/__tests__/template-generation.test.js +687 -0
- package/infrastructure/create-frigg-infrastructure.js +1 -1
- package/infrastructure/docs/POSTGRES-CONFIGURATION.md +630 -0
- package/infrastructure/{DEPLOYMENT-INSTRUCTIONS.md → docs/deployment-instructions.md} +3 -3
- package/infrastructure/{IAM-POLICY-TEMPLATES.md → docs/iam-policy-templates.md} +9 -10
- package/infrastructure/domains/database/aurora-builder.js +809 -0
- package/infrastructure/domains/database/aurora-builder.test.js +950 -0
- package/infrastructure/domains/database/aurora-discovery.js +87 -0
- package/infrastructure/domains/database/aurora-discovery.test.js +188 -0
- package/infrastructure/domains/database/aurora-resolver.js +210 -0
- package/infrastructure/domains/database/aurora-resolver.test.js +347 -0
- package/infrastructure/domains/database/migration-builder.js +633 -0
- package/infrastructure/domains/database/migration-builder.test.js +294 -0
- package/infrastructure/domains/database/migration-resolver.js +163 -0
- package/infrastructure/domains/database/migration-resolver.test.js +337 -0
- package/infrastructure/domains/health/application/ports/IPropertyReconciler.js +164 -0
- package/infrastructure/domains/health/application/ports/IResourceDetector.js +129 -0
- package/infrastructure/domains/health/application/ports/IResourceImporter.js +142 -0
- package/infrastructure/domains/health/application/ports/IStackRepository.js +131 -0
- package/infrastructure/domains/health/application/ports/index.js +26 -0
- package/infrastructure/domains/health/application/use-cases/__tests__/execute-resource-import-use-case.test.js +679 -0
- package/infrastructure/domains/health/application/use-cases/__tests__/mismatch-analyzer-method-name.test.js +167 -0
- package/infrastructure/domains/health/application/use-cases/__tests__/repair-via-import-use-case.test.js +1130 -0
- package/infrastructure/domains/health/application/use-cases/execute-resource-import-use-case.js +221 -0
- package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.js +152 -0
- package/infrastructure/domains/health/application/use-cases/reconcile-properties-use-case.test.js +343 -0
- package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.js +535 -0
- package/infrastructure/domains/health/application/use-cases/repair-via-import-use-case.test.js +376 -0
- package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.js +213 -0
- package/infrastructure/domains/health/application/use-cases/run-health-check-use-case.test.js +441 -0
- package/infrastructure/domains/health/docs/ACME-DEV-DRIFT-ANALYSIS.md +267 -0
- package/infrastructure/domains/health/docs/BUILD-VS-DEPLOYED-TEMPLATE-ANALYSIS.md +324 -0
- package/infrastructure/domains/health/docs/ORPHAN-DETECTION-ANALYSIS.md +386 -0
- package/infrastructure/domains/health/docs/SPEC-CLEANUP-COMMAND.md +1419 -0
- package/infrastructure/domains/health/docs/TDD-IMPLEMENTATION-SUMMARY.md +391 -0
- package/infrastructure/domains/health/docs/TEMPLATE-COMPARISON-IMPLEMENTATION.md +551 -0
- package/infrastructure/domains/health/domain/entities/issue.js +299 -0
- package/infrastructure/domains/health/domain/entities/issue.test.js +528 -0
- package/infrastructure/domains/health/domain/entities/property-mismatch.js +108 -0
- package/infrastructure/domains/health/domain/entities/property-mismatch.test.js +275 -0
- package/infrastructure/domains/health/domain/entities/resource.js +159 -0
- package/infrastructure/domains/health/domain/entities/resource.test.js +432 -0
- package/infrastructure/domains/health/domain/entities/stack-health-report.js +306 -0
- package/infrastructure/domains/health/domain/entities/stack-health-report.test.js +601 -0
- package/infrastructure/domains/health/domain/services/__tests__/health-score-percentage-based.test.js +380 -0
- package/infrastructure/domains/health/domain/services/__tests__/import-progress-monitor.test.js +971 -0
- package/infrastructure/domains/health/domain/services/__tests__/import-template-generator.test.js +1150 -0
- package/infrastructure/domains/health/domain/services/__tests__/logical-id-mapper.test.js +672 -0
- package/infrastructure/domains/health/domain/services/__tests__/template-parser.test.js +496 -0
- package/infrastructure/domains/health/domain/services/__tests__/update-progress-monitor.test.js +419 -0
- package/infrastructure/domains/health/domain/services/health-score-calculator.js +248 -0
- package/infrastructure/domains/health/domain/services/health-score-calculator.test.js +504 -0
- package/infrastructure/domains/health/domain/services/import-progress-monitor.js +195 -0
- package/infrastructure/domains/health/domain/services/import-template-generator.js +435 -0
- package/infrastructure/domains/health/domain/services/logical-id-mapper.js +345 -0
- package/infrastructure/domains/health/domain/services/mismatch-analyzer.js +234 -0
- package/infrastructure/domains/health/domain/services/mismatch-analyzer.test.js +431 -0
- package/infrastructure/domains/health/domain/services/property-mutability-config.js +382 -0
- package/infrastructure/domains/health/domain/services/template-parser.js +245 -0
- package/infrastructure/domains/health/domain/services/update-progress-monitor.js +192 -0
- package/infrastructure/domains/health/domain/value-objects/health-score.js +138 -0
- package/infrastructure/domains/health/domain/value-objects/health-score.test.js +267 -0
- package/infrastructure/domains/health/domain/value-objects/property-mutability.js +161 -0
- package/infrastructure/domains/health/domain/value-objects/property-mutability.test.js +198 -0
- package/infrastructure/domains/health/domain/value-objects/resource-state.js +167 -0
- package/infrastructure/domains/health/domain/value-objects/resource-state.test.js +196 -0
- package/infrastructure/domains/health/domain/value-objects/stack-identifier.js +192 -0
- package/infrastructure/domains/health/domain/value-objects/stack-identifier.test.js +262 -0
- package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-cfn-tagged.test.js +312 -0
- package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-multi-stack.test.js +367 -0
- package/infrastructure/domains/health/infrastructure/adapters/__tests__/orphan-detection-relationship-analysis.test.js +432 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.js +784 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-property-reconciler.test.js +1133 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.js +565 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-resource-detector.test.js +554 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.js +318 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-resource-importer.test.js +398 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.js +777 -0
- package/infrastructure/domains/health/infrastructure/adapters/aws-stack-repository.test.js +580 -0
- package/infrastructure/domains/integration/integration-builder.js +397 -0
- package/infrastructure/domains/integration/integration-builder.test.js +593 -0
- package/infrastructure/domains/integration/integration-resolver.js +170 -0
- package/infrastructure/domains/integration/integration-resolver.test.js +369 -0
- package/infrastructure/domains/integration/websocket-builder.js +69 -0
- package/infrastructure/domains/integration/websocket-builder.test.js +195 -0
- package/infrastructure/domains/networking/vpc-builder.js +1829 -0
- package/infrastructure/domains/networking/vpc-builder.test.js +1262 -0
- package/infrastructure/domains/networking/vpc-discovery.js +177 -0
- package/infrastructure/domains/networking/vpc-discovery.test.js +350 -0
- package/infrastructure/domains/networking/vpc-resolver.js +324 -0
- package/infrastructure/domains/networking/vpc-resolver.test.js +501 -0
- package/infrastructure/domains/parameters/ssm-builder.js +79 -0
- package/infrastructure/domains/parameters/ssm-builder.test.js +189 -0
- package/infrastructure/domains/parameters/ssm-discovery.js +84 -0
- package/infrastructure/domains/parameters/ssm-discovery.test.js +210 -0
- package/infrastructure/{iam-generator.js → domains/security/iam-generator.js} +2 -2
- package/infrastructure/domains/security/kms-builder.js +366 -0
- package/infrastructure/domains/security/kms-builder.test.js +374 -0
- package/infrastructure/domains/security/kms-discovery.js +80 -0
- package/infrastructure/domains/security/kms-discovery.test.js +177 -0
- package/infrastructure/domains/security/kms-resolver.js +96 -0
- package/infrastructure/domains/security/kms-resolver.test.js +216 -0
- package/infrastructure/domains/shared/base-builder.js +112 -0
- package/infrastructure/domains/shared/base-resolver.js +186 -0
- package/infrastructure/domains/shared/base-resolver.test.js +305 -0
- package/infrastructure/domains/shared/builder-orchestrator.js +212 -0
- package/infrastructure/domains/shared/builder-orchestrator.test.js +213 -0
- package/infrastructure/domains/shared/cloudformation-discovery-v2.js +334 -0
- package/infrastructure/domains/shared/cloudformation-discovery.js +375 -0
- package/infrastructure/domains/shared/cloudformation-discovery.test.js +590 -0
- package/infrastructure/domains/shared/environment-builder.js +119 -0
- package/infrastructure/domains/shared/environment-builder.test.js +247 -0
- package/infrastructure/domains/shared/providers/aws-provider-adapter.js +544 -0
- package/infrastructure/domains/shared/providers/aws-provider-adapter.test.js +377 -0
- package/infrastructure/domains/shared/providers/azure-provider-adapter.stub.js +93 -0
- package/infrastructure/domains/shared/providers/cloud-provider-adapter.js +136 -0
- package/infrastructure/domains/shared/providers/gcp-provider-adapter.stub.js +82 -0
- package/infrastructure/domains/shared/providers/provider-factory.js +108 -0
- package/infrastructure/domains/shared/providers/provider-factory.test.js +170 -0
- package/infrastructure/domains/shared/resource-discovery.js +192 -0
- package/infrastructure/domains/shared/resource-discovery.test.js +552 -0
- package/infrastructure/domains/shared/types/app-definition.js +205 -0
- package/infrastructure/domains/shared/types/discovery-result.js +106 -0
- package/infrastructure/domains/shared/types/discovery-result.test.js +258 -0
- package/infrastructure/domains/shared/types/index.js +46 -0
- package/infrastructure/domains/shared/types/resource-ownership.js +108 -0
- package/infrastructure/domains/shared/types/resource-ownership.test.js +101 -0
- package/infrastructure/domains/shared/utilities/base-definition-factory.js +380 -0
- package/infrastructure/domains/shared/utilities/base-definition-factory.js.bak +338 -0
- package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +248 -0
- package/infrastructure/domains/shared/utilities/handler-path-resolver.js +134 -0
- package/infrastructure/domains/shared/utilities/handler-path-resolver.test.js +268 -0
- package/infrastructure/domains/shared/utilities/prisma-layer-manager.js +55 -0
- package/infrastructure/domains/shared/utilities/prisma-layer-manager.test.js +138 -0
- package/infrastructure/{env-validator.js → domains/shared/validation/env-validator.js} +2 -1
- package/infrastructure/domains/shared/validation/env-validator.test.js +173 -0
- package/infrastructure/esbuild.config.js +53 -0
- package/infrastructure/infrastructure-composer.js +87 -0
- package/infrastructure/{serverless-template.test.js → infrastructure-composer.test.js} +115 -24
- package/infrastructure/scripts/build-prisma-layer.js +553 -0
- package/infrastructure/scripts/build-prisma-layer.test.js +102 -0
- package/infrastructure/{build-time-discovery.js → scripts/build-time-discovery.js} +80 -48
- package/infrastructure/{build-time-discovery.test.js → scripts/build-time-discovery.test.js} +5 -4
- package/layers/prisma/nodejs/package.json +8 -0
- package/management-ui/server/utils/cliIntegration.js +1 -1
- package/management-ui/server/utils/environment/awsParameterStore.js +29 -18
- package/package.json +11 -11
- package/frigg-cli/.eslintrc.js +0 -141
- package/frigg-cli/__tests__/unit/commands/build.test.js +0 -251
- package/frigg-cli/__tests__/unit/commands/db-setup.test.js +0 -548
- package/frigg-cli/__tests__/unit/commands/install.test.js +0 -400
- package/frigg-cli/__tests__/unit/commands/ui.test.js +0 -346
- package/frigg-cli/__tests__/unit/utils/database-validator.test.js +0 -366
- package/frigg-cli/__tests__/unit/utils/error-messages.test.js +0 -304
- package/frigg-cli/__tests__/unit/utils/prisma-runner.test.js +0 -486
- package/frigg-cli/__tests__/utils/mock-factory.js +0 -270
- package/frigg-cli/__tests__/utils/prisma-mock.js +0 -194
- package/frigg-cli/__tests__/utils/test-fixtures.js +0 -463
- package/frigg-cli/__tests__/utils/test-setup.js +0 -287
- package/frigg-cli/build-command/index.js +0 -65
- package/frigg-cli/db-setup-command/index.js +0 -193
- package/frigg-cli/deploy-command/index.js +0 -175
- package/frigg-cli/generate-command/__tests__/generate-command.test.js +0 -301
- package/frigg-cli/generate-command/azure-generator.js +0 -43
- package/frigg-cli/generate-command/gcp-generator.js +0 -47
- package/frigg-cli/generate-command/index.js +0 -332
- package/frigg-cli/generate-command/terraform-generator.js +0 -555
- package/frigg-cli/generate-iam-command.js +0 -118
- package/frigg-cli/index.js +0 -75
- package/frigg-cli/index.test.js +0 -158
- package/frigg-cli/init-command/backend-first-handler.js +0 -756
- package/frigg-cli/init-command/index.js +0 -93
- package/frigg-cli/init-command/template-handler.js +0 -143
- package/frigg-cli/install-command/backend-js.js +0 -33
- package/frigg-cli/install-command/commit-changes.js +0 -16
- package/frigg-cli/install-command/environment-variables.js +0 -127
- package/frigg-cli/install-command/environment-variables.test.js +0 -136
- package/frigg-cli/install-command/index.js +0 -54
- package/frigg-cli/install-command/install-package.js +0 -13
- package/frigg-cli/install-command/integration-file.js +0 -30
- package/frigg-cli/install-command/logger.js +0 -12
- package/frigg-cli/install-command/template.js +0 -90
- package/frigg-cli/install-command/validate-package.js +0 -75
- package/frigg-cli/jest.config.js +0 -124
- package/frigg-cli/package.json +0 -54
- package/frigg-cli/start-command/index.js +0 -149
- package/frigg-cli/start-command/start-command.test.js +0 -297
- package/frigg-cli/test/init-command.test.js +0 -180
- package/frigg-cli/test/npm-registry.test.js +0 -319
- package/frigg-cli/ui-command/index.js +0 -154
- package/frigg-cli/utils/app-resolver.js +0 -319
- package/frigg-cli/utils/backend-path.js +0 -25
- package/frigg-cli/utils/database-validator.js +0 -161
- package/frigg-cli/utils/error-messages.js +0 -257
- package/frigg-cli/utils/npm-registry.js +0 -167
- package/frigg-cli/utils/prisma-runner.js +0 -280
- package/frigg-cli/utils/process-manager.js +0 -199
- package/frigg-cli/utils/repo-detection.js +0 -405
- package/infrastructure/aws-discovery.js +0 -1176
- package/infrastructure/aws-discovery.test.js +0 -1220
- package/infrastructure/serverless-template.js +0 -2094
- /package/infrastructure/{WEBSOCKET-CONFIGURATION.md → docs/WEBSOCKET-CONFIGURATION.md} +0 -0
- /package/infrastructure/{GENERATE-IAM-DOCS.md → docs/generate-iam-command.md} +0 -0
- /package/infrastructure/{iam-generator.test.js → domains/security/iam-generator.test.js} +0 -0
- /package/infrastructure/{frigg-deployment-iam-stack.yaml → domains/security/templates/frigg-deployment-iam-stack.yaml} +0 -0
- /package/infrastructure/{iam-policy-basic.json → domains/security/templates/iam-policy-basic.json} +0 -0
- /package/infrastructure/{iam-policy-full.json → domains/security/templates/iam-policy-full.json} +0 -0
- /package/infrastructure/{run-discovery.js → scripts/run-discovery.js} +0 -0
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Base Serverless Definition Factory
|
|
3
|
+
*
|
|
4
|
+
* Utility Layer - Hexagonal Architecture
|
|
5
|
+
*
|
|
6
|
+
* Creates the base serverless.yml configuration with core functions,
|
|
7
|
+
* resources, plugins, and provider settings.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { buildEnvironment } = require('../environment-builder');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Create base serverless definition with core functions and resources
|
|
14
|
+
*
|
|
15
|
+
* This creates the foundation serverless configuration that all
|
|
16
|
+
* Frigg applications need, including:
|
|
17
|
+
* - Core Lambda functions (auth, user, health, dbMigrate)
|
|
18
|
+
* - Error handling infrastructure (SQS, SNS, CloudWatch)
|
|
19
|
+
* - Prisma Lambda Layer
|
|
20
|
+
* - Base plugins and esbuild configuration
|
|
21
|
+
*
|
|
22
|
+
* @param {Object} AppDefinition - Application definition
|
|
23
|
+
* @param {Object} appEnvironmentVars - Environment variables from app definition
|
|
24
|
+
* @param {Object} discoveredResources - AWS resources discovered during build
|
|
25
|
+
* @returns {Object} Base serverless definition
|
|
26
|
+
*/
|
|
27
|
+
function createBaseDefinition(
|
|
28
|
+
AppDefinition,
|
|
29
|
+
appEnvironmentVars,
|
|
30
|
+
discoveredResources
|
|
31
|
+
) {
|
|
32
|
+
const region = process.env.AWS_REGION || 'us-east-1';
|
|
33
|
+
|
|
34
|
+
// Function-level package config to exclude Prisma and AWS SDK
|
|
35
|
+
const functionPackageConfig = {
|
|
36
|
+
exclude: [
|
|
37
|
+
// Exclude AWS SDK (already in Lambda runtime or externalized by esbuild)
|
|
38
|
+
'node_modules/aws-sdk/**',
|
|
39
|
+
'node_modules/@aws-sdk/**',
|
|
40
|
+
|
|
41
|
+
// Exclude Prisma (provided via Lambda Layer)
|
|
42
|
+
'node_modules/@prisma/**',
|
|
43
|
+
'node_modules/.prisma/**',
|
|
44
|
+
'node_modules/prisma/**',
|
|
45
|
+
'node_modules/@friggframework/core/generated/**',
|
|
46
|
+
|
|
47
|
+
// Exclude nested node_modules from symlinked frigg packages (for npm link development)
|
|
48
|
+
'node_modules/@friggframework/core/node_modules/**',
|
|
49
|
+
'node_modules/@friggframework/devtools/node_modules/**',
|
|
50
|
+
|
|
51
|
+
// Exclude development/test files from backend project
|
|
52
|
+
'coverage/**',
|
|
53
|
+
'test/**',
|
|
54
|
+
'src/**',
|
|
55
|
+
'layers/**',
|
|
56
|
+
'**/*.test.js',
|
|
57
|
+
'**/*.spec.js',
|
|
58
|
+
'.git/**',
|
|
59
|
+
'.github/**',
|
|
60
|
+
],
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
frameworkVersion: '>=3.17.0',
|
|
65
|
+
service: AppDefinition.name || 'create-frigg-app',
|
|
66
|
+
package: {
|
|
67
|
+
individually: true,
|
|
68
|
+
},
|
|
69
|
+
useDotenv: true,
|
|
70
|
+
provider: {
|
|
71
|
+
name: AppDefinition.provider || 'aws',
|
|
72
|
+
...(process.env.AWS_PROFILE && { profile: process.env.AWS_PROFILE }),
|
|
73
|
+
runtime: 'nodejs22.x', // Node.js 22.x (latest Lambda runtime with AWS SDK v3)
|
|
74
|
+
timeout: 29, // Set to 29s to give buffer before API Gateway's 30s timeout
|
|
75
|
+
region,
|
|
76
|
+
stage: '${opt:stage}',
|
|
77
|
+
environment: buildEnvironment(appEnvironmentVars, discoveredResources),
|
|
78
|
+
iamRoleStatements: [
|
|
79
|
+
{
|
|
80
|
+
Effect: 'Allow',
|
|
81
|
+
Action: ['sns:Publish'],
|
|
82
|
+
Resource: { Ref: 'InternalErrorBridgeTopic' },
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
Effect: 'Allow',
|
|
86
|
+
Action: [
|
|
87
|
+
'sqs:SendMessage',
|
|
88
|
+
'sqs:SendMessageBatch',
|
|
89
|
+
'sqs:GetQueueUrl',
|
|
90
|
+
'sqs:GetQueueAttributes',
|
|
91
|
+
],
|
|
92
|
+
Resource: [
|
|
93
|
+
{ 'Fn::GetAtt': ['InternalErrorQueue', 'Arn'] },
|
|
94
|
+
{
|
|
95
|
+
'Fn::Join': [
|
|
96
|
+
':',
|
|
97
|
+
[
|
|
98
|
+
'arn:aws:sqs:${self:provider.region}:*:${self:service}--${self:provider.stage}-*Queue',
|
|
99
|
+
],
|
|
100
|
+
],
|
|
101
|
+
},
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
httpApi: {
|
|
106
|
+
payload: '2.0',
|
|
107
|
+
cors: {
|
|
108
|
+
allowedOrigins: ['*'],
|
|
109
|
+
allowedHeaders: ['*'],
|
|
110
|
+
allowedMethods: ['*'],
|
|
111
|
+
allowCredentials: false,
|
|
112
|
+
},
|
|
113
|
+
name: '${opt:stage, "dev"}-${self:service}',
|
|
114
|
+
disableDefaultEndpoint: false,
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
plugins: [
|
|
118
|
+
'serverless-esbuild',
|
|
119
|
+
'serverless-dotenv-plugin',
|
|
120
|
+
'serverless-offline-sqs',
|
|
121
|
+
'serverless-offline',
|
|
122
|
+
'@friggframework/serverless-plugin',
|
|
123
|
+
],
|
|
124
|
+
custom: {
|
|
125
|
+
esbuild: {
|
|
126
|
+
bundle: true,
|
|
127
|
+
minify: true,
|
|
128
|
+
sourcemap: true,
|
|
129
|
+
target: 'node22',
|
|
130
|
+
platform: 'node',
|
|
131
|
+
format: 'cjs',
|
|
132
|
+
external: [
|
|
133
|
+
'@aws-sdk/*',
|
|
134
|
+
'aws-sdk',
|
|
135
|
+
'@prisma/client',
|
|
136
|
+
'prisma',
|
|
137
|
+
'.prisma/*',
|
|
138
|
+
],
|
|
139
|
+
packager: 'npm',
|
|
140
|
+
keepNames: true,
|
|
141
|
+
keepOutputDirectory: false, // Clean up .esbuild directory after packaging
|
|
142
|
+
exclude: [
|
|
143
|
+
'aws-sdk',
|
|
144
|
+
'@aws-sdk/*',
|
|
145
|
+
'@prisma/client',
|
|
146
|
+
'prisma',
|
|
147
|
+
],
|
|
148
|
+
},
|
|
149
|
+
'serverless-offline': {
|
|
150
|
+
httpPort: 3001,
|
|
151
|
+
lambdaPort: 4001,
|
|
152
|
+
websocketPort: 3002,
|
|
153
|
+
location: '.', // Set base directory for handler resolution to current directory
|
|
154
|
+
skipCacheInvalidation: false,
|
|
155
|
+
},
|
|
156
|
+
'serverless-offline-sqs': {
|
|
157
|
+
autoCreate: false,
|
|
158
|
+
apiVersion: '2012-11-05',
|
|
159
|
+
endpoint: 'http://localhost:4566',
|
|
160
|
+
region,
|
|
161
|
+
accessKeyId: 'root',
|
|
162
|
+
secretAccessKey: 'root',
|
|
163
|
+
skipCacheInvalidation: false,
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
functions: {
|
|
167
|
+
auth: {
|
|
168
|
+
handler: 'node_modules/@friggframework/core/handlers/routers/auth.handler',
|
|
169
|
+
layers: [{ Ref: 'PrismaLambdaLayer' }],
|
|
170
|
+
skipEsbuild: true, // Handlers in node_modules don't need bundling
|
|
171
|
+
package: functionPackageConfig,
|
|
172
|
+
events: [
|
|
173
|
+
{ httpApi: { path: '/api/integrations', method: 'ANY' } },
|
|
174
|
+
{
|
|
175
|
+
httpApi: {
|
|
176
|
+
path: '/api/integrations/{proxy+}',
|
|
177
|
+
method: 'ANY',
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
{ httpApi: { path: '/api/authorize', method: 'ANY' } },
|
|
181
|
+
],
|
|
182
|
+
},
|
|
183
|
+
user: {
|
|
184
|
+
handler: 'node_modules/@friggframework/core/handlers/routers/user.handler',
|
|
185
|
+
layers: [{ Ref: 'PrismaLambdaLayer' }],
|
|
186
|
+
skipEsbuild: true, // Handlers in node_modules don't need bundling
|
|
187
|
+
package: functionPackageConfig,
|
|
188
|
+
events: [{ httpApi: { path: '/user/{proxy+}', method: 'ANY' } }],
|
|
189
|
+
},
|
|
190
|
+
health: {
|
|
191
|
+
handler: 'node_modules/@friggframework/core/handlers/routers/health.handler',
|
|
192
|
+
layers: [{ Ref: 'PrismaLambdaLayer' }],
|
|
193
|
+
skipEsbuild: true, // Handlers in node_modules don't need bundling
|
|
194
|
+
package: functionPackageConfig,
|
|
195
|
+
events: [
|
|
196
|
+
{ httpApi: { path: '/health', method: 'GET' } },
|
|
197
|
+
{ httpApi: { path: '/health/{proxy+}', method: 'GET' } },
|
|
198
|
+
],
|
|
199
|
+
},
|
|
200
|
+
dbMigrate: {
|
|
201
|
+
handler: 'node_modules/@friggframework/core/handlers/database-migration-handler.handler',
|
|
202
|
+
// DO NOT use Prisma layer - this function includes Prisma CLI separately
|
|
203
|
+
skipEsbuild: true, // Handlers in node_modules don't need bundling
|
|
204
|
+
timeout: 300, // 5 minutes for long-running migrations
|
|
205
|
+
memorySize: 1024, // Extra memory for Prisma CLI and migration operations
|
|
206
|
+
reservedConcurrency: 1, // Prevent concurrent migrations (CRITICAL for data safety)
|
|
207
|
+
description: 'Runs database migrations via Prisma CLI (invoke manually from CI/CD or triggers). Prisma CLI bundled separately.',
|
|
208
|
+
package: {
|
|
209
|
+
individually: true,
|
|
210
|
+
patterns: [
|
|
211
|
+
// Include handler
|
|
212
|
+
'node_modules/@friggframework/core/handlers/database-migration-handler.js',
|
|
213
|
+
|
|
214
|
+
// Include ONLY PostgreSQL Prisma client (exclude MongoDB)
|
|
215
|
+
'node_modules/@friggframework/core/generated/prisma-postgresql/**',
|
|
216
|
+
'!node_modules/@friggframework/core/generated/prisma-mongodb/**', // Exclude MongoDB client entirely
|
|
217
|
+
|
|
218
|
+
// Include Prisma runtime
|
|
219
|
+
'node_modules/@prisma/client/**',
|
|
220
|
+
'node_modules/.prisma/**',
|
|
221
|
+
'node_modules/prisma/**', // Prisma CLI
|
|
222
|
+
|
|
223
|
+
// Exclude unnecessary engines and files
|
|
224
|
+
'!node_modules/prisma/node_modules/**',
|
|
225
|
+
'!**/query-engine-darwin*', // Exclude macOS binaries (keep rhel for Lambda)
|
|
226
|
+
'!**/runtime/*.wasm', // WASM engines
|
|
227
|
+
'!**/*.md',
|
|
228
|
+
'!**/*.map',
|
|
229
|
+
'!**/LICENSE*',
|
|
230
|
+
'!**/*.d.ts',
|
|
231
|
+
'!**/*.d.mts',
|
|
232
|
+
],
|
|
233
|
+
},
|
|
234
|
+
maximumEventAge: 60,
|
|
235
|
+
maximumRetryAttempts: 0,
|
|
236
|
+
tags: {
|
|
237
|
+
Purpose: 'DatabaseMigration',
|
|
238
|
+
ManagedBy: 'Frigg',
|
|
239
|
+
},
|
|
240
|
+
environment: {
|
|
241
|
+
CI: '1',
|
|
242
|
+
PRISMA_HIDE_UPDATE_MESSAGE: '1',
|
|
243
|
+
PRISMA_MIGRATE_SKIP_SEED: '1',
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
layers: {
|
|
248
|
+
prisma: {
|
|
249
|
+
path: 'layers/prisma',
|
|
250
|
+
name: '${self:service}-prisma-${sls:stage}',
|
|
251
|
+
description: 'Prisma runtime client only (NO CLI) with rhel-openssl-3.0.x binaries (~10-15MB). CLI packaged separately in dbMigrate function.',
|
|
252
|
+
compatibleRuntimes: ['nodejs20.x', 'nodejs22.x'],
|
|
253
|
+
retain: false,
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
resources: {
|
|
257
|
+
Resources: {
|
|
258
|
+
InternalErrorQueue: {
|
|
259
|
+
Type: 'AWS::SQS::Queue',
|
|
260
|
+
Properties: {
|
|
261
|
+
QueueName:
|
|
262
|
+
'${self:service}-internal-error-queue-${self:provider.stage}',
|
|
263
|
+
MessageRetentionPeriod: 300,
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
InternalErrorBridgeTopic: {
|
|
267
|
+
Type: 'AWS::SNS::Topic',
|
|
268
|
+
Properties: {
|
|
269
|
+
Subscription: [
|
|
270
|
+
{
|
|
271
|
+
Protocol: 'sqs',
|
|
272
|
+
Endpoint: {
|
|
273
|
+
'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
],
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
InternalErrorBridgePolicy: {
|
|
280
|
+
Type: 'AWS::SQS::QueuePolicy',
|
|
281
|
+
Properties: {
|
|
282
|
+
Queues: [{ Ref: 'InternalErrorQueue' }],
|
|
283
|
+
PolicyDocument: {
|
|
284
|
+
Version: '2012-10-17',
|
|
285
|
+
Statement: [
|
|
286
|
+
{
|
|
287
|
+
Sid: 'Allow Dead Letter SNS to publish to SQS',
|
|
288
|
+
Effect: 'Allow',
|
|
289
|
+
Principal: { Service: 'sns.amazonaws.com' },
|
|
290
|
+
Resource: {
|
|
291
|
+
'Fn::GetAtt': [
|
|
292
|
+
'InternalErrorQueue',
|
|
293
|
+
'Arn',
|
|
294
|
+
],
|
|
295
|
+
},
|
|
296
|
+
Action: [
|
|
297
|
+
'SQS:SendMessage',
|
|
298
|
+
'SQS:SendMessageBatch',
|
|
299
|
+
],
|
|
300
|
+
Condition: {
|
|
301
|
+
ArnEquals: {
|
|
302
|
+
'aws:SourceArn': {
|
|
303
|
+
Ref: 'InternalErrorBridgeTopic',
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
],
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
ApiGatewayAlarm5xx: {
|
|
313
|
+
Type: 'AWS::CloudWatch::Alarm',
|
|
314
|
+
Properties: {
|
|
315
|
+
AlarmDescription: 'API Gateway 5xx Errors',
|
|
316
|
+
Namespace: 'AWS/ApiGateway',
|
|
317
|
+
MetricName: '5XXError',
|
|
318
|
+
Statistic: 'Sum',
|
|
319
|
+
Threshold: 0,
|
|
320
|
+
ComparisonOperator: 'GreaterThanThreshold',
|
|
321
|
+
EvaluationPeriods: 1,
|
|
322
|
+
Period: 60,
|
|
323
|
+
AlarmActions: [{ Ref: 'InternalErrorBridgeTopic' }],
|
|
324
|
+
Dimensions: [
|
|
325
|
+
{ Name: 'ApiId', Value: { Ref: 'HttpApi' } },
|
|
326
|
+
{ Name: 'Stage', Value: '${self:provider.stage}' },
|
|
327
|
+
],
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
module.exports = {
|
|
336
|
+
createBaseDefinition,
|
|
337
|
+
};
|
|
338
|
+
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Base Definition Factory
|
|
3
|
+
*
|
|
4
|
+
* Tests creation of base serverless configuration
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { createBaseDefinition } = require('./base-definition-factory');
|
|
8
|
+
|
|
9
|
+
describe('Base Definition Factory', () => {
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
delete process.env.AWS_REGION;
|
|
12
|
+
delete process.env.AWS_PROFILE;
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
describe('createBaseDefinition()', () => {
|
|
16
|
+
it('should create base serverless definition with minimal app definition', () => {
|
|
17
|
+
const appDefinition = {
|
|
18
|
+
name: 'test-app',
|
|
19
|
+
};
|
|
20
|
+
const appEnvironmentVars = {};
|
|
21
|
+
const discoveredResources = {};
|
|
22
|
+
|
|
23
|
+
const result = createBaseDefinition(appDefinition, appEnvironmentVars, discoveredResources);
|
|
24
|
+
|
|
25
|
+
expect(result.service).toBe('test-app');
|
|
26
|
+
expect(result.frameworkVersion).toBe('>=3.17.0');
|
|
27
|
+
expect(result.provider.name).toBe('aws');
|
|
28
|
+
expect(result.provider.runtime).toBe('nodejs22.x');
|
|
29
|
+
expect(result.provider.timeout).toBe(29);
|
|
30
|
+
expect(result.provider.stage).toBe('${opt:stage}');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('should default service name to create-frigg-app', () => {
|
|
34
|
+
const result = createBaseDefinition({}, {}, {});
|
|
35
|
+
|
|
36
|
+
expect(result.service).toBe('create-frigg-app');
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should use custom provider if specified', () => {
|
|
40
|
+
const appDefinition = {
|
|
41
|
+
provider: 'custom-provider',
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const result = createBaseDefinition(appDefinition, {}, {});
|
|
45
|
+
|
|
46
|
+
expect(result.provider.name).toBe('custom-provider');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it('should use AWS_REGION environment variable', () => {
|
|
50
|
+
process.env.AWS_REGION = 'eu-west-1';
|
|
51
|
+
|
|
52
|
+
const result = createBaseDefinition({}, {}, {});
|
|
53
|
+
|
|
54
|
+
expect(result.provider.region).toBe('eu-west-1');
|
|
55
|
+
expect(result.custom['serverless-offline-sqs'].region).toBe('eu-west-1');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('should default to us-east-1 region', () => {
|
|
59
|
+
delete process.env.AWS_REGION;
|
|
60
|
+
|
|
61
|
+
const result = createBaseDefinition({}, {}, {});
|
|
62
|
+
|
|
63
|
+
expect(result.provider.region).toBe('us-east-1');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('should include AWS_PROFILE if set', () => {
|
|
67
|
+
process.env.AWS_PROFILE = 'my-profile';
|
|
68
|
+
|
|
69
|
+
const result = createBaseDefinition({}, {}, {});
|
|
70
|
+
|
|
71
|
+
expect(result.provider.profile).toBe('my-profile');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('should not include AWS_PROFILE if not set', () => {
|
|
75
|
+
delete process.env.AWS_PROFILE;
|
|
76
|
+
|
|
77
|
+
const result = createBaseDefinition({}, {}, {});
|
|
78
|
+
|
|
79
|
+
expect(result.provider.profile).toBeUndefined();
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('should include core Lambda functions', () => {
|
|
83
|
+
const result = createBaseDefinition({}, {}, {});
|
|
84
|
+
|
|
85
|
+
expect(result.functions.auth).toBeDefined();
|
|
86
|
+
expect(result.functions.user).toBeDefined();
|
|
87
|
+
expect(result.functions.health).toBeDefined();
|
|
88
|
+
// dbMigrate removed - MigrationBuilder now handles migration infrastructure
|
|
89
|
+
expect(result.functions.dbMigrate).toBeUndefined();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('should configure auth function correctly', () => {
|
|
93
|
+
const result = createBaseDefinition({}, {}, {});
|
|
94
|
+
|
|
95
|
+
expect(result.functions.auth.handler).toBe('node_modules/@friggframework/core/handlers/routers/auth.handler');
|
|
96
|
+
expect(result.functions.auth.layers).toEqual([{ Ref: 'PrismaLambdaLayer' }]);
|
|
97
|
+
expect(result.functions.auth.events).toHaveLength(3);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('should NOT include legacy dbMigrate function', () => {
|
|
101
|
+
const result = createBaseDefinition({}, {}, {});
|
|
102
|
+
|
|
103
|
+
// dbMigrate is legacy - MigrationBuilder handles migration infrastructure
|
|
104
|
+
expect(result.functions.dbMigrate).toBeUndefined();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('should include Prisma Lambda Layer', () => {
|
|
108
|
+
const result = createBaseDefinition({}, {}, {});
|
|
109
|
+
|
|
110
|
+
expect(result.layers.prisma).toBeDefined();
|
|
111
|
+
expect(result.layers.prisma.path).toBe('layers/prisma');
|
|
112
|
+
expect(result.layers.prisma.description).toContain('runtime client only');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('should include error handling resources', () => {
|
|
116
|
+
const result = createBaseDefinition({}, {}, {});
|
|
117
|
+
|
|
118
|
+
expect(result.resources.Resources.InternalErrorQueue).toBeDefined();
|
|
119
|
+
expect(result.resources.Resources.InternalErrorBridgeTopic).toBeDefined();
|
|
120
|
+
expect(result.resources.Resources.InternalErrorBridgePolicy).toBeDefined();
|
|
121
|
+
expect(result.resources.Resources.ApiGatewayAlarm5xx).toBeDefined();
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it('should include base IAM permissions', () => {
|
|
125
|
+
const result = createBaseDefinition({}, {}, {});
|
|
126
|
+
|
|
127
|
+
const snsPermission = result.provider.iamRoleStatements.find(
|
|
128
|
+
stmt => stmt.Action.includes('sns:Publish')
|
|
129
|
+
);
|
|
130
|
+
expect(snsPermission).toBeDefined();
|
|
131
|
+
|
|
132
|
+
const sqsPermission = result.provider.iamRoleStatements.find(
|
|
133
|
+
stmt => stmt.Action.includes('sqs:SendMessage')
|
|
134
|
+
);
|
|
135
|
+
expect(sqsPermission).toBeDefined();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('should include required plugins', () => {
|
|
139
|
+
const result = createBaseDefinition({}, {}, {});
|
|
140
|
+
|
|
141
|
+
expect(result.plugins).toContain('serverless-esbuild');
|
|
142
|
+
// serverless-dotenv-plugin is conditionally loaded only in offline mode
|
|
143
|
+
expect(result.plugins).toContain('serverless-offline-sqs');
|
|
144
|
+
expect(result.plugins).toContain('serverless-offline');
|
|
145
|
+
expect(result.plugins).toContain('@friggframework/serverless-plugin');
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('should configure esbuild correctly', () => {
|
|
149
|
+
const result = createBaseDefinition({}, {}, {});
|
|
150
|
+
|
|
151
|
+
expect(result.custom.esbuild.bundle).toBe(true);
|
|
152
|
+
expect(result.custom.esbuild.minify).toBe(true);
|
|
153
|
+
expect(result.custom.esbuild.target).toBe('node22');
|
|
154
|
+
expect(result.custom.esbuild.external).toContain('@aws-sdk/*');
|
|
155
|
+
expect(result.custom.esbuild.external).toContain('@prisma/client');
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
it('should configure CORS for HTTP API', () => {
|
|
159
|
+
const result = createBaseDefinition({}, {}, {});
|
|
160
|
+
|
|
161
|
+
expect(result.provider.httpApi.cors.allowedOrigins).toEqual(['*']);
|
|
162
|
+
expect(result.provider.httpApi.cors.allowedMethods).toEqual(['*']);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('should merge app environment variables', () => {
|
|
166
|
+
const appEnvironmentVars = {
|
|
167
|
+
API_KEY: "${env:API_KEY, ''}",
|
|
168
|
+
CUSTOM_VAR: "${env:CUSTOM_VAR, ''}",
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const result = createBaseDefinition({}, appEnvironmentVars, {});
|
|
172
|
+
|
|
173
|
+
expect(result.provider.environment.API_KEY).toBe("${env:API_KEY, ''}");
|
|
174
|
+
expect(result.provider.environment.CUSTOM_VAR).toBe("${env:CUSTOM_VAR, ''}");
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
it('should add standard Frigg environment variables', () => {
|
|
178
|
+
const result = createBaseDefinition({}, {}, {});
|
|
179
|
+
|
|
180
|
+
expect(result.provider.environment.FRIGG_STACK).toBe('${self:service}');
|
|
181
|
+
expect(result.provider.environment.FRIGG_STAGE).toBe('${self:provider.stage}');
|
|
182
|
+
expect(result.provider.environment.FRIGG_REGION).toBe('${self:provider.region}');
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
it('should add KMS key ARN from discovered resources', () => {
|
|
186
|
+
const discoveredResources = {
|
|
187
|
+
kmsKeyId: 'arn:aws:kms:us-east-1:123:key/abc',
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const result = createBaseDefinition({}, {}, discoveredResources);
|
|
191
|
+
|
|
192
|
+
expect(result.provider.environment.KMS_KEY_ARN).toBe('arn:aws:kms:us-east-1:123:key/abc');
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('should add database connection info from discovered resources', () => {
|
|
196
|
+
const discoveredResources = {
|
|
197
|
+
auroraClusterEndpoint: 'db.example.com',
|
|
198
|
+
auroraPort: 5432,
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
const result = createBaseDefinition({}, {}, discoveredResources);
|
|
202
|
+
|
|
203
|
+
expect(result.provider.environment.DATABASE_HOST).toBe('db.example.com');
|
|
204
|
+
expect(result.provider.environment.DATABASE_PORT).toBe('5432');
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('should add database secret ARN from discovered resources', () => {
|
|
208
|
+
const discoveredResources = {
|
|
209
|
+
databaseSecretArn: 'arn:aws:secretsmanager:us-east-1:123:secret:db',
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const result = createBaseDefinition({}, {}, discoveredResources);
|
|
213
|
+
|
|
214
|
+
expect(result.provider.environment.DATABASE_SECRET_ARN).toBe('arn:aws:secretsmanager:us-east-1:123:secret:db');
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
it('should configure serverless-offline ports', () => {
|
|
218
|
+
const result = createBaseDefinition({}, {}, {});
|
|
219
|
+
|
|
220
|
+
expect(result.custom['serverless-offline'].httpPort).toBe(3001);
|
|
221
|
+
expect(result.custom['serverless-offline'].lambdaPort).toBe(4001);
|
|
222
|
+
expect(result.custom['serverless-offline'].websocketPort).toBe(3002);
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
it('should configure serverless-offline-sqs for LocalStack', () => {
|
|
226
|
+
const result = createBaseDefinition({}, {}, {});
|
|
227
|
+
|
|
228
|
+
expect(result.custom['serverless-offline-sqs'].endpoint).toBe('http://localhost:4566');
|
|
229
|
+
expect(result.custom['serverless-offline-sqs'].accessKeyId).toBe('root');
|
|
230
|
+
expect(result.custom['serverless-offline-sqs'].secretAccessKey).toBe('root');
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('should set package.individually to true', () => {
|
|
234
|
+
const result = createBaseDefinition({}, {}, {});
|
|
235
|
+
|
|
236
|
+
expect(result.package.individually).toBe(true);
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
it('should enable dotenv only in offline mode', () => {
|
|
240
|
+
const result = createBaseDefinition({}, {}, {});
|
|
241
|
+
|
|
242
|
+
// useDotenv is conditional - only true when process.argv includes 'offline'
|
|
243
|
+
expect(result.useDotenv).toBeDefined();
|
|
244
|
+
expect(typeof result.useDotenv).toBe('boolean');
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
|