@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/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
- this._headers.set(name.toLowerCase(), String(value));
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
- this._headers.delete(name.toLowerCase());
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
- this._headers.set(name.toLowerCase(), String(value));
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
- this._headers.delete(name.toLowerCase());
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
- if (!res.get(HTTP.HEADER.POWERED_BY) ||
941
- res.get(HTTP.HEADER.POWERED_BY) === "Express") {
942
- res.set(HTTP.HEADER.POWERED_BY, JAYPIE.LIB.EXPRESS);
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
- res.set(HTTP.HEADER.PROJECT.ENVIRONMENT, process.env.PROJECT_ENV);
1299
+ safeSetHeader(extRes, HTTP.HEADER.PROJECT.ENVIRONMENT, process.env.PROJECT_ENV);
947
1300
  }
948
1301
  // X-Project-Handler
949
1302
  if (handler) {
950
- res.set(HTTP.HEADER.PROJECT.HANDLER, handler);
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
- res.set(HTTP.HEADER.PROJECT.INVOCATION, currentInvoke);
1308
+ safeSetHeader(extRes, HTTP.HEADER.PROJECT.INVOCATION, currentInvoke);
956
1309
  }
957
1310
  // X-Project-Key
958
1311
  if (process.env.PROJECT_KEY) {
959
- res.set(HTTP.HEADER.PROJECT.KEY, process.env.PROJECT_KEY);
1312
+ safeSetHeader(extRes, HTTP.HEADER.PROJECT.KEY, process.env.PROJECT_KEY);
960
1313
  }
961
1314
  // X-Project-Version
962
1315
  if (version) {
963
- res.set(HTTP.HEADER.PROJECT.VERSION, version);
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.json(response.json());
1662
+ safeSendJson(res, status, response.json());
1240
1663
  }
1241
1664
  else {
1242
- res.status(status).json(response);
1665
+ safeSendJson(res, status, response);
1243
1666
  }
1244
1667
  }
1245
1668
  else if (typeof response === "string") {
1246
1669
  try {
1247
- res.status(status).json(JSON.parse(response));
1670
+ safeSendJson(res, status, JSON.parse(response));
1248
1671
  }
1249
1672
  catch {
1250
- res.status(status).send(response);
1673
+ safeSend(res, status, response);
1251
1674
  }
1252
1675
  }
1253
1676
  else if (response === true) {
1254
- res.status(HTTP.CODE.CREATED).send();
1677
+ safeSend(res, HTTP.CODE.CREATED);
1255
1678
  }
1256
1679
  else {
1257
- res.status(status).send(response);
1680
+ safeSend(res, status, response);
1258
1681
  }
1259
1682
  }
1260
1683
  else {
1261
1684
  // No response
1262
- res.status(HTTP.CODE.NO_CONTENT).send();
1685
+ safeSend(res, HTTP.CODE.NO_CONTENT);
1263
1686
  }
1264
1687
  }
1265
1688
  else {