ESLint plugin for AWS Lambda durable functions best practices
npm install @aws/durable-execution-sdk-js-eslint-pluginESLint plugin for AWS Durable Execution SDK best practices.
``bash`
npm install --save-dev @aws/durable-execution-sdk-js-eslint-plugin
Add the plugin to your ESLint configuration:
`json`
{
"plugins": ["@aws/durable-execution-sdk-js-eslint-plugin"],
"rules": {
"@aws/durable-execution-sdk-js-eslint-plugin/no-nested-durable-operations": "error",
"@aws/durable-execution-sdk-js-eslint-plugin/no-closure-in-durable-operations": "error"
}
}
Or use the recommended configuration:
`json`
{
"extends": ["plugin:@aws/durable-execution-sdk-js-eslint-plugin/recommended"]
}
Prevents using the same context object inside its own durable operation.
#### ❌ Incorrect
`javascript
context.step("step1", async () => {
// Error: Cannot use the same context object inside its own operation
await context.wait(1000);
const result = await context.step("step2", async () => {
return "nested step";
});
return result;
});
`
#### ✅ Correct
`javascript
// Use runInChildContext for nested operations
const result = await context.runInChildContext("block1", async (childCtx) => {
await childCtx.wait(1000);
const result = await childCtx.step("step2", async () => {
return "nested step";
});
return result;
});
`
Prevents modifying closure variables inside durable operations. Reading closure variables is allowed.
#### ❌ Incorrect
`javascript`
const handler = withDurableExecution(
async (event: any, context: DurableContext) => {
let a = 2;
const result = await context.runInChildContext(
async (childContext: DurableContext) => {
const stepResult = await childContext.step(async () => {
// Error: Modifying 'a' from outer scope causes replay inconsistency
a = a + 1;
return "child step completed";
});
return stepResult;
},
);
return result;
},
);
#### ✅ Correct
`javascript
const handler = withDurableExecution(
async (event: any, context: DurableContext) => {
let a = 2;
const result = await context.runInChildContext(
async (childContext: DurableContext) => {
const stepResult = await childContext.step(async () => {
// Reading 'a' is OK
const value = a + 1;
return value;
});
return stepResult;
},
);
return result;
},
);
// Or use local variables
const handler = withDurableExecution(
async (event: any, context: DurableContext) => {
const result = await context.runInChildContext(
async (childContext: DurableContext) => {
const stepResult = await childContext.step(async () => {
let a = 2;
a = a + 1;
return "child step completed";
});
return stepResult;
},
);
return result;
},
);
`
The plugin detects these durable operations:
- steprunInChildContext
- waitForCondition
- waitForCallback
- wait
- parallel
- map`
-
Nesting durable operations with the same context object can cause runtime errors and unexpected behavior in AWS Lambda durable functions. This rule helps catch these issues at development time.
During replay, durable functions skip already-executed steps. If a closure variable is modified inside a step, the modification won't occur during replay, causing different outcomes between initial execution and replay. This leads to non-deterministic behavior and potential bugs.
Reading closure variables is safe because it doesn't change state. Only mutations (assignments, increments, decrements) are problematic.
Apache-2.0