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