@go-to-k/cdkd 0.137.1 → 0.137.2
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 +121 -6
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -41740,6 +41740,16 @@ function attachWebSocketServer(opts) {
|
|
|
41740
41740
|
apisByPath.set(cfg.apiPath, cfg);
|
|
41741
41741
|
apiPaths.push(cfg.apiPath);
|
|
41742
41742
|
}
|
|
41743
|
+
const dispatchChainsByConnection = /* @__PURE__ */ new Map();
|
|
41744
|
+
const enqueueDispatch = (connectionId, work) => {
|
|
41745
|
+
const next = (dispatchChainsByConnection.get(connectionId) ?? Promise.resolve()).then(work);
|
|
41746
|
+
dispatchChainsByConnection.set(connectionId, next);
|
|
41747
|
+
next.finally(() => {
|
|
41748
|
+
if (dispatchChainsByConnection.get(connectionId) === next) dispatchChainsByConnection.delete(connectionId);
|
|
41749
|
+
});
|
|
41750
|
+
return next;
|
|
41751
|
+
};
|
|
41752
|
+
const inFlightDisconnects = /* @__PURE__ */ new Set();
|
|
41743
41753
|
const upgradeListener = (req, socket, head) => {
|
|
41744
41754
|
const pathOnly = (req.url ?? "/").split("?", 1)[0];
|
|
41745
41755
|
const cfg = apisByPath.get(pathOnly);
|
|
@@ -41819,7 +41829,7 @@ function attachWebSocketServer(opts) {
|
|
|
41819
41829
|
ws.on("message", (raw, isBinary) => {
|
|
41820
41830
|
const { body, isBase64Encoded } = bufferToBody(raw, isBinary);
|
|
41821
41831
|
logger.debug(`WebSocket message received for connection ${connectionId}: ${body.slice(0, 200)}`);
|
|
41822
|
-
dispatchMessage(connectionId, cfg, body, isBase64Encoded, handshakeSnapshot).catch((err) => {
|
|
41832
|
+
enqueueDispatch(connectionId, () => dispatchMessage(connectionId, cfg, body, isBase64Encoded, handshakeSnapshot).catch((err) => {
|
|
41823
41833
|
logger.error(`WebSocket message dispatch failed (connection ${connectionId}): ${err instanceof Error ? err.stack ?? err.message : String(err)}`);
|
|
41824
41834
|
try {
|
|
41825
41835
|
ws.send(JSON.stringify({
|
|
@@ -41828,11 +41838,16 @@ function attachWebSocketServer(opts) {
|
|
|
41828
41838
|
requestId: randomUUID()
|
|
41829
41839
|
}));
|
|
41830
41840
|
} catch {}
|
|
41831
|
-
});
|
|
41841
|
+
}));
|
|
41832
41842
|
});
|
|
41833
41843
|
ws.on("close", (code, reason) => {
|
|
41834
|
-
|
|
41844
|
+
const reasonText = reason.toString("utf-8");
|
|
41845
|
+
const disconnectPromise = enqueueDispatch(connectionId, () => onDisconnect(connectionId, cfg, handshakeSnapshot, code, reasonText).catch((err) => {
|
|
41835
41846
|
logger.warn(`WebSocket $disconnect dispatch failed (connection ${connectionId}): ${err instanceof Error ? err.message : String(err)}`);
|
|
41847
|
+
}));
|
|
41848
|
+
inFlightDisconnects.add(disconnectPromise);
|
|
41849
|
+
disconnectPromise.finally(() => {
|
|
41850
|
+
inFlightDisconnects.delete(disconnectPromise);
|
|
41836
41851
|
});
|
|
41837
41852
|
});
|
|
41838
41853
|
ws.on("error", (err) => {
|
|
@@ -41840,9 +41855,9 @@ function attachWebSocketServer(opts) {
|
|
|
41840
41855
|
});
|
|
41841
41856
|
for (const frame of preVerdictFrames) {
|
|
41842
41857
|
const { body, isBase64Encoded } = bufferToBody(frame.raw, frame.isBinary);
|
|
41843
|
-
dispatchMessage(connectionId, cfg, body, isBase64Encoded, handshakeSnapshot).catch((err) => {
|
|
41858
|
+
enqueueDispatch(connectionId, () => dispatchMessage(connectionId, cfg, body, isBase64Encoded, handshakeSnapshot).catch((err) => {
|
|
41844
41859
|
logger.error(`WebSocket buffered-message dispatch failed (connection ${connectionId}): ${err instanceof Error ? err.stack ?? err.message : String(err)}`);
|
|
41845
|
-
});
|
|
41860
|
+
}));
|
|
41846
41861
|
}
|
|
41847
41862
|
preVerdictFrames.length = 0;
|
|
41848
41863
|
};
|
|
@@ -41888,6 +41903,7 @@ function attachWebSocketServer(opts) {
|
|
|
41888
41903
|
});
|
|
41889
41904
|
await invokeRoute(disconnectRoute.targetLambdaLogicalId, event, opts.pool, rieTimeoutMs);
|
|
41890
41905
|
};
|
|
41906
|
+
const SHUTDOWN_DRAIN_MS = 5e3;
|
|
41891
41907
|
let closed = false;
|
|
41892
41908
|
return {
|
|
41893
41909
|
registry,
|
|
@@ -41910,6 +41926,11 @@ function attachWebSocketServer(opts) {
|
|
|
41910
41926
|
}, 5e3).unref();
|
|
41911
41927
|
}));
|
|
41912
41928
|
await Promise.all(closes);
|
|
41929
|
+
if (inFlightDisconnects.size > 0) {
|
|
41930
|
+
const drainStartCount = inFlightDisconnects.size;
|
|
41931
|
+
const drainComplete = Promise.allSettled(Array.from(inFlightDisconnects));
|
|
41932
|
+
if (await Promise.race([drainComplete.then(() => "complete"), new Promise((resolve) => setTimeout(() => resolve("timeout"), SHUTDOWN_DRAIN_MS).unref())]) === "timeout" && inFlightDisconnects.size > 0) logger.warn(`WebSocket graceful shutdown drained for ${SHUTDOWN_DRAIN_MS}ms; ${inFlightDisconnects.size}/${drainStartCount} $disconnect handlers still in flight — leaking past shutdown.`);
|
|
41933
|
+
}
|
|
41913
41934
|
await new Promise((resolve) => {
|
|
41914
41935
|
wss.close(() => resolve());
|
|
41915
41936
|
});
|
|
@@ -42074,6 +42095,95 @@ function safeDecode$1(s) {
|
|
|
42074
42095
|
}
|
|
42075
42096
|
}
|
|
42076
42097
|
|
|
42098
|
+
//#endregion
|
|
42099
|
+
//#region src/local/docker-version.ts
|
|
42100
|
+
/**
|
|
42101
|
+
* Lower bound for `--add-host=<name>:host-gateway` support. The
|
|
42102
|
+
* `host-gateway` magic alias was introduced in Docker 20.10 (October
|
|
42103
|
+
* 2020) and is the load-bearing primitive cdkd uses to let Lambda
|
|
42104
|
+
* containers reach the host's `cdkd local start-api` server on Linux
|
|
42105
|
+
* native dockerd. Without it, the AWS_ENDPOINT_URL_APIGATEWAYMANAGEMENTAPI
|
|
42106
|
+
* override fails with `ENOTFOUND host.docker.internal` at SDK-call time.
|
|
42107
|
+
*
|
|
42108
|
+
* Docker Desktop (macOS / Windows) ships `host.docker.internal` as
|
|
42109
|
+
* a built-in alias regardless of the engine version, but the probe
|
|
42110
|
+
* still fires there to keep the error path uniform — the `host-gateway`
|
|
42111
|
+
* flag itself is harmless on Docker Desktop.
|
|
42112
|
+
*
|
|
42113
|
+
* Issue #527 M2.
|
|
42114
|
+
*/
|
|
42115
|
+
const HOST_GATEWAY_MIN_VERSION = {
|
|
42116
|
+
major: 20,
|
|
42117
|
+
minor: 10,
|
|
42118
|
+
patch: 0
|
|
42119
|
+
};
|
|
42120
|
+
/**
|
|
42121
|
+
* Parse a Docker server version string (`20.10.21` / `24.0.7-rd` /
|
|
42122
|
+
* `27.3.1+podman` etc.) into a comparable `{major, minor, patch}` tuple.
|
|
42123
|
+
* Returns `null` on any unparseable input — the caller treats that as
|
|
42124
|
+
* "version unknown, skip the comparison and let the user proceed with
|
|
42125
|
+
* a warn" rather than hard-failing on a Docker-compatible CLI binary
|
|
42126
|
+
* that doesn't follow Docker's version-string conventions
|
|
42127
|
+
* (e.g. podman / finch).
|
|
42128
|
+
*/
|
|
42129
|
+
function parseDockerVersion(raw) {
|
|
42130
|
+
const trimmed = raw.trim();
|
|
42131
|
+
const match = /^(\d+)\.(\d+)(?:\.(\d+))?/.exec(trimmed);
|
|
42132
|
+
if (!match) return null;
|
|
42133
|
+
return {
|
|
42134
|
+
major: Number(match[1]),
|
|
42135
|
+
minor: Number(match[2]),
|
|
42136
|
+
patch: match[3] !== void 0 ? Number(match[3]) : 0
|
|
42137
|
+
};
|
|
42138
|
+
}
|
|
42139
|
+
/**
|
|
42140
|
+
* Compare two `ParsedDockerVersion` tuples. Returns negative when `a <
|
|
42141
|
+
* b`, zero when equal, positive when `a > b`. Patch-level differences
|
|
42142
|
+
* are part of the ordering so a future bump (e.g. 20.10.0 -> 20.10.5
|
|
42143
|
+
* to fix a CVE-related regression) can be expressed if needed.
|
|
42144
|
+
*/
|
|
42145
|
+
function compareDockerVersions(a, b) {
|
|
42146
|
+
if (a.major !== b.major) return a.major - b.major;
|
|
42147
|
+
if (a.minor !== b.minor) return a.minor - b.minor;
|
|
42148
|
+
return a.patch - b.patch;
|
|
42149
|
+
}
|
|
42150
|
+
/**
|
|
42151
|
+
* Probe the Docker server's version to gate the `--add-host=...:host-gateway`
|
|
42152
|
+
* mapping that WebSocket Lambda containers need to reach the host
|
|
42153
|
+
* server. Issued ONCE per `cdkd local start-api` invocation at WebSocket
|
|
42154
|
+
* attach time — HTTP-only / REST-only sessions skip the probe entirely.
|
|
42155
|
+
*
|
|
42156
|
+
* Throws when:
|
|
42157
|
+
* 1. The docker subprocess itself fails (binary missing, daemon down,
|
|
42158
|
+
* permission error) — the caller's catch surfaces the original
|
|
42159
|
+
* error so the user knows to install / start Docker.
|
|
42160
|
+
* 2. The probe succeeds but the parsed version is < the supported
|
|
42161
|
+
* minimum — caller decides whether to error or warn (the WebSocket
|
|
42162
|
+
* attach loop errors; HTTP-only sessions never call this).
|
|
42163
|
+
*
|
|
42164
|
+
* Implementation: `docker version --format '{{.Server.Version}}'`
|
|
42165
|
+
* returns the daemon's version (not the client's) so a brand-new
|
|
42166
|
+
* client against an old daemon is still caught.
|
|
42167
|
+
*/
|
|
42168
|
+
async function probeHostGatewaySupport() {
|
|
42169
|
+
const rawVersion = (await runDockerStreaming([
|
|
42170
|
+
"version",
|
|
42171
|
+
"--format",
|
|
42172
|
+
"{{.Server.Version}}"
|
|
42173
|
+
], { streamLive: false })).stdout.trim();
|
|
42174
|
+
const parsed = parseDockerVersion(rawVersion);
|
|
42175
|
+
if (rawVersion === "") return {
|
|
42176
|
+
rawVersion,
|
|
42177
|
+
parsed: null,
|
|
42178
|
+
supported: false
|
|
42179
|
+
};
|
|
42180
|
+
return {
|
|
42181
|
+
rawVersion,
|
|
42182
|
+
parsed,
|
|
42183
|
+
supported: parsed === null || compareDockerVersions(parsed, HOST_GATEWAY_MIN_VERSION) >= 0
|
|
42184
|
+
};
|
|
42185
|
+
}
|
|
42186
|
+
|
|
42077
42187
|
//#endregion
|
|
42078
42188
|
//#region src/local/vtl-engine.ts
|
|
42079
42189
|
/** Error thrown when a template references an unsupported VTL feature. */
|
|
@@ -48041,6 +48151,11 @@ async function localStartApiCommand(target, options) {
|
|
|
48041
48151
|
const wsServers = [];
|
|
48042
48152
|
const initialWsApis = initialMaterial.webSocketApis ?? [];
|
|
48043
48153
|
warnUnsupportedWebSocketApis(initialWsApis, logger);
|
|
48154
|
+
if (initialWsApis.filter((api) => !api.unsupported).length > 0) {
|
|
48155
|
+
const probe = await probeHostGatewaySupport();
|
|
48156
|
+
if (!probe.supported) throw new Error(`cdkd local start-api requires Docker ${HOST_GATEWAY_MIN_VERSION.major}.${HOST_GATEWAY_MIN_VERSION.minor}+ for WebSocket API support (--add-host=host.docker.internal:host-gateway needs the 20.10 host-gateway alias). Detected server version: ${probe.rawVersion || "<empty — daemon unreachable or output stripped>"}. Upgrade Docker, or remove the WebSocket API from this app to fall back to HTTP-only start-api.`);
|
|
48157
|
+
if (probe.parsed === null) logger.warn(`Docker server version "${probe.rawVersion}" did not match the canonical "<major>.<minor>" shape; assuming host-gateway support. If WebSocket containers fail to reach the local server, verify your Docker-compatible CLI honors --add-host=host.docker.internal:host-gateway.`);
|
|
48158
|
+
}
|
|
48044
48159
|
for (const api of initialWsApis) {
|
|
48045
48160
|
if (api.unsupported) continue;
|
|
48046
48161
|
const wsLambdaIds = new Set(api.routes.map((r) => r.targetLambdaLogicalId));
|
|
@@ -54015,7 +54130,7 @@ function reorderArgs(argv) {
|
|
|
54015
54130
|
*/
|
|
54016
54131
|
async function main() {
|
|
54017
54132
|
const program = new Command();
|
|
54018
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.137.
|
|
54133
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.137.2");
|
|
54019
54134
|
program.addCommand(createBootstrapCommand());
|
|
54020
54135
|
program.addCommand(createSynthCommand());
|
|
54021
54136
|
program.addCommand(createListCommand());
|