@go-to-k/cdkd 0.137.2 → 0.138.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.
- package/dist/cli.js +219 -87
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -55,7 +55,7 @@ import { AddTagsCommand as AddTagsCommand$1, CloudTrailClient, CreateTrailComman
|
|
|
55
55
|
import { BatchGetProjectsCommand, CodeBuildClient, CreateProjectCommand, DeleteProjectCommand, ListProjectsCommand, ResourceNotFoundException as ResourceNotFoundException$9, UpdateProjectCommand } from "@aws-sdk/client-codebuild";
|
|
56
56
|
import { CreateVectorBucketCommand, DeleteIndexCommand, DeleteVectorBucketCommand, GetVectorBucketCommand, ListIndexesCommand, ListTagsForResourceCommand as ListTagsForResourceCommand$18, ListVectorBucketsCommand, S3VectorsClient } from "@aws-sdk/client-s3vectors";
|
|
57
57
|
import { CreateNamespaceCommand, CreateTableBucketCommand, CreateTableCommand as CreateTableCommand$2, DeleteNamespaceCommand as DeleteNamespaceCommand$1, DeleteTableBucketCommand, DeleteTableCommand as DeleteTableCommand$2, GetTableBucketCommand, GetTableCommand as GetTableCommand$1, ListNamespacesCommand as ListNamespacesCommand$1, ListTableBucketsCommand, ListTablesCommand as ListTablesCommand$1, ListTagsForResourceCommand as ListTagsForResourceCommand$19, NotFoundException as NotFoundException$5, S3TablesClient } from "@aws-sdk/client-s3tables";
|
|
58
|
-
import { AttachTrafficSourcesCommand, AutoScalingClient, CreateAutoScalingGroupCommand, DeleteAutoScalingGroupCommand, DeleteLifecycleHookCommand, DeleteNotificationConfigurationCommand, DescribeAutoScalingGroupsCommand, DescribeLifecycleHooksCommand, DescribeNotificationConfigurationsCommand, DescribeTrafficSourcesCommand, DetachTrafficSourcesCommand, DisableMetricsCollectionCommand, EnableMetricsCollectionCommand, PutLifecycleHookCommand, PutNotificationConfigurationCommand, UpdateAutoScalingGroupCommand } from "@aws-sdk/client-auto-scaling";
|
|
58
|
+
import { AttachLoadBalancerTargetGroupsCommand, AttachLoadBalancersCommand, AttachTrafficSourcesCommand, AutoScalingClient, CreateAutoScalingGroupCommand, CreateOrUpdateTagsCommand, DeleteAutoScalingGroupCommand, DeleteLifecycleHookCommand, DeleteNotificationConfigurationCommand, DeleteTagsCommand as DeleteTagsCommand$1, DescribeAutoScalingGroupsCommand, DescribeLifecycleHooksCommand, DescribeNotificationConfigurationsCommand, DescribeTrafficSourcesCommand, DetachLoadBalancerTargetGroupsCommand, DetachLoadBalancersCommand, DetachTrafficSourcesCommand, DisableMetricsCollectionCommand, EnableMetricsCollectionCommand, PutLifecycleHookCommand, PutNotificationConfigurationCommand, UpdateAutoScalingGroupCommand } from "@aws-sdk/client-auto-scaling";
|
|
59
59
|
import * as readline from "node:readline/promises";
|
|
60
60
|
import { Document, Pair, Scalar, YAMLMap, YAMLSeq, parse as parse$1, stringify } from "yaml";
|
|
61
61
|
import { mkdir, mkdtemp } from "node:fs/promises";
|
|
@@ -29963,27 +29963,36 @@ var ECRProvider = class {
|
|
|
29963
29963
|
* Groups` already provides.
|
|
29964
29964
|
*
|
|
29965
29965
|
* Update has narrower coverage than create: AWS does not support modifying
|
|
29966
|
-
* `AutoScalingGroupName` (immutable)
|
|
29967
|
-
*
|
|
29968
|
-
*
|
|
29969
|
-
* `
|
|
29970
|
-
*
|
|
29971
|
-
*
|
|
29972
|
-
*
|
|
29973
|
-
*
|
|
29974
|
-
*
|
|
29975
|
-
*
|
|
29976
|
-
*
|
|
29977
|
-
*
|
|
29978
|
-
* InstancesPolicy / LaunchTemplate.
|
|
29966
|
+
* `AutoScalingGroupName` (immutable) — that diff still surfaces
|
|
29967
|
+
* `ResourceUpdateNotSupportedError` so the caller can `cdkd deploy
|
|
29968
|
+
* --replace`. The mutable fields handled in-place via
|
|
29969
|
+
* `UpdateAutoScalingGroup` include MinSize / MaxSize / DesiredCapacity /
|
|
29970
|
+
* VPCZoneIdentifier / HealthCheckType / HealthCheckGracePeriod /
|
|
29971
|
+
* DefaultCooldown / Cooldown / NewInstancesProtectedFromScaleIn /
|
|
29972
|
+
* MaxInstanceLifetime / TerminationPolicies / CapacityRebalance /
|
|
29973
|
+
* ServiceLinkedRoleARN / Context / DesiredCapacityType /
|
|
29974
|
+
* DefaultInstanceWarmup / AvailabilityZones / AvailabilityZoneDistribution
|
|
29975
|
+
* / AvailabilityZoneImpairmentPolicy / SkipZonalShiftValidation /
|
|
29976
|
+
* CapacityReservationSpecification / InstanceMaintenancePolicy /
|
|
29977
|
+
* DeletionProtection / MixedInstancesPolicy / LaunchTemplate.
|
|
29979
29978
|
*
|
|
29980
29979
|
* Sub-shape diffs are applied via dedicated AWS APIs before the main
|
|
29981
|
-
* `UpdateAutoScalingGroup` call:
|
|
29982
|
-
* `
|
|
29983
|
-
* `
|
|
29984
|
-
*
|
|
29985
|
-
* `
|
|
29986
|
-
*
|
|
29980
|
+
* `UpdateAutoScalingGroup` call:
|
|
29981
|
+
* - `Tags` → `CreateOrUpdateTags` / `DeleteTags` (#475)
|
|
29982
|
+
* - `LoadBalancerNames` → `AttachLoadBalancers` /
|
|
29983
|
+
* `DetachLoadBalancers` (#476)
|
|
29984
|
+
* - `TargetGroupARNs` → `AttachLoadBalancerTargetGroups` /
|
|
29985
|
+
* `DetachLoadBalancerTargetGroups` (#476)
|
|
29986
|
+
* - `MetricsCollection` → `EnableMetricsCollection` /
|
|
29987
|
+
* `DisableMetricsCollection`
|
|
29988
|
+
* - `LifecycleHookSpecificationList` → per-entry `PutLifecycleHook` /
|
|
29989
|
+
* `DeleteLifecycleHook`
|
|
29990
|
+
* - `TrafficSources` → `AttachTrafficSources` /
|
|
29991
|
+
* `DetachTrafficSources`
|
|
29992
|
+
* - `NotificationConfigurations` → per-topic
|
|
29993
|
+
* `PutNotificationConfiguration` /
|
|
29994
|
+
* `DeleteNotificationConfiguration`
|
|
29995
|
+
*
|
|
29987
29996
|
* Each helper is a no-op when the before/after JSON is identical.
|
|
29988
29997
|
*/
|
|
29989
29998
|
var ASGProvider = class {
|
|
@@ -30091,10 +30100,10 @@ var ASGProvider = class {
|
|
|
30091
30100
|
this.logger.debug(`Updating AutoScalingGroup ${logicalId}: ${physicalId}`);
|
|
30092
30101
|
const stringEq = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
30093
30102
|
if (!stringEq(properties["AutoScalingGroupName"], previousProperties["AutoScalingGroupName"])) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "AutoScalingGroupName is immutable on AWS — UpdateAutoScalingGroup does not accept a new name; the name is fixed at creation. Use cdkd deploy --replace to replace the group.");
|
|
30094
|
-
if (!stringEq(properties["Tags"] ?? [], previousProperties["Tags"] ?? [])) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "Tags updates on AWS::AutoScaling::AutoScalingGroup are not yet implemented in cdkd (AWS exposes CreateOrUpdateTags / DeleteTags); use cdkd deploy --replace, or update the tags via AWS console / CLI.");
|
|
30095
|
-
if (!stringEq(properties["LoadBalancerNames"] ?? [], previousProperties["LoadBalancerNames"] ?? [])) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "LoadBalancerNames diffs on AWS::AutoScaling::AutoScalingGroup are not yet implemented in cdkd (AWS exposes AttachLoadBalancers / DetachLoadBalancers); use cdkd deploy --replace.");
|
|
30096
|
-
if (!stringEq(properties["TargetGroupARNs"] ?? [], previousProperties["TargetGroupARNs"] ?? [])) throw new ResourceUpdateNotSupportedError(resourceType, logicalId, "TargetGroupARNs diffs on AWS::AutoScaling::AutoScalingGroup are not yet implemented in cdkd (AWS exposes AttachLoadBalancerTargetGroups / DetachLoadBalancerTargetGroups); use cdkd deploy --replace.");
|
|
30097
30103
|
try {
|
|
30104
|
+
await this.applyTagsDiff(physicalId, properties["Tags"], previousProperties["Tags"]);
|
|
30105
|
+
await this.applyLoadBalancerNamesDiff(physicalId, properties["LoadBalancerNames"], previousProperties["LoadBalancerNames"]);
|
|
30106
|
+
await this.applyTargetGroupArnsDiff(physicalId, properties["TargetGroupARNs"], previousProperties["TargetGroupARNs"]);
|
|
30098
30107
|
await this.applyMetricsCollectionDiff(physicalId, properties["MetricsCollection"], previousProperties["MetricsCollection"]);
|
|
30099
30108
|
await this.applyLifecycleHooksDiff(physicalId, properties["LifecycleHookSpecificationList"], previousProperties["LifecycleHookSpecificationList"]);
|
|
30100
30109
|
await this.applyTrafficSourcesDiff(physicalId, properties["TrafficSources"], previousProperties["TrafficSources"]);
|
|
@@ -30354,6 +30363,99 @@ var ASGProvider = class {
|
|
|
30354
30363
|
sleep(ms) {
|
|
30355
30364
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
30356
30365
|
}
|
|
30366
|
+
/**
|
|
30367
|
+
* Diff and apply changes to the ASG's `Tags` property via the
|
|
30368
|
+
* `CreateOrUpdateTags` / `DeleteTags` AWS APIs (#475). CFn Tags shape is
|
|
30369
|
+
* `[{Key, Value, PropagateAtLaunch}]`; AWS Tag input adds `ResourceId`
|
|
30370
|
+
* (= the ASG name) and `ResourceType: 'auto-scaling-group'`.
|
|
30371
|
+
*
|
|
30372
|
+
* Diff semantics:
|
|
30373
|
+
* - Removed keys → `DeleteTags`.
|
|
30374
|
+
* - Added keys → `CreateOrUpdateTags`.
|
|
30375
|
+
* - Modified value or `PropagateAtLaunch` flag → `CreateOrUpdateTags`
|
|
30376
|
+
* (the AWS API upserts by `(ResourceId, ResourceType, Key)` tuple, so
|
|
30377
|
+
* a single upsert call replaces the old value).
|
|
30378
|
+
*
|
|
30379
|
+
* No-op when before/after JSON is identical.
|
|
30380
|
+
*/
|
|
30381
|
+
async applyTagsDiff(physicalId, next, prev) {
|
|
30382
|
+
if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;
|
|
30383
|
+
const nextEntries = Array.isArray(next) ? next : [];
|
|
30384
|
+
const prevEntries = Array.isArray(prev) ? prev : [];
|
|
30385
|
+
const nextByKey = /* @__PURE__ */ new Map();
|
|
30386
|
+
for (const t of nextEntries) if (t.Key) nextByKey.set(t.Key, t);
|
|
30387
|
+
const prevByKey = /* @__PURE__ */ new Map();
|
|
30388
|
+
for (const t of prevEntries) if (t.Key) prevByKey.set(t.Key, t);
|
|
30389
|
+
const toDelete = [];
|
|
30390
|
+
for (const [key, tag] of prevByKey) if (!nextByKey.has(key)) toDelete.push(tag);
|
|
30391
|
+
if (toDelete.length > 0) await this.getClient().send(new DeleteTagsCommand$1({ Tags: toDelete.map((t) => ({
|
|
30392
|
+
ResourceId: physicalId,
|
|
30393
|
+
ResourceType: "auto-scaling-group",
|
|
30394
|
+
Key: t.Key
|
|
30395
|
+
})) }));
|
|
30396
|
+
const toUpsert = [];
|
|
30397
|
+
for (const [key, tag] of nextByKey) {
|
|
30398
|
+
const before = prevByKey.get(key);
|
|
30399
|
+
if (JSON.stringify(before) === JSON.stringify(tag)) continue;
|
|
30400
|
+
toUpsert.push(tag);
|
|
30401
|
+
}
|
|
30402
|
+
if (toUpsert.length > 0) await this.getClient().send(new CreateOrUpdateTagsCommand({ Tags: toUpsert.map((t) => ({
|
|
30403
|
+
ResourceId: physicalId,
|
|
30404
|
+
ResourceType: "auto-scaling-group",
|
|
30405
|
+
Key: t.Key,
|
|
30406
|
+
...t.Value !== void 0 && { Value: t.Value },
|
|
30407
|
+
...t.PropagateAtLaunch !== void 0 && { PropagateAtLaunch: t.PropagateAtLaunch }
|
|
30408
|
+
})) }));
|
|
30409
|
+
}
|
|
30410
|
+
/**
|
|
30411
|
+
* Diff `LoadBalancerNames` (Classic Load Balancers) and issue
|
|
30412
|
+
* `AttachLoadBalancers` / `DetachLoadBalancers` for the delta (#476).
|
|
30413
|
+
* Names are opaque strings; AWS allows N attached LBs per ASG so this
|
|
30414
|
+
* helper batches every add into one Attach call and every remove into
|
|
30415
|
+
* one Detach call. No-op when before/after JSON is identical.
|
|
30416
|
+
*/
|
|
30417
|
+
async applyLoadBalancerNamesDiff(physicalId, next, prev) {
|
|
30418
|
+
if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;
|
|
30419
|
+
const nextNames = (Array.isArray(next) ? next : []).filter((n) => typeof n === "string");
|
|
30420
|
+
const prevNames = (Array.isArray(prev) ? prev : []).filter((n) => typeof n === "string");
|
|
30421
|
+
const nextSet = new Set(nextNames);
|
|
30422
|
+
const prevSet = new Set(prevNames);
|
|
30423
|
+
const toAttach = nextNames.filter((n) => !prevSet.has(n));
|
|
30424
|
+
const toDetach = prevNames.filter((n) => !nextSet.has(n));
|
|
30425
|
+
if (toDetach.length > 0) await this.getClient().send(new DetachLoadBalancersCommand({
|
|
30426
|
+
AutoScalingGroupName: physicalId,
|
|
30427
|
+
LoadBalancerNames: toDetach
|
|
30428
|
+
}));
|
|
30429
|
+
if (toAttach.length > 0) await this.getClient().send(new AttachLoadBalancersCommand({
|
|
30430
|
+
AutoScalingGroupName: physicalId,
|
|
30431
|
+
LoadBalancerNames: toAttach
|
|
30432
|
+
}));
|
|
30433
|
+
}
|
|
30434
|
+
/**
|
|
30435
|
+
* Diff `TargetGroupARNs` (ALB / NLB target groups) and issue
|
|
30436
|
+
* `AttachLoadBalancerTargetGroups` /
|
|
30437
|
+
* `DetachLoadBalancerTargetGroups` for the delta (#476). Target-group
|
|
30438
|
+
* ARNs are opaque strings; same per-call batching pattern as
|
|
30439
|
+
* `applyLoadBalancerNamesDiff`. No-op when before/after JSON is
|
|
30440
|
+
* identical.
|
|
30441
|
+
*/
|
|
30442
|
+
async applyTargetGroupArnsDiff(physicalId, next, prev) {
|
|
30443
|
+
if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;
|
|
30444
|
+
const nextArns = (Array.isArray(next) ? next : []).filter((a) => typeof a === "string");
|
|
30445
|
+
const prevArns = (Array.isArray(prev) ? prev : []).filter((a) => typeof a === "string");
|
|
30446
|
+
const nextSet = new Set(nextArns);
|
|
30447
|
+
const prevSet = new Set(prevArns);
|
|
30448
|
+
const toAttach = nextArns.filter((a) => !prevSet.has(a));
|
|
30449
|
+
const toDetach = prevArns.filter((a) => !nextSet.has(a));
|
|
30450
|
+
if (toDetach.length > 0) await this.getClient().send(new DetachLoadBalancerTargetGroupsCommand({
|
|
30451
|
+
AutoScalingGroupName: physicalId,
|
|
30452
|
+
TargetGroupARNs: toDetach
|
|
30453
|
+
}));
|
|
30454
|
+
if (toAttach.length > 0) await this.getClient().send(new AttachLoadBalancerTargetGroupsCommand({
|
|
30455
|
+
AutoScalingGroupName: physicalId,
|
|
30456
|
+
TargetGroupARNs: toAttach
|
|
30457
|
+
}));
|
|
30458
|
+
}
|
|
30357
30459
|
async applyMetricsCollectionDiff(physicalId, next, prev) {
|
|
30358
30460
|
if (JSON.stringify(next ?? []) === JSON.stringify(prev ?? [])) return;
|
|
30359
30461
|
const nextEntries = Array.isArray(next) ? next : [];
|
|
@@ -39898,6 +40000,25 @@ function resolveFnSubInvokeArn(arg) {
|
|
|
39898
40000
|
};
|
|
39899
40001
|
}
|
|
39900
40002
|
|
|
40003
|
+
//#endregion
|
|
40004
|
+
//#region src/local/intrinsic-utils.ts
|
|
40005
|
+
/**
|
|
40006
|
+
* If `value` is a `{ Ref: <string> }` intrinsic, return the referenced
|
|
40007
|
+
* logical ID. Otherwise return `null`.
|
|
40008
|
+
*
|
|
40009
|
+
* Shared across the `src/local/*` resolvers (route discovery, authorizer
|
|
40010
|
+
* resolution, stage attachment) so future intrinsic-shape extensions
|
|
40011
|
+
* (e.g. accepting `Fn::Sub`-bound Refs in REST v1 ResourceId / ParentId)
|
|
40012
|
+
* land in one place instead of three.
|
|
40013
|
+
*/
|
|
40014
|
+
function pickRefLogicalId(value) {
|
|
40015
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
40016
|
+
const ref = value["Ref"];
|
|
40017
|
+
if (typeof ref === "string") return ref;
|
|
40018
|
+
}
|
|
40019
|
+
return null;
|
|
40020
|
+
}
|
|
40021
|
+
|
|
39901
40022
|
//#endregion
|
|
39902
40023
|
//#region src/local/httpv2-service-integration.ts
|
|
39903
40024
|
const logger = getLogger();
|
|
@@ -40363,7 +40484,7 @@ function discoverRestV1Method(logicalId, resource, template, stackName) {
|
|
|
40363
40484
|
const integration = props["Integration"];
|
|
40364
40485
|
if (!integration) throw new Error(`${stackName}/${logicalId} (AWS::ApiGateway::Method): missing Integration property`);
|
|
40365
40486
|
const restApiId = props["RestApiId"];
|
|
40366
|
-
const restApiLogicalId = pickRefLogicalId
|
|
40487
|
+
const restApiLogicalId = pickRefLogicalId(restApiId);
|
|
40367
40488
|
if (!restApiLogicalId) throw new Error(`${stackName}/${logicalId} (AWS::ApiGateway::Method): RestApiId must be a { Ref: '...' } reference (got ${shortJson$1(restApiId)}).`);
|
|
40368
40489
|
const resourceId = props["ResourceId"];
|
|
40369
40490
|
const path = buildRestV1Path(resourceId, restApiLogicalId, template, stackName, logicalId);
|
|
@@ -40723,7 +40844,7 @@ function buildRestV1Path(resourceIdIntrinsic, restApiLogicalId, template, stackN
|
|
|
40723
40844
|
if (Array.isArray(arg) && arg.length === 2 && arg[1] === "RootResourceId") return "/";
|
|
40724
40845
|
}
|
|
40725
40846
|
}
|
|
40726
|
-
const resourceLogicalId = pickRefLogicalId
|
|
40847
|
+
const resourceLogicalId = pickRefLogicalId(resourceIdIntrinsic);
|
|
40727
40848
|
if (!resourceLogicalId) throw new Error(`${stackName}/${methodLogicalId}: ResourceId must be { Ref: '...' } or { 'Fn::GetAtt': [..., 'RootResourceId'] } (got ${shortJson$1(resourceIdIntrinsic)}).`);
|
|
40728
40849
|
const segments = [];
|
|
40729
40850
|
const visited = /* @__PURE__ */ new Set();
|
|
@@ -40743,7 +40864,7 @@ function buildRestV1Path(resourceIdIntrinsic, restApiLogicalId, template, stackN
|
|
|
40743
40864
|
const arg = parentId["Fn::GetAtt"];
|
|
40744
40865
|
if (Array.isArray(arg) && arg[1] === "RootResourceId") break;
|
|
40745
40866
|
}
|
|
40746
|
-
cursor = pickRefLogicalId
|
|
40867
|
+
cursor = pickRefLogicalId(parentId) ?? void 0;
|
|
40747
40868
|
}
|
|
40748
40869
|
return "/" + segments.join("/");
|
|
40749
40870
|
}
|
|
@@ -40758,7 +40879,7 @@ function pickRestV1Stage(restApiLogicalId, template) {
|
|
|
40758
40879
|
for (const [, resource] of Object.entries(resources)) {
|
|
40759
40880
|
if (resource.Type !== "AWS::ApiGateway::Stage") continue;
|
|
40760
40881
|
const props = resource.Properties ?? {};
|
|
40761
|
-
if (pickRefLogicalId
|
|
40882
|
+
if (pickRefLogicalId(props["RestApiId"]) === restApiLogicalId) {
|
|
40762
40883
|
const stageName = props["StageName"];
|
|
40763
40884
|
if (typeof stageName === "string") return stageName;
|
|
40764
40885
|
}
|
|
@@ -40777,7 +40898,7 @@ function pickRestV1Stage(restApiLogicalId, template) {
|
|
|
40777
40898
|
function discoverHttpApiRoute(logicalId, resource, template, stackName) {
|
|
40778
40899
|
const props = resource.Properties ?? {};
|
|
40779
40900
|
const apiId = props["ApiId"];
|
|
40780
|
-
const apiLogicalId = pickRefLogicalId
|
|
40901
|
+
const apiLogicalId = pickRefLogicalId(apiId);
|
|
40781
40902
|
if (!apiLogicalId) throw new Error(`${stackName}/${logicalId} (AWS::ApiGatewayV2::Route): ApiId must be { Ref: '...' } (got ${shortJson$1(apiId)}).`);
|
|
40782
40903
|
const routeKey = props["RouteKey"];
|
|
40783
40904
|
if (typeof routeKey !== "string" || routeKey.length === 0) throw new Error(`${stackName}/${logicalId} (AWS::ApiGatewayV2::Route): RouteKey must be a string`);
|
|
@@ -40990,11 +41111,11 @@ function parseHttpApiTargetIntegration(target, location) {
|
|
|
40990
41111
|
const sep = join[0];
|
|
40991
41112
|
const parts = join[1];
|
|
40992
41113
|
if (sep === "/" && parts.length === 2 && parts[0] === "integrations") {
|
|
40993
|
-
const ref = pickRefLogicalId
|
|
41114
|
+
const ref = pickRefLogicalId(parts[1]);
|
|
40994
41115
|
if (ref) return ref;
|
|
40995
41116
|
}
|
|
40996
41117
|
if (sep === "" && parts.length === 2 && parts[0] === "integrations/") {
|
|
40997
|
-
const ref = pickRefLogicalId
|
|
41118
|
+
const ref = pickRefLogicalId(parts[1]);
|
|
40998
41119
|
if (ref) return ref;
|
|
40999
41120
|
}
|
|
41000
41121
|
}
|
|
@@ -41014,7 +41135,7 @@ function parseHttpApiTargetIntegration(target, location) {
|
|
|
41014
41135
|
if (m) {
|
|
41015
41136
|
const bound = bindings[m[1]];
|
|
41016
41137
|
if (bound !== void 0) {
|
|
41017
|
-
const ref = pickRefLogicalId
|
|
41138
|
+
const ref = pickRefLogicalId(bound);
|
|
41018
41139
|
if (ref) return ref;
|
|
41019
41140
|
}
|
|
41020
41141
|
}
|
|
@@ -41040,17 +41161,6 @@ function parseRouteKey(routeKey) {
|
|
|
41040
41161
|
};
|
|
41041
41162
|
}
|
|
41042
41163
|
/**
|
|
41043
|
-
* If `value` is a `{ Ref: <string> }` intrinsic, return the referenced
|
|
41044
|
-
* logical ID. Otherwise return `null`.
|
|
41045
|
-
*/
|
|
41046
|
-
function pickRefLogicalId$3(value) {
|
|
41047
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
41048
|
-
const ref = value["Ref"];
|
|
41049
|
-
if (typeof ref === "string") return ref;
|
|
41050
|
-
}
|
|
41051
|
-
return null;
|
|
41052
|
-
}
|
|
41053
|
-
/**
|
|
41054
41164
|
* Compact JSON for error messages — caps long objects so a malformed
|
|
41055
41165
|
* intrinsic doesn't dump the whole template into a stderr line.
|
|
41056
41166
|
*/
|
|
@@ -41121,7 +41231,7 @@ function discoverOneApi(logicalId, resource, template, stackName) {
|
|
|
41121
41231
|
apiLogicalId: logicalId,
|
|
41122
41232
|
apiStackName: stackName,
|
|
41123
41233
|
declaredAt,
|
|
41124
|
-
...apiCdkPath !==
|
|
41234
|
+
...apiCdkPath !== void 0 && { apiCdkPath },
|
|
41125
41235
|
routeSelectionExpression,
|
|
41126
41236
|
stage,
|
|
41127
41237
|
routes,
|
|
@@ -41142,7 +41252,7 @@ function collectAuthRoutesForApi(apiLogicalId, template, _stackName) {
|
|
|
41142
41252
|
for (const [, resource] of Object.entries(resources)) {
|
|
41143
41253
|
if (resource.Type !== "AWS::ApiGatewayV2::Route") continue;
|
|
41144
41254
|
const props = resource.Properties ?? {};
|
|
41145
|
-
if (pickRefLogicalId
|
|
41255
|
+
if (pickRefLogicalId(props["ApiId"]) !== apiLogicalId) continue;
|
|
41146
41256
|
const authType = props["AuthorizationType"];
|
|
41147
41257
|
if (authType === void 0) continue;
|
|
41148
41258
|
const routeKey = props["RouteKey"];
|
|
@@ -41194,7 +41304,7 @@ function pickStage$1(apiLogicalId, template) {
|
|
|
41194
41304
|
for (const [, resource] of Object.entries(resources)) {
|
|
41195
41305
|
if (resource.Type !== "AWS::ApiGatewayV2::Stage") continue;
|
|
41196
41306
|
const props = resource.Properties ?? {};
|
|
41197
|
-
if (pickRefLogicalId
|
|
41307
|
+
if (pickRefLogicalId(props["ApiId"]) === apiLogicalId) {
|
|
41198
41308
|
const stageName = props["StageName"];
|
|
41199
41309
|
if (typeof stageName === "string" && stageName.length > 0) return stageName;
|
|
41200
41310
|
}
|
|
@@ -41215,7 +41325,7 @@ function collectRoutesForApi(apiLogicalId, template, stackName) {
|
|
|
41215
41325
|
for (const [routeLogicalId, resource] of Object.entries(resources)) {
|
|
41216
41326
|
if (resource.Type !== "AWS::ApiGatewayV2::Route") continue;
|
|
41217
41327
|
const props = resource.Properties ?? {};
|
|
41218
|
-
if (pickRefLogicalId
|
|
41328
|
+
if (pickRefLogicalId(props["ApiId"]) !== apiLogicalId) continue;
|
|
41219
41329
|
const declaredAt = `${stackName}/${routeLogicalId}`;
|
|
41220
41330
|
const routeKey = props["RouteKey"];
|
|
41221
41331
|
if (typeof routeKey !== "string" || routeKey.length === 0) throw new Error(`${declaredAt}: RouteKey must be a non-empty string.`);
|
|
@@ -41263,11 +41373,11 @@ function parseRouteTarget(target, location) {
|
|
|
41263
41373
|
const sep = join[0];
|
|
41264
41374
|
const parts = join[1];
|
|
41265
41375
|
if (sep === "/" && parts.length === 2 && parts[0] === "integrations") {
|
|
41266
|
-
const ref = pickRefLogicalId
|
|
41376
|
+
const ref = pickRefLogicalId(parts[1]);
|
|
41267
41377
|
if (ref) return ref;
|
|
41268
41378
|
}
|
|
41269
41379
|
if (sep === "" && parts.length === 2 && parts[0] === "integrations/") {
|
|
41270
|
-
const ref = pickRefLogicalId
|
|
41380
|
+
const ref = pickRefLogicalId(parts[1]);
|
|
41271
41381
|
if (ref) return ref;
|
|
41272
41382
|
}
|
|
41273
41383
|
}
|
|
@@ -41288,7 +41398,7 @@ function parseRouteTarget(target, location) {
|
|
|
41288
41398
|
if (m) {
|
|
41289
41399
|
const bound = bindings[m[1]];
|
|
41290
41400
|
if (bound !== void 0) {
|
|
41291
|
-
const ref = pickRefLogicalId
|
|
41401
|
+
const ref = pickRefLogicalId(bound);
|
|
41292
41402
|
if (ref) return ref;
|
|
41293
41403
|
}
|
|
41294
41404
|
}
|
|
@@ -41297,21 +41407,47 @@ function parseRouteTarget(target, location) {
|
|
|
41297
41407
|
}
|
|
41298
41408
|
throw new Error(`${location}: Target must be 'integrations/<id>' literal, Fn::Join with the documented shapes, or Fn::Sub with an 'integrations/\${...}' template.`);
|
|
41299
41409
|
}
|
|
41300
|
-
function pickRefLogicalId$2(value) {
|
|
41301
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
41302
|
-
const ref = value["Ref"];
|
|
41303
|
-
if (typeof ref === "string") return ref;
|
|
41304
|
-
}
|
|
41305
|
-
return null;
|
|
41306
|
-
}
|
|
41307
41410
|
function readApiCdkPath(logicalId, template) {
|
|
41308
41411
|
const resource = template.Resources?.[logicalId];
|
|
41309
|
-
if (!resource) return
|
|
41310
|
-
|
|
41412
|
+
if (!resource) return void 0;
|
|
41413
|
+
const path = readCdkPath(resource);
|
|
41414
|
+
return path === "" ? void 0 : path;
|
|
41311
41415
|
}
|
|
41312
41416
|
|
|
41313
41417
|
//#endregion
|
|
41314
41418
|
//#region src/local/websocket-event.ts
|
|
41419
|
+
/**
|
|
41420
|
+
* AWS API Gateway WebSocket event-payload builders.
|
|
41421
|
+
*
|
|
41422
|
+
* Spec: https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-websocket-api-mapping-template-reference.html
|
|
41423
|
+
*
|
|
41424
|
+
* Three event types — CONNECT / MESSAGE / DISCONNECT — each carry a
|
|
41425
|
+
* shared {@link WebSocketRequestContext} plus per-event fields.
|
|
41426
|
+
*
|
|
41427
|
+
* Fields cdkd populates locally vs mocks (matches design Q1 in
|
|
41428
|
+
* `docs/design/462-websocket-api.md`):
|
|
41429
|
+
*
|
|
41430
|
+
* | Field | Source |
|
|
41431
|
+
* |--------------------------------------|-----------------------------------------|
|
|
41432
|
+
* | `connectionId` | UUID v4 generated at `$connect` |
|
|
41433
|
+
* | `requestId` / `extendedRequestId` | Generated UUID per event |
|
|
41434
|
+
* | `messageId` (MESSAGE only) | Generated UUID per event |
|
|
41435
|
+
* | `requestTime` / `requestTimeEpoch` | `Date.now()` at build time |
|
|
41436
|
+
* | `connectedAt` | Captured at `$connect` |
|
|
41437
|
+
* | `stage` | Resolved Stage Name; `'local'` default |
|
|
41438
|
+
* | `apiId` | `'local'` (mock) |
|
|
41439
|
+
* | `domainName` | `'localhost'` (mock) |
|
|
41440
|
+
* | `identity.sourceIp` | `req.socket.remoteAddress` (real) |
|
|
41441
|
+
* | `identity.userAgent` | Upgrade `User-Agent` header (real) |
|
|
41442
|
+
* | `headers`/`queryStringParameters` | Parsed from upgrade `req` (real) |
|
|
41443
|
+
* | `authorizer` | `null` in v1 (deferred) |
|
|
41444
|
+
*
|
|
41445
|
+
* Per-event `eventType` / `routeKey` / `messageDirection` are fixed by
|
|
41446
|
+
* the lifecycle stage, NOT by the route's `routeKey` field — `routeKey`
|
|
41447
|
+
* carries which user-declared route fired ("$connect" / "$disconnect" /
|
|
41448
|
+
* "$default" / custom).
|
|
41449
|
+
*/
|
|
41450
|
+
const MOCK_ACCOUNT_ID$1 = "123456789012";
|
|
41315
41451
|
const MOCK_DOMAIN_NAME$1 = "localhost";
|
|
41316
41452
|
const MOCK_API_ID$1 = "local";
|
|
41317
41453
|
/**
|
|
@@ -41338,6 +41474,7 @@ function buildRequestContext(routeKey, eventType, connectionId, connectedAt, sta
|
|
|
41338
41474
|
apiId: MOCK_API_ID$1,
|
|
41339
41475
|
authorizer: null,
|
|
41340
41476
|
identity: {
|
|
41477
|
+
accountId: MOCK_ACCOUNT_ID$1,
|
|
41341
41478
|
sourceIp: snapshot.sourceIp ?? "127.0.0.1",
|
|
41342
41479
|
userAgent: snapshot.userAgent ?? ""
|
|
41343
41480
|
}
|
|
@@ -45085,11 +45222,22 @@ function parseCognitoIssuer(issuer) {
|
|
|
45085
45222
|
};
|
|
45086
45223
|
}
|
|
45087
45224
|
/**
|
|
45088
|
-
* Pull a string out of a
|
|
45089
|
-
*
|
|
45090
|
-
*
|
|
45091
|
-
*
|
|
45092
|
-
*
|
|
45225
|
+
* Pull a string out of a literal / `Fn::GetAtt` entry under `ProviderARNs`.
|
|
45226
|
+
*
|
|
45227
|
+
* CDK's `apigateway.CognitoUserPoolsAuthorizer` emits a `Fn::GetAtt:
|
|
45228
|
+
* [<UserPool>, 'Arn']` reference, which is the canonical shape any user
|
|
45229
|
+
* who writes `new CognitoUserPoolsAuthorizer(this, 'auth', { cognitoUserPools: [pool] })`
|
|
45230
|
+
* ends up with (#470). Without `--from-state` we cannot resolve the
|
|
45231
|
+
* deployed pool ARN, so we synthesize an obviously-unreachable placeholder
|
|
45232
|
+
* pointing at a non-existent pool id — the JWKS fetch will fail and
|
|
45233
|
+
* cognito-jwt.ts's pass-through fallback (PR #234) admits every JWT
|
|
45234
|
+
* without signature verification. The warn log names the affected
|
|
45235
|
+
* authorizer + the recommended explicit `providerArns` workaround so
|
|
45236
|
+
* developers who DO want real verification know how to switch over.
|
|
45237
|
+
*
|
|
45238
|
+
* The `location` argument carries the full
|
|
45239
|
+
* `<stack>/<authorizer>.ProviderARNs[<idx>]` path so the warn / error
|
|
45240
|
+
* names the offending entry exactly.
|
|
45093
45241
|
*/
|
|
45094
45242
|
function pickStringFromArn(value, location) {
|
|
45095
45243
|
if (typeof value === "string") return value;
|
|
@@ -45097,7 +45245,11 @@ function pickStringFromArn(value, location) {
|
|
|
45097
45245
|
const obj = value;
|
|
45098
45246
|
if ("Fn::GetAtt" in obj) {
|
|
45099
45247
|
const arg = obj["Fn::GetAtt"];
|
|
45100
|
-
if (Array.isArray(arg) && arg.length === 2 && typeof arg[0] === "string" && arg[1] === "Arn")
|
|
45248
|
+
if (Array.isArray(arg) && arg.length === 2 && typeof arg[0] === "string" && arg[1] === "Arn") {
|
|
45249
|
+
const logicalId = arg[0];
|
|
45250
|
+
getLogger().warn(`${location}: uses Fn::GetAtt against logical ID '${logicalId}'. cdkd local start-api cannot resolve the deployed user pool ARN — synthesizing an unreachable placeholder so JWKS pass-through admits every token. For real signature verification, set 'providerArns: [pool.userPoolArn]' explicitly on the CDK construct.`);
|
|
45251
|
+
return `arn:aws:cognito-idp:us-east-1:000000000000:userpool/us-east-1_cdkdplaceholder${logicalId}`;
|
|
45252
|
+
}
|
|
45101
45253
|
}
|
|
45102
45254
|
}
|
|
45103
45255
|
throw new RouteDiscoveryError(`${location}: must be a literal string (got ${shortJson(value)}).`);
|
|
@@ -45184,7 +45336,7 @@ function detectRestV1Authorizer(methodResource, methodLogicalId, stack) {
|
|
|
45184
45336
|
declaredAt: `${stack.stackName}/${methodLogicalId}`
|
|
45185
45337
|
};
|
|
45186
45338
|
const authorizerId = props["AuthorizerId"];
|
|
45187
|
-
const refLogicalId = pickRefLogicalId
|
|
45339
|
+
const refLogicalId = pickRefLogicalId(authorizerId);
|
|
45188
45340
|
if (!refLogicalId) throw new RouteDiscoveryError(`${stack.stackName}/${methodLogicalId}: AuthorizationType='${stringifyValue(authType)}' but AuthorizerId is missing or not a {Ref:...}.`);
|
|
45189
45341
|
return resolveRestV1Authorizer(refLogicalId, stack.template, stack.stackName, `${stack.stackName}/${methodLogicalId}`);
|
|
45190
45342
|
}
|
|
@@ -45193,18 +45345,11 @@ function detectHttpApiAuthorizer(routeResource, routeLogicalId, stack) {
|
|
|
45193
45345
|
const authType = props["AuthorizationType"];
|
|
45194
45346
|
if (authType === void 0 || authType === "NONE") return void 0;
|
|
45195
45347
|
const authorizerId = props["AuthorizerId"];
|
|
45196
|
-
const refLogicalId = pickRefLogicalId
|
|
45348
|
+
const refLogicalId = pickRefLogicalId(authorizerId);
|
|
45197
45349
|
if (!refLogicalId) throw new RouteDiscoveryError(`${stack.stackName}/${routeLogicalId}: AuthorizationType='${stringifyValue(authType)}' but AuthorizerId is missing or not a {Ref:...}.`);
|
|
45198
45350
|
const scopesRaw = props["AuthorizationScopes"];
|
|
45199
45351
|
return resolveHttpApiAuthorizer(refLogicalId, Array.isArray(scopesRaw) ? scopesRaw.filter((s) => typeof s === "string") : void 0, stack.template, stack.stackName, `${stack.stackName}/${routeLogicalId}`);
|
|
45200
45352
|
}
|
|
45201
|
-
function pickRefLogicalId$1(value) {
|
|
45202
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
45203
|
-
const ref = value["Ref"];
|
|
45204
|
-
if (typeof ref === "string") return ref;
|
|
45205
|
-
}
|
|
45206
|
-
return null;
|
|
45207
|
-
}
|
|
45208
45353
|
function shortJson(value) {
|
|
45209
45354
|
try {
|
|
45210
45355
|
const s = JSON.stringify(value);
|
|
@@ -47775,19 +47920,6 @@ function attachStageContext(routes, stageMap) {
|
|
|
47775
47920
|
route.stage = stage.stageName;
|
|
47776
47921
|
}
|
|
47777
47922
|
}
|
|
47778
|
-
/**
|
|
47779
|
-
* If `value` is a `{ Ref: <string> }` intrinsic, return the referenced
|
|
47780
|
-
* logical ID. Otherwise return `null`. (Duplicated structurally from
|
|
47781
|
-
* `route-discovery.ts` — both modules walk the template independently
|
|
47782
|
-
* and shouldn't grow a coupling for a 5-line helper.)
|
|
47783
|
-
*/
|
|
47784
|
-
function pickRefLogicalId(value) {
|
|
47785
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
47786
|
-
const ref = value["Ref"];
|
|
47787
|
-
if (typeof ref === "string") return ref;
|
|
47788
|
-
}
|
|
47789
|
-
return null;
|
|
47790
|
-
}
|
|
47791
47923
|
|
|
47792
47924
|
//#endregion
|
|
47793
47925
|
//#region src/local/file-watcher.ts
|
|
@@ -54130,7 +54262,7 @@ function reorderArgs(argv) {
|
|
|
54130
54262
|
*/
|
|
54131
54263
|
async function main() {
|
|
54132
54264
|
const program = new Command();
|
|
54133
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
54265
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.138.0");
|
|
54134
54266
|
program.addCommand(createBootstrapCommand());
|
|
54135
54267
|
program.addCommand(createSynthCommand());
|
|
54136
54268
|
program.addCommand(createListCommand());
|