@jaypie/express 1.2.3 → 1.2.4-rc0

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.
@@ -1,12 +1,586 @@
1
1
  'use strict';
2
2
 
3
+ var node_stream = require('node:stream');
3
4
  var errors = require('@jaypie/errors');
4
5
  var kit = require('@jaypie/kit');
5
6
  var expressCors = require('cors');
6
7
  var logger$2 = require('@jaypie/logger');
7
8
  var aws = require('@jaypie/aws');
8
9
  var datadog = require('@jaypie/datadog');
9
- var serverlessExpress = require('@codegenie/serverless-express');
10
+
11
+ //
12
+ //
13
+ // LambdaRequest Class
14
+ //
15
+ /**
16
+ * Mock IncomingMessage that extends Readable stream.
17
+ * Provides Express-compatible request interface from Lambda Function URL events.
18
+ */
19
+ class LambdaRequest extends node_stream.Readable {
20
+ constructor(options) {
21
+ super();
22
+ this.httpVersion = "1.1";
23
+ this.httpVersionMajor = 1;
24
+ this.httpVersionMinor = 1;
25
+ this.complete = false;
26
+ this.baseUrl = "";
27
+ this.params = {};
28
+ this.query = {};
29
+ this.bodyPushed = false;
30
+ this.method = options.method;
31
+ this.url = options.url;
32
+ this.originalUrl = options.url;
33
+ this.path = options.url.split("?")[0];
34
+ this.headers = this.normalizeHeaders(options.headers);
35
+ this.bodyBuffer = options.body ?? null;
36
+ // Store Lambda context
37
+ this._lambdaContext = options.lambdaContext;
38
+ this._lambdaEvent = options.lambdaEvent;
39
+ // Create mock socket
40
+ this.socket = {
41
+ destroy: () => { },
42
+ encrypted: options.protocol === "https",
43
+ remoteAddress: options.remoteAddress,
44
+ };
45
+ this.connection = this.socket;
46
+ }
47
+ //
48
+ // Readable stream implementation
49
+ //
50
+ _read() {
51
+ if (!this.bodyPushed) {
52
+ if (this.bodyBuffer && this.bodyBuffer.length > 0) {
53
+ this.push(this.bodyBuffer);
54
+ }
55
+ this.push(null); // Signal end of stream
56
+ this.bodyPushed = true;
57
+ this.complete = true;
58
+ }
59
+ }
60
+ //
61
+ // Express helper methods
62
+ //
63
+ get(headerName) {
64
+ const key = headerName.toLowerCase();
65
+ const value = this.headers[key];
66
+ return Array.isArray(value) ? value[0] : value;
67
+ }
68
+ header(headerName) {
69
+ return this.get(headerName);
70
+ }
71
+ //
72
+ // Private helpers
73
+ //
74
+ normalizeHeaders(headers) {
75
+ const normalized = {};
76
+ for (const [key, value] of Object.entries(headers)) {
77
+ normalized[key.toLowerCase()] = value;
78
+ }
79
+ return normalized;
80
+ }
81
+ }
82
+ //
83
+ //
84
+ // Factory Function
85
+ //
86
+ /**
87
+ * Create a LambdaRequest from a Function URL event.
88
+ */
89
+ function createLambdaRequest(event, context) {
90
+ // Build URL with query string
91
+ const url = event.rawQueryString
92
+ ? `${event.rawPath}?${event.rawQueryString}`
93
+ : event.rawPath;
94
+ // Decode body if present
95
+ let body = null;
96
+ if (event.body) {
97
+ body = event.isBase64Encoded
98
+ ? Buffer.from(event.body, "base64")
99
+ : Buffer.from(event.body, "utf8");
100
+ }
101
+ // Normalize cookies into Cookie header if not already present
102
+ const headers = { ...event.headers };
103
+ if (event.cookies && event.cookies.length > 0 && !headers.cookie) {
104
+ headers.cookie = event.cookies.join("; ");
105
+ }
106
+ return new LambdaRequest({
107
+ body,
108
+ headers,
109
+ lambdaContext: context,
110
+ lambdaEvent: event,
111
+ method: event.requestContext.http.method,
112
+ protocol: event.requestContext.http.protocol.split("/")[0].toLowerCase(),
113
+ remoteAddress: event.requestContext.http.sourceIp,
114
+ url,
115
+ });
116
+ }
117
+
118
+ //
119
+ //
120
+ // Constants
121
+ //
122
+ const BINARY_CONTENT_TYPE_PATTERNS = [
123
+ /^application\/octet-stream$/,
124
+ /^application\/pdf$/,
125
+ /^application\/zip$/,
126
+ /^audio\//,
127
+ /^font\//,
128
+ /^image\//,
129
+ /^video\//,
130
+ ];
131
+ //
132
+ //
133
+ // LambdaResponseBuffered Class
134
+ //
135
+ /**
136
+ * Mock ServerResponse that buffers the response.
137
+ * Collects status, headers, and body chunks, then returns a Lambda response.
138
+ */
139
+ class LambdaResponseBuffered extends node_stream.Writable {
140
+ constructor() {
141
+ super();
142
+ this.statusCode = 200;
143
+ this.statusMessage = "OK";
144
+ // Mock socket to satisfy Express/finalhandler checks
145
+ this.socket = {
146
+ cork: () => { },
147
+ destroy: () => { },
148
+ remoteAddress: "127.0.0.1",
149
+ uncork: () => { },
150
+ writable: true,
151
+ };
152
+ this._chunks = [];
153
+ this._headers = new Map();
154
+ this._headersSent = false;
155
+ this._resolve = null;
156
+ }
157
+ //
158
+ // Promise-based API for getting final result
159
+ //
160
+ getResult() {
161
+ return new Promise((resolve) => {
162
+ if (this.writableEnded) {
163
+ resolve(this.buildResult());
164
+ }
165
+ else {
166
+ this._resolve = resolve;
167
+ }
168
+ });
169
+ }
170
+ //
171
+ // Header management
172
+ //
173
+ setHeader(name, value) {
174
+ if (this._headersSent) {
175
+ // In production, log warning but don't throw to match Express behavior
176
+ return this;
177
+ }
178
+ this._headers.set(name.toLowerCase(), String(value));
179
+ return this;
180
+ }
181
+ getHeader(name) {
182
+ return this._headers.get(name.toLowerCase());
183
+ }
184
+ removeHeader(name) {
185
+ this._headers.delete(name.toLowerCase());
186
+ }
187
+ getHeaders() {
188
+ const headers = {};
189
+ for (const [key, value] of this._headers) {
190
+ headers[key] = value;
191
+ }
192
+ return headers;
193
+ }
194
+ hasHeader(name) {
195
+ return this._headers.has(name.toLowerCase());
196
+ }
197
+ getHeaderNames() {
198
+ return Array.from(this._headers.keys());
199
+ }
200
+ writeHead(statusCode, statusMessageOrHeaders, headers) {
201
+ this.statusCode = statusCode;
202
+ let headersToSet;
203
+ if (typeof statusMessageOrHeaders === "string") {
204
+ this.statusMessage = statusMessageOrHeaders;
205
+ headersToSet = headers;
206
+ }
207
+ else if (statusMessageOrHeaders &&
208
+ typeof statusMessageOrHeaders === "object") {
209
+ headersToSet = statusMessageOrHeaders;
210
+ }
211
+ if (headersToSet) {
212
+ for (const [key, value] of Object.entries(headersToSet)) {
213
+ if (value !== undefined) {
214
+ this.setHeader(key, value);
215
+ }
216
+ }
217
+ }
218
+ this._headersSent = true;
219
+ return this;
220
+ }
221
+ get headersSent() {
222
+ return this._headersSent;
223
+ }
224
+ //
225
+ // Express compatibility methods
226
+ //
227
+ status(code) {
228
+ this.statusCode = code;
229
+ return this;
230
+ }
231
+ json(data) {
232
+ this.setHeader("content-type", "application/json");
233
+ this.end(JSON.stringify(data));
234
+ return this;
235
+ }
236
+ send(body) {
237
+ if (typeof body === "object" && !Buffer.isBuffer(body)) {
238
+ return this.json(body);
239
+ }
240
+ this.end(body);
241
+ return this;
242
+ }
243
+ //
244
+ // Writable stream implementation
245
+ //
246
+ _write(chunk, encoding, // eslint-disable-line no-undef
247
+ callback) {
248
+ const buffer = Buffer.isBuffer(chunk)
249
+ ? chunk
250
+ : Buffer.from(chunk, encoding);
251
+ this._chunks.push(buffer);
252
+ this._headersSent = true;
253
+ callback();
254
+ }
255
+ _final(callback) {
256
+ if (this._resolve) {
257
+ this._resolve(this.buildResult());
258
+ }
259
+ callback();
260
+ }
261
+ //
262
+ // Private helpers
263
+ //
264
+ buildResult() {
265
+ const body = Buffer.concat(this._chunks);
266
+ const contentType = this.getHeader("content-type") || "";
267
+ // Determine if response should be base64 encoded
268
+ const isBase64Encoded = this.isBinaryContentType(contentType);
269
+ // Build headers object
270
+ const headers = {};
271
+ const cookies = [];
272
+ for (const [key, value] of this._headers) {
273
+ if (key === "set-cookie") {
274
+ // Collect Set-Cookie headers for v2 response format
275
+ if (Array.isArray(value)) {
276
+ cookies.push(...value);
277
+ }
278
+ else {
279
+ cookies.push(value);
280
+ }
281
+ }
282
+ else {
283
+ headers[key] = Array.isArray(value) ? value.join(", ") : String(value);
284
+ }
285
+ }
286
+ const result = {
287
+ body: isBase64Encoded ? body.toString("base64") : body.toString("utf8"),
288
+ headers,
289
+ isBase64Encoded,
290
+ statusCode: this.statusCode,
291
+ };
292
+ // Only include cookies if present (v2 format)
293
+ if (cookies.length > 0) {
294
+ result.cookies = cookies;
295
+ }
296
+ return result;
297
+ }
298
+ isBinaryContentType(contentType) {
299
+ return BINARY_CONTENT_TYPE_PATTERNS.some((pattern) => pattern.test(contentType));
300
+ }
301
+ }
302
+
303
+ //
304
+ //
305
+ // LambdaResponseStreaming Class
306
+ //
307
+ /**
308
+ * Mock ServerResponse that streams directly to Lambda responseStream.
309
+ * Uses awslambda.HttpResponseStream.from() to set status and headers.
310
+ */
311
+ class LambdaResponseStreaming extends node_stream.Writable {
312
+ constructor(responseStream) {
313
+ super();
314
+ this.statusCode = 200;
315
+ this.statusMessage = "OK";
316
+ // Mock socket to satisfy Express/finalhandler checks
317
+ this.socket = {
318
+ cork: () => { },
319
+ destroy: () => { },
320
+ remoteAddress: "127.0.0.1",
321
+ uncork: () => { },
322
+ writable: true,
323
+ };
324
+ this._headers = new Map();
325
+ this._headersSent = false;
326
+ this._pendingWrites = [];
327
+ this._wrappedStream = null;
328
+ this._responseStream = responseStream;
329
+ }
330
+ //
331
+ // Header management
332
+ //
333
+ setHeader(name, value) {
334
+ if (this._headersSent) {
335
+ // In streaming mode, log warning but don't throw
336
+ // Headers cannot be changed after body starts
337
+ return this;
338
+ }
339
+ this._headers.set(name.toLowerCase(), String(value));
340
+ return this;
341
+ }
342
+ getHeader(name) {
343
+ return this._headers.get(name.toLowerCase());
344
+ }
345
+ removeHeader(name) {
346
+ if (!this._headersSent) {
347
+ this._headers.delete(name.toLowerCase());
348
+ }
349
+ }
350
+ getHeaders() {
351
+ const headers = {};
352
+ for (const [key, value] of this._headers) {
353
+ headers[key] = value;
354
+ }
355
+ return headers;
356
+ }
357
+ hasHeader(name) {
358
+ return this._headers.has(name.toLowerCase());
359
+ }
360
+ getHeaderNames() {
361
+ return Array.from(this._headers.keys());
362
+ }
363
+ writeHead(statusCode, statusMessageOrHeaders, headers) {
364
+ if (this._headersSent) {
365
+ return this;
366
+ }
367
+ this.statusCode = statusCode;
368
+ let headersToSet;
369
+ if (typeof statusMessageOrHeaders === "string") {
370
+ this.statusMessage = statusMessageOrHeaders;
371
+ headersToSet = headers;
372
+ }
373
+ else if (statusMessageOrHeaders &&
374
+ typeof statusMessageOrHeaders === "object") {
375
+ headersToSet = statusMessageOrHeaders;
376
+ }
377
+ if (headersToSet) {
378
+ for (const [key, value] of Object.entries(headersToSet)) {
379
+ if (value !== undefined) {
380
+ this.setHeader(key, value);
381
+ }
382
+ }
383
+ }
384
+ this.flushHeaders();
385
+ return this;
386
+ }
387
+ get headersSent() {
388
+ return this._headersSent;
389
+ }
390
+ flushHeaders() {
391
+ if (this._headersSent)
392
+ return;
393
+ const headers = {};
394
+ for (const [key, value] of this._headers) {
395
+ headers[key] = Array.isArray(value) ? value.join(", ") : String(value);
396
+ }
397
+ const metadata = {
398
+ headers,
399
+ statusCode: this.statusCode,
400
+ };
401
+ // Wrap the stream with metadata using awslambda global
402
+ this._wrappedStream = awslambda.HttpResponseStream.from(this._responseStream, metadata);
403
+ this._headersSent = true;
404
+ // Flush pending writes
405
+ for (const { callback, chunk } of this._pendingWrites) {
406
+ this._wrappedStream.write(chunk);
407
+ callback();
408
+ }
409
+ this._pendingWrites = [];
410
+ }
411
+ //
412
+ // Express compatibility methods
413
+ //
414
+ status(code) {
415
+ this.statusCode = code;
416
+ return this;
417
+ }
418
+ json(data) {
419
+ this.setHeader("content-type", "application/json");
420
+ this.end(JSON.stringify(data));
421
+ return this;
422
+ }
423
+ send(body) {
424
+ if (typeof body === "object" && !Buffer.isBuffer(body)) {
425
+ return this.json(body);
426
+ }
427
+ this.end(body);
428
+ return this;
429
+ }
430
+ //
431
+ // Writable stream implementation
432
+ //
433
+ _write(chunk, encoding, // eslint-disable-line no-undef
434
+ callback) {
435
+ const buffer = Buffer.isBuffer(chunk)
436
+ ? chunk
437
+ : Buffer.from(chunk, encoding);
438
+ if (!this._headersSent) {
439
+ // Buffer writes until headers are sent
440
+ this._pendingWrites.push({ callback: () => callback(), chunk: buffer });
441
+ // Auto-flush headers on first write
442
+ this.flushHeaders();
443
+ }
444
+ else {
445
+ this._wrappedStream.write(buffer);
446
+ callback();
447
+ }
448
+ }
449
+ _final(callback) {
450
+ if (!this._headersSent) {
451
+ this.flushHeaders();
452
+ }
453
+ this._wrappedStream?.end();
454
+ callback();
455
+ }
456
+ }
457
+
458
+ //
459
+ //
460
+ // Current Invoke Context
461
+ //
462
+ let currentInvoke = null;
463
+ /**
464
+ * Get the current Lambda invoke context.
465
+ * Used by getCurrentInvokeUuid adapter to get the request ID.
466
+ */
467
+ function getCurrentInvoke() {
468
+ return currentInvoke;
469
+ }
470
+ /**
471
+ * Set the current Lambda invoke context.
472
+ * Called at the start of each Lambda invocation.
473
+ */
474
+ function setCurrentInvoke(event, context) {
475
+ currentInvoke = { context, event };
476
+ }
477
+ /**
478
+ * Clear the current Lambda invoke context.
479
+ * Called at the end of each Lambda invocation.
480
+ */
481
+ function clearCurrentInvoke() {
482
+ currentInvoke = null;
483
+ }
484
+ //
485
+ //
486
+ // Express App Runner
487
+ //
488
+ /**
489
+ * Run Express app with mock request/response.
490
+ * Returns a promise that resolves when the response is complete.
491
+ */
492
+ function runExpressApp(app, req, res) {
493
+ return new Promise((resolve, reject) => {
494
+ // Listen for response completion
495
+ res.on("finish", resolve);
496
+ res.on("error", reject);
497
+ // Run the Express app
498
+ // Cast to Express types since our mocks implement the required interface
499
+ app(req, res);
500
+ });
501
+ }
502
+ //
503
+ //
504
+ // Buffered Handler Factory
505
+ //
506
+ /**
507
+ * Create a Lambda handler that buffers the Express response.
508
+ * Returns the complete response as a Lambda response object.
509
+ *
510
+ * @example
511
+ * ```typescript
512
+ * import express from "express";
513
+ * import { createLambdaHandler } from "@jaypie/express";
514
+ *
515
+ * const app = express();
516
+ * app.get("/", (req, res) => res.json({ message: "Hello" }));
517
+ *
518
+ * export const handler = createLambdaHandler(app);
519
+ * ```
520
+ */
521
+ function createLambdaHandler(app, _options) {
522
+ return async (event, context) => {
523
+ try {
524
+ // Set current invoke for getCurrentInvokeUuid
525
+ setCurrentInvoke(event, context);
526
+ // Create mock request from Lambda event
527
+ const req = createLambdaRequest(event, context);
528
+ // Create buffered response collector
529
+ const res = new LambdaResponseBuffered();
530
+ // Run Express app
531
+ await runExpressApp(app, req, res);
532
+ // Return Lambda response
533
+ return res.getResult();
534
+ }
535
+ finally {
536
+ // Clear current invoke context
537
+ clearCurrentInvoke();
538
+ }
539
+ };
540
+ }
541
+ //
542
+ //
543
+ // Streaming Handler Factory
544
+ //
545
+ /**
546
+ * Create a Lambda handler that streams the Express response.
547
+ * Uses awslambda.streamifyResponse() for Lambda response streaming.
548
+ *
549
+ * @example
550
+ * ```typescript
551
+ * import express from "express";
552
+ * import { createLambdaStreamHandler } from "@jaypie/express";
553
+ *
554
+ * const app = express();
555
+ * app.get("/stream", (req, res) => {
556
+ * res.setHeader("Content-Type", "text/event-stream");
557
+ * res.write("data: Hello\n\n");
558
+ * res.end();
559
+ * });
560
+ *
561
+ * export const handler = createLambdaStreamHandler(app);
562
+ * ```
563
+ */
564
+ function createLambdaStreamHandler(app, _options) {
565
+ // Wrap with awslambda.streamifyResponse for Lambda streaming
566
+ // @ts-expect-error awslambda is a Lambda runtime global
567
+ return awslambda.streamifyResponse(async (event, responseStream, context) => {
568
+ try {
569
+ // Set current invoke for getCurrentInvokeUuid
570
+ setCurrentInvoke(event, context);
571
+ // Create mock request from Lambda event
572
+ const req = createLambdaRequest(event, context);
573
+ // Create streaming response that pipes to Lambda responseStream
574
+ const res = new LambdaResponseStreaming(responseStream);
575
+ // Run Express app
576
+ await runExpressApp(app, req, res);
577
+ }
578
+ finally {
579
+ // Clear current invoke context
580
+ clearCurrentInvoke();
581
+ }
582
+ });
583
+ }
10
584
 
11
585
  //
12
586
  //
@@ -258,40 +832,54 @@ function isWebAdapterMode(req) {
258
832
  return false;
259
833
  }
260
834
  /**
261
- * Adapter for the "@codegenie/serverless-express" uuid
835
+ * Get UUID from Jaypie Lambda adapter context.
836
+ * This is set by createLambdaHandler/createLambdaStreamHandler.
262
837
  */
263
- function getServerlessExpressUuid() {
264
- const currentInvoke = serverlessExpress.getCurrentInvoke();
265
- if (currentInvoke &&
266
- currentInvoke.context &&
267
- currentInvoke.context.awsRequestId) {
838
+ function getJaypieAdapterUuid() {
839
+ const currentInvoke = getCurrentInvoke();
840
+ if (currentInvoke?.context?.awsRequestId) {
268
841
  return currentInvoke.context.awsRequestId;
269
842
  }
270
843
  return undefined;
271
844
  }
845
+ /**
846
+ * Get UUID from request object if it has Lambda context attached.
847
+ * The Jaypie adapter attaches _lambdaContext to the request.
848
+ */
849
+ function getRequestContextUuid(req) {
850
+ if (req && req._lambdaContext?.awsRequestId) {
851
+ return req._lambdaContext.awsRequestId;
852
+ }
853
+ return undefined;
854
+ }
272
855
  //
273
856
  //
274
857
  // Main
275
858
  //
276
859
  /**
277
860
  * Get the current invoke UUID from Lambda context.
278
- * Works in both serverless-express mode and Lambda Web Adapter mode.
861
+ * Works with Jaypie Lambda adapter and Lambda Web Adapter mode.
279
862
  *
280
- * @param req - Optional Express request object. Required for Web Adapter mode
281
- * to extract the x-amzn-request-id header.
863
+ * @param req - Optional Express request object. Used to extract context
864
+ * from Web Adapter headers or Jaypie adapter's _lambdaContext.
282
865
  * @returns The AWS request ID or undefined if not in Lambda context
283
866
  */
284
867
  function getCurrentInvokeUuid(req) {
285
- // If request is provided and has Web Adapter header, use Web Adapter mode
868
+ // Priority 1: Web Adapter mode (header-based)
286
869
  if (isWebAdapterMode(req)) {
287
870
  return getWebAdapterUuid(req);
288
871
  }
289
- // Try serverless-express mode first
290
- const serverlessExpressUuid = getServerlessExpressUuid();
291
- if (serverlessExpressUuid) {
292
- return serverlessExpressUuid;
872
+ // Priority 2: Request has Lambda context attached (Jaypie adapter)
873
+ const requestContextUuid = getRequestContextUuid(req);
874
+ if (requestContextUuid) {
875
+ return requestContextUuid;
876
+ }
877
+ // Priority 3: Global context from Jaypie adapter
878
+ const jaypieAdapterUuid = getJaypieAdapterUuid();
879
+ if (jaypieAdapterUuid) {
880
+ return jaypieAdapterUuid;
293
881
  }
294
- // If no request but we might be in Web Adapter mode, try env var fallback
882
+ // Fallback: Web Adapter env var
295
883
  return getWebAdapterUuid();
296
884
  }
297
885
 
@@ -709,6 +1297,46 @@ function expressHandler(handlerOrOptions, optionsOrHandler) {
709
1297
  };
710
1298
  }
711
1299
 
1300
+ //
1301
+ //
1302
+ // Main
1303
+ //
1304
+ const httpHandler = (statusCode = kit.HTTP.CODE.OK, context = {}) => {
1305
+ // Give a default name if there isn't one
1306
+ if (!context.name) {
1307
+ context.name = "_http";
1308
+ }
1309
+ // Return a function that will be used as an express route
1310
+ return expressHandler(async (req, res) => {
1311
+ // Map the most throwable status codes to errors and throw them!
1312
+ const error = {
1313
+ [kit.HTTP.CODE.BAD_REQUEST]: errors.BadRequestError,
1314
+ [kit.HTTP.CODE.UNAUTHORIZED]: errors.UnauthorizedError,
1315
+ [kit.HTTP.CODE.FORBIDDEN]: errors.ForbiddenError,
1316
+ [kit.HTTP.CODE.NOT_FOUND]: errors.NotFoundError,
1317
+ [kit.HTTP.CODE.METHOD_NOT_ALLOWED]: errors.MethodNotAllowedError,
1318
+ [kit.HTTP.CODE.GONE]: errors.GoneError,
1319
+ [kit.HTTP.CODE.TEAPOT]: errors.TeapotError,
1320
+ [kit.HTTP.CODE.INTERNAL_ERROR]: errors.InternalError,
1321
+ [kit.HTTP.CODE.BAD_GATEWAY]: errors.BadGatewayError,
1322
+ [kit.HTTP.CODE.UNAVAILABLE]: errors.UnavailableError,
1323
+ [kit.HTTP.CODE.GATEWAY_TIMEOUT]: errors.GatewayTimeoutError,
1324
+ };
1325
+ // If this maps to an error, throw it
1326
+ if (error[statusCode]) {
1327
+ logger$2.log.trace(`@knowdev/express: gracefully throwing ${statusCode} up to projectHandler`);
1328
+ throw new error[statusCode]();
1329
+ }
1330
+ // If this is an error and we didn't get thrown, log a warning
1331
+ if (statusCode >= 400) {
1332
+ logger$2.log.warn(`@knowdev/express: status code ${statusCode} not mapped as throwable`);
1333
+ }
1334
+ // Send the response
1335
+ res.status(statusCode);
1336
+ return statusCode === kit.HTTP.CODE.NO_CONTENT ? null : {};
1337
+ }, context);
1338
+ };
1339
+
712
1340
  // Cast logger to extended interface for runtime features not in type definitions
713
1341
  const logger = logger$2.log;
714
1342
  //
@@ -939,46 +1567,6 @@ function expressStreamHandler(handlerOrOptions, optionsOrHandler) {
939
1567
  };
940
1568
  }
941
1569
 
942
- //
943
- //
944
- // Main
945
- //
946
- const httpHandler = (statusCode = kit.HTTP.CODE.OK, context = {}) => {
947
- // Give a default name if there isn't one
948
- if (!context.name) {
949
- context.name = "_http";
950
- }
951
- // Return a function that will be used as an express route
952
- return expressHandler(async (req, res) => {
953
- // Map the most throwable status codes to errors and throw them!
954
- const error = {
955
- [kit.HTTP.CODE.BAD_REQUEST]: errors.BadRequestError,
956
- [kit.HTTP.CODE.UNAUTHORIZED]: errors.UnauthorizedError,
957
- [kit.HTTP.CODE.FORBIDDEN]: errors.ForbiddenError,
958
- [kit.HTTP.CODE.NOT_FOUND]: errors.NotFoundError,
959
- [kit.HTTP.CODE.METHOD_NOT_ALLOWED]: errors.MethodNotAllowedError,
960
- [kit.HTTP.CODE.GONE]: errors.GoneError,
961
- [kit.HTTP.CODE.TEAPOT]: errors.TeapotError,
962
- [kit.HTTP.CODE.INTERNAL_ERROR]: errors.InternalError,
963
- [kit.HTTP.CODE.BAD_GATEWAY]: errors.BadGatewayError,
964
- [kit.HTTP.CODE.UNAVAILABLE]: errors.UnavailableError,
965
- [kit.HTTP.CODE.GATEWAY_TIMEOUT]: errors.GatewayTimeoutError,
966
- };
967
- // If this maps to an error, throw it
968
- if (error[statusCode]) {
969
- logger$2.log.trace(`@knowdev/express: gracefully throwing ${statusCode} up to projectHandler`);
970
- throw new error[statusCode]();
971
- }
972
- // If this is an error and we didn't get thrown, log a warning
973
- if (statusCode >= 400) {
974
- logger$2.log.warn(`@knowdev/express: status code ${statusCode} not mapped as throwable`);
975
- }
976
- // Send the response
977
- res.status(statusCode);
978
- return statusCode === kit.HTTP.CODE.NO_CONTENT ? null : {};
979
- }, context);
980
- };
981
-
982
1570
  //
983
1571
  //
984
1572
  // Main
@@ -1033,14 +1621,20 @@ const notFoundRoute = routes.notFoundRoute;
1033
1621
  const notImplementedRoute = routes.notImplementedRoute;
1034
1622
 
1035
1623
  exports.EXPRESS = EXPRESS;
1624
+ exports.LambdaRequest = LambdaRequest;
1625
+ exports.LambdaResponseBuffered = LambdaResponseBuffered;
1626
+ exports.LambdaResponseStreaming = LambdaResponseStreaming;
1036
1627
  exports.badRequestRoute = badRequestRoute;
1037
1628
  exports.cors = cors;
1629
+ exports.createLambdaHandler = createLambdaHandler;
1630
+ exports.createLambdaStreamHandler = createLambdaStreamHandler;
1038
1631
  exports.createServer = createServer;
1039
1632
  exports.echoRoute = echoRoute;
1040
1633
  exports.expressHandler = expressHandler;
1041
1634
  exports.expressHttpCodeHandler = httpHandler;
1042
1635
  exports.expressStreamHandler = expressStreamHandler;
1043
1636
  exports.forbiddenRoute = forbiddenRoute;
1637
+ exports.getCurrentInvoke = getCurrentInvoke;
1044
1638
  exports.getCurrentInvokeUuid = getCurrentInvokeUuid;
1045
1639
  exports.goneRoute = goneRoute;
1046
1640
  exports.methodNotAllowedRoute = methodNotAllowedRoute;