@friggframework/devtools 2.0.0--canary.461.c89a166.0 → 2.0.0--canary.461.1b7292f.0

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.
@@ -247,7 +247,7 @@ describe('IntegrationBuilder', () => {
247
247
 
248
248
  const result = await integrationBuilder.build(appDefinition, {});
249
249
 
250
- expect(result.functions.testQueueWorker.timeout).toBe(600);
250
+ expect(result.functions.testQueueWorker.timeout).toBe(900); // 15 minutes (Lambda max)
251
251
  });
252
252
 
253
253
  it('should set queue worker reserved concurrency', async () => {
@@ -61,10 +61,11 @@ describe('KmsDiscovery', () => {
61
61
 
62
62
  const result = await kmsDiscovery.discover({});
63
63
 
64
- expect(result.kmsKeyId).toBeNull();
65
- expect(result.kmsKeyArn).toBeNull();
66
- expect(result.defaultKmsKeyId).toBeNull();
67
- expect(result.kmsKeyAlias).toBeNull();
64
+ // When no keys found, properties are undefined (not explicitly set to null)
65
+ expect(result.kmsKeyId).toBeFalsy();
66
+ expect(result.kmsKeyArn).toBeFalsy();
67
+ expect(result.defaultKmsKeyId).toBeFalsy();
68
+ expect(result.kmsKeyAlias).toBeFalsy();
68
69
  });
69
70
 
70
71
  it('should handle KMS key without alias', async () => {
@@ -40,9 +40,9 @@ class CloudFormationDiscovery {
40
40
  this._extractFromOutputs(stack.Outputs, discovered);
41
41
  }
42
42
 
43
- // Extract from resources
43
+ // Extract from resources (now async to query AWS for details)
44
44
  if (resources && resources.length > 0) {
45
- this._extractFromResources(resources, discovered);
45
+ await this._extractFromResources(resources, discovered);
46
46
  }
47
47
 
48
48
  return discovered;
@@ -107,10 +107,30 @@ class CloudFormationDiscovery {
107
107
  * @param {Array} resources - CloudFormation stack resources
108
108
  * @param {Object} discovered - Object to populate with discovered resources
109
109
  */
110
- _extractFromResources(resources, discovered) {
110
+ async _extractFromResources(resources, discovered) {
111
111
  for (const resource of resources) {
112
112
  const { LogicalResourceId, PhysicalResourceId, ResourceType } = resource;
113
113
 
114
+ // Security Group - use to get VPC ID
115
+ if (LogicalResourceId === 'FriggLambdaSecurityGroup' && ResourceType === 'AWS::EC2::SecurityGroup') {
116
+ discovered.securityGroupId = PhysicalResourceId;
117
+ // Query security group to get VPC ID
118
+ if (this.provider && !discovered.vpcId) {
119
+ try {
120
+ const sgDetails = await this.provider.getEC2Client().send(
121
+ new (require('@aws-sdk/client-ec2').DescribeSecurityGroupsCommand)({
122
+ GroupIds: [PhysicalResourceId]
123
+ })
124
+ );
125
+ if (sgDetails.SecurityGroups && sgDetails.SecurityGroups.length > 0) {
126
+ discovered.vpcId = sgDetails.SecurityGroups[0].VpcId;
127
+ }
128
+ } catch (error) {
129
+ console.warn(`Could not get VPC from security group: ${error.message}`);
130
+ }
131
+ }
132
+ }
133
+
114
134
  // Aurora cluster
115
135
  if (LogicalResourceId === 'FriggAuroraCluster' && ResourceType === 'AWS::RDS::DBCluster') {
116
136
  discovered.auroraClusterId = PhysicalResourceId;
@@ -112,14 +112,19 @@ function modifyHandlerPaths(functions) {
112
112
 
113
113
  if (!isOffline) {
114
114
  console.log('Not in offline mode, skipping handler path modification');
115
- return functions;
115
+ // Return shallow copy to prevent mutations (DDD immutability principle)
116
+ return { ...functions };
116
117
  }
117
118
 
118
119
  // In offline mode, don't modify the handler paths at all
119
120
  // serverless-offline will resolve node_modules paths from the working directory
120
121
  console.log('Offline mode detected - keeping original handler paths for serverless-offline');
121
122
 
122
- return functions;
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
+ }, {});
123
128
  }
124
129
 
125
130
  module.exports = {
@@ -58,20 +58,21 @@ describe('Handler Path Resolver', () => {
58
58
  });
59
59
 
60
60
  it('should use npm root if directory search fails (method 2)', () => {
61
- process.cwd = jest.fn().mockReturnValue('/project');
62
- fs.existsSync = jest.fn().mockReturnValue(false);
63
-
61
+ process.cwd = jest.fn().mockReturnValue('/some/unusual/directory');
62
+
64
63
  const { execSync } = require('node:child_process');
65
- execSync.mockReturnValue('/project/node_modules\n');
64
+ // npm root returns a different path
65
+ execSync.mockReturnValue('/usr/local/lib/node_modules\n');
66
66
 
67
- // On second call for npm root, return true
68
- fs.existsSync.mockImplementation((p) =>
69
- p === '/project/node_modules'
70
- );
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
+ });
71
72
 
72
73
  const result = findNodeModulesPath();
73
74
 
74
- expect(result).toBe('/project/node_modules');
75
+ expect(result).toBe('/usr/local/lib/node_modules');
75
76
  expect(execSync).toHaveBeenCalledWith('npm root', { encoding: 'utf8' });
76
77
  });
77
78
 
@@ -129,13 +130,21 @@ describe('Handler Path Resolver', () => {
129
130
  });
130
131
 
131
132
  it('should handle errors during search', () => {
132
- process.cwd = jest.fn().mockImplementation(() => {
133
- throw new Error('cwd error');
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');
134
142
  });
135
143
 
136
144
  const result = findNodeModulesPath();
137
145
 
138
- expect(result).toBe(path.resolve(process.cwd(), '../node_modules'));
146
+ // Should fallback to default path even when search methods fail
147
+ expect(result).toBe(path.resolve('/project', '../node_modules'));
139
148
  });
140
149
  });
141
150
 
@@ -92,8 +92,10 @@ describe('Prisma Layer Manager', () => {
92
92
 
93
93
  await ensurePrismaLayerExists();
94
94
 
95
+ // console.log is called with 2 args: message + path
95
96
  expect(consoleSpy).toHaveBeenCalledWith(
96
- expect.stringContaining('already exists')
97
+ expect.stringContaining('already exists'),
98
+ expect.any(String)
97
99
  );
98
100
 
99
101
  consoleSpy.mockRestore();
@@ -123,8 +125,10 @@ describe('Prisma Layer Manager', () => {
123
125
 
124
126
  await expect(ensurePrismaLayerExists()).rejects.toThrow();
125
127
 
128
+ // console.error is called with 2 args: message + error
126
129
  expect(consoleErrorSpy).toHaveBeenCalledWith(
127
- expect.stringContaining('Failed to build')
130
+ expect.stringContaining('Failed to build'),
131
+ expect.any(String)
128
132
  );
129
133
 
130
134
  consoleErrorSpy.mockRestore();
@@ -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 (process.env[key]) {
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);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@friggframework/devtools",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0--canary.461.c89a166.0",
4
+ "version": "2.0.0--canary.461.1b7292f.0",
5
5
  "dependencies": {
6
6
  "@aws-sdk/client-ec2": "^3.835.0",
7
7
  "@aws-sdk/client-kms": "^3.835.0",
@@ -11,8 +11,8 @@
11
11
  "@babel/eslint-parser": "^7.18.9",
12
12
  "@babel/parser": "^7.25.3",
13
13
  "@babel/traverse": "^7.25.3",
14
- "@friggframework/schemas": "2.0.0--canary.461.c89a166.0",
15
- "@friggframework/test": "2.0.0--canary.461.c89a166.0",
14
+ "@friggframework/schemas": "2.0.0--canary.461.1b7292f.0",
15
+ "@friggframework/test": "2.0.0--canary.461.1b7292f.0",
16
16
  "@hapi/boom": "^10.0.1",
17
17
  "@inquirer/prompts": "^5.3.8",
18
18
  "axios": "^1.7.2",
@@ -34,8 +34,8 @@
34
34
  "serverless-http": "^2.7.0"
35
35
  },
36
36
  "devDependencies": {
37
- "@friggframework/eslint-config": "2.0.0--canary.461.c89a166.0",
38
- "@friggframework/prettier-config": "2.0.0--canary.461.c89a166.0",
37
+ "@friggframework/eslint-config": "2.0.0--canary.461.1b7292f.0",
38
+ "@friggframework/prettier-config": "2.0.0--canary.461.1b7292f.0",
39
39
  "aws-sdk-client-mock": "^4.1.0",
40
40
  "aws-sdk-client-mock-jest": "^4.1.0",
41
41
  "jest": "^30.1.3",
@@ -70,5 +70,5 @@
70
70
  "publishConfig": {
71
71
  "access": "public"
72
72
  },
73
- "gitHead": "c89a166c8477430b9649249d4f2113129bf1acc0"
73
+ "gitHead": "1b7292f30e11b350a075d5dcecded3c8c0f21167"
74
74
  }