@friggframework/devtools 2.0.0-next.45 → 2.0.0-next.47
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 +695 -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,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Resource ownership types for clean architecture
|
|
3
|
+
*
|
|
4
|
+
* This file defines the core types for the three-layer resource architecture:
|
|
5
|
+
* 1. Ownership (STACK | EXTERNAL | AUTO)
|
|
6
|
+
* 2. Discovery (facts about what exists)
|
|
7
|
+
* 3. Resolution (decisions based on ownership + discovery)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Resource ownership states
|
|
12
|
+
* @readonly
|
|
13
|
+
* @enum {string}
|
|
14
|
+
*/
|
|
15
|
+
const ResourceOwnership = {
|
|
16
|
+
/**
|
|
17
|
+
* Resource is managed by our CloudFormation stack
|
|
18
|
+
* CRITICAL: Must be added to template on every deploy or CloudFormation will delete it
|
|
19
|
+
*/
|
|
20
|
+
STACK: 'stack',
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Resource exists outside our stack (another stack, manually created, etc.)
|
|
24
|
+
* Should NOT be added to our template - reference by physical ID only
|
|
25
|
+
*/
|
|
26
|
+
EXTERNAL: 'external',
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Let the system decide based on discovery results
|
|
30
|
+
* - If found in our stack → STACK
|
|
31
|
+
* - If found externally → EXTERNAL
|
|
32
|
+
* - If not found → STACK (create new)
|
|
33
|
+
*/
|
|
34
|
+
AUTO: 'auto'
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Resource decision made by resolver
|
|
39
|
+
* @typedef {Object} ResourceDecision
|
|
40
|
+
* @property {string} ownership - The ownership decision (STACK | EXTERNAL)
|
|
41
|
+
* @property {string} [physicalId] - Physical resource ID (for EXTERNAL or existing STACK resources)
|
|
42
|
+
* @property {string[]} [physicalIds] - Multiple physical IDs (e.g., security groups, subnets)
|
|
43
|
+
* @property {string} reason - Human-readable reason for this decision
|
|
44
|
+
* @property {Object} [metadata] - Additional metadata about the resource
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Ownership intent from app definition
|
|
49
|
+
* @typedef {'stack' | 'external' | 'auto'} OwnershipIntent
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Validate ownership value
|
|
54
|
+
* @param {string} value - Value to validate
|
|
55
|
+
* @param {string} resourceName - Name of resource for error message
|
|
56
|
+
* @throws {Error} If value is not valid ownership
|
|
57
|
+
*/
|
|
58
|
+
function validateOwnership(value, resourceName) {
|
|
59
|
+
const validValues = Object.values(ResourceOwnership);
|
|
60
|
+
if (!validValues.includes(value)) {
|
|
61
|
+
throw new Error(
|
|
62
|
+
`Invalid ownership '${value}' for ${resourceName}. Must be one of: ${validValues.join(', ')}`
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Resolve final ownership from intent and actual state
|
|
69
|
+
* @param {OwnershipIntent} intent - User's intent from app definition
|
|
70
|
+
* @param {boolean} inStack - Whether resource is in our CloudFormation stack
|
|
71
|
+
* @param {boolean} foundExternal - Whether resource was found externally
|
|
72
|
+
* @returns {string} Final ownership (STACK | EXTERNAL)
|
|
73
|
+
*/
|
|
74
|
+
function resolveOwnership(intent, inStack, foundExternal) {
|
|
75
|
+
// Explicit stack
|
|
76
|
+
if (intent === ResourceOwnership.STACK) {
|
|
77
|
+
return ResourceOwnership.STACK;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Explicit external
|
|
81
|
+
if (intent === ResourceOwnership.EXTERNAL) {
|
|
82
|
+
return ResourceOwnership.EXTERNAL;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Auto-decide
|
|
86
|
+
if (intent === ResourceOwnership.AUTO) {
|
|
87
|
+
// CRITICAL: If it's in our stack, it MUST stay in template
|
|
88
|
+
if (inStack) {
|
|
89
|
+
return ResourceOwnership.STACK;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Found externally - use it
|
|
93
|
+
if (foundExternal) {
|
|
94
|
+
return ResourceOwnership.EXTERNAL;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Not found - create new in stack
|
|
98
|
+
return ResourceOwnership.STACK;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
throw new Error(`Invalid ownership intent: ${intent}`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
module.exports = {
|
|
105
|
+
ResourceOwnership,
|
|
106
|
+
validateOwnership,
|
|
107
|
+
resolveOwnership
|
|
108
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
const { ResourceOwnership, validateOwnership, resolveOwnership } = require('./resource-ownership');
|
|
2
|
+
|
|
3
|
+
describe('ResourceOwnership', () => {
|
|
4
|
+
describe('enum values', () => {
|
|
5
|
+
it('should have correct enum values', () => {
|
|
6
|
+
expect(ResourceOwnership.STACK).toBe('stack');
|
|
7
|
+
expect(ResourceOwnership.EXTERNAL).toBe('external');
|
|
8
|
+
expect(ResourceOwnership.AUTO).toBe('auto');
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
describe('validateOwnership', () => {
|
|
13
|
+
it('should accept valid ownership values', () => {
|
|
14
|
+
expect(() => validateOwnership('stack', 'test')).not.toThrow();
|
|
15
|
+
expect(() => validateOwnership('external', 'test')).not.toThrow();
|
|
16
|
+
expect(() => validateOwnership('auto', 'test')).not.toThrow();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should reject invalid ownership values', () => {
|
|
20
|
+
expect(() => validateOwnership('invalid', 'test')).toThrow(
|
|
21
|
+
"Invalid ownership 'invalid' for test"
|
|
22
|
+
);
|
|
23
|
+
expect(() => validateOwnership('managed', 'test')).toThrow();
|
|
24
|
+
expect(() => validateOwnership('', 'test')).toThrow();
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
describe('resolveOwnership', () => {
|
|
29
|
+
describe('with explicit STACK intent', () => {
|
|
30
|
+
it('should return STACK regardless of discovery', () => {
|
|
31
|
+
expect(resolveOwnership('stack', false, false)).toBe('stack');
|
|
32
|
+
expect(resolveOwnership('stack', true, false)).toBe('stack');
|
|
33
|
+
expect(resolveOwnership('stack', false, true)).toBe('stack');
|
|
34
|
+
expect(resolveOwnership('stack', true, true)).toBe('stack');
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('with explicit EXTERNAL intent', () => {
|
|
39
|
+
it('should return EXTERNAL regardless of discovery', () => {
|
|
40
|
+
expect(resolveOwnership('external', false, false)).toBe('external');
|
|
41
|
+
expect(resolveOwnership('external', true, false)).toBe('external');
|
|
42
|
+
expect(resolveOwnership('external', false, true)).toBe('external');
|
|
43
|
+
expect(resolveOwnership('external', true, true)).toBe('external');
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('with AUTO intent', () => {
|
|
48
|
+
it('should return STACK if resource is in stack (CRITICAL: must keep in template)', () => {
|
|
49
|
+
expect(resolveOwnership('auto', true, false)).toBe('stack');
|
|
50
|
+
expect(resolveOwnership('auto', true, true)).toBe('stack');
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should return EXTERNAL if found externally but not in stack', () => {
|
|
54
|
+
expect(resolveOwnership('auto', false, true)).toBe('external');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('should return STACK if not found anywhere (will create new)', () => {
|
|
58
|
+
expect(resolveOwnership('auto', false, false)).toBe('stack');
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('edge cases', () => {
|
|
63
|
+
it('should throw on invalid intent', () => {
|
|
64
|
+
expect(() => resolveOwnership('invalid', false, false)).toThrow(
|
|
65
|
+
'Invalid ownership intent: invalid'
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should handle undefined intent as invalid', () => {
|
|
70
|
+
expect(() => resolveOwnership(undefined, false, false)).toThrow();
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('real-world scenarios', () => {
|
|
75
|
+
it('scenario: fresh deploy, no resources exist', () => {
|
|
76
|
+
const ownership = resolveOwnership('auto', false, false);
|
|
77
|
+
expect(ownership).toBe('stack');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('scenario: redeploy existing stack, resource in stack', () => {
|
|
81
|
+
const ownership = resolveOwnership('auto', true, false);
|
|
82
|
+
expect(ownership).toBe('stack');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('scenario: resource exists in another stack/manually created', () => {
|
|
86
|
+
const ownership = resolveOwnership('auto', false, true);
|
|
87
|
+
expect(ownership).toBe('external');
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('scenario: user explicitly wants to use external VPC', () => {
|
|
91
|
+
const ownership = resolveOwnership('external', false, false);
|
|
92
|
+
expect(ownership).toBe('external');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
it('scenario: user explicitly manages in stack even if external exists', () => {
|
|
96
|
+
const ownership = resolveOwnership('stack', false, true);
|
|
97
|
+
expect(ownership).toBe('stack');
|
|
98
|
+
});
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -0,0 +1,380 @@
|
|
|
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
|
+
// Package config for handlers that skip esbuild (need node_modules dependencies)
|
|
35
|
+
// Include backend src/ and index.js since handlers load the app definition
|
|
36
|
+
const skipEsbuildPackageConfig = {
|
|
37
|
+
// Explicitly include project files that handlers need
|
|
38
|
+
include: [
|
|
39
|
+
// Include DocumentDB TLS certificate if configured
|
|
40
|
+
...(AppDefinition.database?.documentDB?.tlsCAFile
|
|
41
|
+
? [AppDefinition.database.documentDB.tlsCAFile.replace(/^\.\//, '')]
|
|
42
|
+
: []),
|
|
43
|
+
],
|
|
44
|
+
exclude: [
|
|
45
|
+
// Exclude Prisma (provided via Lambda Layer)
|
|
46
|
+
'node_modules/@prisma/**',
|
|
47
|
+
'node_modules/.prisma/**',
|
|
48
|
+
'node_modules/prisma/**',
|
|
49
|
+
'node_modules/@friggframework/core/generated/**',
|
|
50
|
+
|
|
51
|
+
// Exclude AWS SDK (provided by Lambda runtime)
|
|
52
|
+
'node_modules/aws-sdk/**',
|
|
53
|
+
'node_modules/@aws-sdk/**',
|
|
54
|
+
|
|
55
|
+
// Exclude dev/test dependencies
|
|
56
|
+
'node_modules/@friggframework/test/**',
|
|
57
|
+
'node_modules/@friggframework/eslint-config/**',
|
|
58
|
+
'node_modules/@friggframework/prettier-config/**',
|
|
59
|
+
'node_modules/jest/**',
|
|
60
|
+
'node_modules/prettier/**',
|
|
61
|
+
'node_modules/eslint/**',
|
|
62
|
+
|
|
63
|
+
// Exclude ALL nested node_modules (catch any package with nested dependencies)
|
|
64
|
+
'node_modules/**/node_modules/**',
|
|
65
|
+
|
|
66
|
+
// Exclude build tools (not needed at runtime)
|
|
67
|
+
'node_modules/esbuild/**',
|
|
68
|
+
'node_modules/@esbuild/**',
|
|
69
|
+
'node_modules/typescript/**',
|
|
70
|
+
'node_modules/webpack/**',
|
|
71
|
+
'node_modules/osls/**',
|
|
72
|
+
'node_modules/serverless-esbuild/**',
|
|
73
|
+
'node_modules/serverless-jetpack/**',
|
|
74
|
+
'node_modules/serverless-offline/**',
|
|
75
|
+
'node_modules/serverless-offline-sqs/**',
|
|
76
|
+
'node_modules/serverless-dotenv-plugin/**',
|
|
77
|
+
'node_modules/serverless-kms-grants/**',
|
|
78
|
+
// Note: DO NOT exclude serverless-http - it's a runtime dependency!
|
|
79
|
+
|
|
80
|
+
// Exclude local dev files
|
|
81
|
+
'deploy.log',
|
|
82
|
+
'.env.backup',
|
|
83
|
+
'docker-compose.yml',
|
|
84
|
+
'jest.config.js',
|
|
85
|
+
'jest.unit.config.js',
|
|
86
|
+
'.eslintrc.json',
|
|
87
|
+
'.prettierrc',
|
|
88
|
+
'.prettierignore',
|
|
89
|
+
'.markdownlintignore',
|
|
90
|
+
'package-lock.json',
|
|
91
|
+
|
|
92
|
+
// Exclude test files and layers (keep src/ - needed for app definition and integrations)
|
|
93
|
+
'test/**',
|
|
94
|
+
'layers/**',
|
|
95
|
+
'coverage/**',
|
|
96
|
+
// Note: DO NOT exclude src/** - handlers need src/integrations and src/api-modules at runtime
|
|
97
|
+
'**/*.test.js',
|
|
98
|
+
'**/*.spec.js',
|
|
99
|
+
'**/.claude-flow/**',
|
|
100
|
+
'**/.swarm/**',
|
|
101
|
+
],
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// Function-level package config to exclude Prisma and AWS SDK
|
|
105
|
+
const functionPackageConfig = {
|
|
106
|
+
exclude: [
|
|
107
|
+
// Exclude AWS SDK (already in Lambda runtime or externalized by esbuild)
|
|
108
|
+
'node_modules/aws-sdk/**',
|
|
109
|
+
'node_modules/@aws-sdk/**',
|
|
110
|
+
|
|
111
|
+
// Exclude Prisma (provided via Lambda Layer)
|
|
112
|
+
'node_modules/@prisma/**',
|
|
113
|
+
'node_modules/.prisma/**',
|
|
114
|
+
'node_modules/prisma/**',
|
|
115
|
+
'node_modules/@friggframework/core/generated/**',
|
|
116
|
+
|
|
117
|
+
// Exclude nested node_modules from symlinked frigg packages (for npm link development)
|
|
118
|
+
'node_modules/@friggframework/core/node_modules/**',
|
|
119
|
+
'node_modules/@friggframework/devtools/node_modules/**',
|
|
120
|
+
|
|
121
|
+
// Exclude development/test files from backend project
|
|
122
|
+
'coverage/**',
|
|
123
|
+
'test/**',
|
|
124
|
+
'src/**',
|
|
125
|
+
'layers/**',
|
|
126
|
+
'**/*.test.js',
|
|
127
|
+
'**/*.spec.js',
|
|
128
|
+
'.git/**',
|
|
129
|
+
'.github/**',
|
|
130
|
+
|
|
131
|
+
// Exclude AI assistant and development artifacts
|
|
132
|
+
'**/.claude-flow/**',
|
|
133
|
+
'**/.swarm/**',
|
|
134
|
+
'**/CLAUDE.md',
|
|
135
|
+
'**/README.md',
|
|
136
|
+
'**/*.md',
|
|
137
|
+
|
|
138
|
+
// Exclude config and meta files from core
|
|
139
|
+
'node_modules/@friggframework/core/.eslintrc.json',
|
|
140
|
+
'node_modules/@friggframework/core/.gitignore',
|
|
141
|
+
'node_modules/@friggframework/core/jest.config.js',
|
|
142
|
+
'node_modules/@friggframework/core/CHANGELOG.md',
|
|
143
|
+
],
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
frameworkVersion: '>=3.17.0',
|
|
148
|
+
service: AppDefinition.name || 'create-frigg-app',
|
|
149
|
+
package: {
|
|
150
|
+
individually: true,
|
|
151
|
+
},
|
|
152
|
+
// Only use .env for local development (offline mode)
|
|
153
|
+
// Production deployments should use environment vars from infrastructure
|
|
154
|
+
useDotenv: process.argv.includes('offline'),
|
|
155
|
+
provider: {
|
|
156
|
+
name: AppDefinition.provider || 'aws',
|
|
157
|
+
...(process.env.AWS_PROFILE && { profile: process.env.AWS_PROFILE }),
|
|
158
|
+
runtime: 'nodejs22.x', // Node.js 22.x (latest Lambda runtime with AWS SDK v3)
|
|
159
|
+
timeout: 29, // Set to 29s to give buffer before API Gateway's 30s timeout
|
|
160
|
+
region,
|
|
161
|
+
stage: '${opt:stage}',
|
|
162
|
+
environment: buildEnvironment(appEnvironmentVars, discoveredResources),
|
|
163
|
+
iamRoleStatements: [
|
|
164
|
+
{
|
|
165
|
+
Effect: 'Allow',
|
|
166
|
+
Action: ['sns:Publish'],
|
|
167
|
+
Resource: { Ref: 'InternalErrorBridgeTopic' },
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
Effect: 'Allow',
|
|
171
|
+
Action: [
|
|
172
|
+
'sqs:SendMessage',
|
|
173
|
+
'sqs:SendMessageBatch',
|
|
174
|
+
'sqs:GetQueueUrl',
|
|
175
|
+
'sqs:GetQueueAttributes',
|
|
176
|
+
],
|
|
177
|
+
Resource: [
|
|
178
|
+
{ 'Fn::GetAtt': ['InternalErrorQueue', 'Arn'] },
|
|
179
|
+
{
|
|
180
|
+
'Fn::Join': [
|
|
181
|
+
':',
|
|
182
|
+
[
|
|
183
|
+
'arn:aws:sqs:${self:provider.region}:*:${self:service}--${self:provider.stage}-*Queue',
|
|
184
|
+
],
|
|
185
|
+
],
|
|
186
|
+
},
|
|
187
|
+
],
|
|
188
|
+
},
|
|
189
|
+
],
|
|
190
|
+
httpApi: {
|
|
191
|
+
payload: '2.0',
|
|
192
|
+
cors: {
|
|
193
|
+
allowedOrigins: ['*'],
|
|
194
|
+
allowedHeaders: ['*'],
|
|
195
|
+
allowedMethods: ['*'],
|
|
196
|
+
allowCredentials: false,
|
|
197
|
+
},
|
|
198
|
+
name: '${opt:stage, "dev"}-${self:service}',
|
|
199
|
+
disableDefaultEndpoint: false,
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
plugins: [
|
|
203
|
+
'serverless-esbuild',
|
|
204
|
+
// Only load dotenv plugin for offline mode
|
|
205
|
+
...(process.argv.includes('offline') ? ['serverless-dotenv-plugin'] : []),
|
|
206
|
+
'serverless-offline-sqs',
|
|
207
|
+
'serverless-offline',
|
|
208
|
+
'@friggframework/serverless-plugin',
|
|
209
|
+
],
|
|
210
|
+
custom: {
|
|
211
|
+
esbuild: {
|
|
212
|
+
bundle: true,
|
|
213
|
+
minify: true,
|
|
214
|
+
sourcemap: true,
|
|
215
|
+
target: 'node22',
|
|
216
|
+
platform: 'node',
|
|
217
|
+
format: 'cjs',
|
|
218
|
+
external: [
|
|
219
|
+
'@aws-sdk/*',
|
|
220
|
+
'aws-sdk',
|
|
221
|
+
'@prisma/client',
|
|
222
|
+
'prisma',
|
|
223
|
+
'.prisma/*',
|
|
224
|
+
],
|
|
225
|
+
packager: 'npm',
|
|
226
|
+
keepNames: true,
|
|
227
|
+
keepOutputDirectory: false, // Clean up .esbuild directory after packaging
|
|
228
|
+
exclude: [
|
|
229
|
+
'aws-sdk',
|
|
230
|
+
'@aws-sdk/*',
|
|
231
|
+
'@prisma/client',
|
|
232
|
+
'prisma',
|
|
233
|
+
],
|
|
234
|
+
},
|
|
235
|
+
'serverless-offline': {
|
|
236
|
+
httpPort: 3001,
|
|
237
|
+
lambdaPort: 4001,
|
|
238
|
+
websocketPort: 3002,
|
|
239
|
+
location: '.', // Set base directory for handler resolution to current directory
|
|
240
|
+
skipCacheInvalidation: false,
|
|
241
|
+
},
|
|
242
|
+
'serverless-offline-sqs': {
|
|
243
|
+
autoCreate: false,
|
|
244
|
+
apiVersion: '2012-11-05',
|
|
245
|
+
endpoint: 'http://localhost:4566',
|
|
246
|
+
region,
|
|
247
|
+
accessKeyId: 'root',
|
|
248
|
+
secretAccessKey: 'root',
|
|
249
|
+
skipCacheInvalidation: false,
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
functions: {
|
|
253
|
+
auth: {
|
|
254
|
+
handler: 'node_modules/@friggframework/core/handlers/routers/auth.handler',
|
|
255
|
+
layers: [{ Ref: 'PrismaLambdaLayer' }],
|
|
256
|
+
skipEsbuild: true, // Handlers in node_modules don't need bundling
|
|
257
|
+
package: skipEsbuildPackageConfig,
|
|
258
|
+
events: [
|
|
259
|
+
{ httpApi: { path: '/api/integrations', method: 'ANY' } },
|
|
260
|
+
{
|
|
261
|
+
httpApi: {
|
|
262
|
+
path: '/api/integrations/{proxy+}',
|
|
263
|
+
method: 'ANY',
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
{ httpApi: { path: '/api/authorize', method: 'ANY' } },
|
|
267
|
+
],
|
|
268
|
+
},
|
|
269
|
+
user: {
|
|
270
|
+
handler: 'node_modules/@friggframework/core/handlers/routers/user.handler',
|
|
271
|
+
layers: [{ Ref: 'PrismaLambdaLayer' }],
|
|
272
|
+
skipEsbuild: true, // Handlers in node_modules don't need bundling
|
|
273
|
+
package: skipEsbuildPackageConfig,
|
|
274
|
+
events: [{ httpApi: { path: '/user/{proxy+}', method: 'ANY' } }],
|
|
275
|
+
},
|
|
276
|
+
health: {
|
|
277
|
+
handler: 'node_modules/@friggframework/core/handlers/routers/health.handler',
|
|
278
|
+
layers: [{ Ref: 'PrismaLambdaLayer' }],
|
|
279
|
+
skipEsbuild: true, // Handlers in node_modules don't need bundling
|
|
280
|
+
package: skipEsbuildPackageConfig,
|
|
281
|
+
events: [
|
|
282
|
+
{ httpApi: { path: '/health', method: 'GET' } },
|
|
283
|
+
{ httpApi: { path: '/health/{proxy+}', method: 'GET' } },
|
|
284
|
+
],
|
|
285
|
+
},
|
|
286
|
+
// Note: dbMigrate removed - MigrationBuilder now handles migration infrastructure
|
|
287
|
+
// See: packages/devtools/infrastructure/domains/database/migration-builder.js
|
|
288
|
+
},
|
|
289
|
+
layers: {
|
|
290
|
+
prisma: {
|
|
291
|
+
path: 'layers/prisma',
|
|
292
|
+
name: '${self:service}-prisma-${sls:stage}',
|
|
293
|
+
description: 'Prisma runtime client only (NO CLI) with rhel-openssl-3.0.x binaries (~10-15MB). CLI packaged separately in dbMigrate function.',
|
|
294
|
+
compatibleRuntimes: ['nodejs20.x', 'nodejs22.x'],
|
|
295
|
+
retain: false,
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
resources: {
|
|
299
|
+
Resources: {
|
|
300
|
+
InternalErrorQueue: {
|
|
301
|
+
Type: 'AWS::SQS::Queue',
|
|
302
|
+
Properties: {
|
|
303
|
+
QueueName:
|
|
304
|
+
'${self:service}-internal-error-queue-${self:provider.stage}',
|
|
305
|
+
MessageRetentionPeriod: 300,
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
InternalErrorBridgeTopic: {
|
|
309
|
+
Type: 'AWS::SNS::Topic',
|
|
310
|
+
Properties: {
|
|
311
|
+
Subscription: [
|
|
312
|
+
{
|
|
313
|
+
Protocol: 'sqs',
|
|
314
|
+
Endpoint: {
|
|
315
|
+
'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
|
|
316
|
+
},
|
|
317
|
+
},
|
|
318
|
+
],
|
|
319
|
+
},
|
|
320
|
+
},
|
|
321
|
+
InternalErrorBridgePolicy: {
|
|
322
|
+
Type: 'AWS::SQS::QueuePolicy',
|
|
323
|
+
Properties: {
|
|
324
|
+
Queues: [{ Ref: 'InternalErrorQueue' }],
|
|
325
|
+
PolicyDocument: {
|
|
326
|
+
Version: '2012-10-17',
|
|
327
|
+
Statement: [
|
|
328
|
+
{
|
|
329
|
+
Sid: 'Allow Dead Letter SNS to publish to SQS',
|
|
330
|
+
Effect: 'Allow',
|
|
331
|
+
Principal: { Service: 'sns.amazonaws.com' },
|
|
332
|
+
Resource: {
|
|
333
|
+
'Fn::GetAtt': [
|
|
334
|
+
'InternalErrorQueue',
|
|
335
|
+
'Arn',
|
|
336
|
+
],
|
|
337
|
+
},
|
|
338
|
+
Action: [
|
|
339
|
+
'SQS:SendMessage',
|
|
340
|
+
'SQS:SendMessageBatch',
|
|
341
|
+
],
|
|
342
|
+
Condition: {
|
|
343
|
+
ArnEquals: {
|
|
344
|
+
'aws:SourceArn': {
|
|
345
|
+
Ref: 'InternalErrorBridgeTopic',
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
],
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
ApiGatewayAlarm5xx: {
|
|
355
|
+
Type: 'AWS::CloudWatch::Alarm',
|
|
356
|
+
Properties: {
|
|
357
|
+
AlarmDescription: 'API Gateway 5xx Errors',
|
|
358
|
+
Namespace: 'AWS/ApiGateway',
|
|
359
|
+
MetricName: '5XXError',
|
|
360
|
+
Statistic: 'Sum',
|
|
361
|
+
Threshold: 0,
|
|
362
|
+
ComparisonOperator: 'GreaterThanThreshold',
|
|
363
|
+
EvaluationPeriods: 1,
|
|
364
|
+
Period: 60,
|
|
365
|
+
AlarmActions: [{ Ref: 'InternalErrorBridgeTopic' }],
|
|
366
|
+
Dimensions: [
|
|
367
|
+
{ Name: 'ApiId', Value: { Ref: 'HttpApi' } },
|
|
368
|
+
{ Name: 'Stage', Value: '${self:provider.stage}' },
|
|
369
|
+
],
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
},
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
module.exports = {
|
|
378
|
+
createBaseDefinition,
|
|
379
|
+
};
|
|
380
|
+
|