@go-to-k/cdkd 0.132.2 → 0.132.3
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/cli.js +76 -9
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -39510,6 +39510,24 @@ const STREAM_PRELUDE_SEPARATOR = Buffer.from([
|
|
|
39510
39510
|
*/
|
|
39511
39511
|
const STREAM_PRELUDE_MAX_BYTES = 1024 * 1024;
|
|
39512
39512
|
/**
|
|
39513
|
+
* Maximum cumulative bytes of body the streaming Readable will push
|
|
39514
|
+
* before destroying itself with a clear error (defense-in-depth against
|
|
39515
|
+
* a buggy / malicious handler streaming gigabytes — Node's chunked-pipe
|
|
39516
|
+
* machinery handles per-chunk backpressure, but the running total can
|
|
39517
|
+
* still grow without bound on a slow consumer).
|
|
39518
|
+
*
|
|
39519
|
+
* 100 MiB is the default cap — generous enough that no realistic
|
|
39520
|
+
* dev-loop streaming response (token-by-token LLM output, large-file
|
|
39521
|
+
* download, video segment) hits it, low enough that a genuine runaway
|
|
39522
|
+
* surfaces locally before swap pressure kicks in. The HTTP server
|
|
39523
|
+
* converts the destroyed Readable to a truncated response (best-effort —
|
|
39524
|
+
* headers may already be on the wire).
|
|
39525
|
+
*
|
|
39526
|
+
* Consistent with {@link STREAM_PRELUDE_MAX_BYTES} (1 MiB cap on the
|
|
39527
|
+
* pre-body buffer); this is the post-body counterpart.
|
|
39528
|
+
*/
|
|
39529
|
+
const STREAM_BODY_MAX_BYTES = 100 * 1024 * 1024;
|
|
39530
|
+
/**
|
|
39513
39531
|
* POST the event payload to RIE with the `streaming` response-mode
|
|
39514
39532
|
* header, parse the JSON prelude out of the response bytes, and return
|
|
39515
39533
|
* a Readable carrying the post-separator body chunks.
|
|
@@ -39525,10 +39543,26 @@ const STREAM_PRELUDE_MAX_BYTES = 1024 * 1024;
|
|
|
39525
39543
|
* the header, but setting it makes the contract explicit and survives
|
|
39526
39544
|
* future RIE behavior changes.)
|
|
39527
39545
|
*
|
|
39528
|
-
*
|
|
39529
|
-
*
|
|
39530
|
-
*
|
|
39531
|
-
*
|
|
39546
|
+
* **`timeoutMs` bounds the TOTAL wall time of the entire streaming
|
|
39547
|
+
* exchange**, NOT just the prelude wait — the single armed `setTimeout`
|
|
39548
|
+
* covers both the prelude arrival AND the body drain. Once it fires,
|
|
39549
|
+
* `controller.abort()` destroys the underlying Readable, so a
|
|
39550
|
+
* legitimately long-lived streaming handler (e.g. a 15-minute AI / LLM
|
|
39551
|
+
* proxy) will have its connection torn down mid-stream even though
|
|
39552
|
+
* bytes are arriving correctly. Callers MUST size `timeoutMs` to cover
|
|
39553
|
+
* the longest expected handler stream, NOT just the time to first byte.
|
|
39554
|
+
*
|
|
39555
|
+
* Convention: pass `lambda.Timeout * 2` with a 30-second floor — same
|
|
39556
|
+
* order-of-magnitude formula as `invokeRie`, but the absolute value
|
|
39557
|
+
* differs because streaming handlers can intentionally run for the full
|
|
39558
|
+
* Lambda timeout (default 15 minutes for streaming-capable functions).
|
|
39559
|
+
* Splitting the bound into a strict prelude timer + a per-chunk idle
|
|
39560
|
+
* timer that resets on each chunk is deferred to a follow-up — see
|
|
39561
|
+
* issue #503 item 1 for the design discussion.
|
|
39562
|
+
*
|
|
39563
|
+
* The body Readable is additionally guarded by {@link STREAM_BODY_MAX_BYTES}
|
|
39564
|
+
* (100 MiB by default) so a runaway handler can't blow host memory; the
|
|
39565
|
+
* Readable is destroyed with a clear error when the cap trips.
|
|
39532
39566
|
*/
|
|
39533
39567
|
async function invokeRieStreaming(host, port, event, timeoutMs) {
|
|
39534
39568
|
const url = `http://${host}:${port}${INVOKE_PATH}`;
|
|
@@ -39562,7 +39596,7 @@ async function invokeRieStreaming(host, port, event, timeoutMs) {
|
|
|
39562
39596
|
preludeBytes = preludeBytes.subarray(0, separatorIdx);
|
|
39563
39597
|
break;
|
|
39564
39598
|
}
|
|
39565
|
-
if (preludeBytes.length >
|
|
39599
|
+
if (preludeBytes.length > 1048576) {
|
|
39566
39600
|
clearTimeout(timer);
|
|
39567
39601
|
reader.cancel().catch(() => void 0);
|
|
39568
39602
|
throw new Error(`RIE streaming response did not emit the prelude/body separator within ${STREAM_PRELUDE_MAX_BYTES} bytes. The handler likely did not call awslambda.HttpResponseStream.from(stream, metadata).`);
|
|
@@ -39581,13 +39615,31 @@ async function invokeRieStreaming(host, port, event, timeoutMs) {
|
|
|
39581
39615
|
throw new Error(`RIE streaming response prelude is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
|
|
39582
39616
|
}
|
|
39583
39617
|
const stream = new Readable({ read() {} });
|
|
39618
|
+
let bodyBytesPushed = 0;
|
|
39619
|
+
const exceedsCap = (added) => {
|
|
39620
|
+
bodyBytesPushed += added;
|
|
39621
|
+
return bodyBytesPushed > STREAM_BODY_MAX_BYTES;
|
|
39622
|
+
};
|
|
39584
39623
|
(async () => {
|
|
39585
39624
|
try {
|
|
39586
|
-
if (bodyTail && bodyTail.length > 0)
|
|
39625
|
+
if (bodyTail && bodyTail.length > 0) {
|
|
39626
|
+
if (exceedsCap(bodyTail.length)) {
|
|
39627
|
+
reader.cancel().catch(() => void 0);
|
|
39628
|
+
stream.destroy(/* @__PURE__ */ new Error(`RIE streaming body exceeded ${STREAM_BODY_MAX_BYTES} bytes — destroying stream.`));
|
|
39629
|
+
return;
|
|
39630
|
+
}
|
|
39631
|
+
stream.push(bodyTail);
|
|
39632
|
+
}
|
|
39587
39633
|
while (true) {
|
|
39588
39634
|
const { value, done } = await reader.read();
|
|
39589
39635
|
if (done) break;
|
|
39590
|
-
|
|
39636
|
+
const chunk = Buffer.from(value);
|
|
39637
|
+
if (exceedsCap(chunk.length)) {
|
|
39638
|
+
reader.cancel().catch(() => void 0);
|
|
39639
|
+
stream.destroy(/* @__PURE__ */ new Error(`RIE streaming body exceeded ${STREAM_BODY_MAX_BYTES} bytes — destroying stream.`));
|
|
39640
|
+
return;
|
|
39641
|
+
}
|
|
39642
|
+
stream.push(chunk);
|
|
39591
39643
|
}
|
|
39592
39644
|
stream.push(null);
|
|
39593
39645
|
} catch (err) {
|
|
@@ -45522,11 +45574,26 @@ async function handleRequest(req, res, state, opts) {
|
|
|
45522
45574
|
*
|
|
45523
45575
|
* Cookies in the prelude are emitted as multiple `Set-Cookie` headers
|
|
45524
45576
|
* (HTTP API v2 semantics — matching the buffered path's behavior).
|
|
45577
|
+
*
|
|
45578
|
+
* **Conflicting headers stripped**: `Content-Length` and
|
|
45579
|
+
* `Transfer-Encoding` (case-insensitive) are removed from the prelude
|
|
45580
|
+
* before `res.writeHead(...)` — some handlers set these defensively,
|
|
45581
|
+
* but doing so either crashes Node (Content-Length doesn't match the
|
|
45582
|
+
* actual bytes that arrive on the chunked body) or produces a
|
|
45583
|
+
* corrupted Content-Length-but-actually-chunked wire response. Node
|
|
45584
|
+
* automatically emits `Transfer-Encoding: chunked` when no
|
|
45585
|
+
* Content-Length is set, which is what streaming Function URLs
|
|
45586
|
+
* always want.
|
|
45525
45587
|
*/
|
|
45526
45588
|
function writeStreamingResponse(res, result, releasePool) {
|
|
45527
45589
|
const logger = getLogger().child("start-api");
|
|
45528
45590
|
const { prelude, body } = result;
|
|
45529
|
-
const headersOut = {
|
|
45591
|
+
const headersOut = {};
|
|
45592
|
+
for (const [key, value] of Object.entries(prelude.headers)) {
|
|
45593
|
+
const lower = key.toLowerCase();
|
|
45594
|
+
if (lower === "content-length" || lower === "transfer-encoding") continue;
|
|
45595
|
+
headersOut[key] = value;
|
|
45596
|
+
}
|
|
45530
45597
|
if (prelude.cookies && prelude.cookies.length > 0) headersOut["set-cookie"] = prelude.cookies;
|
|
45531
45598
|
res.writeHead(prelude.statusCode, headersOut);
|
|
45532
45599
|
let released = false;
|
|
@@ -51707,7 +51774,7 @@ function reorderArgs(argv) {
|
|
|
51707
51774
|
*/
|
|
51708
51775
|
async function main() {
|
|
51709
51776
|
const program = new Command();
|
|
51710
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.132.
|
|
51777
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.132.3");
|
|
51711
51778
|
program.addCommand(createBootstrapCommand());
|
|
51712
51779
|
program.addCommand(createSynthCommand());
|
|
51713
51780
|
program.addCommand(createListCommand());
|