@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
|
-
|
|
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
|
-
|
|
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.
|
|
1589
|
-
//
|
|
1590
|
-
|
|
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.
|
|
1624
|
-
//
|
|
1625
|
-
|
|
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
|
}
|