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