@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,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Handler Path Resolver Utility
|
|
3
|
+
*
|
|
4
|
+
* Utility Layer - Hexagonal Architecture
|
|
5
|
+
*
|
|
6
|
+
* Handles Lambda handler path resolution for offline mode compatibility.
|
|
7
|
+
* In offline mode, handler paths need to be relative to the working directory
|
|
8
|
+
* rather than absolute paths to node_modules.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Find node_modules path for offline mode handler path resolution
|
|
16
|
+
*
|
|
17
|
+
* Searches upward from current directory to locate node_modules directory
|
|
18
|
+
* using multiple fallback strategies.
|
|
19
|
+
*
|
|
20
|
+
* @returns {string} Path to node_modules directory
|
|
21
|
+
*/
|
|
22
|
+
function findNodeModulesPath() {
|
|
23
|
+
try {
|
|
24
|
+
let currentDir = process.cwd();
|
|
25
|
+
let nodeModulesPath = null;
|
|
26
|
+
|
|
27
|
+
// Strategy 1: Search upward through directory tree
|
|
28
|
+
for (let i = 0; i < 5; i++) {
|
|
29
|
+
const potentialPath = path.join(currentDir, 'node_modules');
|
|
30
|
+
if (fs.existsSync(potentialPath)) {
|
|
31
|
+
nodeModulesPath = potentialPath;
|
|
32
|
+
console.log(
|
|
33
|
+
`Found node_modules at: ${nodeModulesPath} (method 1)`
|
|
34
|
+
);
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
const parentDir = path.dirname(currentDir);
|
|
38
|
+
if (parentDir === currentDir) break;
|
|
39
|
+
currentDir = parentDir;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Strategy 2: Use npm root command
|
|
43
|
+
if (!nodeModulesPath) {
|
|
44
|
+
try {
|
|
45
|
+
const { execSync } = require('node:child_process');
|
|
46
|
+
const npmRoot = execSync('npm root', {
|
|
47
|
+
encoding: 'utf8',
|
|
48
|
+
}).trim();
|
|
49
|
+
if (fs.existsSync(npmRoot)) {
|
|
50
|
+
nodeModulesPath = npmRoot;
|
|
51
|
+
console.log(
|
|
52
|
+
`Found node_modules at: ${nodeModulesPath} (method 2)`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
} catch (npmError) {
|
|
56
|
+
console.error('Error executing npm root:', npmError);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Strategy 3: Search from package.json locations
|
|
61
|
+
if (!nodeModulesPath) {
|
|
62
|
+
currentDir = process.cwd();
|
|
63
|
+
for (let i = 0; i < 5; i++) {
|
|
64
|
+
const packageJsonPath = path.join(currentDir, 'package.json');
|
|
65
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
66
|
+
const potentialNodeModules = path.join(
|
|
67
|
+
currentDir,
|
|
68
|
+
'node_modules'
|
|
69
|
+
);
|
|
70
|
+
if (fs.existsSync(potentialNodeModules)) {
|
|
71
|
+
nodeModulesPath = potentialNodeModules;
|
|
72
|
+
console.log(
|
|
73
|
+
`Found node_modules at: ${nodeModulesPath} (method 3)`
|
|
74
|
+
);
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const parentDir = path.dirname(currentDir);
|
|
79
|
+
if (parentDir === currentDir) break;
|
|
80
|
+
currentDir = parentDir;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (nodeModulesPath) {
|
|
85
|
+
return nodeModulesPath;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Fallback: Assume parent directory
|
|
89
|
+
console.warn(
|
|
90
|
+
'Could not find node_modules path, falling back to default'
|
|
91
|
+
);
|
|
92
|
+
return path.resolve(process.cwd(), '../node_modules');
|
|
93
|
+
} catch (error) {
|
|
94
|
+
console.error('Error finding node_modules path:', error);
|
|
95
|
+
return path.resolve(process.cwd(), '../node_modules');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Modify handler paths for offline mode
|
|
101
|
+
*
|
|
102
|
+
* In serverless-offline mode, handler paths need to be relative
|
|
103
|
+
* to the current working directory rather than using absolute
|
|
104
|
+
* node_modules paths.
|
|
105
|
+
*
|
|
106
|
+
* @param {Object} functions - Serverless functions configuration
|
|
107
|
+
* @returns {Object} Functions with modified handler paths
|
|
108
|
+
*/
|
|
109
|
+
function modifyHandlerPaths(functions) {
|
|
110
|
+
const isOffline = process.argv.includes('offline');
|
|
111
|
+
console.log('isOffline', isOffline);
|
|
112
|
+
|
|
113
|
+
if (!isOffline) {
|
|
114
|
+
console.log('Not in offline mode, skipping handler path modification');
|
|
115
|
+
// Return shallow copy to prevent mutations (DDD immutability principle)
|
|
116
|
+
return { ...functions };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// In offline mode, don't modify the handler paths at all
|
|
120
|
+
// serverless-offline will resolve node_modules paths from the working directory
|
|
121
|
+
console.log('Offline mode detected - keeping original handler paths for serverless-offline');
|
|
122
|
+
|
|
123
|
+
// Return deep copy to prevent mutations (DDD immutability principle)
|
|
124
|
+
return Object.entries(functions).reduce((acc, [key, value]) => {
|
|
125
|
+
acc[key] = { ...value };
|
|
126
|
+
return acc;
|
|
127
|
+
}, {});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
module.exports = {
|
|
131
|
+
findNodeModulesPath,
|
|
132
|
+
modifyHandlerPaths,
|
|
133
|
+
};
|
|
134
|
+
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Handler Path Resolver Utility
|
|
3
|
+
*
|
|
4
|
+
* Tests offline mode handler path resolution
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const { findNodeModulesPath, modifyHandlerPaths } = require('./handler-path-resolver');
|
|
10
|
+
|
|
11
|
+
// Mock fs and child_process
|
|
12
|
+
jest.mock('fs');
|
|
13
|
+
jest.mock('node:child_process', () => ({
|
|
14
|
+
execSync: jest.fn(),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
describe('Handler Path Resolver', () => {
|
|
18
|
+
let originalArgv;
|
|
19
|
+
let originalCwd;
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
originalArgv = process.argv;
|
|
23
|
+
originalCwd = process.cwd;
|
|
24
|
+
jest.clearAllMocks();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
process.argv = originalArgv;
|
|
29
|
+
process.cwd = originalCwd;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('findNodeModulesPath()', () => {
|
|
33
|
+
it('should find node_modules in current directory (method 1)', () => {
|
|
34
|
+
const mockCwd = '/project';
|
|
35
|
+
process.cwd = jest.fn().mockReturnValue(mockCwd);
|
|
36
|
+
fs.existsSync = jest.fn().mockImplementation((p) =>
|
|
37
|
+
p === path.join(mockCwd, 'node_modules')
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
const result = findNodeModulesPath();
|
|
41
|
+
|
|
42
|
+
expect(result).toBe(path.join(mockCwd, 'node_modules'));
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('should search parent directories if not found in current (method 1)', () => {
|
|
46
|
+
const mockCwd = '/project/nested/deep';
|
|
47
|
+
process.cwd = jest.fn().mockReturnValue(mockCwd);
|
|
48
|
+
fs.existsSync = jest.fn().mockImplementation((p) =>
|
|
49
|
+
p === '/project/node_modules'
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
const result = findNodeModulesPath();
|
|
53
|
+
|
|
54
|
+
expect(result).toBe('/project/node_modules');
|
|
55
|
+
expect(fs.existsSync).toHaveBeenCalledWith('/project/nested/deep/node_modules');
|
|
56
|
+
expect(fs.existsSync).toHaveBeenCalledWith('/project/nested/node_modules');
|
|
57
|
+
expect(fs.existsSync).toHaveBeenCalledWith('/project/node_modules');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should use npm root if directory search fails (method 2)', () => {
|
|
61
|
+
process.cwd = jest.fn().mockReturnValue('/some/unusual/directory');
|
|
62
|
+
|
|
63
|
+
const { execSync } = require('node:child_process');
|
|
64
|
+
// npm root returns a different path
|
|
65
|
+
execSync.mockReturnValue('/usr/local/lib/node_modules\n');
|
|
66
|
+
|
|
67
|
+
// Mock to fail directory searches but succeed for npm root result
|
|
68
|
+
fs.existsSync = jest.fn().mockImplementation((p) => {
|
|
69
|
+
// Only succeed for the npm root path (not under /some/unusual/directory)
|
|
70
|
+
return p === '/usr/local/lib/node_modules';
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const result = findNodeModulesPath();
|
|
74
|
+
|
|
75
|
+
expect(result).toBe('/usr/local/lib/node_modules');
|
|
76
|
+
expect(execSync).toHaveBeenCalledWith('npm root', { encoding: 'utf8' });
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('should handle npm root errors gracefully', () => {
|
|
80
|
+
process.cwd = jest.fn().mockReturnValue('/project');
|
|
81
|
+
fs.existsSync = jest.fn().mockReturnValue(false);
|
|
82
|
+
|
|
83
|
+
const { execSync } = require('node:child_process');
|
|
84
|
+
execSync.mockImplementation(() => {
|
|
85
|
+
throw new Error('npm not found');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const result = findNodeModulesPath();
|
|
89
|
+
|
|
90
|
+
// Should fall back to default
|
|
91
|
+
expect(result).toBe(path.resolve('/project', '../node_modules'));
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should search from package.json locations (method 3)', () => {
|
|
95
|
+
process.cwd = jest.fn().mockReturnValue('/project/workspace');
|
|
96
|
+
|
|
97
|
+
let callCount = 0;
|
|
98
|
+
fs.existsSync = jest.fn().mockImplementation((p) => {
|
|
99
|
+
callCount++;
|
|
100
|
+
// First 5 calls fail (directory search)
|
|
101
|
+
if (callCount <= 5) return false;
|
|
102
|
+
// Package.json search
|
|
103
|
+
if (p === '/project/package.json') return true;
|
|
104
|
+
if (p === '/project/node_modules') return true;
|
|
105
|
+
return false;
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const { execSync } = require('node:child_process');
|
|
109
|
+
execSync.mockImplementation(() => {
|
|
110
|
+
throw new Error('npm root failed');
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const result = findNodeModulesPath();
|
|
114
|
+
|
|
115
|
+
expect(result).toBe('/project/node_modules');
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('should fall back to default path if all methods fail', () => {
|
|
119
|
+
process.cwd = jest.fn().mockReturnValue('/project');
|
|
120
|
+
fs.existsSync = jest.fn().mockReturnValue(false);
|
|
121
|
+
|
|
122
|
+
const { execSync } = require('node:child_process');
|
|
123
|
+
execSync.mockImplementation(() => {
|
|
124
|
+
throw new Error('npm root failed');
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
const result = findNodeModulesPath();
|
|
128
|
+
|
|
129
|
+
expect(result).toBe(path.resolve('/project', '../node_modules'));
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('should handle errors during search', () => {
|
|
133
|
+
process.cwd = jest.fn().mockReturnValue('/project');
|
|
134
|
+
// Mock fs.existsSync to throw an error
|
|
135
|
+
fs.existsSync = jest.fn().mockImplementation(() => {
|
|
136
|
+
throw new Error('fs error');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const { execSync } = require('node:child_process');
|
|
140
|
+
execSync.mockImplementation(() => {
|
|
141
|
+
throw new Error('npm error');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const result = findNodeModulesPath();
|
|
145
|
+
|
|
146
|
+
// Should fallback to default path even when search methods fail
|
|
147
|
+
expect(result).toBe(path.resolve('/project', '../node_modules'));
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
describe('modifyHandlerPaths()', () => {
|
|
152
|
+
it('should not modify paths when not in offline mode', () => {
|
|
153
|
+
process.argv = ['node', 'test'];
|
|
154
|
+
|
|
155
|
+
const functions = {
|
|
156
|
+
auth: {
|
|
157
|
+
handler: 'node_modules/@friggframework/core/handlers/routers/auth.handler',
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
const result = modifyHandlerPaths(functions);
|
|
162
|
+
|
|
163
|
+
expect(result.auth.handler).toBe('node_modules/@friggframework/core/handlers/routers/auth.handler');
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('should modify handler paths in offline mode', () => {
|
|
167
|
+
process.argv = ['node', 'test', 'offline'];
|
|
168
|
+
process.cwd = jest.fn().mockReturnValue('/project');
|
|
169
|
+
fs.existsSync = jest.fn().mockImplementation((p) =>
|
|
170
|
+
p === '/project/node_modules'
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
const functions = {
|
|
174
|
+
auth: {
|
|
175
|
+
handler: 'node_modules/@friggframework/core/handlers/routers/auth.handler',
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
const result = modifyHandlerPaths(functions);
|
|
180
|
+
|
|
181
|
+
expect(result.auth.handler).toBe('node_modules/@friggframework/core/handlers/routers/auth.handler');
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('should handle functions without handlers', () => {
|
|
185
|
+
process.argv = ['node', 'test', 'offline'];
|
|
186
|
+
|
|
187
|
+
const functions = {
|
|
188
|
+
noHandler: {
|
|
189
|
+
events: [{ http: { path: '/test' } }],
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const result = modifyHandlerPaths(functions);
|
|
194
|
+
|
|
195
|
+
expect(result.noHandler.handler).toBeUndefined();
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it('should only modify handlers with node_modules path', () => {
|
|
199
|
+
process.argv = ['node', 'test', 'offline'];
|
|
200
|
+
process.cwd = jest.fn().mockReturnValue('/project');
|
|
201
|
+
fs.existsSync = jest.fn().mockImplementation((p) =>
|
|
202
|
+
p === '/project/node_modules'
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
const functions = {
|
|
206
|
+
coreHandler: {
|
|
207
|
+
handler: 'node_modules/@friggframework/core/handlers/auth.handler',
|
|
208
|
+
},
|
|
209
|
+
customHandler: {
|
|
210
|
+
handler: 'src/handlers/custom.handler',
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
const result = modifyHandlerPaths(functions);
|
|
215
|
+
|
|
216
|
+
expect(result.coreHandler.handler).toContain('node_modules');
|
|
217
|
+
expect(result.customHandler.handler).toBe('src/handlers/custom.handler');
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
it('should not mutate original functions object', () => {
|
|
221
|
+
process.argv = ['node', 'test', 'offline'];
|
|
222
|
+
process.cwd = jest.fn().mockReturnValue('/project');
|
|
223
|
+
fs.existsSync = jest.fn().mockImplementation((p) =>
|
|
224
|
+
p === '/project/node_modules'
|
|
225
|
+
);
|
|
226
|
+
|
|
227
|
+
const original = {
|
|
228
|
+
auth: {
|
|
229
|
+
handler: 'node_modules/@friggframework/core/handlers/auth.handler',
|
|
230
|
+
},
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
const result = modifyHandlerPaths(original);
|
|
234
|
+
|
|
235
|
+
// Result should be a copy
|
|
236
|
+
expect(result).not.toBe(original);
|
|
237
|
+
expect(result.auth).not.toBe(original.auth);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('should handle multiple functions', () => {
|
|
241
|
+
process.argv = ['node', 'test', 'offline'];
|
|
242
|
+
process.cwd = jest.fn().mockReturnValue('/project');
|
|
243
|
+
fs.existsSync = jest.fn().mockImplementation((p) =>
|
|
244
|
+
p === '/project/node_modules'
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
const functions = {
|
|
248
|
+
auth: {
|
|
249
|
+
handler: 'node_modules/@friggframework/core/handlers/auth.handler',
|
|
250
|
+
},
|
|
251
|
+
user: {
|
|
252
|
+
handler: 'node_modules/@friggframework/core/handlers/user.handler',
|
|
253
|
+
},
|
|
254
|
+
health: {
|
|
255
|
+
handler: 'node_modules/@friggframework/core/handlers/health.handler',
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
const result = modifyHandlerPaths(functions);
|
|
260
|
+
|
|
261
|
+
expect(Object.keys(result)).toHaveLength(3);
|
|
262
|
+
expect(result.auth.handler).toContain('node_modules');
|
|
263
|
+
expect(result.user.handler).toContain('node_modules');
|
|
264
|
+
expect(result.health.handler).toContain('node_modules');
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prisma Lambda Layer Manager
|
|
3
|
+
*
|
|
4
|
+
* Utility Layer - Hexagonal Architecture
|
|
5
|
+
*
|
|
6
|
+
* Manages Prisma Lambda Layer for serverless deployments.
|
|
7
|
+
* Ensures the layer exists and is built before deployment.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const { buildPrismaLayer } = require('../../../scripts/build-prisma-layer');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Ensure Prisma Lambda Layer exists
|
|
16
|
+
*
|
|
17
|
+
* Automatically builds the layer if it doesn't exist.
|
|
18
|
+
* The layer contains ONLY the Prisma runtime client (minimal, ~10-15MB).
|
|
19
|
+
* Prisma CLI is bundled separately in the dbMigrate function.
|
|
20
|
+
*
|
|
21
|
+
* @param {Object} databaseConfig - Database configuration from app definition
|
|
22
|
+
* @returns {Promise<void>}
|
|
23
|
+
* @throws {Error} If layer build fails
|
|
24
|
+
*/
|
|
25
|
+
async function ensurePrismaLayerExists(databaseConfig = {}) {
|
|
26
|
+
const projectRoot = process.cwd();
|
|
27
|
+
const layerPath = path.join(projectRoot, 'layers/prisma');
|
|
28
|
+
|
|
29
|
+
// Check if layer already exists
|
|
30
|
+
if (fs.existsSync(layerPath)) {
|
|
31
|
+
console.log('✓ Prisma Lambda Layer already exists at', layerPath);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Layer doesn't exist - build it automatically
|
|
36
|
+
console.log('📦 Prisma Lambda Layer not found - building automatically...');
|
|
37
|
+
console.log(' Building MINIMAL layer (runtime only, NO CLI)');
|
|
38
|
+
console.log(' CLI is packaged separately in dbMigrate function');
|
|
39
|
+
console.log(' This may take a minute on first deployment.\n');
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
// Build layer WITHOUT CLI (runtime only for minimal size)
|
|
43
|
+
await buildPrismaLayer(databaseConfig);
|
|
44
|
+
console.log('✓ Prisma Lambda Layer built successfully (~10-15MB)\n');
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error('✗ Failed to build Prisma Lambda Layer:', error.message);
|
|
47
|
+
console.error(' You may need to run: npm install @friggframework/core\n');
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = {
|
|
53
|
+
ensurePrismaLayerExists,
|
|
54
|
+
};
|
|
55
|
+
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for Prisma Layer Manager
|
|
3
|
+
*
|
|
4
|
+
* Tests Prisma Lambda Layer existence checking and building
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const fs = require('fs');
|
|
9
|
+
const { ensurePrismaLayerExists } = require('./prisma-layer-manager');
|
|
10
|
+
|
|
11
|
+
// Mock fs and buildPrismaLayer
|
|
12
|
+
jest.mock('fs');
|
|
13
|
+
jest.mock('../../../scripts/build-prisma-layer', () => ({
|
|
14
|
+
buildPrismaLayer: jest.fn(),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
const { buildPrismaLayer } = require('../../../scripts/build-prisma-layer');
|
|
18
|
+
|
|
19
|
+
describe('Prisma Layer Manager', () => {
|
|
20
|
+
let originalCwd;
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
originalCwd = process.cwd;
|
|
24
|
+
process.cwd = jest.fn().mockReturnValue('/project');
|
|
25
|
+
jest.clearAllMocks();
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
process.cwd = originalCwd;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('ensurePrismaLayerExists()', () => {
|
|
33
|
+
it('should skip build if layer already exists', async () => {
|
|
34
|
+
fs.existsSync = jest.fn().mockReturnValue(true);
|
|
35
|
+
|
|
36
|
+
await ensurePrismaLayerExists();
|
|
37
|
+
|
|
38
|
+
expect(fs.existsSync).toHaveBeenCalledWith('/project/layers/prisma');
|
|
39
|
+
expect(buildPrismaLayer).not.toHaveBeenCalled();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should build layer if it does not exist', async () => {
|
|
43
|
+
fs.existsSync = jest.fn().mockReturnValue(false);
|
|
44
|
+
buildPrismaLayer.mockResolvedValue();
|
|
45
|
+
|
|
46
|
+
await ensurePrismaLayerExists();
|
|
47
|
+
|
|
48
|
+
expect(buildPrismaLayer).toHaveBeenCalledTimes(1);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should pass database config to buildPrismaLayer', async () => {
|
|
52
|
+
fs.existsSync = jest.fn().mockReturnValue(false);
|
|
53
|
+
buildPrismaLayer.mockResolvedValue();
|
|
54
|
+
|
|
55
|
+
const databaseConfig = {
|
|
56
|
+
postgres: { enable: true },
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
await ensurePrismaLayerExists(databaseConfig);
|
|
60
|
+
|
|
61
|
+
expect(buildPrismaLayer).toHaveBeenCalledWith(databaseConfig);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should handle build errors and rethrow', async () => {
|
|
65
|
+
fs.existsSync = jest.fn().mockReturnValue(false);
|
|
66
|
+
buildPrismaLayer.mockRejectedValue(new Error('Build failed'));
|
|
67
|
+
|
|
68
|
+
await expect(ensurePrismaLayerExists()).rejects.toThrow('Build failed');
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('should default to empty database config', async () => {
|
|
72
|
+
fs.existsSync = jest.fn().mockReturnValue(false);
|
|
73
|
+
buildPrismaLayer.mockResolvedValue();
|
|
74
|
+
|
|
75
|
+
await ensurePrismaLayerExists();
|
|
76
|
+
|
|
77
|
+
expect(buildPrismaLayer).toHaveBeenCalledWith({});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('should use correct layer path relative to project root', async () => {
|
|
81
|
+
process.cwd = jest.fn().mockReturnValue('/custom/project/path');
|
|
82
|
+
fs.existsSync = jest.fn().mockReturnValue(true);
|
|
83
|
+
|
|
84
|
+
await ensurePrismaLayerExists();
|
|
85
|
+
|
|
86
|
+
expect(fs.existsSync).toHaveBeenCalledWith('/custom/project/path/layers/prisma');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('should log success when layer already exists', async () => {
|
|
90
|
+
fs.existsSync = jest.fn().mockReturnValue(true);
|
|
91
|
+
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
92
|
+
|
|
93
|
+
await ensurePrismaLayerExists();
|
|
94
|
+
|
|
95
|
+
// console.log is called with 2 args: message + path
|
|
96
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
97
|
+
expect.stringContaining('already exists'),
|
|
98
|
+
expect.any(String)
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
consoleSpy.mockRestore();
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('should log build progress when building layer', async () => {
|
|
105
|
+
fs.existsSync = jest.fn().mockReturnValue(false);
|
|
106
|
+
buildPrismaLayer.mockResolvedValue();
|
|
107
|
+
const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
|
|
108
|
+
|
|
109
|
+
await ensurePrismaLayerExists();
|
|
110
|
+
|
|
111
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
112
|
+
expect.stringContaining('building automatically')
|
|
113
|
+
);
|
|
114
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
115
|
+
expect.stringContaining('built successfully')
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
consoleSpy.mockRestore();
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
it('should log error message on build failure', async () => {
|
|
122
|
+
fs.existsSync = jest.fn().mockReturnValue(false);
|
|
123
|
+
buildPrismaLayer.mockRejectedValue(new Error('Build error'));
|
|
124
|
+
const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
125
|
+
|
|
126
|
+
await expect(ensurePrismaLayerExists()).rejects.toThrow();
|
|
127
|
+
|
|
128
|
+
// console.error is called with 2 args: message + error
|
|
129
|
+
expect(consoleErrorSpy).toHaveBeenCalledWith(
|
|
130
|
+
expect.stringContaining('Failed to build'),
|
|
131
|
+
expect.any(String)
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
consoleErrorSpy.mockRestore();
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
@@ -23,7 +23,8 @@ const validateEnvironmentVariables = (AppDefinition) => {
|
|
|
23
23
|
|
|
24
24
|
for (const [key, value] of Object.entries(AppDefinition.environment)) {
|
|
25
25
|
if (value === true) {
|
|
26
|
-
if (
|
|
26
|
+
// Use 'in' operator to check if key exists (undefined = missing, empty string = present)
|
|
27
|
+
if (key in process.env) {
|
|
27
28
|
results.valid.push(key);
|
|
28
29
|
} else {
|
|
29
30
|
results.missing.push(key);
|