@jaypie/express 1.2.4-rc8 → 1.2.4
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/dist/cjs/adapter/LambdaResponseBuffered.d.ts +13 -5
- package/dist/cjs/adapter/LambdaResponseStreaming.d.ts +8 -2
- package/dist/cjs/adapter/__tests__/debug-harness.d.ts +1 -0
- package/dist/cjs/expressStreamHandler.d.ts +2 -0
- package/dist/cjs/getCurrentInvokeUuid.adapter.d.ts +2 -2
- package/dist/cjs/index.cjs +322 -215
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/cjs/index.d.ts +0 -2
- package/dist/esm/adapter/LambdaResponseBuffered.d.ts +13 -5
- package/dist/esm/adapter/LambdaResponseStreaming.d.ts +8 -2
- package/dist/esm/adapter/__tests__/debug-harness.d.ts +1 -0
- package/dist/esm/expressStreamHandler.d.ts +2 -0
- package/dist/esm/getCurrentInvokeUuid.adapter.d.ts +2 -2
- package/dist/esm/index.d.ts +0 -2
- package/dist/esm/index.js +322 -214
- package/dist/esm/index.js.map +1 -1
- package/package.json +12 -16
- package/dist/cjs/createServer.d.ts +0 -60
- package/dist/cjs/getCurrentInvokeUuid.webadapter.d.ts +0 -12
- package/dist/esm/createServer.d.ts +0 -60
- package/dist/esm/getCurrentInvokeUuid.webadapter.d.ts +0 -12
package/dist/cjs/index.cjs
CHANGED
|
@@ -5,8 +5,8 @@ var node_http = require('node:http');
|
|
|
5
5
|
var errors = require('@jaypie/errors');
|
|
6
6
|
var kit = require('@jaypie/kit');
|
|
7
7
|
var expressCors = require('cors');
|
|
8
|
-
var logger$2 = require('@jaypie/logger');
|
|
9
8
|
var aws = require('@jaypie/aws');
|
|
9
|
+
var logger$2 = require('@jaypie/logger');
|
|
10
10
|
var datadog = require('@jaypie/datadog');
|
|
11
11
|
|
|
12
12
|
//
|
|
@@ -34,6 +34,15 @@ class LambdaRequest extends node_stream.Readable {
|
|
|
34
34
|
this.path = options.url.split("?")[0];
|
|
35
35
|
this.headers = this.normalizeHeaders(options.headers);
|
|
36
36
|
this.bodyBuffer = options.body ?? null;
|
|
37
|
+
// Parse query string from URL
|
|
38
|
+
const queryIndex = options.url.indexOf("?");
|
|
39
|
+
if (queryIndex !== -1) {
|
|
40
|
+
const queryString = options.url.slice(queryIndex + 1);
|
|
41
|
+
const params = new URLSearchParams(queryString);
|
|
42
|
+
for (const [key, value] of params) {
|
|
43
|
+
this.query[key] = value;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
37
46
|
// Store Lambda context
|
|
38
47
|
this._lambdaContext = options.lambdaContext;
|
|
39
48
|
this._lambdaEvent = options.lambdaEvent;
|
|
@@ -44,6 +53,18 @@ class LambdaRequest extends node_stream.Readable {
|
|
|
44
53
|
remoteAddress: options.remoteAddress,
|
|
45
54
|
};
|
|
46
55
|
this.connection = this.socket;
|
|
56
|
+
// Schedule body push for next tick to ensure stream is ready
|
|
57
|
+
// This is needed for body parsers that consume the stream
|
|
58
|
+
if (this.bodyBuffer && this.bodyBuffer.length > 0) {
|
|
59
|
+
process.nextTick(() => {
|
|
60
|
+
if (!this.bodyPushed) {
|
|
61
|
+
this.push(this.bodyBuffer);
|
|
62
|
+
this.push(null);
|
|
63
|
+
this.bodyPushed = true;
|
|
64
|
+
this.complete = true;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
}
|
|
47
68
|
}
|
|
48
69
|
//
|
|
49
70
|
// Readable stream implementation
|
|
@@ -145,6 +166,11 @@ function createLambdaRequest(event, context) {
|
|
|
145
166
|
body = event.isBase64Encoded
|
|
146
167
|
? Buffer.from(event.body, "base64")
|
|
147
168
|
: Buffer.from(event.body, "utf8");
|
|
169
|
+
// Add content-length header if not present (required for body parsers)
|
|
170
|
+
const hasContentLength = Object.keys(headers).some((k) => k.toLowerCase() === "content-length");
|
|
171
|
+
if (!hasContentLength) {
|
|
172
|
+
headers["content-length"] = String(body.length);
|
|
173
|
+
}
|
|
148
174
|
}
|
|
149
175
|
return new LambdaRequest({
|
|
150
176
|
body,
|
|
@@ -162,6 +188,9 @@ function createLambdaRequest(event, context) {
|
|
|
162
188
|
//
|
|
163
189
|
// Constants
|
|
164
190
|
//
|
|
191
|
+
// Symbol to identify Lambda mock responses. Uses Symbol.for() to ensure
|
|
192
|
+
// the same symbol is used across bundles/realms. Survives prototype manipulation.
|
|
193
|
+
const JAYPIE_LAMBDA_MOCK = Symbol.for("@jaypie/express/LambdaMock");
|
|
165
194
|
// Get Node's internal kOutHeaders symbol from ServerResponse prototype.
|
|
166
195
|
// This is needed for compatibility with Datadog dd-trace instrumentation,
|
|
167
196
|
// which patches HTTP methods and expects this internal state to exist.
|
|
@@ -192,22 +221,90 @@ class LambdaResponseBuffered extends node_stream.Writable {
|
|
|
192
221
|
this.socket = {
|
|
193
222
|
remoteAddress: "127.0.0.1",
|
|
194
223
|
};
|
|
224
|
+
// Internal state exposed for direct manipulation by safe response methods
|
|
225
|
+
// that need to bypass dd-trace interception of stream methods
|
|
195
226
|
this._chunks = [];
|
|
227
|
+
this._ended = false; // Track ended state since writableEnded is lost after prototype change
|
|
196
228
|
this._headers = new Map();
|
|
197
229
|
this._headersSent = false;
|
|
198
230
|
this._resolve = null;
|
|
231
|
+
// Mark as Lambda mock response for identification in expressHandler
|
|
232
|
+
this[JAYPIE_LAMBDA_MOCK] = true;
|
|
199
233
|
// Initialize Node's internal kOutHeaders for dd-trace compatibility.
|
|
200
234
|
// dd-trace patches HTTP methods and expects this internal state.
|
|
201
235
|
if (kOutHeaders$1) {
|
|
202
236
|
this[kOutHeaders$1] = Object.create(null);
|
|
203
237
|
}
|
|
238
|
+
// CRITICAL: Define key methods as instance properties to survive Express's
|
|
239
|
+
// setPrototypeOf(res, app.response) in middleware/init.js which would
|
|
240
|
+
// otherwise replace our prototype with ServerResponse.prototype.
|
|
241
|
+
// Instance properties take precedence over prototype properties.
|
|
242
|
+
this.getHeader = this.getHeader.bind(this);
|
|
243
|
+
this.setHeader = this.setHeader.bind(this);
|
|
244
|
+
this.removeHeader = this.removeHeader.bind(this);
|
|
245
|
+
this.hasHeader = this.hasHeader.bind(this);
|
|
246
|
+
this.getHeaders = this.getHeaders.bind(this);
|
|
247
|
+
this.getHeaderNames = this.getHeaderNames.bind(this);
|
|
248
|
+
this.writeHead = this.writeHead.bind(this);
|
|
249
|
+
this.get = this.get.bind(this);
|
|
250
|
+
this.set = this.set.bind(this);
|
|
251
|
+
this.status = this.status.bind(this);
|
|
252
|
+
this.json = this.json.bind(this);
|
|
253
|
+
this.send = this.send.bind(this);
|
|
254
|
+
this.vary = this.vary.bind(this);
|
|
255
|
+
this.end = this.end.bind(this);
|
|
256
|
+
this.write = this.write.bind(this);
|
|
257
|
+
// Also bind internal Writable methods that are called via prototype chain
|
|
258
|
+
this._write = this._write.bind(this);
|
|
259
|
+
this._final = this._final.bind(this);
|
|
260
|
+
// Bind result-building methods
|
|
261
|
+
this.getResult = this.getResult.bind(this);
|
|
262
|
+
this.buildResult = this.buildResult.bind(this);
|
|
263
|
+
this.isBinaryContentType = this.isBinaryContentType.bind(this);
|
|
264
|
+
}
|
|
265
|
+
//
|
|
266
|
+
// Internal bypass methods - completely avoid prototype chain lookup
|
|
267
|
+
// These directly access _headers Map, safe from dd-trace interception
|
|
268
|
+
//
|
|
269
|
+
_internalGetHeader(name) {
|
|
270
|
+
const value = this._headers.get(name.toLowerCase());
|
|
271
|
+
return value ? String(value) : undefined;
|
|
272
|
+
}
|
|
273
|
+
_internalSetHeader(name, value) {
|
|
274
|
+
if (!this._headersSent) {
|
|
275
|
+
const lowerName = name.toLowerCase();
|
|
276
|
+
this._headers.set(lowerName, value);
|
|
277
|
+
// Also sync kOutHeaders for any code that expects it
|
|
278
|
+
if (kOutHeaders$1) {
|
|
279
|
+
const outHeaders = this[kOutHeaders$1];
|
|
280
|
+
if (outHeaders) {
|
|
281
|
+
outHeaders[lowerName] = [name, value];
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
_internalHasHeader(name) {
|
|
287
|
+
return this._headers.has(name.toLowerCase());
|
|
288
|
+
}
|
|
289
|
+
_internalRemoveHeader(name) {
|
|
290
|
+
if (!this._headersSent) {
|
|
291
|
+
const lowerName = name.toLowerCase();
|
|
292
|
+
this._headers.delete(lowerName);
|
|
293
|
+
if (kOutHeaders$1) {
|
|
294
|
+
const outHeaders = this[kOutHeaders$1];
|
|
295
|
+
if (outHeaders) {
|
|
296
|
+
delete outHeaders[lowerName];
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
}
|
|
204
300
|
}
|
|
205
301
|
//
|
|
206
302
|
// Promise-based API for getting final result
|
|
207
303
|
//
|
|
208
304
|
getResult() {
|
|
209
305
|
return new Promise((resolve) => {
|
|
210
|
-
|
|
306
|
+
// Use _ended instead of writableEnded since Express's setPrototypeOf breaks the getter
|
|
307
|
+
if (this._ended) {
|
|
211
308
|
resolve(this.buildResult());
|
|
212
309
|
}
|
|
213
310
|
else {
|
|
@@ -265,36 +362,40 @@ class LambdaResponseBuffered extends node_stream.Writable {
|
|
|
265
362
|
/**
|
|
266
363
|
* Proxy for direct header access (e.g., res.headers['content-type']).
|
|
267
364
|
* Required for compatibility with middleware like helmet that access headers directly.
|
|
365
|
+
* Uses direct _headers access to bypass dd-trace interception.
|
|
268
366
|
*/
|
|
269
367
|
get headers() {
|
|
270
368
|
return new Proxy({}, {
|
|
271
369
|
deleteProperty: (_target, prop) => {
|
|
272
|
-
this.
|
|
370
|
+
this._headers.delete(String(prop).toLowerCase());
|
|
273
371
|
return true;
|
|
274
372
|
},
|
|
275
373
|
get: (_target, prop) => {
|
|
276
374
|
if (typeof prop === "symbol")
|
|
277
375
|
return undefined;
|
|
278
|
-
return this.
|
|
376
|
+
return this._headers.get(String(prop).toLowerCase());
|
|
279
377
|
},
|
|
280
378
|
getOwnPropertyDescriptor: (_target, prop) => {
|
|
281
|
-
|
|
379
|
+
const lowerProp = String(prop).toLowerCase();
|
|
380
|
+
if (this._headers.has(lowerProp)) {
|
|
282
381
|
return {
|
|
283
382
|
configurable: true,
|
|
284
383
|
enumerable: true,
|
|
285
|
-
value: this.
|
|
384
|
+
value: this._headers.get(lowerProp),
|
|
286
385
|
};
|
|
287
386
|
}
|
|
288
387
|
return undefined;
|
|
289
388
|
},
|
|
290
389
|
has: (_target, prop) => {
|
|
291
|
-
return this.
|
|
390
|
+
return this._headers.has(String(prop).toLowerCase());
|
|
292
391
|
},
|
|
293
392
|
ownKeys: () => {
|
|
294
|
-
return this.
|
|
393
|
+
return Array.from(this._headers.keys());
|
|
295
394
|
},
|
|
296
395
|
set: (_target, prop, value) => {
|
|
297
|
-
this.
|
|
396
|
+
if (!this._headersSent) {
|
|
397
|
+
this._headers.set(String(prop).toLowerCase(), value);
|
|
398
|
+
}
|
|
298
399
|
return true;
|
|
299
400
|
},
|
|
300
401
|
});
|
|
@@ -311,9 +412,10 @@ class LambdaResponseBuffered extends node_stream.Writable {
|
|
|
311
412
|
headersToSet = statusMessageOrHeaders;
|
|
312
413
|
}
|
|
313
414
|
if (headersToSet) {
|
|
415
|
+
// Use direct _headers access to bypass dd-trace interception
|
|
314
416
|
for (const [key, value] of Object.entries(headersToSet)) {
|
|
315
417
|
if (value !== undefined) {
|
|
316
|
-
this.
|
|
418
|
+
this._headers.set(key.toLowerCase(), String(value));
|
|
317
419
|
}
|
|
318
420
|
}
|
|
319
421
|
}
|
|
@@ -350,7 +452,8 @@ class LambdaResponseBuffered extends node_stream.Writable {
|
|
|
350
452
|
return this;
|
|
351
453
|
}
|
|
352
454
|
json(data) {
|
|
353
|
-
|
|
455
|
+
// Use direct _headers access to bypass dd-trace interception
|
|
456
|
+
this._headers.set("content-type", "application/json");
|
|
354
457
|
this.end(JSON.stringify(data));
|
|
355
458
|
return this;
|
|
356
459
|
}
|
|
@@ -364,11 +467,12 @@ class LambdaResponseBuffered extends node_stream.Writable {
|
|
|
364
467
|
/**
|
|
365
468
|
* Add a field to the Vary response header.
|
|
366
469
|
* Used by CORS middleware to indicate response varies by Origin.
|
|
470
|
+
* Uses direct _headers access to bypass dd-trace interception.
|
|
367
471
|
*/
|
|
368
472
|
vary(field) {
|
|
369
|
-
const existing = this.
|
|
473
|
+
const existing = this._headers.get("vary");
|
|
370
474
|
if (!existing) {
|
|
371
|
-
this.
|
|
475
|
+
this._headers.set("vary", field);
|
|
372
476
|
}
|
|
373
477
|
else {
|
|
374
478
|
// Append to existing Vary header if field not already present
|
|
@@ -376,7 +480,7 @@ class LambdaResponseBuffered extends node_stream.Writable {
|
|
|
376
480
|
.split(",")
|
|
377
481
|
.map((f) => f.trim().toLowerCase());
|
|
378
482
|
if (!fields.includes(field.toLowerCase())) {
|
|
379
|
-
this.
|
|
483
|
+
this._headers.set("vary", `${existing}, ${field}`);
|
|
380
484
|
}
|
|
381
485
|
}
|
|
382
486
|
return this;
|
|
@@ -394,6 +498,7 @@ class LambdaResponseBuffered extends node_stream.Writable {
|
|
|
394
498
|
callback();
|
|
395
499
|
}
|
|
396
500
|
_final(callback) {
|
|
501
|
+
this._ended = true;
|
|
397
502
|
if (this._resolve) {
|
|
398
503
|
this._resolve(this.buildResult());
|
|
399
504
|
}
|
|
@@ -404,7 +509,8 @@ class LambdaResponseBuffered extends node_stream.Writable {
|
|
|
404
509
|
//
|
|
405
510
|
buildResult() {
|
|
406
511
|
const body = Buffer.concat(this._chunks);
|
|
407
|
-
|
|
512
|
+
// Use direct _headers access to bypass dd-trace interception
|
|
513
|
+
const contentType = this._headers.get("content-type") || "";
|
|
408
514
|
// Determine if response should be base64 encoded
|
|
409
515
|
const isBase64Encoded = this.isBinaryContentType(contentType);
|
|
410
516
|
// Build headers object
|
|
@@ -466,6 +572,8 @@ class LambdaResponseStreaming extends node_stream.Writable {
|
|
|
466
572
|
this.socket = {
|
|
467
573
|
remoteAddress: "127.0.0.1",
|
|
468
574
|
};
|
|
575
|
+
// Internal state exposed for direct manipulation by safe response methods
|
|
576
|
+
// that need to bypass dd-trace interception
|
|
469
577
|
this._headers = new Map();
|
|
470
578
|
this._headersSent = false;
|
|
471
579
|
this._pendingWrites = [];
|
|
@@ -476,6 +584,65 @@ class LambdaResponseStreaming extends node_stream.Writable {
|
|
|
476
584
|
if (kOutHeaders) {
|
|
477
585
|
this[kOutHeaders] = Object.create(null);
|
|
478
586
|
}
|
|
587
|
+
// CRITICAL: Define key methods as instance properties to survive Express's
|
|
588
|
+
// setPrototypeOf(res, app.response) in middleware/init.js which would
|
|
589
|
+
// otherwise replace our prototype with ServerResponse.prototype.
|
|
590
|
+
// Instance properties take precedence over prototype properties.
|
|
591
|
+
this.getHeader = this.getHeader.bind(this);
|
|
592
|
+
this.setHeader = this.setHeader.bind(this);
|
|
593
|
+
this.removeHeader = this.removeHeader.bind(this);
|
|
594
|
+
this.hasHeader = this.hasHeader.bind(this);
|
|
595
|
+
this.getHeaders = this.getHeaders.bind(this);
|
|
596
|
+
this.getHeaderNames = this.getHeaderNames.bind(this);
|
|
597
|
+
this.writeHead = this.writeHead.bind(this);
|
|
598
|
+
this.flushHeaders = this.flushHeaders.bind(this);
|
|
599
|
+
this.get = this.get.bind(this);
|
|
600
|
+
this.set = this.set.bind(this);
|
|
601
|
+
this.status = this.status.bind(this);
|
|
602
|
+
this.json = this.json.bind(this);
|
|
603
|
+
this.send = this.send.bind(this);
|
|
604
|
+
this.vary = this.vary.bind(this);
|
|
605
|
+
this.end = this.end.bind(this);
|
|
606
|
+
this.write = this.write.bind(this);
|
|
607
|
+
// Also bind internal Writable methods that are called via prototype chain
|
|
608
|
+
this._write = this._write.bind(this);
|
|
609
|
+
this._final = this._final.bind(this);
|
|
610
|
+
}
|
|
611
|
+
//
|
|
612
|
+
// Internal bypass methods - completely avoid prototype chain lookup
|
|
613
|
+
// These directly access _headers Map, safe from dd-trace interception
|
|
614
|
+
//
|
|
615
|
+
_internalGetHeader(name) {
|
|
616
|
+
const value = this._headers.get(name.toLowerCase());
|
|
617
|
+
return value ? String(value) : undefined;
|
|
618
|
+
}
|
|
619
|
+
_internalSetHeader(name, value) {
|
|
620
|
+
if (!this._headersSent) {
|
|
621
|
+
const lowerName = name.toLowerCase();
|
|
622
|
+
this._headers.set(lowerName, value);
|
|
623
|
+
// Also sync kOutHeaders for any code that expects it
|
|
624
|
+
if (kOutHeaders) {
|
|
625
|
+
const outHeaders = this[kOutHeaders];
|
|
626
|
+
if (outHeaders) {
|
|
627
|
+
outHeaders[lowerName] = [name, value];
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
_internalHasHeader(name) {
|
|
633
|
+
return this._headers.has(name.toLowerCase());
|
|
634
|
+
}
|
|
635
|
+
_internalRemoveHeader(name) {
|
|
636
|
+
if (!this._headersSent) {
|
|
637
|
+
const lowerName = name.toLowerCase();
|
|
638
|
+
this._headers.delete(lowerName);
|
|
639
|
+
if (kOutHeaders) {
|
|
640
|
+
const outHeaders = this[kOutHeaders];
|
|
641
|
+
if (outHeaders) {
|
|
642
|
+
delete outHeaders[lowerName];
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
}
|
|
479
646
|
}
|
|
480
647
|
//
|
|
481
648
|
// Header management
|
|
@@ -530,36 +697,42 @@ class LambdaResponseStreaming extends node_stream.Writable {
|
|
|
530
697
|
/**
|
|
531
698
|
* Proxy for direct header access (e.g., res.headers['content-type']).
|
|
532
699
|
* Required for compatibility with middleware like helmet that access headers directly.
|
|
700
|
+
* Uses direct _headers access to bypass dd-trace interception.
|
|
533
701
|
*/
|
|
534
702
|
get headers() {
|
|
535
703
|
return new Proxy({}, {
|
|
536
704
|
deleteProperty: (_target, prop) => {
|
|
537
|
-
this.
|
|
705
|
+
if (!this._headersSent) {
|
|
706
|
+
this._headers.delete(String(prop).toLowerCase());
|
|
707
|
+
}
|
|
538
708
|
return true;
|
|
539
709
|
},
|
|
540
710
|
get: (_target, prop) => {
|
|
541
711
|
if (typeof prop === "symbol")
|
|
542
712
|
return undefined;
|
|
543
|
-
return this.
|
|
713
|
+
return this._headers.get(String(prop).toLowerCase());
|
|
544
714
|
},
|
|
545
715
|
getOwnPropertyDescriptor: (_target, prop) => {
|
|
546
|
-
|
|
716
|
+
const lowerProp = String(prop).toLowerCase();
|
|
717
|
+
if (this._headers.has(lowerProp)) {
|
|
547
718
|
return {
|
|
548
719
|
configurable: true,
|
|
549
720
|
enumerable: true,
|
|
550
|
-
value: this.
|
|
721
|
+
value: this._headers.get(lowerProp),
|
|
551
722
|
};
|
|
552
723
|
}
|
|
553
724
|
return undefined;
|
|
554
725
|
},
|
|
555
726
|
has: (_target, prop) => {
|
|
556
|
-
return this.
|
|
727
|
+
return this._headers.has(String(prop).toLowerCase());
|
|
557
728
|
},
|
|
558
729
|
ownKeys: () => {
|
|
559
|
-
return this.
|
|
730
|
+
return Array.from(this._headers.keys());
|
|
560
731
|
},
|
|
561
732
|
set: (_target, prop, value) => {
|
|
562
|
-
this.
|
|
733
|
+
if (!this._headersSent) {
|
|
734
|
+
this._headers.set(String(prop).toLowerCase(), value);
|
|
735
|
+
}
|
|
563
736
|
return true;
|
|
564
737
|
},
|
|
565
738
|
});
|
|
@@ -579,9 +752,10 @@ class LambdaResponseStreaming extends node_stream.Writable {
|
|
|
579
752
|
headersToSet = statusMessageOrHeaders;
|
|
580
753
|
}
|
|
581
754
|
if (headersToSet) {
|
|
755
|
+
// Use direct _headers access to bypass dd-trace interception
|
|
582
756
|
for (const [key, value] of Object.entries(headersToSet)) {
|
|
583
757
|
if (value !== undefined) {
|
|
584
|
-
this.
|
|
758
|
+
this._headers.set(key.toLowerCase(), String(value));
|
|
585
759
|
}
|
|
586
760
|
}
|
|
587
761
|
}
|
|
@@ -639,7 +813,8 @@ class LambdaResponseStreaming extends node_stream.Writable {
|
|
|
639
813
|
return this;
|
|
640
814
|
}
|
|
641
815
|
json(data) {
|
|
642
|
-
|
|
816
|
+
// Use direct _headers access to bypass dd-trace interception
|
|
817
|
+
this._headers.set("content-type", "application/json");
|
|
643
818
|
this.end(JSON.stringify(data));
|
|
644
819
|
return this;
|
|
645
820
|
}
|
|
@@ -653,11 +828,12 @@ class LambdaResponseStreaming extends node_stream.Writable {
|
|
|
653
828
|
/**
|
|
654
829
|
* Add a field to the Vary response header.
|
|
655
830
|
* Used by CORS middleware to indicate response varies by Origin.
|
|
831
|
+
* Uses direct _headers access to bypass dd-trace interception.
|
|
656
832
|
*/
|
|
657
833
|
vary(field) {
|
|
658
|
-
const existing = this.
|
|
834
|
+
const existing = this._headers.get("vary");
|
|
659
835
|
if (!existing) {
|
|
660
|
-
this.
|
|
836
|
+
this._headers.set("vary", field);
|
|
661
837
|
}
|
|
662
838
|
else {
|
|
663
839
|
// Append to existing Vary header if field not already present
|
|
@@ -665,7 +841,7 @@ class LambdaResponseStreaming extends node_stream.Writable {
|
|
|
665
841
|
.split(",")
|
|
666
842
|
.map((f) => f.trim().toLowerCase());
|
|
667
843
|
if (!fields.includes(field.toLowerCase())) {
|
|
668
|
-
this.
|
|
844
|
+
this._headers.set("vary", `${existing}, ${field}`);
|
|
669
845
|
}
|
|
670
846
|
}
|
|
671
847
|
return this;
|
|
@@ -763,6 +939,7 @@ function runExpressApp(app, req, res) {
|
|
|
763
939
|
*/
|
|
764
940
|
function createLambdaHandler(app, _options) {
|
|
765
941
|
return async (event, context) => {
|
|
942
|
+
let result;
|
|
766
943
|
try {
|
|
767
944
|
// Set current invoke for getCurrentInvokeUuid
|
|
768
945
|
setCurrentInvoke(event, context);
|
|
@@ -772,8 +949,38 @@ function createLambdaHandler(app, _options) {
|
|
|
772
949
|
const res = new LambdaResponseBuffered();
|
|
773
950
|
// Run Express app
|
|
774
951
|
await runExpressApp(app, req, res);
|
|
775
|
-
//
|
|
776
|
-
|
|
952
|
+
// Get Lambda response - await explicitly to ensure we have the result
|
|
953
|
+
result = await res.getResult();
|
|
954
|
+
// Debug: Log the response before returning
|
|
955
|
+
console.log("[createLambdaHandler] Returning response:", JSON.stringify({
|
|
956
|
+
statusCode: result.statusCode,
|
|
957
|
+
headers: result.headers,
|
|
958
|
+
bodyLength: result.body?.length,
|
|
959
|
+
isBase64Encoded: result.isBase64Encoded,
|
|
960
|
+
}));
|
|
961
|
+
return result;
|
|
962
|
+
}
|
|
963
|
+
catch (error) {
|
|
964
|
+
// Log any unhandled errors
|
|
965
|
+
console.error("[createLambdaHandler] Unhandled error:", error);
|
|
966
|
+
if (error instanceof Error) {
|
|
967
|
+
console.error("[createLambdaHandler] Stack:", error.stack);
|
|
968
|
+
}
|
|
969
|
+
// Return a proper error response instead of throwing
|
|
970
|
+
return {
|
|
971
|
+
statusCode: 500,
|
|
972
|
+
headers: { "content-type": "application/json" },
|
|
973
|
+
body: JSON.stringify({
|
|
974
|
+
errors: [
|
|
975
|
+
{
|
|
976
|
+
status: 500,
|
|
977
|
+
title: "Internal Server Error",
|
|
978
|
+
detail: error instanceof Error ? error.message : "Unknown error occurred",
|
|
979
|
+
},
|
|
980
|
+
],
|
|
981
|
+
}),
|
|
982
|
+
isBase64Encoded: false,
|
|
983
|
+
};
|
|
777
984
|
}
|
|
778
985
|
finally {
|
|
779
986
|
// Clear current invoke context
|
|
@@ -911,7 +1118,7 @@ const corsHelper = (config = {}) => {
|
|
|
911
1118
|
};
|
|
912
1119
|
return expressCors(options);
|
|
913
1120
|
};
|
|
914
|
-
var
|
|
1121
|
+
var cors_helper = (config) => {
|
|
915
1122
|
const cors = corsHelper(config);
|
|
916
1123
|
return (req, res, next) => {
|
|
917
1124
|
cors(req, res, (error) => {
|
|
@@ -926,154 +1133,10 @@ var cors = (config) => {
|
|
|
926
1133
|
};
|
|
927
1134
|
};
|
|
928
1135
|
|
|
929
|
-
//
|
|
930
|
-
//
|
|
931
|
-
// Constants
|
|
932
|
-
//
|
|
933
|
-
const DEFAULT_PORT = 8080;
|
|
934
|
-
//
|
|
935
|
-
//
|
|
936
|
-
// Main
|
|
937
|
-
//
|
|
938
|
-
/**
|
|
939
|
-
* Creates and starts an Express server with standard Jaypie middleware.
|
|
940
|
-
*
|
|
941
|
-
* Features:
|
|
942
|
-
* - CORS handling (configurable)
|
|
943
|
-
* - JSON body parsing
|
|
944
|
-
* - Listens on PORT env var (default 8080)
|
|
945
|
-
*
|
|
946
|
-
* Usage:
|
|
947
|
-
* ```ts
|
|
948
|
-
* import express from "express";
|
|
949
|
-
* import { createServer, expressHandler } from "@jaypie/express";
|
|
950
|
-
*
|
|
951
|
-
* const app = express();
|
|
952
|
-
*
|
|
953
|
-
* app.get("/", expressHandler(async (req, res) => {
|
|
954
|
-
* return { message: "Hello World" };
|
|
955
|
-
* }));
|
|
956
|
-
*
|
|
957
|
-
* const { server, port } = await createServer(app);
|
|
958
|
-
* console.log(`Server running on port ${port}`);
|
|
959
|
-
* ```
|
|
960
|
-
*
|
|
961
|
-
* @param app - Express application instance
|
|
962
|
-
* @param options - Server configuration options
|
|
963
|
-
* @returns Promise resolving to server instance and port
|
|
964
|
-
*/
|
|
965
|
-
async function createServer(app, options = {}) {
|
|
966
|
-
const { cors: corsConfig, jsonLimit = "1mb", middleware = [], port: portOption, } = options;
|
|
967
|
-
// Determine port
|
|
968
|
-
const port = typeof portOption === "string"
|
|
969
|
-
? parseInt(portOption, 10)
|
|
970
|
-
: (portOption ?? parseInt(process.env.PORT || String(DEFAULT_PORT), 10));
|
|
971
|
-
// Apply CORS middleware (unless explicitly disabled)
|
|
972
|
-
if (corsConfig !== false) {
|
|
973
|
-
app.use(cors(corsConfig));
|
|
974
|
-
}
|
|
975
|
-
// Apply JSON body parser
|
|
976
|
-
// Note: We use dynamic import to avoid requiring express as a direct dependency
|
|
977
|
-
const express = await import('express');
|
|
978
|
-
app.use(express.json({ limit: jsonLimit }));
|
|
979
|
-
// Apply additional middleware
|
|
980
|
-
for (const mw of middleware) {
|
|
981
|
-
app.use(mw);
|
|
982
|
-
}
|
|
983
|
-
// Start server
|
|
984
|
-
return new Promise((resolve, reject) => {
|
|
985
|
-
try {
|
|
986
|
-
const server = app.listen(port, () => {
|
|
987
|
-
// Get the actual port (important when port 0 is passed to get an ephemeral port)
|
|
988
|
-
const address = server.address();
|
|
989
|
-
const actualPort = address?.port ?? port;
|
|
990
|
-
logger$2.log.info(`Server listening on port ${actualPort}`);
|
|
991
|
-
resolve({ port: actualPort, server });
|
|
992
|
-
});
|
|
993
|
-
server.on("error", (error) => {
|
|
994
|
-
logger$2.log.error("Server error", { error });
|
|
995
|
-
reject(error);
|
|
996
|
-
});
|
|
997
|
-
}
|
|
998
|
-
catch (error) {
|
|
999
|
-
reject(error);
|
|
1000
|
-
}
|
|
1001
|
-
});
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
|
-
//
|
|
1005
|
-
//
|
|
1006
|
-
// Constants
|
|
1007
|
-
//
|
|
1008
|
-
const HEADER_AMZN_REQUEST_ID$1 = "x-amzn-request-id";
|
|
1009
|
-
const ENV_AMZN_TRACE_ID = "_X_AMZN_TRACE_ID";
|
|
1010
|
-
//
|
|
1011
|
-
//
|
|
1012
|
-
// Helper Functions
|
|
1013
|
-
//
|
|
1014
|
-
/**
|
|
1015
|
-
* Extract request ID from X-Ray trace ID environment variable
|
|
1016
|
-
* Format: Root=1-5e6b4a90-example;Parent=example;Sampled=1
|
|
1017
|
-
* We extract the trace ID from the Root segment
|
|
1018
|
-
*/
|
|
1019
|
-
function parseTraceId(traceId) {
|
|
1020
|
-
if (!traceId)
|
|
1021
|
-
return undefined;
|
|
1022
|
-
// Extract the Root segment (format: Root=1-{timestamp}-{uuid})
|
|
1023
|
-
const rootMatch = traceId.match(/Root=([^;]+)/);
|
|
1024
|
-
if (rootMatch && rootMatch[1]) {
|
|
1025
|
-
return rootMatch[1];
|
|
1026
|
-
}
|
|
1027
|
-
return undefined;
|
|
1028
|
-
}
|
|
1029
|
-
//
|
|
1030
|
-
//
|
|
1031
|
-
// Main
|
|
1032
|
-
//
|
|
1033
|
-
/**
|
|
1034
|
-
* Get the current invoke UUID from Lambda Web Adapter context.
|
|
1035
|
-
* This function extracts the request ID from either:
|
|
1036
|
-
* 1. The x-amzn-request-id header (set by Lambda Web Adapter)
|
|
1037
|
-
* 2. The _X_AMZN_TRACE_ID environment variable (set by Lambda runtime)
|
|
1038
|
-
*
|
|
1039
|
-
* @param req - Optional Express request object to extract headers from
|
|
1040
|
-
* @returns The AWS request ID or undefined if not in Lambda context
|
|
1041
|
-
*/
|
|
1042
|
-
function getWebAdapterUuid(req) {
|
|
1043
|
-
// First, try to get from request headers
|
|
1044
|
-
if (req && req.headers) {
|
|
1045
|
-
const headerValue = req.headers[HEADER_AMZN_REQUEST_ID$1];
|
|
1046
|
-
if (headerValue) {
|
|
1047
|
-
return Array.isArray(headerValue) ? headerValue[0] : headerValue;
|
|
1048
|
-
}
|
|
1049
|
-
}
|
|
1050
|
-
// Fall back to environment variable (X-Ray trace ID)
|
|
1051
|
-
const traceId = process.env[ENV_AMZN_TRACE_ID];
|
|
1052
|
-
if (traceId) {
|
|
1053
|
-
return parseTraceId(traceId);
|
|
1054
|
-
}
|
|
1055
|
-
return undefined;
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
//
|
|
1059
|
-
//
|
|
1060
|
-
// Constants
|
|
1061
|
-
//
|
|
1062
|
-
const HEADER_AMZN_REQUEST_ID = "x-amzn-request-id";
|
|
1063
1136
|
//
|
|
1064
1137
|
//
|
|
1065
1138
|
// Helper Functions
|
|
1066
1139
|
//
|
|
1067
|
-
/**
|
|
1068
|
-
* Detect if we're running in Lambda Web Adapter mode.
|
|
1069
|
-
* Web Adapter sets the x-amzn-request-id header on requests.
|
|
1070
|
-
*/
|
|
1071
|
-
function isWebAdapterMode(req) {
|
|
1072
|
-
if (req && req.headers && req.headers[HEADER_AMZN_REQUEST_ID]) {
|
|
1073
|
-
return true;
|
|
1074
|
-
}
|
|
1075
|
-
return false;
|
|
1076
|
-
}
|
|
1077
1140
|
/**
|
|
1078
1141
|
* Get UUID from Jaypie Lambda adapter context.
|
|
1079
1142
|
* This is set by createLambdaHandler/createLambdaStreamHandler.
|
|
@@ -1090,8 +1153,12 @@ function getJaypieAdapterUuid() {
|
|
|
1090
1153
|
* The Jaypie adapter attaches _lambdaContext to the request.
|
|
1091
1154
|
*/
|
|
1092
1155
|
function getRequestContextUuid(req) {
|
|
1093
|
-
if (req && req._lambdaContext
|
|
1094
|
-
|
|
1156
|
+
if (req && req._lambdaContext) {
|
|
1157
|
+
const lambdaContext = req
|
|
1158
|
+
._lambdaContext;
|
|
1159
|
+
if (lambdaContext.awsRequestId) {
|
|
1160
|
+
return lambdaContext.awsRequestId;
|
|
1161
|
+
}
|
|
1095
1162
|
}
|
|
1096
1163
|
return undefined;
|
|
1097
1164
|
}
|
|
@@ -1101,29 +1168,20 @@ function getRequestContextUuid(req) {
|
|
|
1101
1168
|
//
|
|
1102
1169
|
/**
|
|
1103
1170
|
* Get the current invoke UUID from Lambda context.
|
|
1104
|
-
* Works with Jaypie Lambda adapter
|
|
1171
|
+
* Works with Jaypie Lambda adapter (createLambdaHandler/createLambdaStreamHandler).
|
|
1105
1172
|
*
|
|
1106
1173
|
* @param req - Optional Express request object. Used to extract context
|
|
1107
|
-
* from
|
|
1174
|
+
* from Jaypie adapter's _lambdaContext.
|
|
1108
1175
|
* @returns The AWS request ID or undefined if not in Lambda context
|
|
1109
1176
|
*/
|
|
1110
1177
|
function getCurrentInvokeUuid(req) {
|
|
1111
|
-
// Priority 1:
|
|
1112
|
-
if (isWebAdapterMode(req)) {
|
|
1113
|
-
return getWebAdapterUuid(req);
|
|
1114
|
-
}
|
|
1115
|
-
// Priority 2: Request has Lambda context attached (Jaypie adapter)
|
|
1178
|
+
// Priority 1: Request has Lambda context attached (Jaypie adapter)
|
|
1116
1179
|
const requestContextUuid = getRequestContextUuid(req);
|
|
1117
1180
|
if (requestContextUuid) {
|
|
1118
1181
|
return requestContextUuid;
|
|
1119
1182
|
}
|
|
1120
|
-
// Priority
|
|
1121
|
-
|
|
1122
|
-
if (jaypieAdapterUuid) {
|
|
1123
|
-
return jaypieAdapterUuid;
|
|
1124
|
-
}
|
|
1125
|
-
// Fallback: Web Adapter env var
|
|
1126
|
-
return getWebAdapterUuid();
|
|
1183
|
+
// Priority 2: Global context from Jaypie adapter
|
|
1184
|
+
return getJaypieAdapterUuid();
|
|
1127
1185
|
}
|
|
1128
1186
|
|
|
1129
1187
|
//
|
|
@@ -1137,7 +1195,11 @@ function getCurrentInvokeUuid(req) {
|
|
|
1137
1195
|
*/
|
|
1138
1196
|
function safeGetHeader(res, name) {
|
|
1139
1197
|
try {
|
|
1140
|
-
// Try
|
|
1198
|
+
// Try internal method first (completely bypasses dd-trace)
|
|
1199
|
+
if (typeof res._internalGetHeader === "function") {
|
|
1200
|
+
return res._internalGetHeader(name);
|
|
1201
|
+
}
|
|
1202
|
+
// Fall back to _headers Map access (Lambda adapter, avoids dd-trace)
|
|
1141
1203
|
if (res._headers instanceof Map) {
|
|
1142
1204
|
const value = res._headers.get(name.toLowerCase());
|
|
1143
1205
|
return value ? String(value) : undefined;
|
|
@@ -1165,7 +1227,12 @@ function safeGetHeader(res, name) {
|
|
|
1165
1227
|
*/
|
|
1166
1228
|
function safeSetHeader(res, name, value) {
|
|
1167
1229
|
try {
|
|
1168
|
-
// Try
|
|
1230
|
+
// Try internal method first (completely bypasses dd-trace)
|
|
1231
|
+
if (typeof res._internalSetHeader === "function") {
|
|
1232
|
+
res._internalSetHeader(name, value);
|
|
1233
|
+
return;
|
|
1234
|
+
}
|
|
1235
|
+
// Fall back to _headers Map access (Lambda adapter, avoids dd-trace)
|
|
1169
1236
|
if (res._headers instanceof Map) {
|
|
1170
1237
|
res._headers.set(name.toLowerCase(), value);
|
|
1171
1238
|
return;
|
|
@@ -1295,21 +1362,41 @@ const logger$1 = logger$2.log;
|
|
|
1295
1362
|
// Helpers - Safe response methods to bypass dd-trace interception
|
|
1296
1363
|
//
|
|
1297
1364
|
/**
|
|
1298
|
-
* Check if response is a Lambda mock response with direct
|
|
1365
|
+
* Check if response is a Lambda mock response with direct internal access.
|
|
1366
|
+
* Uses Symbol marker to survive prototype chain modifications from Express and dd-trace.
|
|
1299
1367
|
*/
|
|
1300
1368
|
function isLambdaMockResponse(res) {
|
|
1301
|
-
return res
|
|
1369
|
+
return res[JAYPIE_LAMBDA_MOCK] === true;
|
|
1302
1370
|
}
|
|
1303
1371
|
/**
|
|
1304
1372
|
* Safely send a JSON response, avoiding dd-trace interception.
|
|
1305
|
-
* For Lambda mock responses, directly
|
|
1373
|
+
* For Lambda mock responses, directly manipulates internal state instead of
|
|
1374
|
+
* using stream methods (write/end) which dd-trace intercepts.
|
|
1306
1375
|
*/
|
|
1307
1376
|
function safeSendJson(res, statusCode, data) {
|
|
1308
1377
|
if (isLambdaMockResponse(res)) {
|
|
1309
|
-
//
|
|
1310
|
-
res.
|
|
1378
|
+
// Use internal method to set header (completely bypasses dd-trace)
|
|
1379
|
+
if (typeof res._internalSetHeader === "function") {
|
|
1380
|
+
res._internalSetHeader("content-type", "application/json");
|
|
1381
|
+
}
|
|
1382
|
+
else {
|
|
1383
|
+
// Fall back to direct _headers manipulation
|
|
1384
|
+
res._headers.set("content-type", "application/json");
|
|
1385
|
+
}
|
|
1311
1386
|
res.statusCode = statusCode;
|
|
1312
|
-
|
|
1387
|
+
// Directly push to chunks array instead of using stream write/end
|
|
1388
|
+
const chunk = Buffer.from(JSON.stringify(data));
|
|
1389
|
+
res._chunks.push(chunk);
|
|
1390
|
+
res._headersSent = true;
|
|
1391
|
+
// Mark as ended so getResult() resolves immediately
|
|
1392
|
+
res._ended = true;
|
|
1393
|
+
// Signal completion if a promise is waiting
|
|
1394
|
+
if (res._resolve) {
|
|
1395
|
+
res._resolve(res.buildResult());
|
|
1396
|
+
}
|
|
1397
|
+
// Emit "finish" event so runExpressApp's promise resolves
|
|
1398
|
+
console.log("[safeSendJson] Emitting finish event");
|
|
1399
|
+
res.emit("finish");
|
|
1313
1400
|
return;
|
|
1314
1401
|
}
|
|
1315
1402
|
// Fall back to standard Express methods for real responses
|
|
@@ -1317,18 +1404,27 @@ function safeSendJson(res, statusCode, data) {
|
|
|
1317
1404
|
}
|
|
1318
1405
|
/**
|
|
1319
1406
|
* Safely send a response body, avoiding dd-trace interception.
|
|
1320
|
-
* For Lambda mock responses, directly
|
|
1407
|
+
* For Lambda mock responses, directly manipulates internal state instead of
|
|
1408
|
+
* using stream methods (write/end) which dd-trace intercepts.
|
|
1321
1409
|
*/
|
|
1322
1410
|
function safeSend(res, statusCode, body) {
|
|
1323
1411
|
if (isLambdaMockResponse(res)) {
|
|
1324
|
-
// Direct
|
|
1412
|
+
// Direct internal state manipulation - bypasses dd-trace completely
|
|
1325
1413
|
res.statusCode = statusCode;
|
|
1326
1414
|
if (body !== undefined) {
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1415
|
+
const chunk = Buffer.from(body);
|
|
1416
|
+
res._chunks.push(chunk);
|
|
1417
|
+
}
|
|
1418
|
+
res._headersSent = true;
|
|
1419
|
+
// Mark as ended so getResult() resolves immediately
|
|
1420
|
+
res._ended = true;
|
|
1421
|
+
// Signal completion if a promise is waiting
|
|
1422
|
+
if (res._resolve) {
|
|
1423
|
+
res._resolve(res.buildResult());
|
|
1424
|
+
}
|
|
1425
|
+
// Emit "finish" event so runExpressApp's promise resolves
|
|
1426
|
+
console.log("[safeSend] Emitting finish event");
|
|
1427
|
+
res.emit("finish");
|
|
1332
1428
|
return;
|
|
1333
1429
|
}
|
|
1334
1430
|
// Fall back to standard Express methods for real responses
|
|
@@ -1595,7 +1691,18 @@ function expressHandler(handlerOrOptions, optionsOrHandler) {
|
|
|
1595
1691
|
}
|
|
1596
1692
|
}
|
|
1597
1693
|
catch (error) {
|
|
1598
|
-
|
|
1694
|
+
// Use console.error for raw stack trace to ensure it appears in CloudWatch
|
|
1695
|
+
// Handle both Error objects and plain thrown values
|
|
1696
|
+
const errorMessage = error instanceof Error
|
|
1697
|
+
? error.message
|
|
1698
|
+
: typeof error === "object" && error !== null
|
|
1699
|
+
? JSON.stringify(error)
|
|
1700
|
+
: String(error);
|
|
1701
|
+
const errorStack = error instanceof Error
|
|
1702
|
+
? error.stack
|
|
1703
|
+
: new Error("Stack trace").stack?.replace("Error: Stack trace", `Error: ${errorMessage}`);
|
|
1704
|
+
console.error("Express response error stack trace:", errorStack);
|
|
1705
|
+
log.fatal(`Express encountered an error while sending the response: ${errorMessage}`);
|
|
1599
1706
|
log.var({ responseError: error });
|
|
1600
1707
|
}
|
|
1601
1708
|
// Log response
|
|
@@ -1695,14 +1802,13 @@ const logger = logger$2.log;
|
|
|
1695
1802
|
// Helper
|
|
1696
1803
|
//
|
|
1697
1804
|
/**
|
|
1698
|
-
*
|
|
1805
|
+
* Get error body from an error
|
|
1699
1806
|
*/
|
|
1700
|
-
function
|
|
1807
|
+
function getErrorBody(error) {
|
|
1701
1808
|
const isJaypieError = error.isProjectError;
|
|
1702
|
-
|
|
1809
|
+
return isJaypieError
|
|
1703
1810
|
? (error.body?.() ?? { error: error.message })
|
|
1704
1811
|
: new errors.UnhandledError().body();
|
|
1705
|
-
return `event: error\ndata: ${JSON.stringify(body)}\n\n`;
|
|
1706
1812
|
}
|
|
1707
1813
|
function expressStreamHandler(handlerOrOptions, optionsOrHandler) {
|
|
1708
1814
|
/* eslint-enable no-redeclare */
|
|
@@ -1722,7 +1828,8 @@ function expressStreamHandler(handlerOrOptions, optionsOrHandler) {
|
|
|
1722
1828
|
//
|
|
1723
1829
|
// Validate
|
|
1724
1830
|
//
|
|
1725
|
-
|
|
1831
|
+
const format = options.format ?? "sse";
|
|
1832
|
+
let { chaos, contentType = aws.getContentTypeForFormat(format), locals, name, secrets, setup = [], teardown = [], unavailable, validate, } = options;
|
|
1726
1833
|
if (typeof handler !== "function") {
|
|
1727
1834
|
throw new errors.BadRequestError(`Argument "${handler}" doesn't match type "function"`);
|
|
1728
1835
|
}
|
|
@@ -1860,9 +1967,10 @@ function expressStreamHandler(handlerOrOptions, optionsOrHandler) {
|
|
|
1860
1967
|
log.fatal("Caught unhandled error in stream handler");
|
|
1861
1968
|
log.info.var({ unhandledError: error.message });
|
|
1862
1969
|
}
|
|
1863
|
-
// Write error
|
|
1970
|
+
// Write error in the appropriate format
|
|
1864
1971
|
try {
|
|
1865
|
-
|
|
1972
|
+
const errorBody = getErrorBody(error);
|
|
1973
|
+
res.write(aws.formatStreamError(errorBody, format));
|
|
1866
1974
|
}
|
|
1867
1975
|
catch {
|
|
1868
1976
|
// Response may already be closed
|
|
@@ -1976,10 +2084,9 @@ exports.LambdaRequest = LambdaRequest;
|
|
|
1976
2084
|
exports.LambdaResponseBuffered = LambdaResponseBuffered;
|
|
1977
2085
|
exports.LambdaResponseStreaming = LambdaResponseStreaming;
|
|
1978
2086
|
exports.badRequestRoute = badRequestRoute;
|
|
1979
|
-
exports.cors =
|
|
2087
|
+
exports.cors = cors_helper;
|
|
1980
2088
|
exports.createLambdaHandler = createLambdaHandler;
|
|
1981
2089
|
exports.createLambdaStreamHandler = createLambdaStreamHandler;
|
|
1982
|
-
exports.createServer = createServer;
|
|
1983
2090
|
exports.echoRoute = echoRoute;
|
|
1984
2091
|
exports.expressHandler = expressHandler;
|
|
1985
2092
|
exports.expressHttpCodeHandler = httpHandler;
|