@jaypie/express 1.2.9 → 1.2.11-dev.0

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
  import type { OutgoingHttpHeaders } from "node:http";
2
2
  import { Writable } from "node:stream";
3
3
  import type { ResponseStream } from "./types.js";
4
+ export declare const JAYPIE_LAMBDA_STREAMING: unique symbol;
4
5
  /**
5
6
  * Mock ServerResponse that streams directly to Lambda responseStream.
6
7
  * Uses awslambda.HttpResponseStream.from() to set status and headers.
package/dist/esm/index.js CHANGED
@@ -624,6 +624,9 @@ class LambdaResponseBuffered extends Writable {
624
624
  //
625
625
  // Constants
626
626
  //
627
+ // Symbol to identify Lambda streaming responses. Uses Symbol.for() to ensure
628
+ // the same symbol is used across bundles/realms. Survives prototype manipulation.
629
+ const JAYPIE_LAMBDA_STREAMING = Symbol.for("@jaypie/express/LambdaStreaming");
627
630
  // Get Node's internal kOutHeaders symbol from ServerResponse prototype.
628
631
  // This is needed for compatibility with Datadog dd-trace instrumentation,
629
632
  // which patches HTTP methods and expects this internal state to exist.
@@ -652,6 +655,14 @@ class LambdaResponseStreaming extends Writable {
652
655
  this._pendingWrites = [];
653
656
  this._wrappedStream = null;
654
657
  this._responseStream = responseStream;
658
+ // Mark as Lambda streaming response using Symbol for reliable detection.
659
+ // Survives prototype manipulation from Express and dd-trace.
660
+ this[JAYPIE_LAMBDA_STREAMING] =
661
+ true;
662
+ // DIAGNOSTIC: Log symbol assignment
663
+ console.log("[DIAG:LambdaResponseStreaming:constructor] Symbol set", {
664
+ symbolValue: this[JAYPIE_LAMBDA_STREAMING],
665
+ });
655
666
  // Initialize Node's internal kOutHeaders for dd-trace compatibility.
656
667
  // dd-trace patches HTTP methods and expects this internal state.
657
668
  if (kOutHeaders) {
@@ -839,8 +850,17 @@ class LambdaResponseStreaming extends Writable {
839
850
  return this._headersSent;
840
851
  }
841
852
  flushHeaders() {
842
- if (this._headersSent)
853
+ // DIAGNOSTIC: Log every flushHeaders call with stack trace
854
+ const caller = new Error().stack?.split("\n").slice(1, 5).join("\n");
855
+ console.log("[DIAG:LambdaResponseStreaming:flushHeaders] Called", {
856
+ statusCode: this.statusCode,
857
+ headersSent: this._headersSent,
858
+ caller,
859
+ });
860
+ if (this._headersSent) {
861
+ console.log("[DIAG:LambdaResponseStreaming:flushHeaders] Already sent, returning early");
843
862
  return;
863
+ }
844
864
  const headers = {};
845
865
  for (const [key, value] of this._headers) {
846
866
  headers[key] = Array.isArray(value) ? value.join(", ") : String(value);
@@ -849,6 +869,10 @@ class LambdaResponseStreaming extends Writable {
849
869
  headers,
850
870
  statusCode: this.statusCode,
851
871
  };
872
+ // DIAGNOSTIC: Log what we're sending to Lambda
873
+ console.log("[DIAG:LambdaResponseStreaming:flushHeaders] Creating wrapped stream", {
874
+ metadata,
875
+ });
852
876
  // Wrap the stream with metadata using awslambda global
853
877
  this._wrappedStream = awslambda.HttpResponseStream.from(this._responseStream, metadata);
854
878
  this._headersSent = true;
@@ -882,6 +906,14 @@ class LambdaResponseStreaming extends Writable {
882
906
  return this;
883
907
  }
884
908
  status(code) {
909
+ // DIAGNOSTIC: Log status code changes
910
+ const caller = new Error().stack?.split("\n").slice(1, 4).join("\n");
911
+ console.log("[DIAG:LambdaResponseStreaming:status] Setting status", {
912
+ newCode: code,
913
+ previousCode: this.statusCode,
914
+ headersSent: this._headersSent,
915
+ caller,
916
+ });
885
917
  this.statusCode = code;
886
918
  return this;
887
919
  }
@@ -927,10 +959,17 @@ class LambdaResponseStreaming extends Writable {
927
959
  const buffer = Buffer.isBuffer(chunk)
928
960
  ? chunk
929
961
  : Buffer.from(chunk, encoding);
962
+ // DIAGNOSTIC: Log every write
963
+ console.log("[DIAG:LambdaResponseStreaming:_write] Called", {
964
+ chunkLength: buffer.length,
965
+ headersSent: this._headersSent,
966
+ statusCode: this.statusCode,
967
+ });
930
968
  if (!this._headersSent) {
931
969
  // Buffer writes until headers are sent
932
970
  this._pendingWrites.push({ callback: () => callback(), chunk: buffer });
933
971
  // Auto-flush headers on first write
972
+ console.log("[DIAG:LambdaResponseStreaming:_write] Auto-flushing headers on first write");
934
973
  this.flushHeaders();
935
974
  }
936
975
  else {
@@ -939,7 +978,14 @@ class LambdaResponseStreaming extends Writable {
939
978
  }
940
979
  }
941
980
  _final(callback) {
981
+ // DIAGNOSTIC: Log _final call
982
+ console.log("[DIAG:LambdaResponseStreaming:_final] Called", {
983
+ headersSent: this._headersSent,
984
+ statusCode: this.statusCode,
985
+ hasWrappedStream: !!this._wrappedStream,
986
+ });
942
987
  if (!this._headersSent) {
988
+ console.log("[DIAG:LambdaResponseStreaming:_final] Headers not sent, flushing now");
943
989
  this.flushHeaders();
944
990
  }
945
991
  this._wrappedStream?.end();
@@ -1024,13 +1070,6 @@ function createLambdaHandler(app, _options) {
1024
1070
  await runExpressApp(app, req, res);
1025
1071
  // Get Lambda response - await explicitly to ensure we have the result
1026
1072
  result = await res.getResult();
1027
- // Debug: Log the response before returning
1028
- console.log("[createLambdaHandler] Returning response:", JSON.stringify({
1029
- statusCode: result.statusCode,
1030
- headers: result.headers,
1031
- bodyLength: result.body?.length,
1032
- isBase64Encoded: result.isBase64Encoded,
1033
- }));
1034
1073
  return result;
1035
1074
  }
1036
1075
  catch (error) {
@@ -1459,7 +1498,8 @@ const decorateResponse = (res, { handler = "", version = process.env.PROJECT_VER
1459
1498
  // X-Powered-By, override "Express" but nothing else
1460
1499
  const currentPoweredBy = safeGetHeader(extRes, HTTP.HEADER.POWERED_BY);
1461
1500
  if (!currentPoweredBy || currentPoweredBy === "Express") {
1462
- safeSetHeader(extRes, HTTP.HEADER.POWERED_BY, JAYPIE.LIB.EXPRESS);
1501
+ // DIAGNOSTIC: Include dev marker to verify build deployment
1502
+ safeSetHeader(extRes, HTTP.HEADER.POWERED_BY, `${JAYPIE.LIB.EXPRESS}#dev-178`);
1463
1503
  }
1464
1504
  // X-Project-Environment
1465
1505
  if (process.env.PROJECT_ENV) {
@@ -1551,6 +1591,22 @@ const logger$1 = log;
1551
1591
  function isLambdaMockResponse(res) {
1552
1592
  return (res[JAYPIE_LAMBDA_MOCK] === true);
1553
1593
  }
1594
+ /**
1595
+ * Check if response is a Lambda streaming response.
1596
+ * Uses Symbol marker to survive prototype chain modifications from Express and dd-trace.
1597
+ */
1598
+ function isLambdaStreamingResponse(res) {
1599
+ const symbolValue = res[JAYPIE_LAMBDA_STREAMING];
1600
+ const result = symbolValue === true;
1601
+ // DIAGNOSTIC: Log symbol check
1602
+ console.log("[DIAG:expressHandler:isLambdaStreamingResponse] Check", {
1603
+ symbolValue,
1604
+ result,
1605
+ resConstructorName: res?.constructor?.name,
1606
+ hasFlushHeaders: typeof res.flushHeaders === "function",
1607
+ });
1608
+ return result;
1609
+ }
1554
1610
  /**
1555
1611
  * Safely send a JSON response, avoiding dd-trace interception.
1556
1612
  * For Lambda mock responses, directly manipulates internal state instead of
@@ -1583,13 +1639,29 @@ function safeSendJson(res, statusCode, data) {
1583
1639
  return;
1584
1640
  }
1585
1641
  // Fall back to standard Express methods for real responses
1642
+ // DIAGNOSTIC: Log before setting status
1643
+ console.log("[DIAG:expressHandler:safeSendJson] Before res.status()", {
1644
+ statusCode,
1645
+ currentStatusCode: res.statusCode,
1646
+ });
1586
1647
  res.status(statusCode);
1648
+ console.log("[DIAG:expressHandler:safeSendJson] After res.status()", {
1649
+ statusCode,
1650
+ newStatusCode: res.statusCode,
1651
+ });
1587
1652
  // CRITICAL: For Lambda streaming responses, flush headers before send to
1588
- // initialize the stream wrapper. Check for _responseStream which is unique
1589
- // to LambdaResponseStreaming (regular Express res doesn't have this).
1590
- if ("_responseStream" in res && typeof res.flushHeaders === "function") {
1653
+ // initialize the stream wrapper. This ensures the status code is captured
1654
+ // before any writes occur (which would auto-flush with default 200).
1655
+ // Uses Symbol marker for reliable detection that survives prototype manipulation.
1656
+ const isStreaming = isLambdaStreamingResponse(res);
1657
+ console.log("[DIAG:expressHandler:safeSendJson] Streaming check", {
1658
+ isStreaming,
1659
+ });
1660
+ if (isStreaming && typeof res.flushHeaders === "function") {
1661
+ console.log("[DIAG:expressHandler:safeSendJson] Calling flushHeaders explicitly");
1591
1662
  res.flushHeaders();
1592
1663
  }
1664
+ console.log("[DIAG:expressHandler:safeSendJson] Calling res.json()");
1593
1665
  res.json(data);
1594
1666
  }
1595
1667
  /**
@@ -1618,17 +1690,35 @@ function safeSend(res, statusCode, body) {
1618
1690
  return;
1619
1691
  }
1620
1692
  // Fall back to standard Express methods for real responses
1693
+ // DIAGNOSTIC: Log before setting status
1694
+ console.log("[DIAG:expressHandler:safeSend] Before res.status()", {
1695
+ statusCode,
1696
+ currentStatusCode: res.statusCode,
1697
+ hasBody: body !== undefined,
1698
+ });
1621
1699
  res.status(statusCode);
1700
+ console.log("[DIAG:expressHandler:safeSend] After res.status()", {
1701
+ statusCode,
1702
+ newStatusCode: res.statusCode,
1703
+ });
1622
1704
  // CRITICAL: For Lambda streaming responses, flush headers before send to
1623
- // initialize the stream wrapper. Check for _responseStream which is unique
1624
- // to LambdaResponseStreaming (regular Express res doesn't have this).
1625
- if ("_responseStream" in res && typeof res.flushHeaders === "function") {
1705
+ // initialize the stream wrapper. This ensures the status code is captured
1706
+ // before any writes occur (which would auto-flush with default 200).
1707
+ // Uses Symbol marker for reliable detection that survives prototype manipulation.
1708
+ const isStreaming = isLambdaStreamingResponse(res);
1709
+ console.log("[DIAG:expressHandler:safeSend] Streaming check", {
1710
+ isStreaming,
1711
+ });
1712
+ if (isStreaming && typeof res.flushHeaders === "function") {
1713
+ console.log("[DIAG:expressHandler:safeSend] Calling flushHeaders explicitly");
1626
1714
  res.flushHeaders();
1627
1715
  }
1628
1716
  if (body !== undefined) {
1717
+ console.log("[DIAG:expressHandler:safeSend] Calling res.send(body)");
1629
1718
  res.send(body);
1630
1719
  }
1631
1720
  else {
1721
+ console.log("[DIAG:expressHandler:safeSend] Calling res.send() (no body)");
1632
1722
  res.send();
1633
1723
  }
1634
1724
  }