lambda-live-debugger 1.10.5 → 1.11.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.
@@ -12,10 +12,11 @@ declare function getCloudFormationStackTemplate(stackName: string, awsConfigurat
12
12
  * @param awsConfiguration
13
13
  * @returns
14
14
  */
15
- declare function getLambdasInStack(stackName: string, awsConfiguration: AwsConfiguration): Promise<Array<{
15
+ declare function getLambdasInStack(stackName: string, awsConfiguration: AwsConfiguration, stackLogicalId?: string): Promise<Array<{
16
16
  lambdaName: string;
17
17
  logicalId: string;
18
18
  stackName: string;
19
+ stackLogicalId: string;
19
20
  }>>;
20
21
  export declare const CloudFormation: {
21
22
  getCloudFormationStackTemplate: typeof getCloudFormationStackTemplate;
@@ -108,7 +108,7 @@ async function getCloudFormationResources(stackName, awsConfiguration) {
108
108
  * @param awsConfiguration
109
109
  * @returns
110
110
  */
111
- async function getLambdasInStack(stackName, awsConfiguration) {
111
+ async function getLambdasInStack(stackName, awsConfiguration, stackLogicalId) {
112
112
  const response = await getCloudFormationResources(stackName, awsConfiguration);
113
113
  const lambdaResources = response?.filter((resource) => resource.ResourceType === 'AWS::Lambda::Function');
114
114
  const nestedStacks = response?.filter((resource) => resource.ResourceType === 'AWS::CloudFormation::Stack');
@@ -117,12 +117,13 @@ async function getLambdasInStack(stackName, awsConfiguration) {
117
117
  lambdaName: resource.PhysicalResourceId,
118
118
  logicalId: resource.LogicalResourceId,
119
119
  stackName: stackName,
120
+ stackLogicalId: stackLogicalId ?? stackName,
120
121
  };
121
122
  }) ?? [];
122
123
  const lambdasInNestedStacks = await Promise.all((nestedStacks ?? []).map(async (nestedStack) => {
123
124
  if (!nestedStack.PhysicalResourceId)
124
125
  return [];
125
- const lambdasInNestedStack = await getLambdasInStack(nestedStack.PhysicalResourceId, awsConfiguration);
126
+ const lambdasInNestedStack = await getLambdasInStack(nestedStack.PhysicalResourceId, awsConfiguration, nestedStack.LogicalResourceId);
126
127
  return lambdasInNestedStack;
127
128
  }));
128
129
  const flattenedLambdasInNestedStacks = lambdasInNestedStacks.flat();
Binary file
@@ -26,5 +26,11 @@ export declare class SamFramework implements IFramework {
26
26
  * @returns Lambda functions
27
27
  */
28
28
  getLambdas(config: LldConfigBase): Promise<LambdaResource[]>;
29
+ /**
30
+ * Recursively parse templates to find all Lambda functions, including nested stacks
31
+ * @param templatePath The path to the CloudFormation/SAM template file
32
+ * @param stackName The name of the stack this template belongs to (for nested stacks)
33
+ */
34
+ private parseLambdasFromTemplate;
29
35
  }
30
36
  export declare const samFramework: SamFramework;
@@ -83,43 +83,32 @@ export class SamFramework {
83
83
  if (!stackName) {
84
84
  throw new Error(`Stack name not found in ${samConfigFile}`);
85
85
  }
86
- const samTemplateContent = await fs.readFile(path.resolve(samTemplateFile), 'utf-8');
87
- const template = yaml.parse(samTemplateContent);
88
- const lambdas = [];
89
- // get all resources of type AWS::Serverless::Function
90
- for (const resourceName in template.Resources) {
91
- const resource = template.Resources[resourceName];
92
- if (resource.Type === 'AWS::Serverless::Function') {
93
- lambdas.push({
94
- Name: resourceName,
95
- ...resource,
96
- });
97
- }
98
- }
86
+ const lambdas = await this.parseLambdasFromTemplate(samTemplateFile, stackName);
99
87
  const lambdasDiscovered = [];
100
88
  Logger.verbose(`[SAM] Found Lambdas`, JSON.stringify(lambdas, null, 2));
101
89
  const lambdasInStack = await CloudFormation.getLambdasInStack(stackName, awsConfiguration);
102
90
  Logger.verbose(`[SAM] Found Lambdas in stack ${stackName}:`, JSON.stringify(lambdasInStack, null, 2));
103
91
  // get tags for each Lambda
104
92
  for (const func of lambdas) {
105
- const handlerFull = path.join(func.Properties.CodeUri ?? '', func.Properties.Handler);
93
+ const handlerFull = path.join(func.codeUri ?? '', func.handler);
106
94
  const handlerParts = handlerFull.split('.');
107
95
  const handler = handlerParts[1];
108
- const functionName = lambdasInStack.find((lambda) => lambda.logicalId === func.Name)?.lambdaName;
96
+ const functionName = lambdasInStack.find((lambda) => lambda.logicalId === func.name &&
97
+ lambda.stackLogicalId === func.stackLogicalId)?.lambdaName;
109
98
  if (!functionName) {
110
- throw new Error(`Function name not found for function: ${func.Name}`);
99
+ throw new Error(`Function name not found for function: ${func.name}`);
111
100
  }
112
101
  let esBuildOptions = undefined;
113
102
  let codePath;
114
- if (func.Metadata?.BuildMethod?.toLowerCase() === 'esbuild') {
115
- if (func.Metadata?.BuildProperties?.EntryPoints?.length > 0) {
116
- codePath = path.join(func.Properties.CodeUri ?? '', func.Metadata?.BuildProperties?.EntryPoints[0]);
103
+ if (func.buildMethod?.toLowerCase() === 'esbuild') {
104
+ if (func.entryPoints && func.entryPoints.length > 0) {
105
+ codePath = path.join(func.codeUri ?? '', func.entryPoints[0]);
117
106
  }
118
107
  esBuildOptions = {
119
- external: func.Metadata?.BuildProperties?.External,
120
- minify: func.Metadata?.BuildProperties?.Minify,
121
- format: func.Metadata?.BuildProperties?.Format,
122
- target: func.Metadata?.BuildProperties?.Target,
108
+ external: func.external,
109
+ minify: func.minify,
110
+ format: func.format,
111
+ target: func.target,
123
112
  };
124
113
  }
125
114
  if (!codePath) {
@@ -164,5 +153,58 @@ export class SamFramework {
164
153
  }
165
154
  return lambdasDiscovered;
166
155
  }
156
+ /**
157
+ * Recursively parse templates to find all Lambda functions, including nested stacks
158
+ * @param templatePath The path to the CloudFormation/SAM template file
159
+ * @param stackName The name of the stack this template belongs to (for nested stacks)
160
+ */
161
+ async parseLambdasFromTemplate(templatePath, stackName) {
162
+ const resolvedTemplatePath = path.resolve(templatePath);
163
+ const templateDir = path.dirname(resolvedTemplatePath);
164
+ let template;
165
+ try {
166
+ const templateContent = await fs.readFile(resolvedTemplatePath, 'utf-8');
167
+ template = yaml.parse(templateContent);
168
+ }
169
+ catch (err) {
170
+ Logger.warn(`[SAM] Could not read or parse template at ${templatePath}: ${err.message}`);
171
+ return [];
172
+ }
173
+ if (!template.Resources) {
174
+ return [];
175
+ }
176
+ const lambdas = [];
177
+ for (const resourceName in template.Resources) {
178
+ const resource = template.Resources[resourceName];
179
+ // Check if it's a Lambda function
180
+ if (resource.Type === 'AWS::Serverless::Function') {
181
+ lambdas.push({
182
+ templatePath,
183
+ name: resourceName,
184
+ codeUri: resource.Properties?.CodeUri,
185
+ handler: resource.Properties?.Handler,
186
+ buildMethod: resource.Metadata?.BuildMethod,
187
+ entryPoints: resource.Metadata?.BuildProperties?.EntryPoints,
188
+ external: resource.Metadata?.BuildProperties?.External,
189
+ minify: resource.Metadata?.BuildProperties?.Minify,
190
+ format: resource.Metadata?.BuildProperties?.Format,
191
+ target: resource.Metadata?.BuildProperties?.Target,
192
+ stackLogicalId: stackName,
193
+ });
194
+ }
195
+ // Check if it's a nested stack
196
+ else if (resource.Type === 'AWS::Serverless::Application' ||
197
+ resource.Type === 'AWS::CloudFormation::Stack') {
198
+ const nestedTemplateLocation = resource.Properties?.Location ?? resource.Properties?.TemplateURL;
199
+ if (nestedTemplateLocation) {
200
+ const nestedTemplatePath = path.resolve(templateDir, nestedTemplateLocation);
201
+ const nestedLambdas = await this.parseLambdasFromTemplate(nestedTemplatePath, resourceName);
202
+ lambdas.push(...nestedLambdas);
203
+ }
204
+ }
205
+ }
206
+ Logger.verbose(JSON.stringify(lambdas, null, 2));
207
+ return lambdas;
208
+ }
167
209
  }
168
210
  export const samFramework = new SamFramework();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lambda-live-debugger",
3
- "version": "1.10.5",
3
+ "version": "1.11.0",
4
4
  "type": "module",
5
5
  "description": "Debug Lambda functions locally like it is running in the cloud",
6
6
  "repository": {
@@ -70,6 +70,8 @@
70
70
  "test-osls-esbuild-esm-observable": "npm run build && RUN_TEST_FROM_CLI=true OBSERVABLE_MODE=true vitest run test/osls-esbuild-esm.test.ts",
71
71
  "test-sam-basic": "npm run build && RUN_TEST_FROM_CLI=true vitest run test/sam-basic.test.ts",
72
72
  "test-sam-basic-observable": "npm run build && RUN_TEST_FROM_CLI=true OBSERVABLE_MODE=true vitest run test/sam-basic.test.ts",
73
+ "test-sam-nested": "npm run build && RUN_TEST_FROM_CLI=true vitest run test/sam-nested.test.ts",
74
+ "test-sam-nested-observable": "npm run build && RUN_TEST_FROM_CLI=true OBSERVABLE_MODE=true vitest run test/sam-nested.test.ts",
73
75
  "test-sam-alt": "npm run build && RUN_TEST_FROM_CLI=true vitest run test/sam-alt.test.ts",
74
76
  "test-sam-alt-observable": "npm run build && RUN_TEST_FROM_CLI=true OBSERVABLE_MODE=true vitest run test/sam-alt.test.ts",
75
77
  "test-terraform-basic": "npm run build && RUN_TEST_FROM_CLI=true vitest run test/terraform-basic.test.ts",
@@ -161,6 +163,7 @@
161
163
  "test/osls-esbuild",
162
164
  "test/osls-esbuild-cjs",
163
165
  "test/sam-basic",
166
+ "test/sam-nested",
164
167
  "test/sam-alt",
165
168
  "test/terraform-basic",
166
169
  "test/opentofu-basic"