lambda-deadline-middleware 1.0.1 → 1.1.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.
- package/README.md +9 -23
- package/dist/context-store.d.ts.map +1 -1
- package/dist/context-store.js +4 -12
- package/dist/context-store.js.map +1 -1
- package/dist/error.d.ts +3 -3
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js.map +1 -1
- package/dist/handler-wrapper.d.ts +4 -3
- package/dist/handler-wrapper.d.ts.map +1 -1
- package/dist/handler-wrapper.js +1 -1
- package/dist/handler-wrapper.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/middleware.d.ts +4 -10
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +39 -53
- package/dist/middleware.js.map +1 -1
- package/dist/types.d.ts +0 -18
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -10
- package/dist/types.js.map +1 -1
- package/package.json +4 -4
- package/src/context-store.ts +5 -19
- package/src/error.ts +3 -3
- package/src/handler-wrapper.ts +7 -8
- package/src/index.ts +2 -9
- package/src/middleware.ts +72 -87
- package/src/types.ts +2 -32
- package/dist/config.d.ts +0 -4
- package/dist/config.d.ts.map +0 -1
- package/dist/config.js +0 -17
- package/dist/config.js.map +0 -1
- package/dist/registration.d.ts +0 -8
- package/dist/registration.d.ts.map +0 -1
- package/dist/registration.js +0 -17
- package/dist/registration.js.map +0 -1
- package/dist/telemetry.d.ts +0 -5
- package/dist/telemetry.d.ts.map +0 -1
- package/dist/telemetry.js +0 -82
- package/dist/telemetry.js.map +0 -1
- package/src/config.ts +0 -18
- package/src/registration.ts +0 -27
- package/src/telemetry.ts +0 -132
package/README.md
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
Zero-dependency AWS SDK v3 middleware that automatically propagates Lambda execution deadlines to outgoing SDK calls via
|
|
7
7
|
`AbortController`-based timeouts.
|
|
8
8
|
|
|
9
|
-
When an AWS SDK call hangs inside a Lambda function, the runtime terminates the process at the configured timeout
|
|
10
|
-
|
|
9
|
+
When an AWS SDK call hangs inside a Lambda function, the runtime terminates the process at the configured timeout
|
|
10
|
+
without throwing an error or giving your code a chance to react. This library prevents that by computing per-request
|
|
11
11
|
deadlines from the Lambda's remaining execution time and aborting requests before the hard timeout fires.
|
|
12
12
|
|
|
13
13
|
## Features
|
|
@@ -17,7 +17,6 @@ deadlines from the Lambda's remaining execution time and aborting requests befor
|
|
|
17
17
|
- Signal composition: preserves caller-provided `AbortSignal` via `AbortSignal.any()`
|
|
18
18
|
- Zero runtime dependencies (`@smithy/types` is compile-time only)
|
|
19
19
|
- Complete no-op when no Lambda context is available
|
|
20
|
-
- Optional OpenTelemetry span events on deadline aborts (detected dynamically)
|
|
21
20
|
- Branded types prevent millisecond/buffer interchange at compile time
|
|
22
21
|
|
|
23
22
|
## Requirements
|
|
@@ -90,7 +89,7 @@ flowchart LR
|
|
|
90
89
|
|
|
91
90
|
### Flush Buffer
|
|
92
91
|
|
|
93
|
-
The flush buffer is subtracted from the remaining Lambda time to leave room for
|
|
92
|
+
The flush buffer is subtracted from the remaining Lambda time to leave room for graceful shutdown and error handling:
|
|
94
93
|
|
|
95
94
|
```typescript
|
|
96
95
|
// Default: 1000ms
|
|
@@ -100,14 +99,6 @@ dynamodb.middlewareStack.use(deadlineMiddleware());
|
|
|
100
99
|
dynamodb.middlewareStack.use(deadlineMiddleware({ flushBufferMs: 500 }));
|
|
101
100
|
```
|
|
102
101
|
|
|
103
|
-
### Telemetry
|
|
104
|
-
|
|
105
|
-
If `@opentelemetry/api` is installed, span events are emitted on deadline aborts. Disable with:
|
|
106
|
-
|
|
107
|
-
```typescript
|
|
108
|
-
dynamodb.middlewareStack.use(deadlineMiddleware({ telemetryEnabled: false }));
|
|
109
|
-
```
|
|
110
|
-
|
|
111
102
|
## Error Handling
|
|
112
103
|
|
|
113
104
|
When remaining time is less than or equal to the flush buffer, the middleware throws `DeadlineExceededError` immediately
|
|
@@ -151,7 +142,7 @@ await dynamodb.send(
|
|
|
151
142
|
|
|
152
143
|
## API Reference
|
|
153
144
|
|
|
154
|
-
### `withLambdaDeadline(handler
|
|
145
|
+
### `withLambdaDeadline(handler)`
|
|
155
146
|
|
|
156
147
|
Wraps a Lambda handler to store the Lambda context in `AsyncLocalStorage`. Required for the middleware to access
|
|
157
148
|
`getRemainingTimeInMillis()`.
|
|
@@ -159,7 +150,6 @@ Wraps a Lambda handler to store the Lambda context in `AsyncLocalStorage`. Requi
|
|
|
159
150
|
```typescript
|
|
160
151
|
function withLambdaDeadline<TEvent, TResult>(
|
|
161
152
|
handler: (event: TEvent, context: LambdaContextLike) => Promise<TResult>,
|
|
162
|
-
options?: DeadlineOptions,
|
|
163
153
|
): (event: TEvent, context: LambdaContextLike) => Promise<TResult>;
|
|
164
154
|
```
|
|
165
155
|
|
|
@@ -193,7 +183,7 @@ function isDeadlineExceeded(error: unknown): error is DeadlineExceededError;
|
|
|
193
183
|
class DeadlineExceededError extends Error {
|
|
194
184
|
readonly name: "DeadlineExceededError";
|
|
195
185
|
readonly deadlineMs: Milliseconds;
|
|
196
|
-
readonly flushBufferMs:
|
|
186
|
+
readonly flushBufferMs: Milliseconds;
|
|
197
187
|
readonly remainingMs: Milliseconds;
|
|
198
188
|
}
|
|
199
189
|
```
|
|
@@ -203,19 +193,15 @@ class DeadlineExceededError extends Error {
|
|
|
203
193
|
```typescript
|
|
204
194
|
interface DeadlineOptions {
|
|
205
195
|
readonly flushBufferMs?: number; // Default: 1000
|
|
206
|
-
readonly telemetryEnabled?: boolean; // Default: true
|
|
207
196
|
}
|
|
208
197
|
```
|
|
209
198
|
|
|
210
199
|
### Types
|
|
211
200
|
|
|
212
|
-
| Type
|
|
213
|
-
|
|
|
214
|
-
| `Milliseconds`
|
|
215
|
-
| `
|
|
216
|
-
| `RequestDeadlineMs` | Branded number for a computed deadline |
|
|
217
|
-
| `DeadlineComputation` | Discriminated union: `"deadline"` \| `"insufficient-time"` \| `"no-context"` |
|
|
218
|
-
| `LambdaContextLike` | Minimal interface: `{ getRemainingTimeInMillis?(): number }` |
|
|
201
|
+
| Type | Description |
|
|
202
|
+
| ------------------- | ------------------------------------------------------------ |
|
|
203
|
+
| `Milliseconds` | Branded number representing a duration in ms |
|
|
204
|
+
| `LambdaContextLike` | Minimal interface: `{ getRemainingTimeInMillis?(): number }` |
|
|
219
205
|
|
|
220
206
|
## Reporting Bugs
|
|
221
207
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"AAOA,iBAAiB,kBAAkB;CACjC;AACF;
|
|
1
|
+
{"mappings":"AAOA,iBAAiB,kBAAkB;CACjC;AACF;AAIA,OAAO,cAAM,MAAO,GAAG,SAAS,sCAAsC,UAAU,MAAI;AAKpF,OAAO,cAAM","names":[],"sources":["src/context-store.ts"],"version":3,"sourcesContent":["// SPDX-FileCopyrightText: 2026 lambda-deadline-middleware contributors\n// SPDX-License-Identifier: MIT\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\n// AsyncLocalStorage propagates the Lambda context through the entire async call chain without parameter threading.\n// SDK middleware executes deep in the Smithy stack where we can't pass the context through function signatures.\nexport interface LambdaContextLike {\n getRemainingTimeInMillis?: () => number;\n}\n\nconst contextStorage = new AsyncLocalStorage<LambdaContextLike>();\n\nexport const run = <T>(context: LambdaContextLike | null | undefined, fn: () => T): T => {\n if (context === null || context === undefined) return fn();\n return contextStorage.run(context, fn);\n};\n\nexport const getRemainingTimeInMillis = (): number | undefined => {\n const store = contextStorage.getStore();\n if (store === undefined) return undefined;\n if (typeof store.getRemainingTimeInMillis !== \"function\") return undefined;\n return store.getRemainingTimeInMillis();\n};\n"]}
|
package/dist/context-store.js
CHANGED
|
@@ -1,23 +1,15 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2026 lambda-deadline-middleware contributors
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
4
|
-
// Sentinel allows AsyncLocalStorage.run() to accept null/undefined context
|
|
5
|
-
// without throwing, while the accessor can distinguish "no context stored"
|
|
6
|
-
// from "context present but missing the method".
|
|
7
|
-
const NO_CONTEXT = Symbol("no-context");
|
|
8
4
|
const contextStorage = new AsyncLocalStorage();
|
|
9
5
|
export const run = (context, fn) => {
|
|
10
|
-
|
|
11
|
-
return contextStorage.run(
|
|
6
|
+
if (context === null || context === undefined) return fn();
|
|
7
|
+
return contextStorage.run(context, fn);
|
|
12
8
|
};
|
|
13
9
|
export const getRemainingTimeInMillis = () => {
|
|
14
10
|
const store = contextStorage.getStore();
|
|
15
|
-
if (store === undefined
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
if (typeof store.getRemainingTimeInMillis !== "function") {
|
|
19
|
-
return undefined;
|
|
20
|
-
}
|
|
11
|
+
if (store === undefined) return undefined;
|
|
12
|
+
if (typeof store.getRemainingTimeInMillis !== "function") return undefined;
|
|
21
13
|
return store.getRemainingTimeInMillis();
|
|
22
14
|
};
|
|
23
15
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":";;AAGA,SAAS,yBAAyB
|
|
1
|
+
{"mappings":";;AAGA,SAAS,yBAAyB;AAQlC,MAAM,iBAAiB,IAAI,kBAAqC;AAEhE,OAAO,MAAM,OAAU,SAA+C,OAAmB;CACvF,IAAI,YAAY,QAAQ,YAAY,WAAW,OAAO,GAAG;CACzD,OAAO,eAAe,IAAI,SAAS,EAAE;AACvC;AAEA,OAAO,MAAM,iCAAqD;CAChE,MAAM,QAAQ,eAAe,SAAS;CACtC,IAAI,UAAU,WAAW,OAAO;CAChC,IAAI,OAAO,MAAM,6BAA6B,YAAY,OAAO;CACjE,OAAO,MAAM,yBAAyB;AACxC","names":[],"sources":["src/context-store.ts"],"version":3,"sourcesContent":["// SPDX-FileCopyrightText: 2026 lambda-deadline-middleware contributors\n// SPDX-License-Identifier: MIT\n\nimport { AsyncLocalStorage } from \"node:async_hooks\";\n\n// AsyncLocalStorage propagates the Lambda context through the entire async call chain without parameter threading.\n// SDK middleware executes deep in the Smithy stack where we can't pass the context through function signatures.\nexport interface LambdaContextLike {\n getRemainingTimeInMillis?: () => number;\n}\n\nconst contextStorage = new AsyncLocalStorage<LambdaContextLike>();\n\nexport const run = <T>(context: LambdaContextLike | null | undefined, fn: () => T): T => {\n if (context === null || context === undefined) return fn();\n return contextStorage.run(context, fn);\n};\n\nexport const getRemainingTimeInMillis = (): number | undefined => {\n const store = contextStorage.getStore();\n if (store === undefined) return undefined;\n if (typeof store.getRemainingTimeInMillis !== \"function\") return undefined;\n return store.getRemainingTimeInMillis();\n};\n"]}
|
package/dist/error.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Milliseconds } from "./types.js";
|
|
2
2
|
interface DeadlineExceededInit {
|
|
3
3
|
readonly deadlineMs: Milliseconds;
|
|
4
|
-
readonly flushBufferMs:
|
|
4
|
+
readonly flushBufferMs: Milliseconds;
|
|
5
5
|
readonly remainingMs: Milliseconds;
|
|
6
6
|
}
|
|
7
7
|
export declare class DeadlineExceededError extends Error {
|
|
8
8
|
override readonly name = "DeadlineExceededError";
|
|
9
9
|
readonly deadlineMs: Milliseconds;
|
|
10
|
-
readonly flushBufferMs:
|
|
10
|
+
readonly flushBufferMs: Milliseconds;
|
|
11
11
|
readonly remainingMs: Milliseconds;
|
|
12
12
|
constructor(init: DeadlineExceededInit);
|
|
13
13
|
}
|
package/dist/error.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"AAGA,cAAc,
|
|
1
|
+
{"mappings":"AAGA,cAAc,oBAAoB;UAExB,qBAAqB;UACpB,YAAY;UACZ,eAAe;UACf,aAAa;AACxB;AAEA,OAAO,cAAM,8BAA8B,MAAM;CAC/C,kBAAkB,OAAO;CACzB,SAAS,YAAY;CACrB,SAAS,eAAe;CACxB,SAAS,aAAa;CAEtB,YAAY,MAAM;AAQpB;AAIA,OAAO,cAAM,qBAAsB,mBAAiB,SAAS","names":[],"sources":["src/error.ts"],"version":3,"sourcesContent":["// SPDX-FileCopyrightText: 2026 lambda-deadline-middleware contributors\n// SPDX-License-Identifier: MIT\n\nimport type { Milliseconds } from \"./types.js\";\n\ninterface DeadlineExceededInit {\n readonly deadlineMs: Milliseconds;\n readonly flushBufferMs: Milliseconds;\n readonly remainingMs: Milliseconds;\n}\n\nexport class DeadlineExceededError extends Error {\n override readonly name = \"DeadlineExceededError\" as const;\n readonly deadlineMs: Milliseconds;\n readonly flushBufferMs: Milliseconds;\n readonly remainingMs: Milliseconds;\n\n constructor(init: DeadlineExceededInit) {\n super(\n `Request deadline exceeded: ${init.deadlineMs}ms deadline (${init.flushBufferMs}ms flush buffer)`,\n );\n this.deadlineMs = init.deadlineMs;\n this.flushBufferMs = init.flushBufferMs;\n this.remainingMs = init.remainingMs;\n }\n}\n\n// Structural check rather than instanceof — works across module boundaries\n// and serialization boundaries where prototype chain may be broken.\nexport const isDeadlineExceeded = (error: unknown): error is DeadlineExceededError => {\n if (error === null || error === undefined) return false;\n if (typeof error !== \"object\") return false;\n return (error as { name?: unknown }).name === \"DeadlineExceededError\";\n};\n"]}
|
package/dist/error.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"AAWA,OAAO,MAAM,8BAA8B,MAAM;CAC/C,AAAkB,OAAO;CACzB,AAAS;CACT,AAAS;CACT,AAAS;CAET,YAAY,MAA4B;EACtC,MACE,8BAA8B,KAAK,WAAW,eAAe,KAAK,cAAc,iBAClF;EACA,KAAK,aAAa,KAAK;EACvB,KAAK,gBAAgB,KAAK;EAC1B,KAAK,cAAc,KAAK;CAC1B;AACF;;;AAIA,OAAO,MAAM,sBAAsB,UAAmD;CACpF,IAAI,UAAU,QAAQ,UAAU,WAAW,OAAO;CAClD,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,OAAQ,MAA6B,SAAS;AAChD","names":[],"sources":["src/error.ts"],"version":3,"sourcesContent":["// SPDX-FileCopyrightText: 2026 lambda-deadline-middleware contributors\n// SPDX-License-Identifier: MIT\n\nimport type {
|
|
1
|
+
{"mappings":"AAWA,OAAO,MAAM,8BAA8B,MAAM;CAC/C,AAAkB,OAAO;CACzB,AAAS;CACT,AAAS;CACT,AAAS;CAET,YAAY,MAA4B;EACtC,MACE,8BAA8B,KAAK,WAAW,eAAe,KAAK,cAAc,iBAClF;EACA,KAAK,aAAa,KAAK;EACvB,KAAK,gBAAgB,KAAK;EAC1B,KAAK,cAAc,KAAK;CAC1B;AACF;;;AAIA,OAAO,MAAM,sBAAsB,UAAmD;CACpF,IAAI,UAAU,QAAQ,UAAU,WAAW,OAAO;CAClD,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,OAAQ,MAA6B,SAAS;AAChD","names":[],"sources":["src/error.ts"],"version":3,"sourcesContent":["// SPDX-FileCopyrightText: 2026 lambda-deadline-middleware contributors\n// SPDX-License-Identifier: MIT\n\nimport type { Milliseconds } from \"./types.js\";\n\ninterface DeadlineExceededInit {\n readonly deadlineMs: Milliseconds;\n readonly flushBufferMs: Milliseconds;\n readonly remainingMs: Milliseconds;\n}\n\nexport class DeadlineExceededError extends Error {\n override readonly name = \"DeadlineExceededError\" as const;\n readonly deadlineMs: Milliseconds;\n readonly flushBufferMs: Milliseconds;\n readonly remainingMs: Milliseconds;\n\n constructor(init: DeadlineExceededInit) {\n super(\n `Request deadline exceeded: ${init.deadlineMs}ms deadline (${init.flushBufferMs}ms flush buffer)`,\n );\n this.deadlineMs = init.deadlineMs;\n this.flushBufferMs = init.flushBufferMs;\n this.remainingMs = init.remainingMs;\n }\n}\n\n// Structural check rather than instanceof — works across module boundaries\n// and serialization boundaries where prototype chain may be broken.\nexport const isDeadlineExceeded = (error: unknown): error is DeadlineExceededError => {\n if (error === null || error === undefined) return false;\n if (typeof error !== \"object\") return false;\n return (error as { name?: unknown }).name === \"DeadlineExceededError\";\n};\n"]}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { LambdaContextLike } from "./context-store.js";
|
|
2
|
-
import type { DeadlineOptions } from "./types.js";
|
|
3
2
|
type AsyncHandler<
|
|
4
3
|
TEvent,
|
|
4
|
+
TContext extends LambdaContextLike,
|
|
5
5
|
TResult
|
|
6
|
-
> = (event: TEvent, context:
|
|
6
|
+
> = (event: TEvent, context: TContext) => Promise<TResult>;
|
|
7
7
|
export declare const withLambdaDeadline: <
|
|
8
8
|
TEvent,
|
|
9
|
+
TContext extends LambdaContextLike,
|
|
9
10
|
TResult
|
|
10
|
-
>(handler: AsyncHandler<TEvent, TResult
|
|
11
|
+
>(handler: AsyncHandler<TEvent, TContext, TResult>) => AsyncHandler<TEvent, TContext, TResult>;
|
|
11
12
|
export {};
|
|
12
13
|
|
|
13
14
|
//# sourceMappingURL=handler-wrapper.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"
|
|
1
|
+
{"mappings":"AAKA,cAAc,yBAAyB;KAElC;CAAa;CAAQ,iBAAiB;CAAmB;KAC5D,OAAO,QACP,SAAS,aACN,QAAQ;AAEb,OAAO,cAAM;CACV;CAAQ,iBAAiB;CAAmB;EAC3C,SAAS,aAAa,QAAQ,UAAU,aACvC,aAAa,QAAQ,UAAU","names":[],"sources":["src/handler-wrapper.ts"],"version":3,"sourcesContent":["// SPDX-FileCopyrightText: 2026 lambda-deadline-middleware contributors\n// SPDX-License-Identifier: MIT\n\nimport { run } from \"./context-store.js\";\n\nimport type { LambdaContextLike } from \"./context-store.js\";\n\ntype AsyncHandler<TEvent, TContext extends LambdaContextLike, TResult> = (\n event: TEvent,\n context: TContext,\n) => Promise<TResult>;\n\nexport const withLambdaDeadline =\n <TEvent, TContext extends LambdaContextLike, TResult>(\n handler: AsyncHandler<TEvent, TContext, TResult>,\n ): AsyncHandler<TEvent, TContext, TResult> =>\n async (event: TEvent, context: TContext): Promise<TResult> =>\n run(context, async () => handler(event, context));\n"]}
|
package/dist/handler-wrapper.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2026 lambda-deadline-middleware contributors
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
import { run } from "./context-store.js";
|
|
4
|
-
export const withLambdaDeadline = (handler
|
|
4
|
+
export const withLambdaDeadline = (handler) => async (event, context) => run(context, async () => handler(event, context));
|
|
5
5
|
|
|
6
6
|
//# sourceMappingURL=handler-wrapper.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":";;AAGA,SAAS,WAAW;AASpB,OAAO,MAAM,sBAET,
|
|
1
|
+
{"mappings":";;AAGA,SAAS,WAAW;AASpB,OAAO,MAAM,sBAET,YAEF,OAAO,OAAe,YACpB,IAAI,SAAS,YAAY,QAAQ,OAAO,OAAO,CAAC","names":[],"sources":["src/handler-wrapper.ts"],"version":3,"sourcesContent":["// SPDX-FileCopyrightText: 2026 lambda-deadline-middleware contributors\n// SPDX-License-Identifier: MIT\n\nimport { run } from \"./context-store.js\";\n\nimport type { LambdaContextLike } from \"./context-store.js\";\n\ntype AsyncHandler<TEvent, TContext extends LambdaContextLike, TResult> = (\n event: TEvent,\n context: TContext,\n) => Promise<TResult>;\n\nexport const withLambdaDeadline =\n <TEvent, TContext extends LambdaContextLike, TResult>(\n handler: AsyncHandler<TEvent, TContext, TResult>,\n ): AsyncHandler<TEvent, TContext, TResult> =>\n async (event: TEvent, context: TContext): Promise<TResult> =>\n run(context, async () => handler(event, context));\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export { withLambdaDeadline } from "./handler-wrapper.js";
|
|
2
|
-
export { deadlineMiddleware } from "./
|
|
2
|
+
export { deadlineMiddleware } from "./middleware.js";
|
|
3
3
|
export { DeadlineExceededError, isDeadlineExceeded } from "./error.js";
|
|
4
4
|
export { getRemainingTimeInMillis } from "./context-store.js";
|
|
5
|
-
export type { Milliseconds,
|
|
5
|
+
export type { Milliseconds, DeadlineOptions } from "./types.js";
|
|
6
6
|
export type { LambdaContextLike } from "./context-store.js";
|
|
7
7
|
|
|
8
8
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"AAGA,SAAS,0BAA0B;AACnC,SAAS,0BAA0B;AACnC,SAAS,uBAAuB,0BAA0B;AAC1D,SAAS,gCAAgC;AAEzC,
|
|
1
|
+
{"mappings":"AAGA,SAAS,0BAA0B;AACnC,SAAS,0BAA0B;AACnC,SAAS,uBAAuB,0BAA0B;AAC1D,SAAS,gCAAgC;AAEzC,cAAc,cAAc,uBAAuB;AAEnD,cAAc,yBAAyB","names":[],"sources":["src/index.ts"],"version":3,"sourcesContent":["// SPDX-FileCopyrightText: 2026 lambda-deadline-middleware contributors\n// SPDX-License-Identifier: MIT\n\nexport { withLambdaDeadline } from \"./handler-wrapper.js\";\nexport { deadlineMiddleware } from \"./middleware.js\";\nexport { DeadlineExceededError, isDeadlineExceeded } from \"./error.js\";\nexport { getRemainingTimeInMillis } from \"./context-store.js\";\n\nexport type { Milliseconds, DeadlineOptions } from \"./types.js\";\n\nexport type { LambdaContextLike } from \"./context-store.js\";\n"]}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// SPDX-FileCopyrightText: 2026 lambda-deadline-middleware contributors
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
export { withLambdaDeadline } from "./handler-wrapper.js";
|
|
4
|
-
export { deadlineMiddleware } from "./
|
|
4
|
+
export { deadlineMiddleware } from "./middleware.js";
|
|
5
5
|
export { DeadlineExceededError, isDeadlineExceeded } from "./error.js";
|
|
6
6
|
export { getRemainingTimeInMillis } from "./context-store.js";
|
|
7
7
|
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":";;AAGA,SAAS,0BAA0B;AACnC,SAAS,0BAA0B;AACnC,SAAS,uBAAuB,0BAA0B;AAC1D,SAAS,gCAAgC","names":[],"sources":["src/index.ts"],"version":3,"sourcesContent":["// SPDX-FileCopyrightText: 2026 lambda-deadline-middleware contributors\n// SPDX-License-Identifier: MIT\n\nexport { withLambdaDeadline } from \"./handler-wrapper.js\";\nexport { deadlineMiddleware } from \"./
|
|
1
|
+
{"mappings":";;AAGA,SAAS,0BAA0B;AACnC,SAAS,0BAA0B;AACnC,SAAS,uBAAuB,0BAA0B;AAC1D,SAAS,gCAAgC","names":[],"sources":["src/index.ts"],"version":3,"sourcesContent":["// SPDX-FileCopyrightText: 2026 lambda-deadline-middleware contributors\n// SPDX-License-Identifier: MIT\n\nexport { withLambdaDeadline } from \"./handler-wrapper.js\";\nexport { deadlineMiddleware } from \"./middleware.js\";\nexport { DeadlineExceededError, isDeadlineExceeded } from \"./error.js\";\nexport { getRemainingTimeInMillis } from \"./context-store.js\";\n\nexport type { Milliseconds, DeadlineOptions } from \"./types.js\";\n\nexport type { LambdaContextLike } from \"./context-store.js\";\n"]}
|
package/dist/middleware.d.ts
CHANGED
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type {
|
|
3
|
-
export declare const computeDeadline: (config: DeadlineMiddlewareConfig) => DeadlineComputation;
|
|
4
|
-
export interface DeadlineTimer {
|
|
5
|
-
readonly controller: AbortController;
|
|
6
|
-
[Symbol.dispose]: () => void;
|
|
7
|
-
}
|
|
8
|
-
export declare const createDeadlineTimer: (deadlineMs: RequestDeadlineMs, config: DeadlineMiddlewareConfig) => DeadlineTimer;
|
|
1
|
+
import type { Pluggable } from "@smithy/types";
|
|
2
|
+
import type { DeadlineOptions } from "./types.js";
|
|
9
3
|
export declare const composeSignals: (existing: AbortSignal | undefined, deadline: AbortSignal) => AbortSignal;
|
|
10
|
-
export declare const
|
|
4
|
+
export declare const deadlineMiddleware: <
|
|
11
5
|
Input extends object,
|
|
12
6
|
Output extends object
|
|
13
|
-
>(
|
|
7
|
+
>(options?: DeadlineOptions) => Pluggable<Input, Output>;
|
|
14
8
|
|
|
15
9
|
//# sourceMappingURL=middleware.d.ts.map
|
package/dist/middleware.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"
|
|
1
|
+
{"mappings":"AAGA,cAKE,iBACK;AAMP,cAAc,uBAAuB;AAErC,OAAO,cAAM,iBACX,UAAU,yBACV,UAAU,gBACT;AAKH,OAAO,cAAM;CAAsB;CAAsB;EACvD,UAAU,oBACT,UAAU,OAAO","names":[],"sources":["src/middleware.ts"],"version":3,"sourcesContent":["// SPDX-FileCopyrightText: 2026 lambda-deadline-middleware contributors\n// SPDX-License-Identifier: MIT\n\nimport type {\n FinalizeHandler,\n FinalizeHandlerArguments,\n FinalizeHandlerOutput,\n HandlerExecutionContext,\n Pluggable,\n} from \"@smithy/types\";\n\nimport { getRemainingTimeInMillis } from \"./context-store.js\";\nimport { DeadlineExceededError } from \"./error.js\";\nimport { milliseconds } from \"./types.js\";\n\nimport type { DeadlineOptions } from \"./types.js\";\n\nexport const composeSignals = (\n existing: AbortSignal | undefined,\n deadline: AbortSignal,\n): AbortSignal => {\n if (existing === undefined) return deadline;\n return AbortSignal.any([existing, deadline]);\n};\n\nexport const deadlineMiddleware = <Input extends object, Output extends object>(\n options?: DeadlineOptions,\n): Pluggable<Input, Output> => {\n const raw = options?.flushBufferMs ?? 1000;\n if (raw < 0) {\n throw new TypeError(`flushBufferMs option must be non-negative, received: ${raw}`);\n }\n const flushBufferMs = milliseconds(raw);\n\n return {\n applyToStack(stack) {\n // Registered at \"finalizeRequest\" (attempt level) rather than API-call level so each retry gets a deadline\n // computed from the actual remaining time at that moment. API-call level would cache a stale deadline\n // across retries, which grow more dangerous after backoff delays eat into remaining time.\n stack.add(\n (\n next: FinalizeHandler<Input, Output>,\n _context: HandlerExecutionContext,\n ): FinalizeHandler<Input, Output> =>\n async (args: FinalizeHandlerArguments<Input>): Promise<FinalizeHandlerOutput<Output>> => {\n const remaining = getRemainingTimeInMillis();\n if (remaining === undefined) return next(args);\n\n const deadline = remaining - flushBufferMs;\n\n if (deadline <= 0) {\n throw new DeadlineExceededError({\n deadlineMs: milliseconds(0),\n flushBufferMs,\n remainingMs: milliseconds(remaining),\n });\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => {\n controller.abort(\n new DeadlineExceededError({\n deadlineMs: milliseconds(deadline),\n flushBufferMs,\n remainingMs: milliseconds(remaining),\n }),\n );\n }, deadline);\n\n // `using` guarantees cleanup (clearTimeout) even if next() throws, the promise rejects,\n // or an external abort signal fires — strictly more reliable than try/finally.\n using _timer = {\n [Symbol.dispose]() {\n clearTimeout(timeoutId);\n },\n };\n\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Smithy request is an opaque object; we access optional signal property\n const request = args.request as { signal?: AbortSignal } | undefined;\n const signal = composeSignals(request?.signal, controller.signal);\n const result = await next({\n ...args,\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- spreading opaque Smithy request to add signal\n request: { ...(args.request as object), signal },\n });\n return result;\n },\n {\n step: \"finalizeRequest\",\n name: \"deadlineMiddleware\",\n override: true,\n },\n );\n },\n };\n};\n"]}
|
package/dist/middleware.js
CHANGED
|
@@ -1,65 +1,47 @@
|
|
|
1
|
-
// SPDX-FileCopyrightText: 2026 lambda-deadline-middleware contributors
|
|
2
|
-
// SPDX-License-Identifier: MIT
|
|
3
1
|
import { getRemainingTimeInMillis } from "./context-store.js";
|
|
4
2
|
import { DeadlineExceededError } from "./error.js";
|
|
5
3
|
import { milliseconds } from "./types.js";
|
|
6
|
-
export const computeDeadline = (config) => {
|
|
7
|
-
const remaining = getRemainingTimeInMillis();
|
|
8
|
-
if (remaining === undefined) {
|
|
9
|
-
return { kind: "no-context" };
|
|
10
|
-
}
|
|
11
|
-
const deadline = remaining - config.flushBufferMs;
|
|
12
|
-
if (deadline <= 0) {
|
|
13
|
-
return {
|
|
14
|
-
kind: "insufficient-time",
|
|
15
|
-
remaining: milliseconds(remaining),
|
|
16
|
-
buffer: config.flushBufferMs
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- branded narrowing: deadline is validated > 0 above
|
|
20
|
-
return {
|
|
21
|
-
kind: "deadline",
|
|
22
|
-
value: deadline
|
|
23
|
-
};
|
|
24
|
-
};
|
|
25
|
-
export const createDeadlineTimer = (deadlineMs, config) => {
|
|
26
|
-
const controller = new AbortController();
|
|
27
|
-
const remaining = milliseconds(deadlineMs + config.flushBufferMs);
|
|
28
|
-
const error = new DeadlineExceededError({
|
|
29
|
-
deadlineMs: milliseconds(deadlineMs),
|
|
30
|
-
flushBufferMs: config.flushBufferMs,
|
|
31
|
-
remainingMs: remaining
|
|
32
|
-
});
|
|
33
|
-
const timeoutId = setTimeout(() => {
|
|
34
|
-
controller.abort(error);
|
|
35
|
-
}, deadlineMs);
|
|
36
|
-
return {
|
|
37
|
-
controller,
|
|
38
|
-
[Symbol.dispose]() {
|
|
39
|
-
clearTimeout(timeoutId);
|
|
40
|
-
}
|
|
41
|
-
};
|
|
42
|
-
};
|
|
43
4
|
export const composeSignals = (existing, deadline) => {
|
|
44
5
|
if (existing === undefined) return deadline;
|
|
45
6
|
return AbortSignal.any([existing, deadline]);
|
|
46
7
|
};
|
|
47
|
-
export const
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
8
|
+
export const deadlineMiddleware = (options) => {
|
|
9
|
+
const raw = options?.flushBufferMs ?? 1e3;
|
|
10
|
+
if (raw < 0) {
|
|
11
|
+
throw new TypeError(`flushBufferMs option must be non-negative, received: ${raw}`);
|
|
12
|
+
}
|
|
13
|
+
const flushBufferMs = milliseconds(raw);
|
|
14
|
+
return { applyToStack(stack) {
|
|
15
|
+
// Registered at "finalizeRequest" (attempt level) rather than API-call level so each retry gets a deadline
|
|
16
|
+
// computed from the actual remaining time at that moment. API-call level would cache a stale deadline
|
|
17
|
+
// across retries, which grow more dangerous after backoff delays eat into remaining time.
|
|
18
|
+
stack.add((next, _context) => async (args) => {
|
|
19
|
+
const remaining = getRemainingTimeInMillis();
|
|
20
|
+
if (remaining === undefined) return next(args);
|
|
21
|
+
const deadline = remaining - flushBufferMs;
|
|
22
|
+
if (deadline <= 0) {
|
|
23
|
+
throw new DeadlineExceededError({
|
|
24
|
+
deadlineMs: milliseconds(0),
|
|
25
|
+
flushBufferMs,
|
|
26
|
+
remainingMs: milliseconds(remaining)
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
const controller = new AbortController();
|
|
30
|
+
const timeoutId = setTimeout(() => {
|
|
31
|
+
controller.abort(new DeadlineExceededError({
|
|
32
|
+
deadlineMs: milliseconds(deadline),
|
|
33
|
+
flushBufferMs,
|
|
34
|
+
remainingMs: milliseconds(remaining)
|
|
35
|
+
}));
|
|
36
|
+
}, deadline);
|
|
57
37
|
// `using` guarantees cleanup (clearTimeout) even if next() throws, the promise rejects,
|
|
58
38
|
// or an external abort signal fires — strictly more reliable than try/finally.
|
|
59
|
-
using
|
|
39
|
+
using _timer = { [Symbol.dispose]() {
|
|
40
|
+
clearTimeout(timeoutId);
|
|
41
|
+
} };
|
|
60
42
|
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Smithy request is an opaque object; we access optional signal property
|
|
61
43
|
const request = args.request;
|
|
62
|
-
const signal = composeSignals(request?.signal,
|
|
44
|
+
const signal = composeSignals(request?.signal, controller.signal);
|
|
63
45
|
const result = await next({
|
|
64
46
|
...args,
|
|
65
47
|
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- spreading opaque Smithy request to add signal
|
|
@@ -69,8 +51,12 @@ export const deadlineMiddlewareHandler = (config) => (next, _context) => async (
|
|
|
69
51
|
}
|
|
70
52
|
});
|
|
71
53
|
return result;
|
|
72
|
-
}
|
|
73
|
-
|
|
54
|
+
}, {
|
|
55
|
+
step: "finalizeRequest",
|
|
56
|
+
name: "deadlineMiddleware",
|
|
57
|
+
override: true
|
|
58
|
+
});
|
|
59
|
+
} };
|
|
74
60
|
};
|
|
75
61
|
|
|
76
62
|
//# sourceMappingURL=middleware.js.map
|
package/dist/middleware.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"
|
|
1
|
+
{"mappings":"AAWA,SAAS,gCAAgC;AACzC,SAAS,6BAA6B;AACtC,SAAS,oBAAoB;AAI7B,OAAO,MAAM,kBACX,UACA,aACgB;CAChB,IAAI,aAAa,WAAW,OAAO;CACnC,OAAO,YAAY,IAAI,CAAC,UAAU,QAAQ,CAAC;AAC7C;AAEA,OAAO,MAAM,sBACX,YAC6B;CAC7B,MAAM,MAAM,SAAS,iBAAiB;CACtC,IAAI,MAAM,GAAG;EACX,MAAM,IAAI,UAAU,wDAAwD,KAAK;CACnF;CACA,MAAM,gBAAgB,aAAa,GAAG;CAEtC,OAAO,EACL,aAAa,OAAO;;;;EAIlB,MAAM,KAEF,MACA,aAEA,OAAO,SAAkF;GACvF,MAAM,YAAY,yBAAyB;GAC3C,IAAI,cAAc,WAAW,OAAO,KAAK,IAAI;GAE7C,MAAM,WAAW,YAAY;GAE7B,IAAI,YAAY,GAAG;IACjB,MAAM,IAAI,sBAAsB;KAC9B,YAAY,aAAa,CAAC;KAC1B;KACA,aAAa,aAAa,SAAS;IACrC,CAAC;GACH;GAEA,MAAM,aAAa,IAAI,gBAAgB;GACvC,MAAM,YAAY,iBAAiB;IACjC,WAAW,MACT,IAAI,sBAAsB;KACxB,YAAY,aAAa,QAAQ;KACjC;KACA,aAAa,aAAa,SAAS;IACrC,CAAC,CACH;GACF,GAAG,QAAQ;;;GAIX,MAAM,SAAS,EACb,CAAC,OAAO,WAAW;IACjB,aAAa,SAAS;GACxB,EACF;;GAGA,MAAM,UAAU,KAAK;GACrB,MAAM,SAAS,eAAe,SAAS,QAAQ,WAAW,MAAM;GAChE,MAAM,SAAS,MAAM,KAAK;IACxB,GAAG;;IAEH,SAAS;KAAE,GAAI,KAAK;KAAoB;IAAO;GACjD,CAAC;GACD,OAAO;EACT,GACF;GACE,MAAM;GACN,MAAM;GACN,UAAU;EACZ,CACF;CACF,EACF;AACF","names":[],"sources":["src/middleware.ts"],"version":3,"sourcesContent":["// SPDX-FileCopyrightText: 2026 lambda-deadline-middleware contributors\n// SPDX-License-Identifier: MIT\n\nimport type {\n FinalizeHandler,\n FinalizeHandlerArguments,\n FinalizeHandlerOutput,\n HandlerExecutionContext,\n Pluggable,\n} from \"@smithy/types\";\n\nimport { getRemainingTimeInMillis } from \"./context-store.js\";\nimport { DeadlineExceededError } from \"./error.js\";\nimport { milliseconds } from \"./types.js\";\n\nimport type { DeadlineOptions } from \"./types.js\";\n\nexport const composeSignals = (\n existing: AbortSignal | undefined,\n deadline: AbortSignal,\n): AbortSignal => {\n if (existing === undefined) return deadline;\n return AbortSignal.any([existing, deadline]);\n};\n\nexport const deadlineMiddleware = <Input extends object, Output extends object>(\n options?: DeadlineOptions,\n): Pluggable<Input, Output> => {\n const raw = options?.flushBufferMs ?? 1000;\n if (raw < 0) {\n throw new TypeError(`flushBufferMs option must be non-negative, received: ${raw}`);\n }\n const flushBufferMs = milliseconds(raw);\n\n return {\n applyToStack(stack) {\n // Registered at \"finalizeRequest\" (attempt level) rather than API-call level so each retry gets a deadline\n // computed from the actual remaining time at that moment. API-call level would cache a stale deadline\n // across retries, which grow more dangerous after backoff delays eat into remaining time.\n stack.add(\n (\n next: FinalizeHandler<Input, Output>,\n _context: HandlerExecutionContext,\n ): FinalizeHandler<Input, Output> =>\n async (args: FinalizeHandlerArguments<Input>): Promise<FinalizeHandlerOutput<Output>> => {\n const remaining = getRemainingTimeInMillis();\n if (remaining === undefined) return next(args);\n\n const deadline = remaining - flushBufferMs;\n\n if (deadline <= 0) {\n throw new DeadlineExceededError({\n deadlineMs: milliseconds(0),\n flushBufferMs,\n remainingMs: milliseconds(remaining),\n });\n }\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => {\n controller.abort(\n new DeadlineExceededError({\n deadlineMs: milliseconds(deadline),\n flushBufferMs,\n remainingMs: milliseconds(remaining),\n }),\n );\n }, deadline);\n\n // `using` guarantees cleanup (clearTimeout) even if next() throws, the promise rejects,\n // or an external abort signal fires — strictly more reliable than try/finally.\n using _timer = {\n [Symbol.dispose]() {\n clearTimeout(timeoutId);\n },\n };\n\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- Smithy request is an opaque object; we access optional signal property\n const request = args.request as { signal?: AbortSignal } | undefined;\n const signal = composeSignals(request?.signal, controller.signal);\n const result = await next({\n ...args,\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- spreading opaque Smithy request to add signal\n request: { ...(args.request as object), signal },\n });\n return result;\n },\n {\n step: \"finalizeRequest\",\n name: \"deadlineMiddleware\",\n override: true,\n },\n );\n },\n };\n};\n"]}
|
package/dist/types.d.ts
CHANGED
|
@@ -6,27 +6,9 @@ type Brand<
|
|
|
6
6
|
readonly [BrandSymbol]: B;
|
|
7
7
|
};
|
|
8
8
|
export type Milliseconds = Brand<number, "Milliseconds">;
|
|
9
|
-
export type FlushBufferMs = Brand<number, "FlushBufferMs">;
|
|
10
|
-
export type RequestDeadlineMs = Brand<number, "RequestDeadlineMs">;
|
|
11
9
|
export declare const milliseconds: (value: number) => Milliseconds;
|
|
12
|
-
export declare const flushBufferMs: (value: number) => FlushBufferMs;
|
|
13
|
-
export type DeadlineComputation = {
|
|
14
|
-
readonly kind: "deadline";
|
|
15
|
-
readonly value: RequestDeadlineMs;
|
|
16
|
-
} | {
|
|
17
|
-
readonly kind: "insufficient-time";
|
|
18
|
-
readonly remaining: Milliseconds;
|
|
19
|
-
readonly buffer: FlushBufferMs;
|
|
20
|
-
} | {
|
|
21
|
-
readonly kind: "no-context";
|
|
22
|
-
};
|
|
23
|
-
export interface DeadlineMiddlewareConfig {
|
|
24
|
-
readonly flushBufferMs: FlushBufferMs;
|
|
25
|
-
readonly telemetryEnabled: boolean;
|
|
26
|
-
}
|
|
27
10
|
export interface DeadlineOptions {
|
|
28
11
|
readonly flushBufferMs?: number;
|
|
29
|
-
readonly telemetryEnabled?: boolean;
|
|
30
12
|
}
|
|
31
13
|
export {};
|
|
32
14
|
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"AAKA,cAAc;KAET;CAAM;CAAG;IAAoB,IAAI;WAAY,cAAc;AAAE;AAElE,YAAY,eAAe,cAAc;AAEzC,
|
|
1
|
+
{"mappings":"AAKA,cAAc;KAET;CAAM;CAAG;IAAoB,IAAI;WAAY,cAAc;AAAE;AAElE,YAAY,eAAe,cAAc;AAEzC,OAAO,cAAM,eAAgB,kBAAgB;AAQ7C,iBAAiB,gBAAgB;UACtB;AACX","names":[],"sources":["src/types.ts"],"version":3,"sourcesContent":["// SPDX-FileCopyrightText: 2026 lambda-deadline-middleware contributors\n// SPDX-License-Identifier: MIT\n\n// Branded type prevents interchange errors at compile time (e.g. passing seconds where milliseconds are expected).\n// Zero runtime cost. Smart constructor below validates at the boundary and brands the value.\ndeclare const BrandSymbol: unique symbol;\n\ntype Brand<T, B extends string> = T & { readonly [BrandSymbol]: B };\n\nexport type Milliseconds = Brand<number, \"Milliseconds\">;\n\nexport const milliseconds = (value: number): Milliseconds => {\n if (!Number.isFinite(value)) {\n throw new TypeError(`milliseconds value must be finite, received: ${value}`);\n }\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- branded type constructor: value is validated above\n return value as Milliseconds;\n};\n\nexport interface DeadlineOptions {\n readonly flushBufferMs?: number;\n}\n"]}
|
package/dist/types.js
CHANGED
|
@@ -5,15 +5,5 @@ export const milliseconds = (value) => {
|
|
|
5
5
|
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- branded type constructor: value is validated above
|
|
6
6
|
return value;
|
|
7
7
|
};
|
|
8
|
-
export const flushBufferMs = (value) => {
|
|
9
|
-
if (!Number.isFinite(value)) {
|
|
10
|
-
throw new TypeError(`flushBufferMs value must be finite, received: ${value}`);
|
|
11
|
-
}
|
|
12
|
-
if (value < 0) {
|
|
13
|
-
throw new TypeError(`flushBufferMs must be non-negative, received: ${value}`);
|
|
14
|
-
}
|
|
15
|
-
// oxlint-disable-next-line typescript/no-unsafe-type-assertion -- branded type constructor: value is validated above
|
|
16
|
-
return value;
|
|
17
|
-
};
|
|
18
8
|
|
|
19
9
|
//# sourceMappingURL=types.js.map
|
package/dist/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"mappings":"
|
|
1
|
+
{"mappings":"AAWA,OAAO,MAAM,gBAAgB,UAAgC;CAC3D,IAAI,CAAC,OAAO,SAAS,KAAK,GAAG;EAC3B,MAAM,IAAI,UAAU,gDAAgD,OAAO;CAC7E;;CAEA,OAAO;AACT","names":[],"sources":["src/types.ts"],"version":3,"sourcesContent":["// SPDX-FileCopyrightText: 2026 lambda-deadline-middleware contributors\n// SPDX-License-Identifier: MIT\n\n// Branded type prevents interchange errors at compile time (e.g. passing seconds where milliseconds are expected).\n// Zero runtime cost. Smart constructor below validates at the boundary and brands the value.\ndeclare const BrandSymbol: unique symbol;\n\ntype Brand<T, B extends string> = T & { readonly [BrandSymbol]: B };\n\nexport type Milliseconds = Brand<number, \"Milliseconds\">;\n\nexport const milliseconds = (value: number): Milliseconds => {\n if (!Number.isFinite(value)) {\n throw new TypeError(`milliseconds value must be finite, received: ${value}`);\n }\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- branded type constructor: value is validated above\n return value as Milliseconds;\n};\n\nexport interface DeadlineOptions {\n readonly flushBufferMs?: number;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lambda-deadline-middleware",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "AWS SDK v3 middleware for automatic Lambda deadline propagation via AbortController-based timeouts",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -64,11 +64,11 @@
|
|
|
64
64
|
"conventional-changelog-conventionalcommits": "^9.3.1",
|
|
65
65
|
"knip": "^6.16.1",
|
|
66
66
|
"lefthook": "^2.1.9",
|
|
67
|
-
"oxc-transform": "^0.
|
|
68
|
-
"oxfmt": "^0.
|
|
67
|
+
"oxc-transform": "^0.135.0",
|
|
68
|
+
"oxfmt": "^0.54.0",
|
|
69
69
|
"oxlint": "^1.69.0",
|
|
70
70
|
"oxlint-tsgolint": "^0.23.0",
|
|
71
|
-
"semantic-release": "25.0.
|
|
71
|
+
"semantic-release": "25.0.5",
|
|
72
72
|
"typescript": "^6.0.3",
|
|
73
73
|
"vitest": "^4.1.8"
|
|
74
74
|
},
|
package/src/context-store.ts
CHANGED
|
@@ -9,30 +9,16 @@ export interface LambdaContextLike {
|
|
|
9
9
|
getRemainingTimeInMillis?: () => number;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
// without throwing, while the accessor can distinguish "no context stored"
|
|
14
|
-
// from "context present but missing the method".
|
|
15
|
-
const NO_CONTEXT: unique symbol = Symbol("no-context");
|
|
16
|
-
|
|
17
|
-
type StoreValue = LambdaContextLike | typeof NO_CONTEXT;
|
|
18
|
-
|
|
19
|
-
const contextStorage = new AsyncLocalStorage<StoreValue>();
|
|
12
|
+
const contextStorage = new AsyncLocalStorage<LambdaContextLike>();
|
|
20
13
|
|
|
21
14
|
export const run = <T>(context: LambdaContextLike | null | undefined, fn: () => T): T => {
|
|
22
|
-
|
|
23
|
-
return contextStorage.run(
|
|
15
|
+
if (context === null || context === undefined) return fn();
|
|
16
|
+
return contextStorage.run(context, fn);
|
|
24
17
|
};
|
|
25
18
|
|
|
26
19
|
export const getRemainingTimeInMillis = (): number | undefined => {
|
|
27
20
|
const store = contextStorage.getStore();
|
|
28
|
-
|
|
29
|
-
if (
|
|
30
|
-
return undefined;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (typeof store.getRemainingTimeInMillis !== "function") {
|
|
34
|
-
return undefined;
|
|
35
|
-
}
|
|
36
|
-
|
|
21
|
+
if (store === undefined) return undefined;
|
|
22
|
+
if (typeof store.getRemainingTimeInMillis !== "function") return undefined;
|
|
37
23
|
return store.getRemainingTimeInMillis();
|
|
38
24
|
};
|