@jaypie/express 1.2.4-rc1 → 1.2.4-rc10
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 +31 -5
- package/dist/cjs/adapter/LambdaResponseStreaming.d.ts +28 -2
- package/dist/cjs/index.cjs +442 -19
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/adapter/LambdaResponseBuffered.d.ts +31 -5
- package/dist/esm/adapter/LambdaResponseStreaming.d.ts +28 -2
- package/dist/esm/index.js +442 -19
- package/dist/esm/index.js.map +1 -1
- package/package.json +1 -1
package/dist/esm/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Readable, Writable } from 'node:stream';
|
|
2
|
+
import { ServerResponse } from 'node:http';
|
|
2
3
|
import { CorsError, BadRequestError, UnhandledError, GatewayTimeoutError, UnavailableError, BadGatewayError, InternalError, TeapotError, GoneError, MethodNotAllowedError, NotFoundError, ForbiddenError, UnauthorizedError, NotImplementedError } from '@jaypie/errors';
|
|
3
4
|
import { force, envBoolean, JAYPIE, HTTP, getHeaderFrom, jaypieHandler } from '@jaypie/kit';
|
|
4
5
|
import expressCors from 'cors';
|
|
@@ -159,6 +160,10 @@ function createLambdaRequest(event, context) {
|
|
|
159
160
|
//
|
|
160
161
|
// Constants
|
|
161
162
|
//
|
|
163
|
+
// Get Node's internal kOutHeaders symbol from ServerResponse prototype.
|
|
164
|
+
// This is needed for compatibility with Datadog dd-trace instrumentation,
|
|
165
|
+
// which patches HTTP methods and expects this internal state to exist.
|
|
166
|
+
const kOutHeaders$1 = Object.getOwnPropertySymbols(ServerResponse.prototype).find((s) => s.toString() === "Symbol(kOutHeaders)");
|
|
162
167
|
const BINARY_CONTENT_TYPE_PATTERNS = [
|
|
163
168
|
/^application\/octet-stream$/,
|
|
164
169
|
/^application\/pdf$/,
|
|
@@ -185,10 +190,53 @@ class LambdaResponseBuffered extends Writable {
|
|
|
185
190
|
this.socket = {
|
|
186
191
|
remoteAddress: "127.0.0.1",
|
|
187
192
|
};
|
|
193
|
+
// Internal state exposed for direct manipulation by safe response methods
|
|
194
|
+
// that need to bypass dd-trace interception of stream methods
|
|
188
195
|
this._chunks = [];
|
|
189
196
|
this._headers = new Map();
|
|
190
197
|
this._headersSent = false;
|
|
191
198
|
this._resolve = null;
|
|
199
|
+
// Initialize Node's internal kOutHeaders for dd-trace compatibility.
|
|
200
|
+
// dd-trace patches HTTP methods and expects this internal state.
|
|
201
|
+
if (kOutHeaders$1) {
|
|
202
|
+
this[kOutHeaders$1] = Object.create(null);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
//
|
|
206
|
+
// Internal bypass methods - completely avoid prototype chain lookup
|
|
207
|
+
// These directly access _headers Map, safe from dd-trace interception
|
|
208
|
+
//
|
|
209
|
+
_internalGetHeader(name) {
|
|
210
|
+
const value = this._headers.get(name.toLowerCase());
|
|
211
|
+
return value ? String(value) : undefined;
|
|
212
|
+
}
|
|
213
|
+
_internalSetHeader(name, value) {
|
|
214
|
+
if (!this._headersSent) {
|
|
215
|
+
const lowerName = name.toLowerCase();
|
|
216
|
+
this._headers.set(lowerName, value);
|
|
217
|
+
// Also sync kOutHeaders for any code that expects it
|
|
218
|
+
if (kOutHeaders$1) {
|
|
219
|
+
const outHeaders = this[kOutHeaders$1];
|
|
220
|
+
if (outHeaders) {
|
|
221
|
+
outHeaders[lowerName] = [name, value];
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
_internalHasHeader(name) {
|
|
227
|
+
return this._headers.has(name.toLowerCase());
|
|
228
|
+
}
|
|
229
|
+
_internalRemoveHeader(name) {
|
|
230
|
+
if (!this._headersSent) {
|
|
231
|
+
const lowerName = name.toLowerCase();
|
|
232
|
+
this._headers.delete(lowerName);
|
|
233
|
+
if (kOutHeaders$1) {
|
|
234
|
+
const outHeaders = this[kOutHeaders$1];
|
|
235
|
+
if (outHeaders) {
|
|
236
|
+
delete outHeaders[lowerName];
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
192
240
|
}
|
|
193
241
|
//
|
|
194
242
|
// Promise-based API for getting final result
|
|
@@ -211,14 +259,31 @@ class LambdaResponseBuffered extends Writable {
|
|
|
211
259
|
// In production, log warning but don't throw to match Express behavior
|
|
212
260
|
return this;
|
|
213
261
|
}
|
|
214
|
-
|
|
262
|
+
const lowerName = name.toLowerCase();
|
|
263
|
+
this._headers.set(lowerName, String(value));
|
|
264
|
+
// Sync with kOutHeaders for dd-trace compatibility
|
|
265
|
+
// Node stores as { 'header-name': ['Header-Name', value] }
|
|
266
|
+
if (kOutHeaders$1) {
|
|
267
|
+
const outHeaders = this[kOutHeaders$1];
|
|
268
|
+
if (outHeaders) {
|
|
269
|
+
outHeaders[lowerName] = [name, String(value)];
|
|
270
|
+
}
|
|
271
|
+
}
|
|
215
272
|
return this;
|
|
216
273
|
}
|
|
217
274
|
getHeader(name) {
|
|
218
275
|
return this._headers.get(name.toLowerCase());
|
|
219
276
|
}
|
|
220
277
|
removeHeader(name) {
|
|
221
|
-
|
|
278
|
+
const lowerName = name.toLowerCase();
|
|
279
|
+
this._headers.delete(lowerName);
|
|
280
|
+
// Sync with kOutHeaders for dd-trace compatibility
|
|
281
|
+
if (kOutHeaders$1) {
|
|
282
|
+
const outHeaders = this[kOutHeaders$1];
|
|
283
|
+
if (outHeaders) {
|
|
284
|
+
delete outHeaders[lowerName];
|
|
285
|
+
}
|
|
286
|
+
}
|
|
222
287
|
}
|
|
223
288
|
getHeaders() {
|
|
224
289
|
const headers = {};
|
|
@@ -233,6 +298,43 @@ class LambdaResponseBuffered extends Writable {
|
|
|
233
298
|
getHeaderNames() {
|
|
234
299
|
return Array.from(this._headers.keys());
|
|
235
300
|
}
|
|
301
|
+
/**
|
|
302
|
+
* Proxy for direct header access (e.g., res.headers['content-type']).
|
|
303
|
+
* Required for compatibility with middleware like helmet that access headers directly.
|
|
304
|
+
*/
|
|
305
|
+
get headers() {
|
|
306
|
+
return new Proxy({}, {
|
|
307
|
+
deleteProperty: (_target, prop) => {
|
|
308
|
+
this.removeHeader(String(prop));
|
|
309
|
+
return true;
|
|
310
|
+
},
|
|
311
|
+
get: (_target, prop) => {
|
|
312
|
+
if (typeof prop === "symbol")
|
|
313
|
+
return undefined;
|
|
314
|
+
return this.getHeader(String(prop));
|
|
315
|
+
},
|
|
316
|
+
getOwnPropertyDescriptor: (_target, prop) => {
|
|
317
|
+
if (this.hasHeader(String(prop))) {
|
|
318
|
+
return {
|
|
319
|
+
configurable: true,
|
|
320
|
+
enumerable: true,
|
|
321
|
+
value: this.getHeader(String(prop)),
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
return undefined;
|
|
325
|
+
},
|
|
326
|
+
has: (_target, prop) => {
|
|
327
|
+
return this.hasHeader(String(prop));
|
|
328
|
+
},
|
|
329
|
+
ownKeys: () => {
|
|
330
|
+
return this.getHeaderNames();
|
|
331
|
+
},
|
|
332
|
+
set: (_target, prop, value) => {
|
|
333
|
+
this.setHeader(String(prop), value);
|
|
334
|
+
return true;
|
|
335
|
+
},
|
|
336
|
+
});
|
|
337
|
+
}
|
|
236
338
|
writeHead(statusCode, statusMessageOrHeaders, headers) {
|
|
237
339
|
this.statusCode = statusCode;
|
|
238
340
|
let headersToSet;
|
|
@@ -260,6 +362,25 @@ class LambdaResponseBuffered extends Writable {
|
|
|
260
362
|
//
|
|
261
363
|
// Express compatibility methods
|
|
262
364
|
//
|
|
365
|
+
/**
|
|
366
|
+
* Express-style alias for getHeader().
|
|
367
|
+
* Used by middleware like decorateResponse that use res.get().
|
|
368
|
+
* Note: Directly accesses _headers to avoid prototype chain issues with bundled code.
|
|
369
|
+
*/
|
|
370
|
+
get(name) {
|
|
371
|
+
return this._headers.get(name.toLowerCase());
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Express-style alias for setHeader().
|
|
375
|
+
* Used by middleware like decorateResponse that use res.set().
|
|
376
|
+
* Note: Directly accesses _headers to avoid prototype chain issues with bundled code.
|
|
377
|
+
*/
|
|
378
|
+
set(name, value) {
|
|
379
|
+
if (!this._headersSent) {
|
|
380
|
+
this._headers.set(name.toLowerCase(), String(value));
|
|
381
|
+
}
|
|
382
|
+
return this;
|
|
383
|
+
}
|
|
263
384
|
status(code) {
|
|
264
385
|
this.statusCode = code;
|
|
265
386
|
return this;
|
|
@@ -276,6 +397,26 @@ class LambdaResponseBuffered extends Writable {
|
|
|
276
397
|
this.end(body);
|
|
277
398
|
return this;
|
|
278
399
|
}
|
|
400
|
+
/**
|
|
401
|
+
* Add a field to the Vary response header.
|
|
402
|
+
* Used by CORS middleware to indicate response varies by Origin.
|
|
403
|
+
*/
|
|
404
|
+
vary(field) {
|
|
405
|
+
const existing = this.getHeader("vary");
|
|
406
|
+
if (!existing) {
|
|
407
|
+
this.setHeader("vary", field);
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
// Append to existing Vary header if field not already present
|
|
411
|
+
const fields = String(existing)
|
|
412
|
+
.split(",")
|
|
413
|
+
.map((f) => f.trim().toLowerCase());
|
|
414
|
+
if (!fields.includes(field.toLowerCase())) {
|
|
415
|
+
this.setHeader("vary", `${existing}, ${field}`);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return this;
|
|
419
|
+
}
|
|
279
420
|
//
|
|
280
421
|
// Writable stream implementation
|
|
281
422
|
//
|
|
@@ -336,6 +477,14 @@ class LambdaResponseBuffered extends Writable {
|
|
|
336
477
|
}
|
|
337
478
|
}
|
|
338
479
|
|
|
480
|
+
//
|
|
481
|
+
//
|
|
482
|
+
// Constants
|
|
483
|
+
//
|
|
484
|
+
// Get Node's internal kOutHeaders symbol from ServerResponse prototype.
|
|
485
|
+
// This is needed for compatibility with Datadog dd-trace instrumentation,
|
|
486
|
+
// which patches HTTP methods and expects this internal state to exist.
|
|
487
|
+
const kOutHeaders = Object.getOwnPropertySymbols(ServerResponse.prototype).find((s) => s.toString() === "Symbol(kOutHeaders)");
|
|
339
488
|
//
|
|
340
489
|
//
|
|
341
490
|
// LambdaResponseStreaming Class
|
|
@@ -353,11 +502,54 @@ class LambdaResponseStreaming extends Writable {
|
|
|
353
502
|
this.socket = {
|
|
354
503
|
remoteAddress: "127.0.0.1",
|
|
355
504
|
};
|
|
505
|
+
// Internal state exposed for direct manipulation by safe response methods
|
|
506
|
+
// that need to bypass dd-trace interception
|
|
356
507
|
this._headers = new Map();
|
|
357
508
|
this._headersSent = false;
|
|
358
509
|
this._pendingWrites = [];
|
|
359
510
|
this._wrappedStream = null;
|
|
360
511
|
this._responseStream = responseStream;
|
|
512
|
+
// Initialize Node's internal kOutHeaders for dd-trace compatibility.
|
|
513
|
+
// dd-trace patches HTTP methods and expects this internal state.
|
|
514
|
+
if (kOutHeaders) {
|
|
515
|
+
this[kOutHeaders] = Object.create(null);
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
//
|
|
519
|
+
// Internal bypass methods - completely avoid prototype chain lookup
|
|
520
|
+
// These directly access _headers Map, safe from dd-trace interception
|
|
521
|
+
//
|
|
522
|
+
_internalGetHeader(name) {
|
|
523
|
+
const value = this._headers.get(name.toLowerCase());
|
|
524
|
+
return value ? String(value) : undefined;
|
|
525
|
+
}
|
|
526
|
+
_internalSetHeader(name, value) {
|
|
527
|
+
if (!this._headersSent) {
|
|
528
|
+
const lowerName = name.toLowerCase();
|
|
529
|
+
this._headers.set(lowerName, value);
|
|
530
|
+
// Also sync kOutHeaders for any code that expects it
|
|
531
|
+
if (kOutHeaders) {
|
|
532
|
+
const outHeaders = this[kOutHeaders];
|
|
533
|
+
if (outHeaders) {
|
|
534
|
+
outHeaders[lowerName] = [name, value];
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
_internalHasHeader(name) {
|
|
540
|
+
return this._headers.has(name.toLowerCase());
|
|
541
|
+
}
|
|
542
|
+
_internalRemoveHeader(name) {
|
|
543
|
+
if (!this._headersSent) {
|
|
544
|
+
const lowerName = name.toLowerCase();
|
|
545
|
+
this._headers.delete(lowerName);
|
|
546
|
+
if (kOutHeaders) {
|
|
547
|
+
const outHeaders = this[kOutHeaders];
|
|
548
|
+
if (outHeaders) {
|
|
549
|
+
delete outHeaders[lowerName];
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
361
553
|
}
|
|
362
554
|
//
|
|
363
555
|
// Header management
|
|
@@ -368,7 +560,16 @@ class LambdaResponseStreaming extends Writable {
|
|
|
368
560
|
// Headers cannot be changed after body starts
|
|
369
561
|
return this;
|
|
370
562
|
}
|
|
371
|
-
|
|
563
|
+
const lowerName = name.toLowerCase();
|
|
564
|
+
this._headers.set(lowerName, String(value));
|
|
565
|
+
// Sync with kOutHeaders for dd-trace compatibility
|
|
566
|
+
// Node stores as { 'header-name': ['Header-Name', value] }
|
|
567
|
+
if (kOutHeaders) {
|
|
568
|
+
const outHeaders = this[kOutHeaders];
|
|
569
|
+
if (outHeaders) {
|
|
570
|
+
outHeaders[lowerName] = [name, String(value)];
|
|
571
|
+
}
|
|
572
|
+
}
|
|
372
573
|
return this;
|
|
373
574
|
}
|
|
374
575
|
getHeader(name) {
|
|
@@ -376,7 +577,15 @@ class LambdaResponseStreaming extends Writable {
|
|
|
376
577
|
}
|
|
377
578
|
removeHeader(name) {
|
|
378
579
|
if (!this._headersSent) {
|
|
379
|
-
|
|
580
|
+
const lowerName = name.toLowerCase();
|
|
581
|
+
this._headers.delete(lowerName);
|
|
582
|
+
// Sync with kOutHeaders for dd-trace compatibility
|
|
583
|
+
if (kOutHeaders) {
|
|
584
|
+
const outHeaders = this[kOutHeaders];
|
|
585
|
+
if (outHeaders) {
|
|
586
|
+
delete outHeaders[lowerName];
|
|
587
|
+
}
|
|
588
|
+
}
|
|
380
589
|
}
|
|
381
590
|
}
|
|
382
591
|
getHeaders() {
|
|
@@ -392,6 +601,43 @@ class LambdaResponseStreaming extends Writable {
|
|
|
392
601
|
getHeaderNames() {
|
|
393
602
|
return Array.from(this._headers.keys());
|
|
394
603
|
}
|
|
604
|
+
/**
|
|
605
|
+
* Proxy for direct header access (e.g., res.headers['content-type']).
|
|
606
|
+
* Required for compatibility with middleware like helmet that access headers directly.
|
|
607
|
+
*/
|
|
608
|
+
get headers() {
|
|
609
|
+
return new Proxy({}, {
|
|
610
|
+
deleteProperty: (_target, prop) => {
|
|
611
|
+
this.removeHeader(String(prop));
|
|
612
|
+
return true;
|
|
613
|
+
},
|
|
614
|
+
get: (_target, prop) => {
|
|
615
|
+
if (typeof prop === "symbol")
|
|
616
|
+
return undefined;
|
|
617
|
+
return this.getHeader(String(prop));
|
|
618
|
+
},
|
|
619
|
+
getOwnPropertyDescriptor: (_target, prop) => {
|
|
620
|
+
if (this.hasHeader(String(prop))) {
|
|
621
|
+
return {
|
|
622
|
+
configurable: true,
|
|
623
|
+
enumerable: true,
|
|
624
|
+
value: this.getHeader(String(prop)),
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
return undefined;
|
|
628
|
+
},
|
|
629
|
+
has: (_target, prop) => {
|
|
630
|
+
return this.hasHeader(String(prop));
|
|
631
|
+
},
|
|
632
|
+
ownKeys: () => {
|
|
633
|
+
return this.getHeaderNames();
|
|
634
|
+
},
|
|
635
|
+
set: (_target, prop, value) => {
|
|
636
|
+
this.setHeader(String(prop), value);
|
|
637
|
+
return true;
|
|
638
|
+
},
|
|
639
|
+
});
|
|
640
|
+
}
|
|
395
641
|
writeHead(statusCode, statusMessageOrHeaders, headers) {
|
|
396
642
|
if (this._headersSent) {
|
|
397
643
|
return this;
|
|
@@ -443,6 +689,25 @@ class LambdaResponseStreaming extends Writable {
|
|
|
443
689
|
//
|
|
444
690
|
// Express compatibility methods
|
|
445
691
|
//
|
|
692
|
+
/**
|
|
693
|
+
* Express-style alias for getHeader().
|
|
694
|
+
* Used by middleware like decorateResponse that use res.get().
|
|
695
|
+
* Note: Directly accesses _headers to avoid prototype chain issues with bundled code.
|
|
696
|
+
*/
|
|
697
|
+
get(name) {
|
|
698
|
+
return this._headers.get(name.toLowerCase());
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* Express-style alias for setHeader().
|
|
702
|
+
* Used by middleware like decorateResponse that use res.set().
|
|
703
|
+
* Note: Directly accesses _headers to avoid prototype chain issues with bundled code.
|
|
704
|
+
*/
|
|
705
|
+
set(name, value) {
|
|
706
|
+
if (!this._headersSent) {
|
|
707
|
+
this._headers.set(name.toLowerCase(), String(value));
|
|
708
|
+
}
|
|
709
|
+
return this;
|
|
710
|
+
}
|
|
446
711
|
status(code) {
|
|
447
712
|
this.statusCode = code;
|
|
448
713
|
return this;
|
|
@@ -459,6 +724,26 @@ class LambdaResponseStreaming extends Writable {
|
|
|
459
724
|
this.end(body);
|
|
460
725
|
return this;
|
|
461
726
|
}
|
|
727
|
+
/**
|
|
728
|
+
* Add a field to the Vary response header.
|
|
729
|
+
* Used by CORS middleware to indicate response varies by Origin.
|
|
730
|
+
*/
|
|
731
|
+
vary(field) {
|
|
732
|
+
const existing = this.getHeader("vary");
|
|
733
|
+
if (!existing) {
|
|
734
|
+
this.setHeader("vary", field);
|
|
735
|
+
}
|
|
736
|
+
else {
|
|
737
|
+
// Append to existing Vary header if field not already present
|
|
738
|
+
const fields = String(existing)
|
|
739
|
+
.split(",")
|
|
740
|
+
.map((f) => f.trim().toLowerCase());
|
|
741
|
+
if (!fields.includes(field.toLowerCase())) {
|
|
742
|
+
this.setHeader("vary", `${existing}, ${field}`);
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
return this;
|
|
746
|
+
}
|
|
462
747
|
//
|
|
463
748
|
// Writable stream implementation
|
|
464
749
|
//
|
|
@@ -915,6 +1200,73 @@ function getCurrentInvokeUuid(req) {
|
|
|
915
1200
|
return getWebAdapterUuid();
|
|
916
1201
|
}
|
|
917
1202
|
|
|
1203
|
+
//
|
|
1204
|
+
//
|
|
1205
|
+
// Helpers
|
|
1206
|
+
//
|
|
1207
|
+
/**
|
|
1208
|
+
* Safely get a header value from response.
|
|
1209
|
+
* Handles both Express Response and Lambda adapter responses.
|
|
1210
|
+
* Defensive against dd-trace instrumentation issues.
|
|
1211
|
+
*/
|
|
1212
|
+
function safeGetHeader(res, name) {
|
|
1213
|
+
try {
|
|
1214
|
+
// Try internal method first (completely bypasses dd-trace)
|
|
1215
|
+
if (typeof res._internalGetHeader === "function") {
|
|
1216
|
+
return res._internalGetHeader(name);
|
|
1217
|
+
}
|
|
1218
|
+
// Fall back to _headers Map access (Lambda adapter, avoids dd-trace)
|
|
1219
|
+
if (res._headers instanceof Map) {
|
|
1220
|
+
const value = res._headers.get(name.toLowerCase());
|
|
1221
|
+
return value ? String(value) : undefined;
|
|
1222
|
+
}
|
|
1223
|
+
// Fall back to getHeader (more standard than get)
|
|
1224
|
+
if (typeof res.getHeader === "function") {
|
|
1225
|
+
const value = res.getHeader(name);
|
|
1226
|
+
return value ? String(value) : undefined;
|
|
1227
|
+
}
|
|
1228
|
+
// Last resort: try get
|
|
1229
|
+
if (typeof res.get === "function") {
|
|
1230
|
+
const value = res.get(name);
|
|
1231
|
+
return value ? String(value) : undefined;
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
catch {
|
|
1235
|
+
// Silently fail - caller will handle missing value
|
|
1236
|
+
}
|
|
1237
|
+
return undefined;
|
|
1238
|
+
}
|
|
1239
|
+
/**
|
|
1240
|
+
* Safely set a header value on response.
|
|
1241
|
+
* Handles both Express Response and Lambda adapter responses.
|
|
1242
|
+
* Defensive against dd-trace instrumentation issues.
|
|
1243
|
+
*/
|
|
1244
|
+
function safeSetHeader(res, name, value) {
|
|
1245
|
+
try {
|
|
1246
|
+
// Try internal method first (completely bypasses dd-trace)
|
|
1247
|
+
if (typeof res._internalSetHeader === "function") {
|
|
1248
|
+
res._internalSetHeader(name, value);
|
|
1249
|
+
return;
|
|
1250
|
+
}
|
|
1251
|
+
// Fall back to _headers Map access (Lambda adapter, avoids dd-trace)
|
|
1252
|
+
if (res._headers instanceof Map) {
|
|
1253
|
+
res._headers.set(name.toLowerCase(), value);
|
|
1254
|
+
return;
|
|
1255
|
+
}
|
|
1256
|
+
// Fall back to setHeader (more standard than set)
|
|
1257
|
+
if (typeof res.setHeader === "function") {
|
|
1258
|
+
res.setHeader(name, value);
|
|
1259
|
+
return;
|
|
1260
|
+
}
|
|
1261
|
+
// Last resort: try set
|
|
1262
|
+
if (typeof res.set === "function") {
|
|
1263
|
+
res.set(name, value);
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
catch {
|
|
1267
|
+
// Silently fail - header just won't be set
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
918
1270
|
//
|
|
919
1271
|
//
|
|
920
1272
|
// Main
|
|
@@ -931,36 +1283,37 @@ const decorateResponse = (res, { handler = "", version = process.env.PROJECT_VER
|
|
|
931
1283
|
log$1.warn("decorateResponse called but response is not an object");
|
|
932
1284
|
return;
|
|
933
1285
|
}
|
|
1286
|
+
const extRes = res;
|
|
934
1287
|
try {
|
|
935
1288
|
//
|
|
936
1289
|
//
|
|
937
1290
|
// Decorate Headers
|
|
938
1291
|
//
|
|
939
1292
|
// X-Powered-By, override "Express" but nothing else
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
1293
|
+
const currentPoweredBy = safeGetHeader(extRes, HTTP.HEADER.POWERED_BY);
|
|
1294
|
+
if (!currentPoweredBy || currentPoweredBy === "Express") {
|
|
1295
|
+
safeSetHeader(extRes, HTTP.HEADER.POWERED_BY, JAYPIE.LIB.EXPRESS);
|
|
943
1296
|
}
|
|
944
1297
|
// X-Project-Environment
|
|
945
1298
|
if (process.env.PROJECT_ENV) {
|
|
946
|
-
|
|
1299
|
+
safeSetHeader(extRes, HTTP.HEADER.PROJECT.ENVIRONMENT, process.env.PROJECT_ENV);
|
|
947
1300
|
}
|
|
948
1301
|
// X-Project-Handler
|
|
949
1302
|
if (handler) {
|
|
950
|
-
|
|
1303
|
+
safeSetHeader(extRes, HTTP.HEADER.PROJECT.HANDLER, handler);
|
|
951
1304
|
}
|
|
952
1305
|
// X-Project-Invocation
|
|
953
1306
|
const currentInvoke = getCurrentInvokeUuid();
|
|
954
1307
|
if (currentInvoke) {
|
|
955
|
-
|
|
1308
|
+
safeSetHeader(extRes, HTTP.HEADER.PROJECT.INVOCATION, currentInvoke);
|
|
956
1309
|
}
|
|
957
1310
|
// X-Project-Key
|
|
958
1311
|
if (process.env.PROJECT_KEY) {
|
|
959
|
-
|
|
1312
|
+
safeSetHeader(extRes, HTTP.HEADER.PROJECT.KEY, process.env.PROJECT_KEY);
|
|
960
1313
|
}
|
|
961
1314
|
// X-Project-Version
|
|
962
1315
|
if (version) {
|
|
963
|
-
|
|
1316
|
+
safeSetHeader(extRes, HTTP.HEADER.PROJECT.VERSION, version);
|
|
964
1317
|
}
|
|
965
1318
|
//
|
|
966
1319
|
//
|
|
@@ -1020,6 +1373,76 @@ function summarizeResponse(res, extras) {
|
|
|
1020
1373
|
|
|
1021
1374
|
// Cast logger to extended interface for runtime features not in type definitions
|
|
1022
1375
|
const logger$1 = log;
|
|
1376
|
+
//
|
|
1377
|
+
//
|
|
1378
|
+
// Helpers - Safe response methods to bypass dd-trace interception
|
|
1379
|
+
//
|
|
1380
|
+
/**
|
|
1381
|
+
* Check if response is a Lambda mock response with direct internal access.
|
|
1382
|
+
*/
|
|
1383
|
+
function isLambdaMockResponse(res) {
|
|
1384
|
+
const mock = res;
|
|
1385
|
+
return (mock._headers instanceof Map &&
|
|
1386
|
+
Array.isArray(mock._chunks) &&
|
|
1387
|
+
typeof mock.buildResult === "function");
|
|
1388
|
+
}
|
|
1389
|
+
/**
|
|
1390
|
+
* Safely send a JSON response, avoiding dd-trace interception.
|
|
1391
|
+
* For Lambda mock responses, directly manipulates internal state instead of
|
|
1392
|
+
* using stream methods (write/end) which dd-trace intercepts.
|
|
1393
|
+
*/
|
|
1394
|
+
function safeSendJson(res, statusCode, data) {
|
|
1395
|
+
if (isLambdaMockResponse(res)) {
|
|
1396
|
+
// Use internal method to set header (completely bypasses dd-trace)
|
|
1397
|
+
if (typeof res._internalSetHeader === "function") {
|
|
1398
|
+
res._internalSetHeader("content-type", "application/json");
|
|
1399
|
+
}
|
|
1400
|
+
else {
|
|
1401
|
+
// Fall back to direct _headers manipulation
|
|
1402
|
+
res._headers.set("content-type", "application/json");
|
|
1403
|
+
}
|
|
1404
|
+
res.statusCode = statusCode;
|
|
1405
|
+
// Directly push to chunks array instead of using stream write/end
|
|
1406
|
+
const chunk = Buffer.from(JSON.stringify(data));
|
|
1407
|
+
res._chunks.push(chunk);
|
|
1408
|
+
res._headersSent = true;
|
|
1409
|
+
// Signal completion if a promise is waiting
|
|
1410
|
+
if (res._resolve) {
|
|
1411
|
+
res._resolve(res.buildResult());
|
|
1412
|
+
}
|
|
1413
|
+
return;
|
|
1414
|
+
}
|
|
1415
|
+
// Fall back to standard Express methods for real responses
|
|
1416
|
+
res.status(statusCode).json(data);
|
|
1417
|
+
}
|
|
1418
|
+
/**
|
|
1419
|
+
* Safely send a response body, avoiding dd-trace interception.
|
|
1420
|
+
* For Lambda mock responses, directly manipulates internal state instead of
|
|
1421
|
+
* using stream methods (write/end) which dd-trace intercepts.
|
|
1422
|
+
*/
|
|
1423
|
+
function safeSend(res, statusCode, body) {
|
|
1424
|
+
if (isLambdaMockResponse(res)) {
|
|
1425
|
+
// Direct internal state manipulation - bypasses dd-trace completely
|
|
1426
|
+
res.statusCode = statusCode;
|
|
1427
|
+
if (body !== undefined) {
|
|
1428
|
+
const chunk = Buffer.from(body);
|
|
1429
|
+
res._chunks.push(chunk);
|
|
1430
|
+
}
|
|
1431
|
+
res._headersSent = true;
|
|
1432
|
+
// Signal completion if a promise is waiting
|
|
1433
|
+
if (res._resolve) {
|
|
1434
|
+
res._resolve(res.buildResult());
|
|
1435
|
+
}
|
|
1436
|
+
return;
|
|
1437
|
+
}
|
|
1438
|
+
// Fall back to standard Express methods for real responses
|
|
1439
|
+
if (body !== undefined) {
|
|
1440
|
+
res.status(statusCode).send(body);
|
|
1441
|
+
}
|
|
1442
|
+
else {
|
|
1443
|
+
res.status(statusCode).send();
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1023
1446
|
function expressHandler(handlerOrOptions, optionsOrHandler) {
|
|
1024
1447
|
/* eslint-enable no-redeclare */
|
|
1025
1448
|
let handler;
|
|
@@ -1236,30 +1659,30 @@ function expressHandler(handlerOrOptions, optionsOrHandler) {
|
|
|
1236
1659
|
if (typeof response === "object") {
|
|
1237
1660
|
if (typeof response.json ===
|
|
1238
1661
|
"function") {
|
|
1239
|
-
res
|
|
1662
|
+
safeSendJson(res, status, response.json());
|
|
1240
1663
|
}
|
|
1241
1664
|
else {
|
|
1242
|
-
res
|
|
1665
|
+
safeSendJson(res, status, response);
|
|
1243
1666
|
}
|
|
1244
1667
|
}
|
|
1245
1668
|
else if (typeof response === "string") {
|
|
1246
1669
|
try {
|
|
1247
|
-
res
|
|
1670
|
+
safeSendJson(res, status, JSON.parse(response));
|
|
1248
1671
|
}
|
|
1249
1672
|
catch {
|
|
1250
|
-
res
|
|
1673
|
+
safeSend(res, status, response);
|
|
1251
1674
|
}
|
|
1252
1675
|
}
|
|
1253
1676
|
else if (response === true) {
|
|
1254
|
-
res
|
|
1677
|
+
safeSend(res, HTTP.CODE.CREATED);
|
|
1255
1678
|
}
|
|
1256
1679
|
else {
|
|
1257
|
-
res
|
|
1680
|
+
safeSend(res, status, response);
|
|
1258
1681
|
}
|
|
1259
1682
|
}
|
|
1260
1683
|
else {
|
|
1261
1684
|
// No response
|
|
1262
|
-
res
|
|
1685
|
+
safeSend(res, HTTP.CODE.NO_CONTENT);
|
|
1263
1686
|
}
|
|
1264
1687
|
}
|
|
1265
1688
|
else {
|