lambda-deadline-middleware 1.0.1 → 2.0.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.
Files changed (46) hide show
  1. package/README.md +163 -85
  2. package/dist/context-store.d.ts +25 -2
  3. package/dist/context-store.d.ts.map +1 -1
  4. package/dist/context-store.js +45 -14
  5. package/dist/context-store.js.map +1 -1
  6. package/dist/error.d.ts +6 -7
  7. package/dist/error.d.ts.map +1 -1
  8. package/dist/error.js.map +1 -1
  9. package/dist/index.d.ts +3 -4
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +2 -3
  12. package/dist/index.js.map +1 -1
  13. package/dist/middleware.d.ts +3 -11
  14. package/dist/middleware.d.ts.map +1 -1
  15. package/dist/middleware.js +22 -73
  16. package/dist/middleware.js.map +1 -1
  17. package/dist/types.d.ts +0 -28
  18. package/dist/types.d.ts.map +1 -1
  19. package/dist/types.js +1 -17
  20. package/dist/types.js.map +1 -1
  21. package/package.json +22 -22
  22. package/src/context-store.ts +73 -21
  23. package/src/error.ts +6 -8
  24. package/src/index.ts +3 -12
  25. package/src/middleware.ts +36 -101
  26. package/src/types.ts +0 -46
  27. package/dist/config.d.ts +0 -4
  28. package/dist/config.d.ts.map +0 -1
  29. package/dist/config.js +0 -17
  30. package/dist/config.js.map +0 -1
  31. package/dist/handler-wrapper.d.ts +0 -13
  32. package/dist/handler-wrapper.d.ts.map +0 -1
  33. package/dist/handler-wrapper.js +0 -6
  34. package/dist/handler-wrapper.js.map +0 -1
  35. package/dist/registration.d.ts +0 -8
  36. package/dist/registration.d.ts.map +0 -1
  37. package/dist/registration.js +0 -17
  38. package/dist/registration.js.map +0 -1
  39. package/dist/telemetry.d.ts +0 -5
  40. package/dist/telemetry.d.ts.map +0 -1
  41. package/dist/telemetry.js +0 -82
  42. package/dist/telemetry.js.map +0 -1
  43. package/src/config.ts +0 -18
  44. package/src/handler-wrapper.ts +0 -19
  45. package/src/registration.ts +0 -27
  46. package/src/telemetry.ts +0 -132
package/README.md CHANGED
@@ -3,22 +3,65 @@
3
3
 
4
4
  # lambda-deadline-middleware
5
5
 
6
- Zero-dependency AWS SDK v3 middleware that automatically propagates Lambda execution deadlines to outgoing SDK calls via
7
- `AbortController`-based timeouts.
6
+ [![npm version](https://img.shields.io/npm/v/lambda-deadline-middleware)](https://www.npmjs.com/package/lambda-deadline-middleware)
7
+ [![CI](https://github.com/mikkopiu/lambda-deadline-middleware/actions/workflows/ci.yml/badge.svg)](https://github.com/mikkopiu/lambda-deadline-middleware/actions/workflows/ci.yml)
8
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
8
9
 
9
- When an AWS SDK call hangs inside a Lambda function, the runtime terminates the process at the configured timeout,
10
- destroying in-flight OpenTelemetry/X-Ray spans without export. This library prevents that by computing per-request
11
- deadlines from the Lambda's remaining execution time and aborting requests before the hard timeout fires.
10
+ Zero-dependency AWS SDK v3 middleware that propagates Lambda deadlines to outgoing SDK calls via `AbortSignal`.
11
+
12
+ When an AWS SDK call hangs inside a Lambda, the runtime kills the process without throwing an error or giving your code
13
+ a chance to react. This library attaches an `AbortSignal` to every outgoing SDK request so your calls fail fast instead
14
+ of getting killed silently. The signal comes either from an external source you provide or is computed once from the
15
+ Lambda's remaining execution time.
16
+
17
+ ```typescript
18
+ import { withLambdaDeadline, deadlineMiddleware } from "lambda-deadline-middleware";
19
+ import { DynamoDBClient, GetItemCommand } from "@aws-sdk/client-dynamodb";
20
+
21
+ const dynamodb = new DynamoDBClient({});
22
+ dynamodb.middlewareStack.use(deadlineMiddleware());
23
+
24
+ export const handler = withLambdaDeadline(async (event, context) => {
25
+ return dynamodb.send(new GetItemCommand({ TableName: "my-table", Key: { id: { S: event.id } } }));
26
+ });
27
+ ```
12
28
 
13
29
  ## Features
14
30
 
15
- - Automatic deadline propagation, no manual timeout configuration per call
16
- - Fresh deadline per retry: each SDK retry attempt uses _current_ remaining time
17
- - Signal composition: preserves caller-provided `AbortSignal` via `AbortSignal.any()`
18
- - Zero runtime dependencies (`@smithy/types` is compile-time only)
19
- - Complete no-op when no Lambda context is available
20
- - Optional OpenTelemetry span events on deadline aborts (detected dynamically)
21
- - Branded types prevent millisecond/buffer interchange at compile time
31
+ - Zero runtime dependencies
32
+ - Bring your own `AbortSignal` (from Middy, a manual `AbortController`, or any framework), or let the library compute
33
+ one from `getRemainingTimeInMillis()` at invocation start
34
+ - One signal per invocation: covers all SDK calls and retries within the handler
35
+ - Works alongside per-request `abortSignal` options you pass to `.send()`
36
+ - Complete no-op outside Lambda (safe in local dev and tests)
37
+
38
+ ## How It Works
39
+
40
+ ```mermaid
41
+ flowchart LR
42
+ subgraph Handler startup
43
+ A[Lambda Runtime] --> B[withLambdaDeadline]
44
+ B -->|compute signal once| C[AbortSignal.timeout]
45
+ B --> D[Your Handler]
46
+ end
47
+
48
+ subgraph Per outgoing SDK request
49
+ E[SDK .send] --> F[Deadline Middleware]
50
+ F -->|signal exists?| G[Compose with request signal]
51
+ F -->|no signal| H[Pass through]
52
+ G --> I[HTTP Request]
53
+ H --> I
54
+ end
55
+
56
+ D --> E
57
+
58
+ style F fill:#f9a825,stroke:#f57f17
59
+ style B fill:#66bb6a,stroke:#2e7d32
60
+ ```
61
+
62
+ `withLambdaDeadline` computes `AbortSignal.timeout(remaining - flushBuffer)` once at invocation start and stores it in
63
+ `AsyncLocalStorage`. The middleware reads it on each outgoing request and composes it with any per-request signal. If
64
+ `setDeadlineSignal()` was called, that signal is used instead.
22
65
 
23
66
  ## Requirements
24
67
 
@@ -28,18 +71,17 @@ deadlines from the Lambda's remaining execution time and aborting requests befor
28
71
  ## Installation
29
72
 
30
73
  ```bash
31
- pnpm add lambda-deadline-middleware
74
+ npm install lambda-deadline-middleware
32
75
  ```
33
76
 
34
77
  ## Usage
35
78
 
36
- Setup requires two pieces:
79
+ Setup has two parts:
37
80
 
38
- 1. **Wrap your handler** with `withLambdaDeadline`. This stores the Lambda `context` (specifically
39
- `getRemainingTimeInMillis()`) in `AsyncLocalStorage` so the SDK middleware can read it. The SDK middleware stack has
40
- no access to the Lambda context on its own.
81
+ 1. **Wrap your handler** with `withLambdaDeadline`. Computes the deadline signal once and stores it in
82
+ `AsyncLocalStorage`.
41
83
 
42
- 2. **Register the middleware** on each SDK client via the standard `middlewareStack.use()` pattern.
84
+ 2. **Register the middleware** on each SDK client.
43
85
 
44
86
  ```typescript
45
87
  import { withLambdaDeadline, deadlineMiddleware } from "lambda-deadline-middleware";
@@ -58,74 +100,114 @@ export const handler = withLambdaDeadline(async (event, context) => {
58
100
  });
59
101
  ```
60
102
 
61
- Every SDK call through `dynamodb` now receives a timeout derived from the Lambda's remaining execution time minus a
62
- configurable flush buffer (default: 1000ms).
103
+ Every SDK call through `dynamodb` gets an `AbortSignal` that fires at `remainingTime - flushBuffer` ms from handler
104
+ start.
63
105
 
64
- ## How It Works
106
+ ## External Signal (Middy, manual AbortController, etc.)
65
107
 
66
- ```mermaid
67
- flowchart LR
68
- subgraph Lambda Invocation
69
- direction LR
70
- A[Lambda Runtime] --> B[withLambdaDeadline]
71
- B --> C[Your Handler]
72
- C --> D[SDK .send]
73
- end
108
+ If you already have an `AbortSignal` (from Middy's `timeoutEarlyInMillis`, a manual controller, or anything else), pass
109
+ it in directly:
74
110
 
75
- subgraph Per Attempt
76
- direction LR
77
- D --> E[Deadline Middleware]
78
- E -->|getRemainingTimeInMillis\nminus flush buffer| F[AbortController\n+ setTimeout]
79
- F --> G[HTTP Request]
80
- end
111
+ ```typescript
112
+ import {
113
+ withLambdaDeadline,
114
+ deadlineMiddleware,
115
+ setDeadlineSignal,
116
+ } from "lambda-deadline-middleware";
117
+ import middy from "@middy/core";
118
+ import { DynamoDBClient, GetItemCommand } from "@aws-sdk/client-dynamodb";
81
119
 
82
- style E fill:#f9a825,stroke:#f57f17
83
- style B fill:#66bb6a,stroke:#2e7d32
120
+ const dynamodb = new DynamoDBClient({});
121
+ dynamodb.middlewareStack.use(deadlineMiddleware());
122
+
123
+ // Middy passes { signal } as the third argument when timeoutEarlyInMillis is set
124
+ const baseHandler = async (event, context, { signal }) => {
125
+ setDeadlineSignal(signal);
126
+ const result = await dynamodb.send(
127
+ new GetItemCommand({
128
+ /* ... */
129
+ }),
130
+ );
131
+ return { statusCode: 200, body: JSON.stringify(result) };
132
+ };
133
+
134
+ export const handler = withLambdaDeadline(
135
+ middy({ timeoutEarlyInMillis: 1000 }).handler(baseHandler),
136
+ );
84
137
  ```
85
138
 
86
- `withLambdaDeadline` stores the Lambda context in `AsyncLocalStorage`. The deadline middleware reads it on every attempt
87
- (including retries), computes a fresh timeout, and attaches an `AbortSignal` to the outgoing HTTP request.
139
+ When an external signal is set via `setDeadlineSignal()`:
88
140
 
89
- ## Configuration
141
+ - It **replaces** the auto-computed signal for the current invocation
142
+ - The signal is composed with any per-request `AbortSignal` via `AbortSignal.any()`
143
+ - You control when and why the abort fires
90
144
 
91
- ### Flush Buffer
145
+ When no external signal is set, the auto-computed signal (from `withLambdaDeadline`) is used.
92
146
 
93
- The flush buffer is subtracted from the remaining Lambda time to leave room for telemetry export and error handling:
147
+ **Important:** `withLambdaDeadline` must wrap the outside. It creates the `AsyncLocalStorage` scope that
148
+ `setDeadlineSignal` writes into.
94
149
 
95
150
  ```typescript
96
- // Default: 1000ms
97
- dynamodb.middlewareStack.use(deadlineMiddleware());
151
+ // ✅ Correct: withLambdaDeadline on the outside
152
+ export const handler = withLambdaDeadline(
153
+ middy({ timeoutEarlyInMillis: 1000 }).handler(baseHandler),
154
+ );
98
155
 
99
- // Custom: 500ms
100
- dynamodb.middlewareStack.use(deadlineMiddleware({ flushBufferMs: 500 }));
156
+ // ❌ Wrong: store doesn't exist yet when baseHandler runs
157
+ export const handler = middy({ timeoutEarlyInMillis: 1000 }).handler(
158
+ withLambdaDeadline(baseHandler),
159
+ );
101
160
  ```
102
161
 
103
- ### Telemetry
162
+ `setDeadlineSignal` only affects AWS SDK calls (via the Smithy middleware stack). For other async work, pass the signal
163
+ yourself:
104
164
 
105
- If `@opentelemetry/api` is installed, span events are emitted on deadline aborts. Disable with:
165
+ ```typescript
166
+ const baseHandler = async (event, context, { signal }) => {
167
+ setDeadlineSignal(signal);
168
+
169
+ // SDK calls: aborted via middleware
170
+ const item = await dynamodb.send(
171
+ new GetItemCommand({
172
+ /* ... */
173
+ }),
174
+ );
175
+
176
+ // Non-SDK calls: pass signal manually
177
+ const response = await fetch("https://api.example.com/data", { signal });
178
+ };
179
+ ```
180
+
181
+ ## Configuration
182
+
183
+ ### Flush Buffer
184
+
185
+ Subtracted from remaining Lambda time to leave room for cleanup:
106
186
 
107
187
  ```typescript
108
- dynamodb.middlewareStack.use(deadlineMiddleware({ telemetryEnabled: false }));
188
+ // Default: 1000ms flush buffer
189
+ export const handler = withLambdaDeadline(myHandler);
190
+
191
+ // Custom: 500ms flush buffer
192
+ export const handler = withLambdaDeadline(myHandler, { flushBufferMs: 500 });
109
193
  ```
110
194
 
195
+ Only applies to automatic signal computation. With `setDeadlineSignal()`, you control timing yourself.
196
+
111
197
  ## Error Handling
112
198
 
113
- When remaining time is less than or equal to the flush buffer, the middleware throws `DeadlineExceededError` immediately
114
- without dispatching an HTTP request.
199
+ When remaining time flush buffer, `withLambdaDeadline` throws `DeadlineExceededError` without calling the handler:
115
200
 
116
201
  ```typescript
117
202
  import { isDeadlineExceeded } from "lambda-deadline-middleware";
118
203
 
119
204
  try {
120
- await dynamodb.send(
121
- new GetItemCommand({
122
- /* ... */
123
- }),
124
- );
205
+ await handler(event, context);
125
206
  } catch (error) {
126
207
  if (isDeadlineExceeded(error)) {
127
- console.log(`Deadline exceeded: ${error.deadlineMs}ms`);
128
- console.log(`Remaining time was: ${error.remainingMs}ms`);
208
+ console.log(
209
+ `Deadline exceeded: remaining ${error.remainingMs}ms, buffer ${error.flushBufferMs}ms`,
210
+ );
129
211
  }
130
212
  throw error;
131
213
  }
@@ -133,11 +215,11 @@ try {
133
215
 
134
216
  ## Signal Composition
135
217
 
136
- If you pass an `AbortSignal` to a request, the middleware composes both signals:
218
+ If you pass an `AbortSignal` to a specific SDK request, the middleware composes both. Whichever fires first wins:
137
219
 
138
220
  ```typescript
139
221
  const controller = new AbortController();
140
- setTimeout(() => controller.abort(), 5000);
222
+ setTimeout(() => controller.abort(), 2000);
141
223
 
142
224
  await dynamodb.send(
143
225
  new GetItemCommand({
@@ -153,8 +235,7 @@ await dynamodb.send(
153
235
 
154
236
  ### `withLambdaDeadline(handler, options?)`
155
237
 
156
- Wraps a Lambda handler to store the Lambda context in `AsyncLocalStorage`. Required for the middleware to access
157
- `getRemainingTimeInMillis()`.
238
+ Wraps a Lambda handler. Computes `AbortSignal.timeout(remaining - flushBuffer)` and stores it in `AsyncLocalStorage`.
158
239
 
159
240
  ```typescript
160
241
  function withLambdaDeadline<TEvent, TResult>(
@@ -163,25 +244,25 @@ function withLambdaDeadline<TEvent, TResult>(
163
244
  ): (event: TEvent, context: LambdaContextLike) => Promise<TResult>;
164
245
  ```
165
246
 
166
- ### `deadlineMiddleware(options?)`
247
+ ### `deadlineMiddleware()`
167
248
 
168
- Returns a `Pluggable` for `client.middlewareStack.use()`.
249
+ Returns a `Pluggable` for `client.middlewareStack.use()`. Attaches the stored deadline signal to outgoing requests.
169
250
 
170
251
  ```typescript
171
- function deadlineMiddleware(options?: DeadlineOptions): Pluggable<object, object>;
252
+ function deadlineMiddleware(): Pluggable<object, object>;
172
253
  ```
173
254
 
174
- ### `getRemainingTimeInMillis()`
255
+ ### `setDeadlineSignal(signal)`
175
256
 
176
- Accessor for the current Lambda's remaining execution time. Returns `undefined` outside a Lambda context.
257
+ Replaces the deadline signal for the current invocation. Must be called within a `withLambdaDeadline()` scope.
177
258
 
178
259
  ```typescript
179
- function getRemainingTimeInMillis(): number | undefined;
260
+ function setDeadlineSignal(signal: AbortSignal): void;
180
261
  ```
181
262
 
182
263
  ### `isDeadlineExceeded(error)`
183
264
 
184
- Type guard for deadline-triggered abort errors.
265
+ Type guard for deadline-triggered errors.
185
266
 
186
267
  ```typescript
187
268
  function isDeadlineExceeded(error: unknown): error is DeadlineExceededError;
@@ -189,12 +270,14 @@ function isDeadlineExceeded(error: unknown): error is DeadlineExceededError;
189
270
 
190
271
  ### `DeadlineExceededError`
191
272
 
273
+ Thrown by `withLambdaDeadline` when `remainingTime ≤ flushBufferMs`.
274
+
192
275
  ```typescript
193
276
  class DeadlineExceededError extends Error {
194
277
  readonly name: "DeadlineExceededError";
195
- readonly deadlineMs: Milliseconds;
196
- readonly flushBufferMs: FlushBufferMs;
197
- readonly remainingMs: Milliseconds;
278
+ readonly deadlineMs: number;
279
+ readonly flushBufferMs: number;
280
+ readonly remainingMs: number;
198
281
  }
199
282
  ```
200
283
 
@@ -203,29 +286,24 @@ class DeadlineExceededError extends Error {
203
286
  ```typescript
204
287
  interface DeadlineOptions {
205
288
  readonly flushBufferMs?: number; // Default: 1000
206
- readonly telemetryEnabled?: boolean; // Default: true
207
289
  }
208
290
  ```
209
291
 
210
292
  ### Types
211
293
 
212
- | Type | Description |
213
- | --------------------- | ---------------------------------------------------------------------------- |
214
- | `Milliseconds` | Branded number representing a duration in ms |
215
- | `FlushBufferMs` | Branded number for the flush buffer |
216
- | `RequestDeadlineMs` | Branded number for a computed deadline |
217
- | `DeadlineComputation` | Discriminated union: `"deadline"` \| `"insufficient-time"` \| `"no-context"` |
218
- | `LambdaContextLike` | Minimal interface: `{ getRemainingTimeInMillis?(): number }` |
294
+ | Type | Description |
295
+ | ------------------- | ---------------------------------------------------------------- |
296
+ | `LambdaContextLike` | Minimal interface: `{ getRemainingTimeInMillis?: () => number }` |
219
297
 
220
298
  ## Reporting Bugs
221
299
 
222
- Found a bug? Please open a [GitHub Issue](https://github.com/mikkopiu/lambda-deadline-middleware/issues/new) with:
300
+ Found a bug? Please open a [GitHub Issue](https://github.com/mikkopiu/lambda-deadline-middleware/issues/new) with a
301
+ minimal reproduction, your Node.js version, and AWS SDK version. For security vulnerabilities, see
302
+ [SECURITY.md](SECURITY.md).
223
303
 
224
- - Your Node.js version and AWS SDK version
225
- - A minimal code snippet reproducing the problem
226
- - Expected vs actual behavior
304
+ ## Changelog
227
305
 
228
- For security vulnerabilities, see [SECURITY.md](SECURITY.md) instead.
306
+ See [GitHub Releases](https://github.com/mikkopiu/lambda-deadline-middleware/releases).
229
307
 
230
308
  ## License
231
309
 
@@ -1,7 +1,30 @@
1
+ import type { DeadlineOptions } from "./types.js";
1
2
  export interface LambdaContextLike {
2
3
  getRemainingTimeInMillis?: () => number;
3
4
  }
4
- export declare const run: <T>(context: LambdaContextLike | null | undefined, fn: () => T) => T;
5
- export declare const getRemainingTimeInMillis: () => number | undefined;
5
+ /**
6
+ * Store an external AbortSignal for the current invocation.
7
+ * When set, the SDK middleware uses this signal directly instead of
8
+ * the auto-computed deadline signal.
9
+ *
10
+ * Call this at the start of your handler, before any SDK calls.
11
+ * The signal is scoped to the current async context (AsyncLocalStorage).
12
+ */
13
+ export declare const setDeadlineSignal: (signal: AbortSignal) => void;
14
+ /**
15
+ * Retrieve the deadline signal for the current invocation, if one exists.
16
+ */
17
+ export declare const getDeadlineSignal: () => AbortSignal | undefined;
18
+ type AsyncHandler<
19
+ TEvent,
20
+ TContext extends LambdaContextLike,
21
+ TResult
22
+ > = (event: TEvent, context: TContext) => Promise<TResult>;
23
+ export declare const withLambdaDeadline: <
24
+ TEvent,
25
+ TContext extends LambdaContextLike,
26
+ TResult
27
+ >(handler: AsyncHandler<TEvent, TContext, TResult>, options?: DeadlineOptions) => AsyncHandler<TEvent, TContext, TResult>;
28
+ export {};
6
29
 
7
30
  //# sourceMappingURL=context-store.d.ts.map
@@ -1 +1 @@
1
- {"mappings":"AAOA,iBAAiB,kBAAkB;CACjC;AACF;AAWA,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\n// Sentinel allows AsyncLocalStorage.run() to accept null/undefined context\n// without throwing, while the accessor can distinguish \"no context stored\"\n// from \"context present but missing the method\".\nconst NO_CONTEXT: unique symbol = Symbol(\"no-context\");\n\ntype StoreValue = LambdaContextLike | typeof NO_CONTEXT;\n\nconst contextStorage = new AsyncLocalStorage<StoreValue>();\n\nexport const run = <T>(context: LambdaContextLike | null | undefined, fn: () => T): T => {\n const value: StoreValue = context ?? NO_CONTEXT;\n return contextStorage.run(value, fn);\n};\n\nexport const getRemainingTimeInMillis = (): number | undefined => {\n const store = contextStorage.getStore();\n\n if (store === undefined || store === NO_CONTEXT) {\n return undefined;\n }\n\n if (typeof store.getRemainingTimeInMillis !== \"function\") {\n return undefined;\n }\n\n return store.getRemainingTimeInMillis();\n};\n"]}
1
+ {"mappings":"AAOA,cAAc,uBAAuB;AAIrC,iBAAiB,kBAAkB;CACjC;AACF;;;;;;;;;AAgBA,OAAO,cAAM,oBAAqB,QAAQ;;;;AAW1C,OAAO,cAAM,yBAAwB;KAOhC;CAAa;CAAQ,iBAAiB;CAAmB;KAC5D,OAAO,QACP,SAAS,aACN,QAAQ;AAEb,OAAO,cAAM;CACV;CAAQ,iBAAiB;CAAmB;EAC3C,SAAS,aAAa,QAAQ,UAAU,UACxC,UAAU,oBACT,aAAa,QAAQ,UAAU","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\nimport { DeadlineExceededError } from \"./error.js\";\n\nimport type { DeadlineOptions } from \"./types.js\";\n\n// AsyncLocalStorage propagates the deadline signal through the entire async call chain.\n// SDK middleware executes deep in the Smithy stack where we can't pass it through function signatures.\nexport interface LambdaContextLike {\n getRemainingTimeInMillis?: () => number;\n}\n\ninterface DeadlineStore {\n signal?: AbortSignal;\n}\n\nconst contextStorage = new AsyncLocalStorage<DeadlineStore>();\n\n/**\n * Store an external AbortSignal for the current invocation.\n * When set, the SDK middleware uses this signal directly instead of\n * the auto-computed deadline signal.\n *\n * Call this at the start of your handler, before any SDK calls.\n * The signal is scoped to the current async context (AsyncLocalStorage).\n */\nexport const setDeadlineSignal = (signal: AbortSignal): void => {\n const store = contextStorage.getStore();\n if (store === undefined) {\n throw new Error(\"setDeadlineSignal() must be called within a withLambdaDeadline() scope\");\n }\n store.signal = signal;\n};\n\n/**\n * Retrieve the deadline signal for the current invocation, if one exists.\n */\nexport const getDeadlineSignal = (): AbortSignal | undefined => {\n const store = contextStorage.getStore();\n if (store === undefined) return undefined;\n return store.signal;\n};\n\n// Handler wrapper — computes the deadline signal once at invocation start and stores it via AsyncLocalStorage.\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 options?: DeadlineOptions,\n ): AsyncHandler<TEvent, TContext, TResult> =>\n async (event: TEvent, context: TContext): Promise<TResult> => {\n const store: DeadlineStore = {};\n\n // Compute the auto-deadline signal once, up front.\n // If the user calls setDeadlineSignal() later, it overwrites this.\n // oxlint-disable-next-line typescript/no-unnecessary-condition -- runtime safety: context may be null/undefined despite types (e.g. untyped callers)\n if (context !== null && context !== undefined) {\n const remaining =\n typeof context.getRemainingTimeInMillis === \"function\"\n ? context.getRemainingTimeInMillis()\n : undefined;\n\n if (remaining !== undefined) {\n const rawBuffer = options?.flushBufferMs ?? 1000;\n if (rawBuffer < 0) {\n throw new TypeError(`flushBufferMs option must be non-negative, received: ${rawBuffer}`);\n }\n const deadline = remaining - rawBuffer;\n\n if (deadline <= 0) {\n throw new DeadlineExceededError({\n deadlineMs: 0,\n flushBufferMs: rawBuffer,\n remainingMs: remaining,\n });\n }\n\n store.signal = AbortSignal.timeout(deadline);\n }\n }\n\n return contextStorage.run(store, async () => handler(event, context));\n };\n"]}
@@ -1,24 +1,55 @@
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");
4
+ import { DeadlineExceededError } from "./error.js";
8
5
  const contextStorage = new AsyncLocalStorage();
9
- export const run = (context, fn) => {
10
- const value = context ?? NO_CONTEXT;
11
- return contextStorage.run(value, fn);
12
- };
13
- export const getRemainingTimeInMillis = () => {
6
+ /**
7
+ * Store an external AbortSignal for the current invocation.
8
+ * When set, the SDK middleware uses this signal directly instead of
9
+ * the auto-computed deadline signal.
10
+ *
11
+ * Call this at the start of your handler, before any SDK calls.
12
+ * The signal is scoped to the current async context (AsyncLocalStorage).
13
+ */
14
+ export const setDeadlineSignal = (signal) => {
14
15
  const store = contextStorage.getStore();
15
- if (store === undefined || store === NO_CONTEXT) {
16
- return undefined;
16
+ if (store === undefined) {
17
+ throw new Error("setDeadlineSignal() must be called within a withLambdaDeadline() scope");
17
18
  }
18
- if (typeof store.getRemainingTimeInMillis !== "function") {
19
- return undefined;
19
+ store.signal = signal;
20
+ };
21
+ /**
22
+ * Retrieve the deadline signal for the current invocation, if one exists.
23
+ */
24
+ export const getDeadlineSignal = () => {
25
+ const store = contextStorage.getStore();
26
+ if (store === undefined) return undefined;
27
+ return store.signal;
28
+ };
29
+ export const withLambdaDeadline = (handler, options) => async (event, context) => {
30
+ const store = {};
31
+ // Compute the auto-deadline signal once, up front.
32
+ // If the user calls setDeadlineSignal() later, it overwrites this.
33
+ // oxlint-disable-next-line typescript/no-unnecessary-condition -- runtime safety: context may be null/undefined despite types (e.g. untyped callers)
34
+ if (context !== null && context !== undefined) {
35
+ const remaining = typeof context.getRemainingTimeInMillis === "function" ? context.getRemainingTimeInMillis() : undefined;
36
+ if (remaining !== undefined) {
37
+ const rawBuffer = options?.flushBufferMs ?? 1e3;
38
+ if (rawBuffer < 0) {
39
+ throw new TypeError(`flushBufferMs option must be non-negative, received: ${rawBuffer}`);
40
+ }
41
+ const deadline = remaining - rawBuffer;
42
+ if (deadline <= 0) {
43
+ throw new DeadlineExceededError({
44
+ deadlineMs: 0,
45
+ flushBufferMs: rawBuffer,
46
+ remainingMs: remaining
47
+ });
48
+ }
49
+ store.signal = AbortSignal.timeout(deadline);
50
+ }
20
51
  }
21
- return store.getRemainingTimeInMillis();
52
+ return contextStorage.run(store, async () => handler(event, context));
22
53
  };
23
54
 
24
55
  //# sourceMappingURL=context-store.js.map
@@ -1 +1 @@
1
- {"mappings":";;AAGA,SAAS,yBAAyB;;;;AAWlC,MAAM,aAA4B,OAAO,YAAY;AAIrD,MAAM,iBAAiB,IAAI,kBAA8B;AAEzD,OAAO,MAAM,OAAU,SAA+C,OAAmB;CACvF,MAAM,QAAoB,WAAW;CACrC,OAAO,eAAe,IAAI,OAAO,EAAE;AACrC;AAEA,OAAO,MAAM,iCAAqD;CAChE,MAAM,QAAQ,eAAe,SAAS;CAEtC,IAAI,UAAU,aAAa,UAAU,YAAY;EAC/C,OAAO;CACT;CAEA,IAAI,OAAO,MAAM,6BAA6B,YAAY;EACxD,OAAO;CACT;CAEA,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\n// Sentinel allows AsyncLocalStorage.run() to accept null/undefined context\n// without throwing, while the accessor can distinguish \"no context stored\"\n// from \"context present but missing the method\".\nconst NO_CONTEXT: unique symbol = Symbol(\"no-context\");\n\ntype StoreValue = LambdaContextLike | typeof NO_CONTEXT;\n\nconst contextStorage = new AsyncLocalStorage<StoreValue>();\n\nexport const run = <T>(context: LambdaContextLike | null | undefined, fn: () => T): T => {\n const value: StoreValue = context ?? NO_CONTEXT;\n return contextStorage.run(value, fn);\n};\n\nexport const getRemainingTimeInMillis = (): number | undefined => {\n const store = contextStorage.getStore();\n\n if (store === undefined || store === NO_CONTEXT) {\n return undefined;\n }\n\n if (typeof store.getRemainingTimeInMillis !== \"function\") {\n return undefined;\n }\n\n return store.getRemainingTimeInMillis();\n};\n"]}
1
+ {"mappings":";;AAGA,SAAS,yBAAyB;AAElC,SAAS,6BAA6B;AActC,MAAM,iBAAiB,IAAI,kBAAiC;;;;;;;;;AAU5D,OAAO,MAAM,qBAAqB,WAA8B;CAC9D,MAAM,QAAQ,eAAe,SAAS;CACtC,IAAI,UAAU,WAAW;EACvB,MAAM,IAAI,MAAM,wEAAwE;CAC1F;CACA,MAAM,SAAS;AACjB;;;;AAKA,OAAO,MAAM,0BAAmD;CAC9D,MAAM,QAAQ,eAAe,SAAS;CACtC,IAAI,UAAU,WAAW,OAAO;CAChC,OAAO,MAAM;AACf;AAQA,OAAO,MAAM,sBAET,SACA,YAEF,OAAO,OAAe,YAAwC;CAC5D,MAAM,QAAuB,CAAC;;;;CAK9B,IAAI,YAAY,QAAQ,YAAY,WAAW;EAC7C,MAAM,YACJ,OAAO,QAAQ,6BAA6B,aACxC,QAAQ,yBAAyB,IACjC;EAEN,IAAI,cAAc,WAAW;GAC3B,MAAM,YAAY,SAAS,iBAAiB;GAC5C,IAAI,YAAY,GAAG;IACjB,MAAM,IAAI,UAAU,wDAAwD,WAAW;GACzF;GACA,MAAM,WAAW,YAAY;GAE7B,IAAI,YAAY,GAAG;IACjB,MAAM,IAAI,sBAAsB;KAC9B,YAAY;KACZ,eAAe;KACf,aAAa;IACf,CAAC;GACH;GAEA,MAAM,SAAS,YAAY,QAAQ,QAAQ;EAC7C;CACF;CAEA,OAAO,eAAe,IAAI,OAAO,YAAY,QAAQ,OAAO,OAAO,CAAC;AACtE","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\nimport { DeadlineExceededError } from \"./error.js\";\n\nimport type { DeadlineOptions } from \"./types.js\";\n\n// AsyncLocalStorage propagates the deadline signal through the entire async call chain.\n// SDK middleware executes deep in the Smithy stack where we can't pass it through function signatures.\nexport interface LambdaContextLike {\n getRemainingTimeInMillis?: () => number;\n}\n\ninterface DeadlineStore {\n signal?: AbortSignal;\n}\n\nconst contextStorage = new AsyncLocalStorage<DeadlineStore>();\n\n/**\n * Store an external AbortSignal for the current invocation.\n * When set, the SDK middleware uses this signal directly instead of\n * the auto-computed deadline signal.\n *\n * Call this at the start of your handler, before any SDK calls.\n * The signal is scoped to the current async context (AsyncLocalStorage).\n */\nexport const setDeadlineSignal = (signal: AbortSignal): void => {\n const store = contextStorage.getStore();\n if (store === undefined) {\n throw new Error(\"setDeadlineSignal() must be called within a withLambdaDeadline() scope\");\n }\n store.signal = signal;\n};\n\n/**\n * Retrieve the deadline signal for the current invocation, if one exists.\n */\nexport const getDeadlineSignal = (): AbortSignal | undefined => {\n const store = contextStorage.getStore();\n if (store === undefined) return undefined;\n return store.signal;\n};\n\n// Handler wrapper — computes the deadline signal once at invocation start and stores it via AsyncLocalStorage.\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 options?: DeadlineOptions,\n ): AsyncHandler<TEvent, TContext, TResult> =>\n async (event: TEvent, context: TContext): Promise<TResult> => {\n const store: DeadlineStore = {};\n\n // Compute the auto-deadline signal once, up front.\n // If the user calls setDeadlineSignal() later, it overwrites this.\n // oxlint-disable-next-line typescript/no-unnecessary-condition -- runtime safety: context may be null/undefined despite types (e.g. untyped callers)\n if (context !== null && context !== undefined) {\n const remaining =\n typeof context.getRemainingTimeInMillis === \"function\"\n ? context.getRemainingTimeInMillis()\n : undefined;\n\n if (remaining !== undefined) {\n const rawBuffer = options?.flushBufferMs ?? 1000;\n if (rawBuffer < 0) {\n throw new TypeError(`flushBufferMs option must be non-negative, received: ${rawBuffer}`);\n }\n const deadline = remaining - rawBuffer;\n\n if (deadline <= 0) {\n throw new DeadlineExceededError({\n deadlineMs: 0,\n flushBufferMs: rawBuffer,\n remainingMs: remaining,\n });\n }\n\n store.signal = AbortSignal.timeout(deadline);\n }\n }\n\n return contextStorage.run(store, async () => handler(event, context));\n };\n"]}
package/dist/error.d.ts CHANGED
@@ -1,14 +1,13 @@
1
- import type { FlushBufferMs, Milliseconds } from "./types.js";
2
1
  interface DeadlineExceededInit {
3
- readonly deadlineMs: Milliseconds;
4
- readonly flushBufferMs: FlushBufferMs;
5
- readonly remainingMs: Milliseconds;
2
+ readonly deadlineMs: number;
3
+ readonly flushBufferMs: number;
4
+ readonly remainingMs: number;
6
5
  }
7
6
  export declare class DeadlineExceededError extends Error {
8
7
  override readonly name = "DeadlineExceededError";
9
- readonly deadlineMs: Milliseconds;
10
- readonly flushBufferMs: FlushBufferMs;
11
- readonly remainingMs: Milliseconds;
8
+ readonly deadlineMs: number;
9
+ readonly flushBufferMs: number;
10
+ readonly remainingMs: number;
12
11
  constructor(init: DeadlineExceededInit);
13
12
  }
14
13
  export declare const isDeadlineExceeded: (error: unknown) => error is DeadlineExceededError;
@@ -1 +1 @@
1
- {"mappings":"AAGA,cAAc,eAAe,oBAAoB;UAEvC,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 { FlushBufferMs, Milliseconds } from \"./types.js\";\n\ninterface DeadlineExceededInit {\n readonly deadlineMs: Milliseconds;\n readonly flushBufferMs: FlushBufferMs;\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: FlushBufferMs;\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
+ {"mappings":"UAGU,qBAAqB;UACpB;UACA;UACA;AACX;AAEA,OAAO,cAAM,8BAA8B,MAAM;CAC/C,kBAAkB,OAAO;CACzB,SAAS;CACT,SAAS;CACT,SAAS;CAET,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\ninterface DeadlineExceededInit {\n readonly deadlineMs: number;\n readonly flushBufferMs: number;\n readonly remainingMs: number;\n}\n\nexport class DeadlineExceededError extends Error {\n override readonly name = \"DeadlineExceededError\" as const;\n readonly deadlineMs: number;\n readonly flushBufferMs: number;\n readonly remainingMs: number;\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 { FlushBufferMs, Milliseconds } from \"./types.js\";\n\ninterface DeadlineExceededInit {\n readonly deadlineMs: Milliseconds;\n readonly flushBufferMs: FlushBufferMs;\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: FlushBufferMs;\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
+ {"mappings":"AASA,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\ninterface DeadlineExceededInit {\n readonly deadlineMs: number;\n readonly flushBufferMs: number;\n readonly remainingMs: number;\n}\n\nexport class DeadlineExceededError extends Error {\n override readonly name = \"DeadlineExceededError\" as const;\n readonly deadlineMs: number;\n readonly flushBufferMs: number;\n readonly remainingMs: number;\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/index.d.ts CHANGED
@@ -1,8 +1,7 @@
1
- export { withLambdaDeadline } from "./handler-wrapper.js";
2
- export { deadlineMiddleware } from "./registration.js";
1
+ export { withLambdaDeadline, setDeadlineSignal } from "./context-store.js";
2
+ export { deadlineMiddleware } from "./middleware.js";
3
3
  export { DeadlineExceededError, isDeadlineExceeded } from "./error.js";
4
- export { getRemainingTimeInMillis } from "./context-store.js";
5
- export type { Milliseconds, FlushBufferMs, RequestDeadlineMs, DeadlineComputation, DeadlineMiddlewareConfig, DeadlineOptions } from "./types.js";
4
+ export type { DeadlineOptions } from "./types.js";
6
5
  export type { LambdaContextLike } from "./context-store.js";
7
6
 
8
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"mappings":"AAGA,SAAS,0BAA0B;AACnC,SAAS,0BAA0B;AACnC,SAAS,uBAAuB,0BAA0B;AAC1D,SAAS,gCAAgC;AAEzC,cACE,cACA,eACA,mBACA,qBACA,0BACA,uBACK;AAEP,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 \"./registration.js\";\nexport { DeadlineExceededError, isDeadlineExceeded } from \"./error.js\";\nexport { getRemainingTimeInMillis } from \"./context-store.js\";\n\nexport type {\n Milliseconds,\n FlushBufferMs,\n RequestDeadlineMs,\n DeadlineComputation,\n DeadlineMiddlewareConfig,\n DeadlineOptions,\n} from \"./types.js\";\n\nexport type { LambdaContextLike } from \"./context-store.js\";\n"]}
1
+ {"mappings":"AAGA,SAAS,oBAAoB,yBAAyB;AACtD,SAAS,0BAA0B;AACnC,SAAS,uBAAuB,0BAA0B;AAE1D,cAAc,uBAAuB;AACrC,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, setDeadlineSignal } from \"./context-store.js\";\nexport { deadlineMiddleware } from \"./middleware.js\";\nexport { DeadlineExceededError, isDeadlineExceeded } from \"./error.js\";\n\nexport type { DeadlineOptions } from \"./types.js\";\nexport type { LambdaContextLike } from \"./context-store.js\";\n"]}
package/dist/index.js CHANGED
@@ -1,8 +1,7 @@
1
1
  // SPDX-FileCopyrightText: 2026 lambda-deadline-middleware contributors
2
2
  // SPDX-License-Identifier: MIT
3
- export { withLambdaDeadline } from "./handler-wrapper.js";
4
- export { deadlineMiddleware } from "./registration.js";
3
+ export { withLambdaDeadline, setDeadlineSignal } from "./context-store.js";
4
+ export { deadlineMiddleware } from "./middleware.js";
5
5
  export { DeadlineExceededError, isDeadlineExceeded } from "./error.js";
6
- export { getRemainingTimeInMillis } from "./context-store.js";
7
6
 
8
7
  //# sourceMappingURL=index.js.map
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 \"./registration.js\";\nexport { DeadlineExceededError, isDeadlineExceeded } from \"./error.js\";\nexport { getRemainingTimeInMillis } from \"./context-store.js\";\n\nexport type {\n Milliseconds,\n FlushBufferMs,\n RequestDeadlineMs,\n DeadlineComputation,\n DeadlineMiddlewareConfig,\n DeadlineOptions,\n} from \"./types.js\";\n\nexport type { LambdaContextLike } from \"./context-store.js\";\n"]}
1
+ {"mappings":";;AAGA,SAAS,oBAAoB,yBAAyB;AACtD,SAAS,0BAA0B;AACnC,SAAS,uBAAuB,0BAA0B","names":[],"sources":["src/index.ts"],"version":3,"sourcesContent":["// SPDX-FileCopyrightText: 2026 lambda-deadline-middleware contributors\n// SPDX-License-Identifier: MIT\n\nexport { withLambdaDeadline, setDeadlineSignal } from \"./context-store.js\";\nexport { deadlineMiddleware } from \"./middleware.js\";\nexport { DeadlineExceededError, isDeadlineExceeded } from \"./error.js\";\n\nexport type { DeadlineOptions } from \"./types.js\";\nexport type { LambdaContextLike } from \"./context-store.js\";\n"]}
@@ -1,15 +1,7 @@
1
- import type { DeadlineComputation, DeadlineMiddlewareConfig, RequestDeadlineMs } from "./types.js";
2
- import type { FinalizeRequestMiddleware } from "@smithy/types";
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;
9
- export declare const composeSignals: (existing: AbortSignal | undefined, deadline: AbortSignal) => AbortSignal;
10
- export declare const deadlineMiddlewareHandler: <
1
+ import type { Pluggable } from "@smithy/types";
2
+ export declare const deadlineMiddleware: <
11
3
  Input extends object,
12
4
  Output extends object
13
- >(config: DeadlineMiddlewareConfig) => FinalizeRequestMiddleware<Input, Output>;
5
+ >() => Pluggable<Input, Output>;
14
6
 
15
7
  //# sourceMappingURL=middleware.d.ts.map