@go-to-k/cdkd 0.115.0 → 0.115.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { a as setAwsClients, i as resetAwsClients, r as getAwsClients, t as AwsClients } from "./aws-clients-CuHRHcyW.js";
3
- import { A as resolveApp, B as CdkdError, C as AssetPublisher, D as Synthesizer, E as buildDockerImage, F as warnDeprecatedNoPrefixCliFlag, G as PartialFailureError, I as AssemblyReader, J as ResourceUpdateNotSupportedError, K as ProvisioningError, M as resolveSkipPrefix, N as resolveStateBucketWithDefault, O as getDefaultStateBucketName, P as resolveStateBucketWithDefaultAndSource, R as resolveBucketRegion, S as shouldRetainResource, T as WorkGraph, U as LocalInvokeBuildError, X as StackHasActiveImportsError, Y as RouteDiscoveryError, Z as StackTerminationProtectionError, _ as DiffCalculator, a as withRetry, at as getLogger, b as LockManager, c as collectInlinePolicyNamesManagedBySiblings, ct as getLiveRenderer, d as normalizeAwsTagsToCfn, dt as generateResourceName, f as resolveExplicitPhysicalId, ft as generateResourceNameWithFallback, g as IntrinsicFunctionResolver, h as assertRegionMatch, i as withResourceDeadline, j as resolveCaptureObservedState, k as getLegacyStateBucketName, l as CDK_PATH_TAG, lt as PATTERN_B_NAME_PROPERTIES, m as CloudControlProvider, mt as withStackName, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nt as normalizeAwsError, o as IMPLICIT_DELETE_DEPENDENCIES, p as ProviderRegistry, pt as withSkipPrefix, q as ResourceTimeoutError, r as DeployEngine, rt as withErrorHandling, s as IAMRoleProvider, st as runStackBuffered, t as DEFAULT_RESOURCE_TIMEOUT_MS, u as matchesCdkPath, ut as PATTERN_B_RESOURCE_TYPES, v as DagBuilder, w as stringifyValue, x as S3StateBackend, y as TemplateParser } from "./deploy-engine-CAg_d5uX.js";
3
+ import { $ as StackTerminationProtectionError, A as getDefaultStateBucketName, B as resolveBucketRegion, C as AssetPublisher, D as getDockerCmd, E as buildDockerImage, F as resolveStateBucketWithDefault, G as LocalInvokeBuildError, H as CdkdError, I as resolveStateBucketWithDefaultAndSource, J as ProvisioningError, L as warnDeprecatedNoPrefixCliFlag, M as resolveApp, N as resolveCaptureObservedState, O as runDockerStreaming, P as resolveSkipPrefix, Q as StackHasActiveImportsError, R as AssemblyReader, S as shouldRetainResource, T as WorkGraph, X as ResourceUpdateNotSupportedError, Y as ResourceTimeoutError, Z as RouteDiscoveryError, _ as DiffCalculator, a as withRetry, at as withErrorHandling, b as LockManager, c as collectInlinePolicyNamesManagedBySiblings, d as normalizeAwsTagsToCfn, dt as PATTERN_B_NAME_PROPERTIES, f as resolveExplicitPhysicalId, ft as PATTERN_B_RESOURCE_TYPES, g as IntrinsicFunctionResolver, gt as withStackName, h as assertRegionMatch, ht as withSkipPrefix, i as withResourceDeadline, it as normalizeAwsError, j as getLegacyStateBucketName, k as Synthesizer, l as CDK_PATH_TAG, lt as runStackBuffered, m as CloudControlProvider, mt as generateResourceNameWithFallback, n as DEFAULT_RESOURCE_WARN_AFTER_MS, o as IMPLICIT_DELETE_DEPENDENCIES, p as ProviderRegistry, pt as generateResourceName, q as PartialFailureError, r as DeployEngine, s as IAMRoleProvider, st as getLogger, t as DEFAULT_RESOURCE_TIMEOUT_MS, u as matchesCdkPath, ut as getLiveRenderer, v as DagBuilder, w as stringifyValue, x as S3StateBackend, y as TemplateParser } from "./deploy-engine-AoZgViZN.js";
4
4
  import { createHash, createPublicKey, createVerify, randomBytes, randomUUID } from "node:crypto";
5
5
  import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, ListBucketsCommand, ListDirectoryBucketsCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchBucket, PutBucketAccelerateConfigurationCommand, PutBucketAnalyticsConfigurationCommand, PutBucketCorsCommand, PutBucketEncryptionCommand, PutBucketIntelligentTieringConfigurationCommand, PutBucketInventoryConfigurationCommand, PutBucketLifecycleConfigurationCommand, PutBucketLoggingCommand, PutBucketMetricsConfigurationCommand, PutBucketNotificationConfigurationCommand, PutBucketOwnershipControlsCommand, PutBucketPolicyCommand, PutBucketReplicationCommand, PutBucketTaggingCommand, PutBucketVersioningCommand, PutBucketWebsiteCommand, PutObjectCommand, PutObjectLockConfigurationCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
6
6
  import { AddRoleToInstanceProfileCommand, AddUserToGroupCommand, AttachGroupPolicyCommand, AttachUserPolicyCommand, CreateGroupCommand, CreateInstanceProfileCommand, CreateLoginProfileCommand, CreateUserCommand, DeleteAccessKeyCommand, DeleteGroupCommand, DeleteGroupPolicyCommand, DeleteInstanceProfileCommand, DeleteLoginProfileCommand, DeleteRolePolicyCommand, DeleteUserCommand, DeleteUserPermissionsBoundaryCommand, DeleteUserPolicyCommand, DetachGroupPolicyCommand, DetachUserPolicyCommand, GetGroupCommand, GetGroupPolicyCommand, GetInstanceProfileCommand, GetRolePolicyCommand, GetUserCommand, GetUserPolicyCommand, IAMClient, ListAccessKeysCommand, ListAttachedGroupPoliciesCommand, ListAttachedUserPoliciesCommand, ListGroupPoliciesCommand, ListGroupsForUserCommand, ListInstanceProfilesCommand, ListUserPoliciesCommand, ListUserTagsCommand, ListUsersCommand, NoSuchEntityException, PutGroupPolicyCommand, PutRolePolicyCommand, PutUserPermissionsBoundaryCommand, PutUserPolicyCommand, RemoveRoleFromInstanceProfileCommand, RemoveUserFromGroupCommand, TagUserCommand, UntagUserCommand, UpdateLoginProfileCommand } from "@aws-sdk/client-iam";
@@ -27,7 +27,6 @@ import { tmpdir } from "node:os";
27
27
  import { AssociateVPCWithHostedZoneCommand, ChangeResourceRecordSetsCommand, ChangeTagsForResourceCommand, CreateHostedZoneCommand, CreateQueryLoggingConfigCommand, DeleteHostedZoneCommand, DeleteQueryLoggingConfigCommand, DisassociateVPCFromHostedZoneCommand, GetHostedZoneCommand, ListHostedZonesByNameCommand, ListHostedZonesCommand, ListQueryLoggingConfigsCommand, ListResourceRecordSetsCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$6, Route53Client, UpdateHostedZoneCommentCommand } from "@aws-sdk/client-route-53";
28
28
  import { AddTagsCommand, CreateListenerCommand, CreateLoadBalancerCommand, CreateTargetGroupCommand, DeleteListenerCommand, DeleteLoadBalancerCommand, DeleteTargetGroupCommand, DescribeListenersCommand, DescribeLoadBalancerAttributesCommand, DescribeLoadBalancersCommand, DescribeTagsCommand, DescribeTargetGroupsCommand, ElasticLoadBalancingV2Client, ModifyListenerCommand, ModifyLoadBalancerAttributesCommand, ModifyTargetGroupCommand, RemoveTagsCommand, SetIpAddressTypeCommand, SetSecurityGroupsCommand, SetSubnetsCommand } from "@aws-sdk/client-elastic-load-balancing-v2";
29
29
  import { CreateAliasCommand, CreateKeyCommand, DeleteAliasCommand, DescribeKeyCommand, DisableKeyCommand, DisableKeyRotationCommand, EnableKeyCommand, EnableKeyRotationCommand, GetKeyPolicyCommand, GetKeyRotationStatusCommand, KMSClient, ListAliasesCommand, ListKeysCommand, ListResourceTagsCommand, NotFoundException as NotFoundException$2, PutKeyPolicyCommand, ScheduleKeyDeletionCommand, TagResourceCommand as TagResourceCommand$8, UntagResourceCommand as UntagResourceCommand$8, UpdateAliasCommand, UpdateKeyDescriptionCommand } from "@aws-sdk/client-kms";
30
- import { promisify } from "node:util";
31
30
  import { CreateRepositoryCommand, DeleteLifecyclePolicyCommand, DeleteRepositoryCommand, DeleteRepositoryPolicyCommand, DescribeRepositoriesCommand, ECRClient, GetAuthorizationTokenCommand, GetLifecyclePolicyCommand, LifecyclePolicyNotFoundException, ListTagsForResourceCommand as ListTagsForResourceCommand$7, PutImageScanningConfigurationCommand, PutImageTagMutabilityCommand, PutLifecyclePolicyCommand, RepositoryNotFoundException, SetRepositoryPolicyCommand, TagResourceCommand as TagResourceCommand$9 } from "@aws-sdk/client-ecr";
32
31
  import graphlib from "graphlib";
33
32
  import { AddTagsToResourceCommand as AddTagsToResourceCommand$1, CreateDBClusterCommand, CreateDBInstanceCommand, CreateDBProxyCommand, CreateDBProxyEndpointCommand, CreateDBSubnetGroupCommand, DBProxyEndpointNotFoundFault, DBProxyNotFoundFault, DBProxyTargetGroupNotFoundFault, DBProxyTargetNotFoundFault, DeleteDBClusterCommand, DeleteDBInstanceCommand, DeleteDBProxyCommand, DeleteDBProxyEndpointCommand, DeleteDBSubnetGroupCommand, DeregisterDBProxyTargetsCommand, DescribeDBClustersCommand, DescribeDBInstancesCommand, DescribeDBProxiesCommand, DescribeDBProxyEndpointsCommand, DescribeDBProxyTargetGroupsCommand, DescribeDBProxyTargetsCommand, DescribeDBSubnetGroupsCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$8, ModifyDBClusterCommand, ModifyDBInstanceCommand, ModifyDBProxyCommand, ModifyDBProxyEndpointCommand, ModifyDBProxyTargetGroupCommand, ModifyDBSubnetGroupCommand, RDSClient, RegisterDBProxyTargetsCommand, RemoveTagsFromResourceCommand as RemoveTagsFromResourceCommand$1 } from "@aws-sdk/client-rds";
@@ -58,6 +57,7 @@ import { CreateNamespaceCommand, CreateTableBucketCommand, CreateTableCommand as
58
57
  import { AttachTrafficSourcesCommand, AutoScalingClient, CreateAutoScalingGroupCommand, DeleteAutoScalingGroupCommand, DeleteLifecycleHookCommand, DeleteNotificationConfigurationCommand, DescribeAutoScalingGroupsCommand, DescribeLifecycleHooksCommand, DescribeNotificationConfigurationsCommand, DescribeTrafficSourcesCommand, DetachTrafficSourcesCommand, DisableMetricsCollectionCommand, EnableMetricsCollectionCommand, PutLifecycleHookCommand, PutNotificationConfigurationCommand, UpdateAutoScalingGroupCommand } from "@aws-sdk/client-auto-scaling";
59
58
  import * as readline from "node:readline/promises";
60
59
  import { createServer } from "node:net";
60
+ import { promisify } from "node:util";
61
61
  import { setTimeout as setTimeout$1 } from "node:timers/promises";
62
62
  import { readFile } from "fs/promises";
63
63
  import { createServer as createServer$1 } from "node:http";
@@ -37331,7 +37331,7 @@ function resolveRuntimeCodeMountPath(runtime) {
37331
37331
 
37332
37332
  //#endregion
37333
37333
  //#region src/local/docker-runner.ts
37334
- const execFileAsync$3 = promisify(execFile);
37334
+ const execFileAsync$2 = promisify(execFile);
37335
37335
  /**
37336
37336
  * Wraps `docker pull` / `docker run` / `docker rm` for `cdkd local invoke`.
37337
37337
  *
@@ -37366,11 +37366,11 @@ async function pullImage(image, skipPull) {
37366
37366
  }
37367
37367
  if (getLogger().getLevel() === "debug") {
37368
37368
  logger.info(`Pulling ${image}...`);
37369
- await runForeground$1("docker", ["pull", image]);
37369
+ await runForeground$1(getDockerCmd(), ["pull", image]);
37370
37370
  return;
37371
37371
  }
37372
37372
  logger.debug(`Pulling ${image} (silent — pass --verbose to stream progress)`);
37373
- await runCaptured("docker", ["pull", image], image);
37373
+ await runCaptured(getDockerCmd(), ["pull", image], image);
37374
37374
  }
37375
37375
  /**
37376
37376
  * Run a child process with stdout / stderr captured (not inherited).
@@ -37441,9 +37441,9 @@ async function runDetached(opts) {
37441
37441
  entryPointTail = opts.entryPoint.slice(1);
37442
37442
  }
37443
37443
  args.push(opts.image, ...entryPointTail, ...opts.cmd);
37444
- getLogger().child("docker").debug(`docker ${redactAwsCredentialsInArgs(args).join(" ")}`);
37444
+ getLogger().child("docker").debug(`${getDockerCmd()} ${redactAwsCredentialsInArgs(args).join(" ")}`);
37445
37445
  try {
37446
- const { stdout } = await execFileAsync$3("docker", args, { maxBuffer: 10 * 1024 * 1024 });
37446
+ const { stdout } = await execFileAsync$2(getDockerCmd(), args, { maxBuffer: 10 * 1024 * 1024 });
37447
37447
  return stdout.trim();
37448
37448
  } catch (error) {
37449
37449
  const err = error;
@@ -37455,7 +37455,7 @@ async function runDetached(opts) {
37455
37455
  * stops the stream (used by the caller in a `finally` block).
37456
37456
  */
37457
37457
  function streamLogs(containerId) {
37458
- const proc = spawn("docker", [
37458
+ const proc = spawn(getDockerCmd(), [
37459
37459
  "logs",
37460
37460
  "-f",
37461
37461
  containerId
@@ -37480,7 +37480,7 @@ async function removeContainer(containerId) {
37480
37480
  if (!containerId) return;
37481
37481
  const logger = getLogger().child("docker");
37482
37482
  try {
37483
- await execFileAsync$3("docker", [
37483
+ await execFileAsync$2(getDockerCmd(), [
37484
37484
  "rm",
37485
37485
  "-f",
37486
37486
  containerId
@@ -37497,16 +37497,17 @@ async function removeContainer(containerId) {
37497
37497
  * otherwise see at the first run call. Called once up front.
37498
37498
  */
37499
37499
  async function ensureDockerAvailable() {
37500
+ const cmd = getDockerCmd();
37500
37501
  try {
37501
- await execFileAsync$3("docker", [
37502
+ await execFileAsync$2(cmd, [
37502
37503
  "version",
37503
37504
  "--format",
37504
37505
  "{{.Server.Version}}"
37505
37506
  ]);
37506
37507
  } catch (error) {
37507
37508
  const err = error;
37508
- if (err.code === "ENOENT") throw new DockerRunnerError("docker is not installed or not on PATH. cdkd local invoke needs Docker install Docker Desktop or the docker CLI and retry.");
37509
- throw new DockerRunnerError(`docker daemon is not reachable: ${err.stderr?.trim() || err.message || String(error)}. Start Docker Desktop / the docker daemon and retry.`);
37509
+ if (err.code === "ENOENT") throw new DockerRunnerError(`${cmd} is not installed or not on PATH. cdkd local invoke needs Docker (or a compatible CLI specified via CDK_DOCKER) install it and retry.`);
37510
+ throw new DockerRunnerError(`${cmd} daemon is not reachable: ${err.stderr?.trim() || err.message || String(error)}. Start Docker Desktop / the docker daemon and retry.`);
37510
37511
  }
37511
37512
  }
37512
37513
  /**
@@ -37593,7 +37594,6 @@ function runForeground$1(cmd, args) {
37593
37594
 
37594
37595
  //#endregion
37595
37596
  //#region src/local/ecr-puller.ts
37596
- const execFileAsync$2 = promisify(execFile);
37597
37597
  /**
37598
37598
  * ECR pull fallback for `cdkd local invoke` against deployed container
37599
37599
  * Lambdas (PR 5, D5.2). When `Code.ImageUri` resolves to an ECR URI but
@@ -37665,7 +37665,7 @@ async function pullEcrImage(imageUri, options) {
37665
37665
  ecr.destroy();
37666
37666
  }
37667
37667
  logger.info(`Pulling ${imageUri}...`);
37668
- await runForeground("docker", ["pull", imageUri]);
37668
+ await runForeground(getDockerCmd(), ["pull", imageUri]);
37669
37669
  return imageUri;
37670
37670
  }
37671
37671
  /**
@@ -37679,33 +37679,20 @@ async function ecrLogin(client, accountId, region) {
37679
37679
  const authData = (await client.send(new GetAuthorizationTokenCommand({}))).authorizationData?.[0];
37680
37680
  if (!authData?.authorizationToken) throw new LocalInvokeBuildError("Failed to get ECR authorization token");
37681
37681
  const [username, password] = Buffer.from(authData.authorizationToken, "base64").toString().split(":");
37682
+ if (!username || password === void 0) throw new LocalInvokeBuildError("ECR authorization token has unexpected shape (missing username/password)");
37682
37683
  const endpoint = authData.proxyEndpoint || `https://${accountId}.dkr.ecr.${region}.amazonaws.com`;
37683
- await new Promise((resolve, reject) => {
37684
- const proc = spawn("docker", [
37684
+ try {
37685
+ await runDockerStreaming([
37685
37686
  "login",
37686
37687
  "--username",
37687
37688
  username,
37688
37689
  "--password-stdin",
37689
37690
  endpoint
37690
- ], { stdio: [
37691
- "pipe",
37692
- "pipe",
37693
- "pipe"
37694
- ] });
37695
- let stderr = "";
37696
- proc.stderr?.on("data", (data) => {
37697
- stderr += data.toString();
37698
- });
37699
- proc.on("close", (code) => {
37700
- if (code === 0) resolve();
37701
- else reject(new LocalInvokeBuildError(`ECR login failed: ${stderr.trim()}`));
37702
- });
37703
- proc.on("error", (err) => {
37704
- reject(new LocalInvokeBuildError(`ECR login failed: ${err.message}`));
37705
- });
37706
- proc.stdin?.write(password);
37707
- proc.stdin?.end();
37708
- });
37691
+ ], { input: password });
37692
+ } catch (err) {
37693
+ const e = err;
37694
+ throw new LocalInvokeBuildError(`ECR login failed: ${e.stderr?.trim() || e.message || String(err)}`);
37695
+ }
37709
37696
  }
37710
37697
  /**
37711
37698
  * `docker image inspect <uri>` returns non-zero when the image is not in
@@ -37714,7 +37701,7 @@ async function ecrLogin(client, accountId, region) {
37714
37701
  */
37715
37702
  async function verifyImageInLocalCache(imageUri) {
37716
37703
  try {
37717
- await execFileAsync$2("docker", [
37704
+ await runDockerStreaming([
37718
37705
  "image",
37719
37706
  "inspect",
37720
37707
  imageUri
@@ -37732,7 +37719,7 @@ async function verifyImageInLocalCache(imageUri) {
37732
37719
  */
37733
37720
  async function isImageInLocalCache(imageRef) {
37734
37721
  try {
37735
- await execFileAsync$2("docker", [
37722
+ await runDockerStreaming([
37736
37723
  "image",
37737
37724
  "inspect",
37738
37725
  imageRef
@@ -37782,10 +37769,24 @@ async function buildContainerImage(asset, cdkOutDir, options) {
37782
37769
  }
37783
37770
  logger.info(`Building container image (platform=${platform})...`);
37784
37771
  logger.debug(`Local tag: ${tag}`);
37785
- await buildDockerImage(asset, cdkOutDir, tag, {
37772
+ const actualTag = await buildDockerImage(asset, cdkOutDir, {
37773
+ tag,
37786
37774
  platform,
37787
- wrapError: (stderr) => new LocalInvokeBuildError(`docker build failed for container Lambda asset (${asset.source.directory}): ${stderr}`)
37775
+ wrapError: (stderr) => new LocalInvokeBuildError(`docker build failed for container Lambda asset (${asset.source.directory ?? asset.source.executable?.join(" ")}): ${stderr}`)
37788
37776
  });
37777
+ if (actualTag !== tag) {
37778
+ logger.debug(`Re-tagging executable-built image '${actualTag}' → '${tag}'`);
37779
+ try {
37780
+ await runDockerStreaming([
37781
+ "tag",
37782
+ actualTag,
37783
+ tag
37784
+ ]);
37785
+ } catch (err) {
37786
+ const e = err;
37787
+ throw new LocalInvokeBuildError(`docker tag failed re-tagging '${actualTag}' → '${tag}': ${e.stderr?.trim() || e.message || String(err)}`);
37788
+ }
37789
+ }
37789
37790
  return tag;
37790
37791
  }
37791
37792
  /**
@@ -37800,27 +37801,49 @@ function architectureToPlatform(architecture) {
37800
37801
  return architecture === "arm64" ? "linux/arm64" : "linux/amd64";
37801
37802
  }
37802
37803
  /**
37803
- * Build a stable local tag derived from the asset's build context. We
37804
- * fingerprint `directory + dockerFile + dockerBuildTarget + dockerBuildArgs`
37805
- * so an iteration that doesn't change those fields hits Docker's layer
37806
- * cache; an iteration that DOES change them gets a fresh tag (the old
37807
- * tag stays around in `docker images` but harmlessly).
37804
+ * Build a stable local tag derived from the asset's build context.
37805
+ *
37806
+ * Fingerprints every field that affects the produced image so an iteration
37807
+ * that doesn't change those fields hits Docker's layer cache; an iteration
37808
+ * that DOES change them gets a fresh tag (the old tag stays around in
37809
+ * `docker images` but harmlessly). The fingerprint covers the full CDK
37810
+ * `DockerImageSource` schema so `dockerBuildSecrets` / `dockerBuildContexts`
37811
+ * / `cacheFrom` / etc. changes also bust the local cache as expected.
37808
37812
  */
37809
37813
  function computeLocalTag(source) {
37810
37814
  const hash = createHash("sha256");
37811
- hash.update(source.directory);
37812
- hash.update("\0");
37813
- hash.update(source.dockerFile ?? "");
37814
- hash.update("\0");
37815
- hash.update(source.dockerBuildTarget ?? "");
37815
+ pushField(hash, "directory", source.directory ?? "");
37816
+ pushField(hash, "executable", (source.executable ?? []).join(" "));
37817
+ pushField(hash, "dockerFile", source.dockerFile ?? "");
37818
+ pushField(hash, "dockerBuildTarget", source.dockerBuildTarget ?? "");
37819
+ pushField(hash, "networkMode", source.networkMode ?? "");
37820
+ pushField(hash, "platform", source.platform ?? "");
37821
+ pushField(hash, "dockerBuildSsh", source.dockerBuildSsh ?? "");
37822
+ pushField(hash, "cacheDisabled", source.cacheDisabled ? "1" : "0");
37823
+ pushMap(hash, "dockerBuildArgs", source.dockerBuildArgs);
37824
+ pushMap(hash, "dockerBuildContexts", source.dockerBuildContexts);
37825
+ pushMap(hash, "dockerBuildSecrets", source.dockerBuildSecrets);
37826
+ pushField(hash, "dockerOutputs", (source.dockerOutputs ?? []).join(""));
37827
+ pushField(hash, "cacheFrom", (source.cacheFrom ?? []).map((o) => JSON.stringify(o)).join(""));
37828
+ pushField(hash, "cacheTo", source.cacheTo ? JSON.stringify(source.cacheTo) : "");
37829
+ return `cdkd-local-invoke-${hash.digest("hex").slice(0, 16)}`;
37830
+ }
37831
+ function pushField(hash, name, value) {
37832
+ hash.update(name);
37833
+ hash.update("=");
37834
+ hash.update(value);
37816
37835
  hash.update("\0");
37817
- if (source.dockerBuildArgs) for (const [k, v] of Object.entries(source.dockerBuildArgs)) {
37836
+ }
37837
+ function pushMap(hash, name, value) {
37838
+ hash.update(name);
37839
+ hash.update("={");
37840
+ if (value) for (const [k, v] of Object.entries(value)) {
37818
37841
  hash.update(k);
37819
37842
  hash.update("=");
37820
37843
  hash.update(v);
37821
- hash.update("\0");
37844
+ hash.update(";");
37822
37845
  }
37823
- return `cdkd-local-invoke-${hash.digest("hex").slice(0, 16)}`;
37846
+ hash.update("}\0");
37824
37847
  }
37825
37848
 
37826
37849
  //#endregion
@@ -39987,19 +40010,52 @@ function resolveHttpApiAuthorizer(authorizerLogicalId, routeAuthorizationScopes,
39987
40010
  throw new RouteDiscoveryError(`${stackName}/${authorizerLogicalId}: AWS::ApiGatewayV2::Authorizer.AuthorizerType '${String(authType)}' is not supported by cdkd local start-api (only REQUEST / JWT).`);
39988
40011
  }
39989
40012
  /**
40013
+ * Thrown by {@link resolveLambdaArn} when the authorizer's
40014
+ * `AuthorizerUri` intrinsic does not resolve to a same-template Lambda
40015
+ * (cross-stack reference, imported Lambda, hand-rolled `Fn::Sub` outside
40016
+ * the invoke-ARN wrapper).
40017
+ *
40018
+ * Caught by {@link attachAuthorizers} and converted into a per-route
40019
+ * `unsupported` flag — symmetric with how `route-discovery.ts` handles
40020
+ * an unresolvable `IntegrationUri`. The route appears in the route
40021
+ * table as `[501 Not Implemented]` and returns HTTP 501 + the
40022
+ * `reason` at request time. The alternative ("attach no authorizer,
40023
+ * leave route normal") would be **unsafe** — it would let a request
40024
+ * hit a user-protected route without any auth check just because the
40025
+ * authorizer Lambda lives in another stack.
40026
+ *
40027
+ * Private to this module: `attachAuthorizers` is the only legitimate
40028
+ * consumer.
40029
+ */
40030
+ var AuthorizerLambdaUnresolvableError = class AuthorizerLambdaUnresolvableError extends RouteDiscoveryError {
40031
+ reason;
40032
+ constructor(reason) {
40033
+ super(reason);
40034
+ this.reason = reason;
40035
+ this.name = "AuthorizerLambdaUnresolvableError";
40036
+ Object.setPrototypeOf(this, AuthorizerLambdaUnresolvableError.prototype);
40037
+ }
40038
+ };
40039
+ /**
39990
40040
  * Resolve a Lambda ARN intrinsic to its logical ID. Delegates to the
39991
40041
  * shared `resolveLambdaArnIntrinsic` in `intrinsic-lambda-arn.ts`
39992
40042
  * (extracted in issue #286 Gaps 3 / 4); accepts `Ref` /
39993
40043
  * `Fn::GetAtt: [..., 'Arn']` / the REST v1 invoke-ARN `Fn::Join` wrapper
39994
40044
  * (now also used by CDK 2.x's `HttpLambdaAuthorizer` for HTTP API v2 —
39995
40045
  * verified via real `cdk synth` 2026-05-12) / the `Fn::Sub` invoke-ARN
39996
- * wrapper (both 1-arg and 2-arg forms). Any other shape hard-errors with
39997
- * the offending route + raw intrinsic named.
40046
+ * wrapper (both 1-arg and 2-arg forms).
40047
+ *
40048
+ * On an unresolvable intrinsic throws {@link AuthorizerLambdaUnresolvableError}
40049
+ * (caught by `attachAuthorizers` and converted into a per-route
40050
+ * deferred-501) instead of the generic `RouteDiscoveryError`, so
40051
+ * `cdkd local start-api` can boot against an app with a cross-stack
40052
+ * authorizer Lambda — symmetric with the route-level `IntegrationUri`
40053
+ * unresolvable case (issue #431).
39998
40054
  */
39999
40055
  function resolveLambdaArn(value, location) {
40000
40056
  const outcome = resolveLambdaArnIntrinsic(value);
40001
40057
  if (outcome.kind === "resolved") return outcome.logicalId;
40002
- throw new RouteDiscoveryError(`${location}: ${outcome.detail} (got ${shortJson(value)}). Only { Ref }, { Fn::GetAtt: [..., 'Arn'] }, the REST v1 invoke-ARN Fn::Join wrapper, and the Fn::Sub invoke-ARN wrapper are supported.`);
40058
+ throw new AuthorizerLambdaUnresolvableError(`${location}: ${outcome.detail} (got ${shortJson(value)}). Only { Ref }, { Fn::GetAtt: [..., 'Arn'] }, the REST v1 invoke-ARN Fn::Join wrapper, and the Fn::Sub invoke-ARN wrapper are supported.`);
40003
40059
  }
40004
40060
  /**
40005
40061
  * REST v1 IdentitySource for TOKEN authorizers must be exactly one
@@ -40187,6 +40243,13 @@ function attachAuthorizers(stacks, routes) {
40187
40243
  ...authorizer && { authorizer }
40188
40244
  });
40189
40245
  } catch (err) {
40246
+ if (err instanceof AuthorizerLambdaUnresolvableError) {
40247
+ out.push({ route: {
40248
+ ...route,
40249
+ unsupported: { reason: `${route.declaredAt}: authorizer Lambda Arn unresolvable — ${err.reason}` }
40250
+ } });
40251
+ continue;
40252
+ }
40190
40253
  errors.push(err instanceof Error ? err.message : String(err));
40191
40254
  }
40192
40255
  }
@@ -42727,7 +42790,7 @@ async function createTaskNetwork(options = {}) {
42727
42790
  await pullImage(METADATA_ENDPOINT_IMAGE, options.skipPull ?? false);
42728
42791
  logger.info(`Creating docker network ${networkName} (subnet ${METADATA_ENDPOINT_SUBNET})...`);
42729
42792
  try {
42730
- await execFileAsync$1("docker", [
42793
+ await execFileAsync$1(getDockerCmd(), [
42731
42794
  "network",
42732
42795
  "create",
42733
42796
  "--driver",
@@ -42763,7 +42826,7 @@ async function createTaskNetwork(options = {}) {
42763
42826
  logger.info("Starting ECS local-container-endpoints sidecar at 169.254.170.2...");
42764
42827
  let sidecarContainerId;
42765
42828
  try {
42766
- const { stdout } = await execFileAsync$1("docker", sidecarArgs, { maxBuffer: 10 * 1024 * 1024 });
42829
+ const { stdout } = await execFileAsync$1(getDockerCmd(), sidecarArgs, { maxBuffer: 10 * 1024 * 1024 });
42767
42830
  sidecarContainerId = stdout.trim();
42768
42831
  } catch (err) {
42769
42832
  await destroyNetworkOnly(networkName);
@@ -42809,7 +42872,7 @@ async function destroyNetworkOnly(networkName) {
42809
42872
  if (!networkName) return;
42810
42873
  const logger = getLogger().child("ecs-network");
42811
42874
  try {
42812
- await execFileAsync$1("docker", [
42875
+ await execFileAsync$1(getDockerCmd(), [
42813
42876
  "network",
42814
42877
  "rm",
42815
42878
  networkName
@@ -43002,7 +43065,7 @@ async function cleanupEcsRun(state, options) {
43002
43065
  await destroyTaskNetwork(state.network);
43003
43066
  state.network = void 0;
43004
43067
  for (const v of state.dockerVolumeNames) try {
43005
- await execFileAsync("docker", [
43068
+ await execFileAsync(getDockerCmd(), [
43006
43069
  "volume",
43007
43070
  "rm",
43008
43071
  v
@@ -43066,7 +43129,7 @@ async function runEcsTask(task, options, state) {
43066
43129
  logger.info(`Starting container '${container.name}' (image=${imagePlan.get(container.name)})`);
43067
43130
  let id;
43068
43131
  try {
43069
- const { stdout } = await execFileAsync("docker", args, { maxBuffer: 10 * 1024 * 1024 });
43132
+ const { stdout } = await execFileAsync(getDockerCmd(), args, { maxBuffer: 10 * 1024 * 1024 });
43070
43133
  id = stdout.trim();
43071
43134
  } catch (err) {
43072
43135
  const e = err;
@@ -43175,7 +43238,7 @@ async function waitForContainerHealthy(containerId, displayName) {
43175
43238
  let lastStatus = "";
43176
43239
  while (Date.now() < deadline) {
43177
43240
  try {
43178
- const { stdout } = await execFileAsync("docker", [
43241
+ const { stdout } = await execFileAsync(getDockerCmd(), [
43179
43242
  "inspect",
43180
43243
  "--format",
43181
43244
  "{{.State.Health.Status}}",
@@ -43198,7 +43261,7 @@ async function waitForContainerHealthy(containerId, displayName) {
43198
43261
  }
43199
43262
  async function waitForContainerExit(containerId) {
43200
43263
  try {
43201
- const { stdout } = await execFileAsync("docker", ["wait", containerId], { maxBuffer: 1024 * 1024 });
43264
+ const { stdout } = await execFileAsync(getDockerCmd(), ["wait", containerId], { maxBuffer: 1024 * 1024 });
43202
43265
  const code = Number.parseInt(stdout.trim(), 10);
43203
43266
  return Number.isFinite(code) ? code : 1;
43204
43267
  } catch (err) {
@@ -43208,7 +43271,7 @@ async function waitForContainerExit(containerId) {
43208
43271
  }
43209
43272
  async function stopContainer(containerId, graceSeconds) {
43210
43273
  try {
43211
- await execFileAsync("docker", [
43274
+ await execFileAsync(getDockerCmd(), [
43212
43275
  "stop",
43213
43276
  "-t",
43214
43277
  String(graceSeconds),
@@ -43224,7 +43287,7 @@ function sleep(ms) {
43224
43287
  * every line. Returns a stop function for the caller's `finally`.
43225
43288
  */
43226
43289
  function streamContainerLogs(containerName, containerId) {
43227
- const proc = spawn("docker", [
43290
+ const proc = spawn(getDockerCmd(), [
43228
43291
  "logs",
43229
43292
  "-f",
43230
43293
  containerId
@@ -43291,10 +43354,21 @@ async function prepareOneImage(task, container, options) {
43291
43354
  else if (entries.length === 1) asset = entries[0][1];
43292
43355
  if (!asset) throw new EcsTaskRunnerError(`Container '${container.name}' references a CDK asset image but no matching entry was found in cdk.out. Re-synthesize the CDK app and retry.`);
43293
43356
  const tag = `cdkd-local-run-task-${(image.assetHash ?? "single").slice(0, 16)}`;
43294
- await buildDockerImage(asset, cdkOutDir, tag, {
43357
+ const actualTag = await buildDockerImage(asset, cdkOutDir, {
43358
+ tag,
43295
43359
  ...options.platformOverride !== void 0 && { platform: options.platformOverride },
43296
- wrapError: (stderr) => new LocalInvokeBuildError(`docker build failed for ECS container '${container.name}' (${asset.source.directory}): ${stderr}`)
43360
+ wrapError: (stderr) => new LocalInvokeBuildError(`docker build failed for ECS container '${container.name}' (${asset.source.directory ?? asset.source.executable?.join(" ")}): ${stderr}`)
43297
43361
  });
43362
+ if (actualTag !== tag) try {
43363
+ await runDockerStreaming([
43364
+ "tag",
43365
+ actualTag,
43366
+ tag
43367
+ ]);
43368
+ } catch (err) {
43369
+ const e = err;
43370
+ throw new LocalInvokeBuildError(`docker tag failed re-tagging '${actualTag}' → '${tag}' for ECS container '${container.name}': ${e.stderr?.trim() || e.message || String(err)}`);
43371
+ }
43298
43372
  return tag;
43299
43373
  }
43300
43374
  }
@@ -43321,7 +43395,7 @@ async function realizeDockerVolumes(volumes, state) {
43321
43395
  const dockerVolumeName = `cdkd-local-${v.name}-${randHex(4)}`;
43322
43396
  args.push(dockerVolumeName);
43323
43397
  try {
43324
- await execFileAsync("docker", args);
43398
+ await execFileAsync(getDockerCmd(), args);
43325
43399
  state.dockerVolumeNames.push(dockerVolumeName);
43326
43400
  logger.debug(`Created docker volume ${dockerVolumeName} for task volume '${v.name}'`);
43327
43401
  } catch (err) {
@@ -45520,7 +45594,7 @@ function reorderArgs(argv) {
45520
45594
  */
45521
45595
  async function main() {
45522
45596
  const program = new Command();
45523
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.115.0");
45597
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.115.2");
45524
45598
  program.addCommand(createBootstrapCommand());
45525
45599
  program.addCommand(createSynthCommand());
45526
45600
  program.addCommand(createListCommand());