@friggframework/devtools 2.0.0--canary.398.a2fbc38.0 → 2.0.0--canary.398.d5e0e8e.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.
@@ -4,28 +4,7 @@ const path = require('path');
4
4
  async function buildCommand(options) {
5
5
  console.log('Building the serverless application...');
6
6
 
7
- // Run AWS discovery first
8
- try {
9
- const discoveryPath = path.resolve(__dirname, '../../infrastructure/run-discovery.js');
10
- console.log('🔍 Running AWS resource discovery...');
11
-
12
- const discoveryResult = spawnSync('node', [discoveryPath], {
13
- stdio: 'inherit',
14
- shell: true,
15
- env: process.env
16
- });
17
-
18
- if (discoveryResult.status !== 0) {
19
- console.error('❌ AWS discovery failed. Build cannot continue.');
20
- console.error(' See error messages above for troubleshooting steps.');
21
- process.exit(1);
22
- }
23
- } catch (error) {
24
- console.error('❌ Could not run AWS discovery:', error.message);
25
- console.error(' This may indicate a configuration or dependency issue.');
26
- process.exit(1);
27
- }
28
-
7
+ // AWS discovery is now handled directly in serverless-template.js
29
8
  console.log('📦 Packaging serverless application...');
30
9
  const backendPath = path.resolve(process.cwd());
31
10
  const infrastructurePath = 'infrastructure.js';
@@ -4,28 +4,7 @@ const path = require('path');
4
4
  async function deployCommand(options) {
5
5
  console.log('Deploying the serverless application...');
6
6
 
7
- // Run AWS discovery first
8
- try {
9
- const discoveryPath = path.resolve(__dirname, '../../infrastructure/run-discovery.js');
10
- console.log('🔍 Running AWS resource discovery...');
11
-
12
- const discoveryResult = spawnSync('node', [discoveryPath], {
13
- stdio: 'inherit',
14
- shell: true,
15
- env: process.env
16
- });
17
-
18
- if (discoveryResult.status !== 0) {
19
- console.error('❌ AWS discovery failed. Deployment cannot continue.');
20
- console.error(' See error messages above for troubleshooting steps.');
21
- process.exit(1);
22
- }
23
- } catch (error) {
24
- console.error('❌ Could not run AWS discovery:', error.message);
25
- console.error(' This may indicate a configuration or dependency issue.');
26
- process.exit(1);
27
- }
28
-
7
+ // AWS discovery is now handled directly in serverless-template.js
29
8
  console.log('🚀 Deploying serverless application...');
30
9
  const backendPath = path.resolve(process.cwd());
31
10
  const infrastructurePath = 'infrastructure.js';
@@ -149,27 +149,71 @@ class AWSDiscovery {
149
149
  */
150
150
  async isSubnetPrivate(subnetId) {
151
151
  try {
152
- const command = new DescribeRouteTablesCommand({
152
+ // First, get the subnet details to find its VPC
153
+ const subnetCommand = new DescribeSubnetsCommand({
154
+ SubnetIds: [subnetId]
155
+ });
156
+ const subnetResponse = await this.ec2Client.send(subnetCommand);
157
+
158
+ if (!subnetResponse.Subnets || subnetResponse.Subnets.length === 0) {
159
+ throw new Error(`Subnet ${subnetId} not found`);
160
+ }
161
+
162
+ const subnet = subnetResponse.Subnets[0];
163
+ const vpcId = subnet.VpcId;
164
+
165
+ // Get all route tables for this VPC
166
+ const routeTablesCommand = new DescribeRouteTablesCommand({
153
167
  Filters: [
154
168
  {
155
- Name: 'association.subnet-id',
156
- Values: [subnetId]
169
+ Name: 'vpc-id',
170
+ Values: [vpcId]
157
171
  }
158
172
  ]
159
173
  });
160
174
 
161
- const response = await this.ec2Client.send(command);
175
+ const routeTablesResponse = await this.ec2Client.send(routeTablesCommand);
162
176
 
163
- for (const routeTable of response.RouteTables || []) {
164
- for (const route of routeTable.Routes || []) {
165
- // If there's a route to an Internet Gateway, it's a public subnet
166
- if (route.GatewayId && route.GatewayId.startsWith('igw-')) {
167
- return false;
177
+ // Find the route table for this subnet
178
+ let routeTable = null;
179
+
180
+ // First check for explicit association
181
+ for (const rt of routeTablesResponse.RouteTables || []) {
182
+ for (const assoc of rt.Associations || []) {
183
+ if (assoc.SubnetId === subnetId) {
184
+ routeTable = rt;
185
+ break;
186
+ }
187
+ }
188
+ if (routeTable) break;
189
+ }
190
+
191
+ // If no explicit association, use the main route table
192
+ if (!routeTable) {
193
+ for (const rt of routeTablesResponse.RouteTables || []) {
194
+ for (const assoc of rt.Associations || []) {
195
+ if (assoc.Main === true) {
196
+ routeTable = rt;
197
+ break;
198
+ }
168
199
  }
200
+ if (routeTable) break;
201
+ }
202
+ }
203
+
204
+ if (!routeTable) {
205
+ console.warn(`No route table found for subnet ${subnetId}`);
206
+ return true; // Default to private for safety
207
+ }
208
+
209
+ // Check if route table has a route to an Internet Gateway
210
+ for (const route of routeTable.Routes || []) {
211
+ if (route.GatewayId && route.GatewayId.startsWith('igw-')) {
212
+ return false; // It's a public subnet
169
213
  }
170
214
  }
171
215
 
172
- return true; // No IGW route found, assume private
216
+ return true; // No IGW route found, it's private
173
217
  } catch (error) {
174
218
  console.warn(`Could not determine if subnet ${subnetId} is private:`, error);
175
219
  return true; // Default to private for safety
@@ -4,7 +4,7 @@ const { composeServerlessDefinition } = require('./serverless-template');
4
4
 
5
5
  const { findNearestBackendPackageJson } = require('@friggframework/core');
6
6
 
7
- function createFriggInfrastructure() {
7
+ async function createFriggInfrastructure() {
8
8
  const backendPath = findNearestBackendPackageJson();
9
9
  if (!backendPath) {
10
10
  throw new Error('Could not find backend package.json');
@@ -23,7 +23,7 @@ function createFriggInfrastructure() {
23
23
  // __dirname,
24
24
  // './serverless-template.js'
25
25
  // ));
26
- const definition = composeServerlessDefinition(
26
+ const definition = await composeServerlessDefinition(
27
27
  appDefinition,
28
28
  backend.IntegrationFactory
29
29
  );
@@ -10,6 +10,8 @@ const { findNearestBackendPackageJson } = require('@friggframework/core');
10
10
  const path = require('path');
11
11
 
12
12
  async function runDiscovery() {
13
+ let appDefinition;
14
+
13
15
  try {
14
16
  console.log('🔍 Starting AWS resource discovery...');
15
17
 
@@ -30,7 +32,7 @@ async function runDiscovery() {
30
32
 
31
33
  // Load the app definition
32
34
  const backend = require(backendFilePath);
33
- const appDefinition = backend.Definition;
35
+ appDefinition = backend.Definition;
34
36
 
35
37
  if (!appDefinition) {
36
38
  console.log('⚠️ No Definition found in backend/index.js, skipping discovery');
@@ -1,5 +1,17 @@
1
1
  const path = require('path');
2
2
  const fs = require('fs');
3
+ const { AWSDiscovery } = require('./aws-discovery');
4
+
5
+ /**
6
+ * Check if AWS discovery should run based on AppDefinition
7
+ * @param {Object} AppDefinition - Application definition
8
+ * @returns {boolean} True if discovery should run
9
+ */
10
+ const shouldRunDiscovery = (AppDefinition) => {
11
+ return AppDefinition.vpc?.enable === true ||
12
+ AppDefinition.encryption?.useDefaultKMSForFieldLevelEncryption === true ||
13
+ AppDefinition.ssm?.enable === true;
14
+ };
3
15
 
4
16
  /**
5
17
  * Find the actual path to node_modules directory
@@ -438,7 +450,44 @@ const createVPCInfrastructure = (AppDefinition) => {
438
450
  * @param {boolean} [AppDefinition.websockets.enable=false] - Enable WebSocket support for live update streaming
439
451
  * @returns {Object} Complete serverless framework configuration
440
452
  */
441
- const composeServerlessDefinition = (AppDefinition) => {
453
+ const composeServerlessDefinition = async (AppDefinition) => {
454
+ // Store discovered resources
455
+ let discoveredResources = {};
456
+
457
+ // Run AWS discovery if needed
458
+ if (shouldRunDiscovery(AppDefinition)) {
459
+ console.log('🔍 Running AWS resource discovery for serverless template...');
460
+ try {
461
+ const region = process.env.AWS_REGION || 'us-east-1';
462
+ const discovery = new AWSDiscovery(region);
463
+
464
+ const config = {
465
+ vpc: AppDefinition.vpc || {},
466
+ encryption: AppDefinition.encryption || {},
467
+ ssm: AppDefinition.ssm || {}
468
+ };
469
+
470
+ discoveredResources = await discovery.discoverResources(config);
471
+
472
+ console.log('✅ AWS discovery completed successfully!');
473
+ if (discoveredResources.defaultVpcId) {
474
+ console.log(` VPC: ${discoveredResources.defaultVpcId}`);
475
+ }
476
+ if (discoveredResources.privateSubnetIds) {
477
+ console.log(` Subnets: ${discoveredResources.privateSubnetIds.join(', ')}`);
478
+ }
479
+ if (discoveredResources.defaultSecurityGroupId) {
480
+ console.log(` Security Group: ${discoveredResources.defaultSecurityGroupId}`);
481
+ }
482
+ if (discoveredResources.defaultKmsKeyId) {
483
+ console.log(` KMS Key: ${discoveredResources.defaultKmsKeyId}`);
484
+ }
485
+ } catch (error) {
486
+ console.error('❌ AWS discovery failed:', error.message);
487
+ throw new Error(`AWS discovery failed: ${error.message}`);
488
+ }
489
+ }
490
+
442
491
  const definition = {
443
492
  frameworkVersion: '>=3.17.0',
444
493
  service: AppDefinition.name || 'create-frigg-app',
@@ -456,6 +505,14 @@ const composeServerlessDefinition = (AppDefinition) => {
456
505
  environment: {
457
506
  STAGE: '${opt:stage}',
458
507
  AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1,
508
+ // Add discovered resources to environment if available
509
+ ...(discoveredResources.defaultVpcId && { AWS_DISCOVERY_VPC_ID: discoveredResources.defaultVpcId }),
510
+ ...(discoveredResources.defaultSecurityGroupId && { AWS_DISCOVERY_SECURITY_GROUP_ID: discoveredResources.defaultSecurityGroupId }),
511
+ ...(discoveredResources.privateSubnetIds && discoveredResources.privateSubnetIds[0] && { AWS_DISCOVERY_SUBNET_ID_1: discoveredResources.privateSubnetIds[0] }),
512
+ ...(discoveredResources.privateSubnetIds && discoveredResources.privateSubnetIds[1] && { AWS_DISCOVERY_SUBNET_ID_2: discoveredResources.privateSubnetIds[1] }),
513
+ ...(discoveredResources.publicSubnetId && { AWS_DISCOVERY_PUBLIC_SUBNET_ID: discoveredResources.publicSubnetId }),
514
+ ...(discoveredResources.defaultRouteTableId && { AWS_DISCOVERY_ROUTE_TABLE_ID: discoveredResources.defaultRouteTableId }),
515
+ ...(discoveredResources.defaultKmsKeyId && { AWS_DISCOVERY_KMS_KEY_ID: discoveredResources.defaultKmsKeyId }),
459
516
  },
460
517
  iamRoleStatements: [
461
518
  {
@@ -666,7 +723,7 @@ const composeServerlessDefinition = (AppDefinition) => {
666
723
 
667
724
  // Configure KMS grants with discovered default key
668
725
  definition.custom.kmsGrants = {
669
- kmsKeyId: '${env:AWS_DISCOVERY_KMS_KEY_ID}'
726
+ kmsKeyId: discoveredResources.defaultKmsKeyId || '${env:AWS_DISCOVERY_KMS_KEY_ID}'
670
727
  };
671
728
  }
672
729
 
@@ -720,11 +777,11 @@ const composeServerlessDefinition = (AppDefinition) => {
720
777
  // VPC configuration using discovered or explicitly provided resources
721
778
  const vpcConfig = {
722
779
  securityGroupIds: AppDefinition.vpc.securityGroupIds ||
723
- ['${env:AWS_DISCOVERY_SECURITY_GROUP_ID}'],
724
- subnetIds: AppDefinition.vpc.subnetIds || [
725
- '${env:AWS_DISCOVERY_SUBNET_ID_1}',
726
- '${env:AWS_DISCOVERY_SUBNET_ID_2}'
727
- ]
780
+ (discoveredResources.defaultSecurityGroupId ? [discoveredResources.defaultSecurityGroupId] : ['${env:AWS_DISCOVERY_SECURITY_GROUP_ID}']),
781
+ subnetIds: AppDefinition.vpc.subnetIds ||
782
+ (discoveredResources.privateSubnetIds && discoveredResources.privateSubnetIds.length >= 2 ?
783
+ [discoveredResources.privateSubnetIds[0], discoveredResources.privateSubnetIds[1]] :
784
+ ['${env:AWS_DISCOVERY_SUBNET_ID_1}', '${env:AWS_DISCOVERY_SUBNET_ID_2}'])
728
785
  };
729
786
 
730
787
  // Set VPC config for Lambda functions
@@ -746,7 +803,7 @@ const composeServerlessDefinition = (AppDefinition) => {
746
803
  Type: 'AWS::EC2::NatGateway',
747
804
  Properties: {
748
805
  AllocationId: { 'Fn::GetAtt': ['FriggNATGatewayEIP', 'AllocationId'] },
749
- SubnetId: '${env:AWS_DISCOVERY_PUBLIC_SUBNET_ID}', // Discovery finds public subnet
806
+ SubnetId: discoveredResources.publicSubnetId || '${env:AWS_DISCOVERY_PUBLIC_SUBNET_ID}', // Discovery finds public subnet
750
807
  Tags: [
751
808
  { Key: 'Name', Value: '${self:service}-${self:provider.stage}-nat-gateway' }
752
809
  ]
@@ -757,7 +814,7 @@ const composeServerlessDefinition = (AppDefinition) => {
757
814
  definition.resources.Resources.FriggLambdaRouteTable = {
758
815
  Type: 'AWS::EC2::RouteTable',
759
816
  Properties: {
760
- VpcId: '${env:AWS_DISCOVERY_VPC_ID}',
817
+ VpcId: discoveredResources.defaultVpcId || '${env:AWS_DISCOVERY_VPC_ID}',
761
818
  Tags: [
762
819
  { Key: 'Name', Value: '${self:service}-${self:provider.stage}-lambda-rt' }
763
820
  ]
@@ -777,7 +834,7 @@ const composeServerlessDefinition = (AppDefinition) => {
777
834
  definition.resources.Resources.FriggSubnet1RouteAssociation = {
778
835
  Type: 'AWS::EC2::SubnetRouteTableAssociation',
779
836
  Properties: {
780
- SubnetId: '${env:AWS_DISCOVERY_SUBNET_ID_1}',
837
+ SubnetId: discoveredResources.privateSubnetIds?.[0] || '${env:AWS_DISCOVERY_SUBNET_ID_1}',
781
838
  RouteTableId: { Ref: 'FriggLambdaRouteTable' }
782
839
  }
783
840
  };
@@ -785,7 +842,7 @@ const composeServerlessDefinition = (AppDefinition) => {
785
842
  definition.resources.Resources.FriggSubnet2RouteAssociation = {
786
843
  Type: 'AWS::EC2::SubnetRouteTableAssociation',
787
844
  Properties: {
788
- SubnetId: '${env:AWS_DISCOVERY_SUBNET_ID_2}',
845
+ SubnetId: discoveredResources.privateSubnetIds?.[1] || '${env:AWS_DISCOVERY_SUBNET_ID_2}',
789
846
  RouteTableId: { Ref: 'FriggLambdaRouteTable' }
790
847
  }
791
848
  };
@@ -795,7 +852,7 @@ const composeServerlessDefinition = (AppDefinition) => {
795
852
  definition.resources.Resources.VPCEndpointS3 = {
796
853
  Type: 'AWS::EC2::VPCEndpoint',
797
854
  Properties: {
798
- VpcId: '${env:AWS_DISCOVERY_VPC_ID}',
855
+ VpcId: discoveredResources.defaultVpcId || '${env:AWS_DISCOVERY_VPC_ID}',
799
856
  ServiceName: 'com.amazonaws.${self:provider.region}.s3',
800
857
  VpcEndpointType: 'Gateway',
801
858
  RouteTableIds: [{ Ref: 'FriggLambdaRouteTable' }]
@@ -805,7 +862,7 @@ const composeServerlessDefinition = (AppDefinition) => {
805
862
  definition.resources.Resources.VPCEndpointDynamoDB = {
806
863
  Type: 'AWS::EC2::VPCEndpoint',
807
864
  Properties: {
808
- VpcId: '${env:AWS_DISCOVERY_VPC_ID}',
865
+ VpcId: discoveredResources.defaultVpcId || '${env:AWS_DISCOVERY_VPC_ID}',
809
866
  ServiceName: 'com.amazonaws.${self:provider.region}.dynamodb',
810
867
  VpcEndpointType: 'Gateway',
811
868
  RouteTableIds: [{ Ref: 'FriggLambdaRouteTable' }]
@@ -905,28 +962,8 @@ const composeServerlessDefinition = (AppDefinition) => {
905
962
  definition.custom[queueReference] = queueName;
906
963
  }
907
964
 
908
- // Check if AWS discovery environment variables are missing and error
909
- const needsDiscovery = AppDefinition.vpc?.enable ||
910
- AppDefinition.encryption?.useDefaultKMSForFieldLevelEncryption ||
911
- AppDefinition.ssm?.enable;
912
-
913
- if (needsDiscovery && !process.env.AWS_DISCOVERY_VPC_ID) {
914
- console.error('❌ AWS discovery environment variables not found');
915
- console.error('');
916
- console.error('🚨 Your AppDefinition requires AWS discovery but variables are missing:');
917
- if (AppDefinition.vpc?.enable) console.error(' ❌ VPC support (vpc.enable: true)');
918
- if (AppDefinition.encryption?.useDefaultKMSForFieldLevelEncryption) console.error(' ❌ KMS encryption (encryption.useDefaultKMSForFieldLevelEncryption: true)');
919
- if (AppDefinition.ssm?.enable) console.error(' ❌ SSM parameters (ssm.enable: true)');
920
- console.error('');
921
- console.error('💡 Run AWS discovery before building:');
922
- console.error(' node node_modules/@friggframework/devtools/infrastructure/run-discovery.js');
923
- console.error('');
924
- console.error('🔧 Or use the build command which runs discovery automatically:');
925
- console.error(' npx frigg build');
926
-
927
- // Exit the process instead of throwing to avoid serverless circular reference issues
928
- process.exit(1);
929
- }
965
+ // Discovery has already run successfully at this point if needed
966
+ // The discoveredResources object contains all the necessary AWS resources
930
967
 
931
968
  // Add websocket function if enabled
932
969
  if (AppDefinition.websockets?.enable === true) {
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.398.a2fbc38.0",
4
+ "version": "2.0.0--canary.398.d5e0e8e.0",
5
5
  "dependencies": {
6
6
  "@aws-sdk/client-ec2": "^3.835.0",
7
7
  "@aws-sdk/client-kms": "^3.835.0",
@@ -9,7 +9,7 @@
9
9
  "@babel/eslint-parser": "^7.18.9",
10
10
  "@babel/parser": "^7.25.3",
11
11
  "@babel/traverse": "^7.25.3",
12
- "@friggframework/test": "2.0.0--canary.398.a2fbc38.0",
12
+ "@friggframework/test": "2.0.0--canary.398.d5e0e8e.0",
13
13
  "@hapi/boom": "^10.0.1",
14
14
  "@inquirer/prompts": "^5.3.8",
15
15
  "axios": "^1.7.2",
@@ -31,8 +31,8 @@
31
31
  "serverless-http": "^2.7.0"
32
32
  },
33
33
  "devDependencies": {
34
- "@friggframework/eslint-config": "2.0.0--canary.398.a2fbc38.0",
35
- "@friggframework/prettier-config": "2.0.0--canary.398.a2fbc38.0",
34
+ "@friggframework/eslint-config": "2.0.0--canary.398.d5e0e8e.0",
35
+ "@friggframework/prettier-config": "2.0.0--canary.398.d5e0e8e.0",
36
36
  "prettier": "^2.7.1",
37
37
  "serverless": "3.39.0",
38
38
  "serverless-dotenv-plugin": "^6.0.0",
@@ -64,5 +64,5 @@
64
64
  "publishConfig": {
65
65
  "access": "public"
66
66
  },
67
- "gitHead": "a2fbc389033dc1b5ea73edc30fe53292f36887b4"
67
+ "gitHead": "d5e0e8e6ef0dd2b12c5ed4f3fac3ab1037d76673"
68
68
  }