@go-to-k/cdkd 0.84.0 → 0.85.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/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  - **Drop-in CDK compatible** — your existing CDK app code runs as-is.
6
6
  - **Up to 15x faster deploys than the AWS CDK CLI (CloudFormation)**
7
- - **Run AWS resources locally without deploying** — invoke Lambdas, serve API Gateway routes, and run ECS task definitions against Docker. SAM-compatible mental model, no `template.yaml` round-trip.
7
+ - **Run AWS resources locally without deploying** — invoke Lambdas, run ECS tasks, and serve API Gateway routes from Docker.
8
8
 
9
9
  ![cdkd demo](https://github.com/user-attachments/assets/0128730d-186d-4bd3-abea-aabc80ba4dd5)
10
10
 
package/dist/cli.js CHANGED
@@ -76798,6 +76798,13 @@ function inspectImageForSubstitutions(image, resources) {
76798
76798
  return { pseudo: false, state: true };
76799
76799
  }
76800
76800
  }
76801
+ const join11 = obj["Fn::Join"];
76802
+ if (Array.isArray(join11) && join11.length === 2 && Array.isArray(join11[1])) {
76803
+ const scan = { pseudo: false, state: false };
76804
+ inspectIntrinsicNeeds(join11[1], resources, scan);
76805
+ if (scan.pseudo || scan.state)
76806
+ return scan;
76807
+ }
76801
76808
  let sub;
76802
76809
  const subRaw = obj["Fn::Sub"];
76803
76810
  if (typeof subRaw === "string")
@@ -76823,6 +76830,42 @@ function inspectImageForSubstitutions(image, resources) {
76823
76830
  }
76824
76831
  return { pseudo, state };
76825
76832
  }
76833
+ function inspectIntrinsicNeeds(node, resources, scan) {
76834
+ if (node === null || node === void 0)
76835
+ return;
76836
+ if (typeof node === "string" || typeof node === "number" || typeof node === "boolean")
76837
+ return;
76838
+ if (Array.isArray(node)) {
76839
+ for (const item of node)
76840
+ inspectIntrinsicNeeds(item, resources, scan);
76841
+ return;
76842
+ }
76843
+ if (typeof node !== "object")
76844
+ return;
76845
+ const obj = node;
76846
+ if (typeof obj["Ref"] === "string") {
76847
+ const target = obj["Ref"];
76848
+ if (target.startsWith("AWS::"))
76849
+ scan.pseudo = true;
76850
+ else if (resources[target]?.Type === "AWS::ECR::Repository")
76851
+ scan.state = true;
76852
+ return;
76853
+ }
76854
+ const getAtt = obj["Fn::GetAtt"];
76855
+ if (getAtt !== void 0) {
76856
+ let lid;
76857
+ if (Array.isArray(getAtt) && typeof getAtt[0] === "string")
76858
+ lid = getAtt[0];
76859
+ else if (typeof getAtt === "string")
76860
+ lid = getAtt.split(".")[0];
76861
+ if (lid && resources[lid]?.Type === "AWS::ECR::Repository")
76862
+ scan.state = true;
76863
+ return;
76864
+ }
76865
+ for (const value of Object.values(obj)) {
76866
+ inspectIntrinsicNeeds(value, resources, scan);
76867
+ }
76868
+ }
76826
76869
  function parseEcsTarget(target) {
76827
76870
  if (typeof target !== "string" || target.length === 0) {
76828
76871
  throw new EcsTaskResolutionError(
@@ -77147,6 +77190,20 @@ function parseContainerImage(raw, containerName, taskLogicalId, resources, _stac
77147
77190
  if (getAttImage) {
77148
77191
  return classifyResolvedImage(getAttImage);
77149
77192
  }
77193
+ const joinResolved = tryResolveImageFnJoin(raw, resources, context);
77194
+ if (joinResolved.kind === "resolved") {
77195
+ return classifyResolvedImage(joinResolved.uri);
77196
+ }
77197
+ if (joinResolved.kind === "needs-state") {
77198
+ throw new EcsTaskResolutionError(
77199
+ `Container '${containerName}' in task '${taskLogicalId}' references same-stack ECR repository '${joinResolved.repoLogicalId}' via Fn::Join. cdkd local run-task cannot resolve the repository URI without state \u2014 pass --from-state (the stack must have been deployed via cdkd deploy), build via ContainerImage.fromAsset, or pin a public image.`
77200
+ );
77201
+ }
77202
+ if (joinResolved.kind === "unsupported-join") {
77203
+ throw new EcsTaskResolutionError(
77204
+ `Container '${containerName}' in task '${taskLogicalId}' has an unsupported Fn::Join Image shape: ${joinResolved.reason}. cdkd local run-task recognizes the canonical CDK 2.x ContainerImage.fromEcrRepository Fn::Join shape (delimiter "" with nested Fn::Select/Fn::Split over an ECR Repository Arn GetAtt + Ref to the repo).`
77205
+ );
77206
+ }
77150
77207
  const flat = extractImageString(raw);
77151
77208
  if (!flat) {
77152
77209
  throw new EcsTaskResolutionError(
@@ -77298,6 +77355,228 @@ function extractImageString(value) {
77298
77355
  }
77299
77356
  return void 0;
77300
77357
  }
77358
+ function tryResolveImageFnJoin(raw, resources, context) {
77359
+ if (!raw || typeof raw !== "object")
77360
+ return { kind: "not-applicable" };
77361
+ const obj = raw;
77362
+ const arg = obj["Fn::Join"];
77363
+ if (arg === void 0)
77364
+ return { kind: "not-applicable" };
77365
+ if (!Array.isArray(arg) || arg.length !== 2 || !Array.isArray(arg[1])) {
77366
+ return { kind: "unsupported-join", reason: "Fn::Join must be [delimiter, [elements]]" };
77367
+ }
77368
+ const [delimiter, elements] = arg;
77369
+ if (typeof delimiter !== "string") {
77370
+ return {
77371
+ kind: "unsupported-join",
77372
+ reason: `Fn::Join delimiter must be a string, got ${typeof delimiter}`
77373
+ };
77374
+ }
77375
+ const repoLogicalId = findEcrRepositoryRefInTree(elements, resources);
77376
+ const stateResources = context?.stateResources;
77377
+ if (repoLogicalId && !stateResources) {
77378
+ return { kind: "needs-state", repoLogicalId };
77379
+ }
77380
+ const parts = [];
77381
+ for (const element of elements) {
77382
+ const r = resolveImageIntrinsic(element, resources, context);
77383
+ if (r === void 0) {
77384
+ if (!repoLogicalId)
77385
+ return { kind: "not-applicable" };
77386
+ return {
77387
+ kind: "unsupported-join",
77388
+ reason: "one or more Fn::Join elements could not be resolved"
77389
+ };
77390
+ }
77391
+ parts.push(r);
77392
+ }
77393
+ return { kind: "resolved", uri: parts.join(delimiter) };
77394
+ }
77395
+ function findEcrRepositoryRefInTree(node, resources) {
77396
+ if (node === null || node === void 0)
77397
+ return void 0;
77398
+ if (typeof node === "string" || typeof node === "number" || typeof node === "boolean") {
77399
+ return void 0;
77400
+ }
77401
+ if (Array.isArray(node)) {
77402
+ for (const item of node) {
77403
+ const hit = findEcrRepositoryRefInTree(item, resources);
77404
+ if (hit)
77405
+ return hit;
77406
+ }
77407
+ return void 0;
77408
+ }
77409
+ if (typeof node !== "object")
77410
+ return void 0;
77411
+ const obj = node;
77412
+ if (typeof obj["Ref"] === "string") {
77413
+ const target = obj["Ref"];
77414
+ if (resources[target]?.Type === "AWS::ECR::Repository")
77415
+ return target;
77416
+ return void 0;
77417
+ }
77418
+ const getAtt = obj["Fn::GetAtt"];
77419
+ if (getAtt !== void 0) {
77420
+ let lid;
77421
+ if (Array.isArray(getAtt) && typeof getAtt[0] === "string")
77422
+ lid = getAtt[0];
77423
+ else if (typeof getAtt === "string")
77424
+ lid = getAtt.split(".")[0];
77425
+ if (lid && resources[lid]?.Type === "AWS::ECR::Repository")
77426
+ return lid;
77427
+ return void 0;
77428
+ }
77429
+ for (const value of Object.values(obj)) {
77430
+ const hit = findEcrRepositoryRefInTree(value, resources);
77431
+ if (hit)
77432
+ return hit;
77433
+ }
77434
+ return void 0;
77435
+ }
77436
+ function resolveImageIntrinsic(node, resources, context) {
77437
+ const v = resolveImageIntrinsicAny(node, resources, context);
77438
+ if (typeof v === "string")
77439
+ return v;
77440
+ if (typeof v === "number" || typeof v === "boolean")
77441
+ return String(v);
77442
+ return void 0;
77443
+ }
77444
+ function resolveImageIntrinsicAny(node, resources, context) {
77445
+ if (node === null || node === void 0)
77446
+ return void 0;
77447
+ if (typeof node === "string" || typeof node === "number" || typeof node === "boolean") {
77448
+ return node;
77449
+ }
77450
+ if (Array.isArray(node)) {
77451
+ return void 0;
77452
+ }
77453
+ if (typeof node !== "object")
77454
+ return void 0;
77455
+ const obj = node;
77456
+ const keys = Object.keys(obj);
77457
+ if (keys.length !== 1)
77458
+ return void 0;
77459
+ const intrinsic = keys[0];
77460
+ const arg = obj[intrinsic];
77461
+ if (intrinsic === "Ref") {
77462
+ if (typeof arg !== "string")
77463
+ return void 0;
77464
+ if (arg.startsWith("AWS::")) {
77465
+ const p = context?.pseudoParameters;
77466
+ if (!p)
77467
+ return void 0;
77468
+ if (arg === "AWS::URLSuffix")
77469
+ return p.urlSuffix;
77470
+ if (arg === "AWS::Partition")
77471
+ return p.partition;
77472
+ if (arg === "AWS::Region")
77473
+ return p.region;
77474
+ if (arg === "AWS::AccountId")
77475
+ return p.accountId;
77476
+ return void 0;
77477
+ }
77478
+ const refResource = resources[arg];
77479
+ if (refResource?.Type !== "AWS::ECR::Repository")
77480
+ return void 0;
77481
+ const stateEntry = context?.stateResources?.[arg];
77482
+ if (!stateEntry)
77483
+ return void 0;
77484
+ return stateEntry.physicalId;
77485
+ }
77486
+ if (intrinsic === "Fn::GetAtt") {
77487
+ let logicalId;
77488
+ let attr;
77489
+ if (Array.isArray(arg) && arg.length === 2 && typeof arg[0] === "string" && typeof arg[1] === "string") {
77490
+ logicalId = arg[0];
77491
+ attr = arg[1];
77492
+ } else if (typeof arg === "string") {
77493
+ const dot = arg.indexOf(".");
77494
+ if (dot > 0 && dot < arg.length - 1) {
77495
+ logicalId = arg.slice(0, dot);
77496
+ attr = arg.slice(dot + 1);
77497
+ }
77498
+ }
77499
+ if (!logicalId || !attr)
77500
+ return void 0;
77501
+ if (resources[logicalId]?.Type !== "AWS::ECR::Repository")
77502
+ return void 0;
77503
+ const cached = context?.stateResources?.[logicalId]?.attributes?.[attr];
77504
+ if (typeof cached === "string" && cached.length > 0)
77505
+ return cached;
77506
+ return void 0;
77507
+ }
77508
+ if (intrinsic === "Fn::Split") {
77509
+ if (!Array.isArray(arg) || arg.length !== 2)
77510
+ return void 0;
77511
+ const argArr = arg;
77512
+ const delim = argArr[0];
77513
+ if (typeof delim !== "string")
77514
+ return void 0;
77515
+ const src = resolveImageIntrinsicAny(argArr[1], resources, context);
77516
+ if (typeof src !== "string")
77517
+ return void 0;
77518
+ return src.split(delim);
77519
+ }
77520
+ if (intrinsic === "Fn::Select") {
77521
+ if (!Array.isArray(arg) || arg.length !== 2)
77522
+ return void 0;
77523
+ const argArr = arg;
77524
+ const rawIndex = argArr[0];
77525
+ let index;
77526
+ if (typeof rawIndex === "number") {
77527
+ index = rawIndex;
77528
+ } else if (typeof rawIndex === "string" && /^-?\d+$/.test(rawIndex)) {
77529
+ index = Number.parseInt(rawIndex, 10);
77530
+ }
77531
+ if (index === void 0 || !Number.isFinite(index))
77532
+ return void 0;
77533
+ const list = resolveImageIntrinsicAny(argArr[1], resources, context);
77534
+ if (Array.isArray(list)) {
77535
+ if (index < 0 || index >= list.length)
77536
+ return void 0;
77537
+ const picked = list[index];
77538
+ if (typeof picked === "string")
77539
+ return picked;
77540
+ return void 0;
77541
+ }
77542
+ if (Array.isArray(argArr[1])) {
77543
+ const listLiteral = argArr[1];
77544
+ if (index < 0 || index >= listLiteral.length)
77545
+ return void 0;
77546
+ return resolveImageIntrinsic(listLiteral[index], resources, context);
77547
+ }
77548
+ return void 0;
77549
+ }
77550
+ if (intrinsic === "Fn::Join") {
77551
+ if (!Array.isArray(arg) || arg.length !== 2)
77552
+ return void 0;
77553
+ const [delim, parts] = arg;
77554
+ if (typeof delim !== "string" || !Array.isArray(parts))
77555
+ return void 0;
77556
+ const resolved = [];
77557
+ for (const part of parts) {
77558
+ const r = resolveImageIntrinsic(part, resources, context);
77559
+ if (r === void 0)
77560
+ return void 0;
77561
+ resolved.push(r);
77562
+ }
77563
+ return resolved.join(delim);
77564
+ }
77565
+ if (intrinsic === "Fn::Sub") {
77566
+ let template;
77567
+ if (typeof arg === "string")
77568
+ template = arg;
77569
+ else if (Array.isArray(arg) && typeof arg[0] === "string")
77570
+ template = arg[0];
77571
+ if (template === void 0)
77572
+ return void 0;
77573
+ const out = substituteImagePlaceholders(template, resources, context);
77574
+ if (out.includes("${"))
77575
+ return void 0;
77576
+ return out;
77577
+ }
77578
+ return void 0;
77579
+ }
77301
77580
  function parseVolume(raw, idx, taskLogicalId) {
77302
77581
  if (!raw || typeof raw !== "object") {
77303
77582
  throw new EcsTaskResolutionError(`Task '${taskLogicalId}' Volumes[${idx}] is not an object.`);
@@ -79601,7 +79880,7 @@ function reorderArgs(argv) {
79601
79880
  }
79602
79881
  async function main() {
79603
79882
  const program = new Command18();
79604
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.84.0");
79883
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.85.0");
79605
79884
  program.addCommand(createBootstrapCommand());
79606
79885
  program.addCommand(createSynthCommand());
79607
79886
  program.addCommand(createListCommand());