lambda-live-debugger 1.0.4 → 1.0.6

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/README.md CHANGED
@@ -109,6 +109,8 @@ The configuration is saved to `lldebugger.config.ts`.
109
109
  -o, --observable Observable mode
110
110
  -i --interval <interval> Observable mode interval (default: "3000")
111
111
  --config-env <evironment> SAM environment
112
+ --sam-config-file <file> SAM configuration file
113
+ --sam-template-file <file> SAM template file
112
114
  --profile <profile> AWS profile to use
113
115
  --region <region> AWS region to use
114
116
  --role <role> AWS role to use
@@ -322,6 +324,7 @@ If you have a new feature idea, please create and issue.
322
324
 
323
325
  (alphabetical)
324
326
 
327
+ - [Roger Chi](https://rogerchi.com/)
325
328
  - ⭐ Your name here for notable code or documentation contributions or sample projects submitted with a bug report that resulted in tool improvement.
326
329
 
327
330
  ## Disclaimer
@@ -21,6 +21,8 @@ export async function getConfigFromCliArgs(supportedFrameworks = []) {
21
21
  program.option('-o, --observable', 'Observable mode');
22
22
  program.option('-i --interval <interval>', 'Observable mode interval', defaultObservableInterval.toString());
23
23
  program.option('--config-env <evironment>', 'SAM environment');
24
+ program.option('--sam-config-file <file>', 'SAM configuration file');
25
+ program.option('--sam-template-file <file>', 'SAM template file');
24
26
  program.option('--profile <profile>', 'AWS profile to use');
25
27
  program.option('--region <region>', 'AWS region to use');
26
28
  program.option('--role <role>', 'AWS role to use');
@@ -86,6 +86,18 @@ export async function getConfigFromWizard({ configFromCliArgs, supportedFramewor
86
86
  message: 'Would you like to enter SAM environment?',
87
87
  default: configFromCliArgs.configEnv ?? currentConfig?.configEnv,
88
88
  },
89
+ {
90
+ type: 'input',
91
+ name: 'samConfigFile',
92
+ message: 'Would you like to enter SAM configuration file (default = samconfig.toml)?',
93
+ default: configFromCliArgs.samConfigFile ?? currentConfig?.samConfigFile,
94
+ },
95
+ {
96
+ type: 'input',
97
+ name: 'samTemplateFile',
98
+ message: 'Would you like to enter SAM template file (default = template.yaml)?',
99
+ default: configFromCliArgs.samTemplateFile ?? currentConfig?.samTemplateFile,
100
+ },
89
101
  ]);
90
102
  answers = { ...answers, ...samAnswers };
91
103
  }
@@ -108,9 +120,9 @@ export async function getConfigFromWizard({ configFromCliArgs, supportedFramewor
108
120
  type: 'confirm',
109
121
  name: 'observable',
110
122
  message: 'Do you want to use observable mode, which just sends events to the debugger and do not use the respose?',
111
- default: configFromCliArgs.observable !== undefined
123
+ default: !!(configFromCliArgs.observable !== undefined
112
124
  ? configFromCliArgs.observable
113
- : currentConfig?.observable,
125
+ : currentConfig?.observable),
114
126
  },
115
127
  ]);
116
128
  answers = { ...answers, ...answersObservable };
@@ -273,7 +285,7 @@ import { type LldConfigTs } from "lambda-live-debugger";
273
285
  export default {
274
286
  // Framework to use
275
287
  framework: "${config.framework}",
276
- // AWS CDK context
288
+ // AWS CDK framework context
277
289
  context: ${config.context ? JSON.stringify(config.context) : undefined},
278
290
  // Serverless Framework stage
279
291
  stage: "${config.stage}",
@@ -287,8 +299,12 @@ export default {
287
299
  region: "${config.region}",
288
300
  // AWS role
289
301
  role: "${config.role}",
290
- // SAM environment
302
+ // SAM framework environment
291
303
  configEnv: "${config.configEnv}",
304
+ // SAM framework configuration file
305
+ samConfigFile: "${config.samConfigFile}",
306
+ // SAM framework template file
307
+ samTemplateFile: "${config.samTemplateFile}",
292
308
  // Observable mode
293
309
  observable: ${config.observable},
294
310
  // Observable mode interval
@@ -327,6 +343,8 @@ function getConfigFromAnswers(answers) {
327
343
  region: answers.region,
328
344
  role: answers.role,
329
345
  configEnv: answers.configEnv,
346
+ samConfigFile: answers.samConfigFile,
347
+ samTemplateFile: answers.samTemplateFile,
330
348
  observable: answers.observable,
331
349
  interval: answers.interval !== undefined
332
350
  ? answers.interval
@@ -17,7 +17,7 @@ async function readConfig() {
17
17
  const supportedFrameworks = ResourceDiscovery.getSupportedFrameworksNames();
18
18
  const configFromCliArgs = await getConfigFromCliArgs(supportedFrameworks);
19
19
  Configuration.setConfig(configFromCliArgs); // not complete config
20
- const currentFramework = await ResourceDiscovery.getCurrentFrameworkName();
20
+ const currentFramework = await ResourceDiscovery.getCurrentFrameworkName(configFromCliArgs);
21
21
  Logger.setVerbose(configFromCliArgs.verbose === true);
22
22
  const configFileName = configFromCliArgs.config || configFileDefaultName;
23
23
  const configFromConfigFile = (await getConfigTsFromConfigFile(configFileName))
Binary file
@@ -142,21 +142,64 @@ export class CdkFramework {
142
142
  */
143
143
  async getLambdasDataFromCdkByCompilingAndRunning(cdkConfigPath, config) {
144
144
  const entryFile = await this.getCdkEntryFile(cdkConfigPath);
145
- // Define a plugin to prepend custom code to .ts or .tsx files
145
+ let isESM = false;
146
+ const packageJsonPath = await findPackageJson(entryFile);
147
+ if (packageJsonPath) {
148
+ try {
149
+ const packageJson = JSON.parse(await fs.readFile(packageJsonPath, { encoding: 'utf-8' }));
150
+ if (packageJson.type === 'module') {
151
+ isESM = true;
152
+ Logger.verbose(`[CDK] Using ESM format`);
153
+ }
154
+ }
155
+ catch (err) {
156
+ Logger.error(`Error reading CDK package.json (${packageJsonPath}): ${err.message}`, err);
157
+ }
158
+ }
159
+ const rootDir = process.cwd();
160
+ // Plugin that:
161
+ // - Fixes __dirname issues
162
+ // - Injects code to get the file path of the Lambda function and CDK hierarchy
146
163
  const injectCodePlugin = {
147
164
  name: 'injectCode',
148
165
  setup(build) {
149
166
  build.onLoad({ filter: /.*/ }, async (args) => {
150
- const absolutePath = path.resolve(args.path);
151
- let source = await fs.readFile(absolutePath, 'utf8');
167
+ // fix __dirname issues
168
+ const isWindows = /^win/.test(process.platform);
169
+ const esc = (p) => (isWindows ? p.replace(/\\/g, '/') : p);
170
+ const variables = `
171
+ const __fileloc = {
172
+ filename: "${esc(args.path)}",
173
+ dirname: "${esc(path.dirname(args.path))}",
174
+ relativefilename: "${esc(path.relative(rootDir, args.path))}",
175
+ relativedirname: "${esc(path.relative(rootDir, path.dirname(args.path)))}",
176
+ import: { meta: { url: "file://${esc(args.path)}" } }
177
+ };
178
+ `;
179
+ let fileContent = new TextDecoder().decode(await fs.readFile(args.path));
180
+ // remove shebang
181
+ if (fileContent.startsWith('#!')) {
182
+ const firstNewLine = fileContent.indexOf('\n');
183
+ fileContent = fileContent.slice(firstNewLine + 1);
184
+ }
185
+ let contents;
186
+ if (args.path.endsWith('.ts') || args.path.endsWith('.js')) {
187
+ // add the variables at the top of the file, that contains the file location
188
+ contents = `${variables}\n${fileContent}`;
189
+ }
190
+ else {
191
+ contents = fileContent;
192
+ }
193
+ const loader = args.path.split('.').pop();
194
+ // Inject code to get the file path of the Lambda function and CDK hierarchy
152
195
  if (args.path.includes('aws-cdk-lib/aws-lambda/lib/function.')) {
153
196
  const codeToFind = 'try{jsiiDeprecationWarnings().aws_cdk_lib_aws_lambda_FunctionProps(props)}';
154
- if (!source.includes(codeToFind)) {
197
+ if (!contents.includes(codeToFind)) {
155
198
  throw new Error(`Can not find code to inject in ${args.path}`);
156
199
  }
157
200
  // Inject code to get the file path of the Lambda function and CDK hierarchy
158
201
  // path to match it with the Lambda function. Store data in the global variable.
159
- source = source.replace(codeToFind, `;
202
+ contents = contents.replace(codeToFind, `;
160
203
  global.lambdas = global.lambdas ?? [];
161
204
 
162
205
  const lambdaInfo = {
@@ -180,36 +223,20 @@ export class CdkFramework {
180
223
  }
181
224
  if (args.path.includes('aws-cdk-lib/aws-s3-deployment/lib/bucket-deployment.')) {
182
225
  const codeToFind = 'super(scope,id),this.requestDestinationArn=!1;';
183
- if (!source.includes(codeToFind)) {
226
+ if (!contents.includes(codeToFind)) {
184
227
  throw new Error(`Can not find code to inject in ${args.path}`);
185
228
  }
186
229
  // Inject code to prevent deploying the assets
187
- source = source.replace(codeToFind, codeToFind + `return;`);
230
+ contents = contents.replace(codeToFind, codeToFind + `return;`);
188
231
  }
189
232
  return {
190
- contents: source,
191
- loader: 'default',
233
+ contents,
234
+ loader,
192
235
  };
193
236
  });
194
237
  },
195
238
  };
196
- let isESM = false;
197
- // get packgage.json
198
- const packageJsonPath = await findPackageJson(entryFile);
199
- if (packageJsonPath) {
200
- try {
201
- const packageJson = JSON.parse(await fs.readFile(packageJsonPath, { encoding: 'utf-8' }));
202
- if (packageJson.type === 'module') {
203
- isESM = true;
204
- Logger.verbose(`[CDK] Using ESM format`);
205
- }
206
- }
207
- catch (err) {
208
- Logger.error(`Error reading CDK package.json (${packageJsonPath}): ${err.message}`, err);
209
- }
210
- }
211
239
  const compileOutput = path.join(getProjectDirname(), outputFolder, `compiledCdk.${isESM ? 'mjs' : 'cjs'}`);
212
- const dirname = path.join(...[getProjectDirname(), config.subfolder, 'x'].filter((p) => p));
213
240
  try {
214
241
  // Build CDK code
215
242
  await esbuild.build({
@@ -228,7 +255,6 @@ export class CdkFramework {
228
255
  banner: {
229
256
  js: [
230
257
  `import { createRequire as topLevelCreateRequire } from 'module';`,
231
- `import.meta.url = 'file:///${dirname}/cdkFrameworkWorker.mjs';`,
232
258
  `global.require = global.require ?? topLevelCreateRequire(import.meta.url);`,
233
259
  `import { fileURLToPath as topLevelFileUrlToPath, URL as topLevelURL } from "url"`,
234
260
  `global.__dirname = global.__dirname ?? topLevelFileUrlToPath(new topLevelURL(".", import.meta.url))`,
@@ -238,10 +264,15 @@ export class CdkFramework {
238
264
  : {
239
265
  format: 'cjs',
240
266
  target: 'node18',
241
- banner: {
242
- js: [`__dirname = '${dirname}';`].join('\n'),
243
- },
244
267
  }),
268
+ define: {
269
+ // replace __dirname,... with the a variable that contains the file location
270
+ __filename: '__fileloc.filename',
271
+ __dirname: '__fileloc.dirname',
272
+ __relativefilename: '__fileloc.relativefilename',
273
+ __relativedirname: '__fileloc.relativedirname',
274
+ 'import.meta.url': '__fileloc.import.meta.url',
275
+ },
245
276
  });
246
277
  }
247
278
  catch (error) {
@@ -1,12 +1,5 @@
1
1
  // @ts-nocheck
2
- import { createRequire as topLevelCreateRequire } from 'module';
3
- const require = topLevelCreateRequire(import.meta.url);
4
- import path from 'path';
5
-
6
2
  import { workerData, parentPort } from 'node:worker_threads';
7
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
8
- import fs from 'fs/promises'; // do not delete this line
9
-
10
3
  import { Logger } from '../logger.mjs';
11
4
 
12
5
  Logger.setVerbose(workerData.verbose);
@@ -21,7 +14,6 @@ parentPort.on('message', async (data) => {
21
14
  Logger.verbose(`[Worker ${workerData.workerId}] Received message`, data);
22
15
 
23
16
  // execute code to get the data into global.lambdas
24
- await fixCdkPaths(workerData.awsCdkLibPath);
25
17
  await import(data.compileOutput);
26
18
 
27
19
  if (!global.lambdas || global.lambdas?.length === 0) {
@@ -43,7 +35,7 @@ parentPort.on('message', async (data) => {
43
35
  }));
44
36
 
45
37
  Logger.verbose(
46
- `[CDK] [Worker] Sending found lambdas`,
38
+ `[CDK] [Worker] Sending found Lambdas`,
47
39
  JSON.stringify(lambdas, null, 2),
48
40
  );
49
41
  parentPort.postMessage(lambdas);
@@ -53,91 +45,6 @@ parentPort.on('message', async (data) => {
53
45
  }
54
46
  });
55
47
 
56
- /**
57
- * Some paths are not resolved correctly in the CDK code, so we need to fix them
58
- */
59
- async function fixCdkPaths(awsCdkLibPath) {
60
- // leave this lines for manual debugging
61
- //const awsCdkLibPath = path.resolve("node_modules/aws-cdk-lib");
62
- //const path = require("path");
63
-
64
- Logger.verbose(`[CDK] [Worker] aws-cdk-lib PATH ${awsCdkLibPath}`);
65
-
66
- const pathsFix = {
67
- 'custom-resource-handlers/': `${awsCdkLibPath}/custom-resource-handlers/`,
68
- 'aws-custom-resource-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/custom-resources/aws-custom-resource-handler`,
69
- 'auto-delete-objects-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-s3/auto-delete-objects-handler`,
70
- 'notifications-resource-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-s3/notifications-resource-handler`,
71
- 'drop-spam-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-ses/drop-spam-handler`,
72
- 'aws-stepfunctions-tasks/role-policy-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-stepfunctions-tasks/role-policy-handler`,
73
- 'eval-nodejs-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-stepfunctions-tasks/eval-nodejs-handler`,
74
- 'cross-account-zone-delegation-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-route53/cross-account-zone-delegation-handler`,
75
- 'delete-existing-record-set-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-route53/delete-existing-record-set-handler`,
76
- 'aws-api-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-events-targets/aws-api-handler`,
77
- 'log-retention-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-logs/log-retention-handler`,
78
- 'cluster-resource-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-eks/cluster-resource-handler`,
79
- 'auto-delete-images-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-ecr/auto-delete-images-handler`,
80
- 'bucket-deployment-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-s3-deployment/bucket-deployment-handler`,
81
- 'nodejs-entrypoint-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/core/nodejs-entrypoint-handler`,
82
- 'restrict-default-security-group-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-ec2/restrict-default-security-group-handler`,
83
- 'dns-validated-certificate-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-certificatemanager/dns-validated-certificate-handler`,
84
- 'auto-delete-underlying-resources-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-synthetics/auto-delete-underlying-resources-handler`,
85
- 'replica-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-dynamodb/replica-handler`,
86
- 'oidc-handler': `${awsCdkLibPath}/custom-resource-handlers/dist/aws-iam/oidc-handler`,
87
- };
88
-
89
- // Create a proxy to intercept calls to the path module so we can fix paths
90
- const pathProxy = new Proxy(path, {
91
- get(target, prop) {
92
- if (typeof target[prop] === 'function') {
93
- return function (...args) {
94
- if (prop === 'resolve') {
95
- let resolvedPath = target[prop].apply(target, args);
96
-
97
- for (const [key, value] of Object.entries(pathsFix)) {
98
- if (resolvedPath.includes(key)) {
99
- // replace the beginning of the path with the value
100
- const i = resolvedPath.indexOf(key);
101
- const newResolvedPath = `${value}${resolvedPath.substring(i + key.length)}`;
102
- Logger.verbose(
103
- `[CDK] [Worker] Fixing path ${resolvedPath} -> ${newResolvedPath}`,
104
- );
105
- resolvedPath = newResolvedPath;
106
- }
107
- }
108
-
109
- return resolvedPath;
110
- }
111
- if (prop === 'join') {
112
- let resolvedPath = target[prop].apply(target, args);
113
-
114
- for (const [key, value] of Object.entries(pathsFix)) {
115
- if (resolvedPath.includes(key)) {
116
- // replace the beginning of the path with the value
117
- const i = resolvedPath.indexOf(key);
118
- const newResolvedPath = `${value}${resolvedPath.substring(i + key.length)}`;
119
- Logger.verbose(
120
- `[CDK] [Worker] Fixing path ${resolvedPath} -> ${newResolvedPath}`,
121
- );
122
- resolvedPath = newResolvedPath;
123
- }
124
- }
125
-
126
- return resolvedPath;
127
- }
128
- return target[prop].apply(target, args);
129
- };
130
- }
131
- return target[prop];
132
- },
133
- });
134
-
135
- // Override the path module in the require cache
136
- require.cache[require.resolve('path')] = {
137
- exports: pathProxy,
138
- };
139
- }
140
-
141
48
  process.on('unhandledRejection', (error) => {
142
49
  Logger.error(`[CDK] [Worker] Unhandled Rejection`, error);
143
50
  });
@@ -12,7 +12,7 @@ export interface IFramework {
12
12
  * Can this class handle the current project
13
13
  * @returns
14
14
  */
15
- canHandle(): Promise<boolean>;
15
+ canHandle(config: LldConfigBase): Promise<boolean>;
16
16
  /**
17
17
  * Get Lambda functions
18
18
  * @param config
@@ -5,8 +5,6 @@ import { LldConfigBase } from '../types/lldConfig.js';
5
5
  * Support for AWS SAM framework
6
6
  */
7
7
  export declare class SamFramework implements IFramework {
8
- protected samConfigFile: string;
9
- protected samTemplateFile: string;
10
8
  /**
11
9
  * Framework name
12
10
  */
@@ -15,7 +13,13 @@ export declare class SamFramework implements IFramework {
15
13
  * Can this class handle the current project
16
14
  * @returns
17
15
  */
18
- canHandle(): Promise<boolean>;
16
+ canHandle(config: LldConfigBase): Promise<boolean>;
17
+ /**
18
+ * Get configuration files
19
+ * @param config Configuration
20
+ * @returns Configuration files
21
+ */
22
+ private getConfigFiles;
19
23
  /**
20
24
  * Get Lambda functions
21
25
  * @param config Configuration
@@ -10,8 +10,6 @@ import { Logger } from '../logger.mjs';
10
10
  * Support for AWS SAM framework
11
11
  */
12
12
  export class SamFramework {
13
- samConfigFile = 'samconfig.toml';
14
- samTemplateFile = 'template.yaml';
15
13
  /**
16
14
  * Framework name
17
15
  */
@@ -22,23 +20,37 @@ export class SamFramework {
22
20
  * Can this class handle the current project
23
21
  * @returns
24
22
  */
25
- async canHandle() {
23
+ async canHandle(config) {
24
+ const { samConfigFile, samTemplateFile } = this.getConfigFiles(config);
26
25
  try {
27
- await fs.access(path.resolve(this.samConfigFile), constants.F_OK);
26
+ await fs.access(samConfigFile, constants.F_OK);
28
27
  }
29
28
  catch {
30
- Logger.verbose(`[SAM] This is not a SAM framework project. ${path.resolve(this.samConfigFile)} not found.`);
29
+ Logger.verbose(`[SAM] This is not a SAM framework project. ${samConfigFile} not found.`);
31
30
  return false;
32
31
  }
33
32
  try {
34
- await fs.access(path.resolve(this.samTemplateFile), constants.F_OK);
33
+ await fs.access(samTemplateFile, constants.F_OK);
35
34
  }
36
35
  catch {
37
- Logger.verbose(`[SAM] This is not a SAM framework project. ${path.resolve(this.samTemplateFile)} not found.`);
36
+ Logger.verbose(`[SAM] This is not a SAM framework project. ${samTemplateFile} not found.`);
38
37
  return false;
39
38
  }
40
39
  return true;
41
40
  }
41
+ /**
42
+ * Get configuration files
43
+ * @param config Configuration
44
+ * @returns Configuration files
45
+ */
46
+ getConfigFiles(config) {
47
+ const samConfigFile = config.samConfigFile ?? 'samconfig.toml';
48
+ const samTemplateFile = config.samTemplateFile ?? 'template.yaml';
49
+ return {
50
+ samConfigFile: path.resolve(samConfigFile),
51
+ samTemplateFile: path.resolve(samTemplateFile),
52
+ };
53
+ }
42
54
  /**
43
55
  * Get Lambda functions
44
56
  * @param config Configuration
@@ -50,14 +62,22 @@ export class SamFramework {
50
62
  profile: config.profile,
51
63
  role: config.role,
52
64
  };
65
+ const { samConfigFile, samTemplateFile } = this.getConfigFiles(config);
53
66
  const environment = config.configEnv ?? 'default';
54
- const samConfigContent = await fs.readFile(path.resolve(this.samConfigFile), 'utf-8');
55
- const samConfig = toml.parse(samConfigContent);
67
+ const samConfigContent = await fs.readFile(samConfigFile, 'utf-8');
68
+ let samConfig;
69
+ // is toml extension
70
+ if (samConfigFile.endsWith('.toml')) {
71
+ samConfig = toml.parse(samConfigContent);
72
+ }
73
+ else {
74
+ samConfig = yaml.parse(samConfigContent);
75
+ }
56
76
  const stackName = samConfig[environment]?.global?.parameters?.stack_name;
57
77
  if (!stackName) {
58
- throw new Error(`Stack name not found in ${path.resolve(this.samConfigFile)}`);
78
+ throw new Error(`Stack name not found in ${samConfigFile}`);
59
79
  }
60
- const samTemplateContent = await fs.readFile(path.resolve(this.samTemplateFile), 'utf-8');
80
+ const samTemplateContent = await fs.readFile(path.resolve(samTemplateFile), 'utf-8');
61
81
  const template = yaml.parse(samTemplateContent);
62
82
  const lambdas = [];
63
83
  // get all resources of type AWS::Serverless::Function
@@ -22,23 +22,31 @@ async function runInWorker(input) {
22
22
  environment: input.environment,
23
23
  verbose: Configuration.config.verbose,
24
24
  });
25
+ worker.used = false;
26
+ worker.toKill = false;
25
27
  }
26
28
  else {
27
29
  Logger.verbose(`[Function ${input.fuctionRequest.functionId}] [Worker ${input.fuctionRequest.workerId}] Reusing worker`);
28
30
  }
29
- worker.on('message', (msg) => {
31
+ worker.onMessage = (msg) => {
30
32
  Logger.verbose(`[Function ${input.fuctionRequest.functionId}] [Worker ${input.fuctionRequest.workerId}] Worker message`, JSON.stringify(msg));
33
+ worker.used = false;
31
34
  if (msg?.errorType) {
32
35
  reject(msg);
33
36
  }
34
37
  else {
35
38
  resolve(msg);
36
39
  }
37
- });
38
- worker.on('error', (err) => {
40
+ if (worker.toKill) {
41
+ worker.toKill = false;
42
+ void worker.terminate();
43
+ }
44
+ };
45
+ worker.onError = (err) => {
39
46
  Logger.error(`[Function ${input.fuctionRequest.functionId}] [Worker ${input.fuctionRequest.workerId}] Error`, err);
40
47
  reject(err);
41
- });
48
+ };
49
+ worker.used = true;
42
50
  worker.postMessage({
43
51
  env: input.fuctionRequest.env,
44
52
  event: input.fuctionRequest.event,
@@ -78,6 +86,12 @@ function startWorker(input) {
78
86
  workers.delete(input.workerId);
79
87
  });
80
88
  workers.set(input.workerId, worker);
89
+ worker.on('message', (msg) => {
90
+ worker?.onMessage?.(msg);
91
+ });
92
+ worker.on('error', (err) => {
93
+ worker?.onError?.(err);
94
+ });
81
95
  return worker;
82
96
  }
83
97
  /**
@@ -87,7 +101,19 @@ async function stopAllWorkers() {
87
101
  Logger.verbose('Stopping all workers');
88
102
  const promises = [];
89
103
  for (const worker of workers.values()) {
90
- promises.push(worker.terminate());
104
+ if (worker.used) {
105
+ worker.toKill = true;
106
+ // set timout for 5 minutes and kill the worker if it is still running
107
+ setTimeout(() => {
108
+ if (worker.toKill) {
109
+ worker.toKill = false;
110
+ void worker.terminate();
111
+ }
112
+ }, 300000);
113
+ }
114
+ else {
115
+ promises.push(worker.terminate());
116
+ }
91
117
  }
92
118
  workers.clear();
93
119
  await Promise.all(promises);
@@ -13,16 +13,16 @@ Logger.verbose(
13
13
 
14
14
  parentPort.on('message', async (data) => {
15
15
  Logger.verbose(`[Worker ${workerData.workerId}] Received message`, data);
16
- const mod = await import(workerData.artifactFile);
17
- const fn = mod[workerData.handler];
16
+ try {
17
+ const mod = await import(workerData.artifactFile);
18
+ const fn = mod[workerData.handler];
18
19
 
19
- if (!fn) {
20
- throw new Error(
21
- `Handler '${workerData.handler}' not found for function '${workerData.functionId}'`,
22
- );
23
- }
20
+ if (!fn) {
21
+ throw new Error(
22
+ `Handler '${workerData.handler}' not found for function '${workerData.functionId}'`,
23
+ );
24
+ }
24
25
 
25
- try {
26
26
  const context = {
27
27
  ...data.context,
28
28
  getRemainingTimeInMillis: () => 2147483647, // Max 32-bit signed integer
@@ -6,7 +6,7 @@ declare function getSupportedFrameworksNames(): string[];
6
6
  /**
7
7
  * Get the name of the current framework
8
8
  */
9
- declare function getCurrentFrameworkName(): Promise<string | undefined>;
9
+ declare function getCurrentFrameworkName(config: LldConfig): Promise<string | undefined>;
10
10
  declare function getLambdas(config: LldConfig): Promise<{
11
11
  codePath: string;
12
12
  functionName: string;
@@ -22,8 +22,8 @@ function getSupportedFrameworksNames() {
22
22
  /**
23
23
  * Get the name of the current framework
24
24
  */
25
- async function getCurrentFrameworkName() {
26
- const framework = await getCurrentFramework(frameworksSupported);
25
+ async function getCurrentFrameworkName(config) {
26
+ const framework = await getCurrentFramework(frameworksSupported, config);
27
27
  return framework?.name;
28
28
  }
29
29
  async function getLambdas(config) {
@@ -37,7 +37,7 @@ async function getLambdas(config) {
37
37
  frameworks = frameworks.filter((f) => f.name === config.framework);
38
38
  }
39
39
  }
40
- const framework = await getCurrentFramework(frameworks);
40
+ const framework = await getCurrentFramework(frameworks, config);
41
41
  if (framework) {
42
42
  Logger.verbose(`Getting resources with '${framework.name}' framework`);
43
43
  resources = await framework.getLambdas(config);
@@ -61,10 +61,10 @@ async function getLambdas(config) {
61
61
  /**
62
62
  * Get the current framework
63
63
  */
64
- async function getCurrentFramework(frameworks) {
64
+ async function getCurrentFramework(frameworks, config) {
65
65
  let framework;
66
66
  for (const f of frameworks) {
67
- if (await f.canHandle()) {
67
+ if (await f.canHandle(config)) {
68
68
  framework = f;
69
69
  break;
70
70
  }
@@ -18,10 +18,6 @@ export type LldConfigBase = {
18
18
  * Filter by function name
19
19
  */
20
20
  function?: string;
21
- /**
22
- * SAM environment
23
- */
24
- configEnv?: string;
25
21
  /**
26
22
  * Observable mode
27
23
  * @default false
@@ -40,6 +36,18 @@ export type LldConfigBase = {
40
36
  * Start debugger
41
37
  */
42
38
  start?: boolean;
39
+ /**
40
+ * SAM framework cli parameter config-env
41
+ */
42
+ configEnv?: string;
43
+ /**
44
+ * SAM framework cli parameter config-file
45
+ */
46
+ samConfigFile?: string;
47
+ /**
48
+ * SAM framework cli parameter template-file
49
+ */
50
+ samTemplateFile?: string;
43
51
  /**
44
52
  * Resources discovery function
45
53
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lambda-live-debugger",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "type": "module",
5
5
  "description": "Debug Lambda functions locally like it is running in the cloud",
6
6
  "repository": {
@@ -59,6 +59,8 @@
59
59
  "test-sls-esbuild-esm-observable": "npm run build && RUN_TEST_FROM_CLI=true OBSERVABLE_MODE=true vitest run test/sls-esbuild-esm.test.ts",
60
60
  "test-sam-basic": "npm run build && RUN_TEST_FROM_CLI=true vitest run test/sam-basic.test.ts",
61
61
  "test-sam-basic-observable": "npm run build && RUN_TEST_FROM_CLI=true OBSERVABLE_MODE=true vitest run test/sam-basic.test.ts",
62
+ "test-sam-alt": "npm run build && RUN_TEST_FROM_CLI=true vitest run test/sam-alt.test.ts",
63
+ "test-sam-alt-observable": "npm run build && RUN_TEST_FROM_CLI=true OBSERVABLE_MODE=true vitest run test/sam-alt.test.ts",
62
64
  "test-terraform-basic": "npm run build && RUN_TEST_FROM_CLI=true vitest run test/terraform-basic.test.ts",
63
65
  "test-terraform-basic-observable": "npm run build && RUN_TEST_FROM_CLI=true OBSERVABLE_MODE=true vitest run test/terraform-basic.test.ts",
64
66
  "docs:dev": "vitepress dev",
@@ -137,6 +139,7 @@
137
139
  "test/sls-basic",
138
140
  "test/sls-esbuild",
139
141
  "test/sam-basic",
142
+ "test/sam-alt",
140
143
  "test/terraform-basic"
141
144
  ]
142
145
  }