@go-to-k/cdkd 0.75.1 → 0.76.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
@@ -24,6 +24,7 @@
24
24
  - **Diff calculation**: Self-implemented resource/property-level diff between desired template and current state
25
25
  - **S3-based state management**: No DynamoDB required, uses S3 conditional writes for locking
26
26
  - **DAG-based parallelization**: Analyze `Ref`/`Fn::GetAtt` dependencies and execute in parallel
27
+ - **Rollback on failure**: When a deploy errors mid-stack, cdkd rolls back the resources it just created so the stack state stays consistent (CloudFormation parity — but cdkd does this without round-tripping through CFn). Pass `cdkd deploy --no-rollback` to skip rollback and keep the partial state for Terraform-style inspection / repair. See [Rollback behavior](#rollback-behavior).
27
28
  - **`--no-wait` for async resources**: Skip the multi-minute wait on CloudFront / RDS / ElastiCache / NAT Gateway and return as soon as the create call returns (CloudFormation always blocks)
28
29
  - **VPC route DependsOn relaxation (on by default)**: Drop CDK-injected defensive `DependsOn` edges from VPC Lambdas onto private-subnet routes so `CloudFront::Distribution` and `Lambda::Url` start their ~3-min propagation in parallel with NAT Gateway stabilization (~50% faster on VPC + Lambda + CloudFront stacks). Pass `--no-aggressive-vpc-parallel` to opt out.
29
30
 
@@ -161,7 +162,7 @@ the full per-type table.
161
162
  | Asset publishing (ECR) | ✅ | Self-implemented Docker image publishing |
162
163
  | Custom Resources (SNS-backed) | ✅ | SNS Topic ServiceToken + S3 response |
163
164
  | Custom Resources (CDK Provider) | ✅ | isCompleteHandler/onEventHandler async pattern detection |
164
- | Rollback | ✅ | --no-rollback flag to skip |
165
+ | Rollback | ✅ | Auto-rollback on mid-deploy failure (deletes already-completed resources to keep state consistent); `--no-rollback` skips for Terraform-style failed-state inspection. See [Rollback behavior](#rollback-behavior) below. |
165
166
  | DeletionPolicy: Retain | ✅ | Skip deletion for retained resources |
166
167
  | UpdateReplacePolicy: Retain | ✅ | Keep old resource on replacement |
167
168
  | Implicit delete dependencies | ✅ | VPC/IGW/EventBus/Subnet/RouteTable ordering |
@@ -180,33 +181,15 @@ the full per-type table.
180
181
 
181
182
  ## Installation
182
183
 
183
- ### From npm
184
-
185
184
  ```bash
186
185
  npm i -g @go-to-k/cdkd # latest release
187
186
  npm i -g @go-to-k/cdkd@0.0.2 # pin to a specific version
188
187
  ```
189
188
 
190
- The installed binary is `cdkd` — run it the same way in either install path.
189
+ The installed binary is `cdkd`.
191
190
 
192
191
  > cdkd is an experimental / educational project and is not intended for production use — see the warning at the top of this README. Pin to a specific version if you need reproducible installs.
193
192
 
194
- ### From source
195
-
196
- ```bash
197
- git clone https://github.com/go-to-k/cdkd.git
198
- cd cdkd
199
- pnpm install
200
- pnpm run build
201
- npm link
202
- ```
203
-
204
- If `cdkd` is not found after `npm link`, set an alias in the current shell:
205
-
206
- ```bash
207
- alias cdkd="node $(pwd)/dist/cli.js"
208
- ```
209
-
210
193
  ## Quick Start
211
194
 
212
195
  > **First-time setup**: cdkd requires a one-time `cdkd bootstrap` per AWS
@@ -405,6 +388,27 @@ cdkd state destroy --all -y # every stack in the bucket
405
388
  cdkd state destroy MyStack --region us-east-1
406
389
  ```
407
390
 
391
+ ## Rollback behavior
392
+
393
+ When a deploy fails mid-stack (e.g. a resource hits a validation error
394
+ or AWS rejects the request), cdkd by default **rolls back the
395
+ already-completed resources in the same deploy** so the stack state
396
+ stays consistent — every resource cdkd just created in this run is
397
+ deleted in reverse dependency order, the state record is updated to
398
+ match, and the CLI exits non-zero. Resources that existed before this
399
+ deploy are NOT touched.
400
+
401
+ Pass `cdkd deploy --no-rollback` to skip the rollback (Terraform-style:
402
+ the partial state is preserved so you can `cdkd state show <stack>`,
403
+ inspect what landed, fix the underlying issue, and re-run `cdkd deploy`
404
+ to continue from the half-deployed state). Recommended only when you
405
+ plan to manually inspect / repair; the default is safer for CI.
406
+
407
+ Mid-deploy state is also saved per-resource as work completes, so even
408
+ if cdkd itself crashes between the failure and the rollback, the state
409
+ file accurately reflects what's on AWS and a follow-up `cdkd destroy`
410
+ won't orphan anything.
411
+
408
412
  ## `--no-wait`: skip async-resource waits
409
413
 
410
414
  CloudFront / RDS / ElastiCache / NAT Gateway typically take 1–15
@@ -493,28 +497,16 @@ Two `orphan` variants at different granularities:
493
497
  Both `cdkd destroy` (synth-driven) and `cdkd state destroy`
494
498
  (state-driven, no synth) delete AWS resources + state.
495
499
 
496
- ## Stack termination protection
497
-
498
- CDK's `new Stack(app, 'X', { terminationProtection: true })` is
499
- honored by `cdkd destroy` and `cdkd destroy --all`. A protected
500
- stack is refused before the lock is acquired and before any
501
- per-resource delete runs; in `--all` runs sibling unprotected
502
- stacks still destroy and the protected ones contribute to the
503
- partial-failure exit code 2.
500
+ ## `--remove-protection`: one-shot bypass for protected resources
504
501
 
505
- Bypass workflow:
506
-
507
- 1. Edit the CDK code to set `terminationProtection: false`.
508
- 2. Redeploy: `cdkd deploy MyStack`.
509
- 3. Retry: `cdkd destroy MyStack`.
510
-
511
- `cdkd state destroy` (state-only, no synth) does **not** honor
512
- `terminationProtection` — the flag is a CDK property surfaced via
513
- synth and is not stored in cdkd's state.json. Use `cdkd destroy`
514
- when synth is available, or accept that `state destroy` is the
502
+ CDK's `new Stack(app, 'X', { terminationProtection: true })` is honored
503
+ by `cdkd destroy` (refused before any per-resource delete). The
504
+ state-only path `cdkd state destroy` does NOT honor it — that's the
515
505
  explicit "I know what I'm doing, ignore CDK guards" escape hatch.
516
506
 
517
- ### `--remove-protection`: one-shot bypass for protected resources
507
+ For resource-level protection (`DeletionProtection` etc.), the standard
508
+ workflow is edit CDK → redeploy → destroy. `--remove-protection` is the
509
+ one-shot bypass:
518
510
 
519
511
  `cdkd destroy --remove-protection` and `cdkd state destroy
520
512
  --remove-protection` flip every protection flag off in-place
@@ -538,25 +530,14 @@ types:
538
530
  | `AWS::Cognito::UserPool` | `DeletionProtection` (`ACTIVE` / `INACTIVE`) |
539
531
  | `AWS::AutoScaling::AutoScalingGroup` | `DeletionProtection` (`none` / `prevent-force-deletion` / `prevent-all-deletion`) — flag also sets `ForceDelete: true` so AWS terminates running instances as part of the delete |
540
532
 
541
- The flip-off call is idempotent providers always issue it when
542
- the flag is set, regardless of whether the resource currently has
543
- protection on. This is per-PR-level: a single `--remove-protection`
544
- covers every protection-bearing type listed above; there is no
545
- per-type variant.
546
-
547
- The interactive confirmation prompt is updated when the flag is
548
- set: `About to destroy N resources from stack "X", REMOVING
549
- DELETION PROTECTION on K of them. Continue? (y/N)`. The default
550
- flips from `Y/n` to `y/N` so the destructive bypass requires an
551
- explicit `y` / `yes`. `--yes` / `-y` / `-f` skips the prompt.
552
-
553
- Other protected resource types (CloudFront Distributions, Lambda
554
- function reserved concurrency, S3 bucket retention, etc.) are
555
- out of scope — the flag list is curated to the cases where AWS
556
- exposes a synchronous "flip protection off" API call.
533
+ A single `--remove-protection` covers every type listed above (no
534
+ per-type variant). The interactive confirm prompt switches to
535
+ `y/N` (requiring an explicit `y` for the destructive bypass);
536
+ `--yes` / `-y` / `-f` skips it.
557
537
 
558
- `cdkd diff` (read-only) and `cdkd deploy` (forward-only) are
559
- unaffected only destroy is gated.
538
+ Out of scope: types where AWS doesn't expose a synchronous "flip
539
+ protection off" API call (CloudFront Distributions, Lambda function
540
+ reserved concurrency, S3 bucket retention, etc.).
560
541
 
561
542
  ## `publish-assets`: synth + build + publish, no deploy
562
543
 
@@ -584,8 +565,8 @@ but reusing cdkd's synthesis / asset / construct-path plumbing — no
584
565
  `template.yaml` to maintain, no `cdk synth | sam ...` round-trip.
585
566
 
586
567
  Requires Docker. v1 supports Node.js and Python runtimes (`nodejs18.x` /
587
- `nodejs20.x` / `nodejs22.x` / `python3.11` / `python3.12` / `python3.13`);
588
- other runtimes follow in subsequent PRs.
568
+ `nodejs20.x` / `nodejs22.x` / `nodejs24.x` / `python3.11` / `python3.12` /
569
+ `python3.13` / `python3.14`); other runtimes follow in subsequent PRs.
589
570
 
590
571
  **Container Lambdas (PR 5 of #224)** — `lambda.DockerImageFunction(...)` /
591
572
  `Code.ImageUri` is supported alongside ZIP Lambdas. cdkd reads the
package/dist/cli.js CHANGED
@@ -70567,9 +70567,11 @@ var SUPPORTED_RUNTIMES = {
70567
70567
  "nodejs18.x": { image: "public.ecr.aws/lambda/nodejs:18", fileExtension: ".js" },
70568
70568
  "nodejs20.x": { image: "public.ecr.aws/lambda/nodejs:20", fileExtension: ".js" },
70569
70569
  "nodejs22.x": { image: "public.ecr.aws/lambda/nodejs:22", fileExtension: ".js" },
70570
+ "nodejs24.x": { image: "public.ecr.aws/lambda/nodejs:24", fileExtension: ".js" },
70570
70571
  "python3.11": { image: "public.ecr.aws/lambda/python:3.11", fileExtension: ".py" },
70571
70572
  "python3.12": { image: "public.ecr.aws/lambda/python:3.12", fileExtension: ".py" },
70572
- "python3.13": { image: "public.ecr.aws/lambda/python:3.13", fileExtension: ".py" }
70573
+ "python3.13": { image: "public.ecr.aws/lambda/python:3.13", fileExtension: ".py" },
70574
+ "python3.14": { image: "public.ecr.aws/lambda/python:3.14", fileExtension: ".py" }
70573
70575
  };
70574
70576
  var UnsupportedRuntimeError = class _UnsupportedRuntimeError extends Error {
70575
70577
  constructor(runtime, message) {
@@ -70598,12 +70600,12 @@ function resolveRuntimeSpec(runtime) {
70598
70600
  if (runtime.startsWith("java") || runtime.startsWith("dotnet") || runtime.startsWith("ruby") || runtime.startsWith("go") || runtime.startsWith("provided")) {
70599
70601
  throw new UnsupportedRuntimeError(
70600
70602
  runtime,
70601
- `Runtime '${runtime}' is not supported in cdkd local invoke v1. Only Node.js (nodejs18.x / nodejs20.x / nodejs22.x) and Python (python3.11 / python3.12 / python3.13) runtimes are supported. Other runtimes follow in subsequent PRs.`
70603
+ `Runtime '${runtime}' is not supported in cdkd local invoke v1. Only Node.js (nodejs18.x / nodejs20.x / nodejs22.x / nodejs24.x) and Python (python3.11 / python3.12 / python3.13 / python3.14) runtimes are supported. Other runtimes follow in subsequent PRs.`
70602
70604
  );
70603
70605
  }
70604
70606
  throw new UnsupportedRuntimeError(
70605
70607
  runtime,
70606
- `Unknown runtime '${runtime}'. cdkd local invoke v1 supports nodejs18.x / nodejs20.x / nodejs22.x / python3.11 / python3.12 / python3.13.`
70608
+ `Unknown runtime '${runtime}'. cdkd local invoke v1 supports nodejs18.x / nodejs20.x / nodejs22.x / nodejs24.x / python3.11 / python3.12 / python3.13 / python3.14.`
70607
70609
  );
70608
70610
  }
70609
70611
 
@@ -73023,8 +73025,25 @@ function resourceMatches(pattern, methodArn) {
73023
73025
  return true;
73024
73026
  if (!pattern.includes("*") && !pattern.includes("?"))
73025
73027
  return false;
73026
- const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*").replace(/\?/g, ".");
73027
- const re = new RegExp(`^${escaped}$`);
73028
+ let regex = "";
73029
+ for (let i = 0; i < pattern.length; i++) {
73030
+ const ch = pattern[i];
73031
+ if (ch === "*") {
73032
+ if (pattern[i + 1] === "*") {
73033
+ regex += ".*";
73034
+ i++;
73035
+ } else {
73036
+ regex += "[^:/]*";
73037
+ }
73038
+ } else if (ch === "?") {
73039
+ regex += "[^:/]";
73040
+ } else if (".+^${}()|[]\\".includes(ch)) {
73041
+ regex += "\\" + ch;
73042
+ } else {
73043
+ regex += ch;
73044
+ }
73045
+ }
73046
+ const re = new RegExp(`^${regex}$`);
73028
73047
  return re.test(methodArn);
73029
73048
  }
73030
73049
  function evaluateCachedLambdaPolicy(cached, methodArn) {
@@ -73226,10 +73245,10 @@ function pickStringClaim(claims, key) {
73226
73245
  function extractBearer(header) {
73227
73246
  if (!header)
73228
73247
  return void 0;
73229
- const m = /^\s*Bearer\s+(.+)\s*$/i.exec(header);
73248
+ const m = /^\s*Bearer\s+([A-Za-z0-9._-]+)\s*$/i.exec(header);
73230
73249
  if (!m)
73231
73250
  return void 0;
73232
- return m[1].trim();
73251
+ return m[1];
73233
73252
  }
73234
73253
  function parseJwt(token) {
73235
73254
  const parts = token.split(".");
@@ -77011,7 +77030,7 @@ function reorderArgs(argv) {
77011
77030
  }
77012
77031
  async function main() {
77013
77032
  const program = new Command16();
77014
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.75.1");
77033
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.76.0");
77015
77034
  program.addCommand(createBootstrapCommand());
77016
77035
  program.addCommand(createSynthCommand());
77017
77036
  program.addCommand(createListCommand());