@go-to-k/cdkd 0.94.14 → 0.95.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 +79 -261
- package/dist/cli.js +81 -23
- package/dist/cli.js.map +1 -1
- package/dist/go-to-k-cdkd-0.95.0.tgz +0 -0
- package/package.json +1 -1
- package/dist/go-to-k-cdkd-0.94.14.tgz +0 -0
package/README.md
CHANGED
|
@@ -160,12 +160,20 @@ cdkd has three command families:
|
|
|
160
160
|
use them to inspect / clean up state when the source is gone or
|
|
161
161
|
you don't want to synth. `cdkd state destroy` is the CDK-app-free
|
|
162
162
|
counterpart of `cdkd destroy`.
|
|
163
|
-
- **`cdkd local ...` subcommands** (`local invoke`, `local start-api
|
|
164
|
-
run synthesized
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
163
|
+
- **`cdkd local ...` subcommands** (`local invoke`, `local start-api`,
|
|
164
|
+
`local run-task`) run synthesized workloads locally inside Docker
|
|
165
|
+
containers. The Lambda variants (`local invoke` / `local start-api`)
|
|
166
|
+
bundle the AWS Lambda Runtime Interface Emulator (RIE); `local invoke`
|
|
167
|
+
runs a single Lambda once, and `local start-api` stands up a
|
|
168
|
+
long-running HTTP server that maps API Gateway / HTTP API / Function
|
|
169
|
+
URL routes to local Lambda invocations. `local run-task` is the ECS
|
|
170
|
+
counterpart — it locates an `AWS::ECS::TaskDefinition` from the
|
|
171
|
+
synthesized template and stands up every container in `dependsOn`
|
|
172
|
+
order on a per-task docker network with the AWS-published metadata
|
|
173
|
+
endpoints sidecar, so containers see `ECS_CONTAINER_METADATA_URI_V4`
|
|
174
|
+
(and optionally task-role creds via `--assume-task-role`) just like
|
|
175
|
+
they would on Fargate / ECS. No AWS API calls beyond optional STS /
|
|
176
|
+
Secrets resolution, no state bucket needed.
|
|
169
177
|
|
|
170
178
|
Options like `--app`, `--state-bucket`, and `--context` can be omitted if configured via `cdk.json` or environment variables (`CDKD_APP`, `CDKD_STATE_BUCKET`).
|
|
171
179
|
|
|
@@ -376,6 +384,71 @@ Lambda-ServiceToken Active wait).
|
|
|
376
384
|
See [docs/cli-reference.md](docs/cli-reference.md) for the full
|
|
377
385
|
type-pair allowlist and trade-off notes.
|
|
378
386
|
|
|
387
|
+
## Local execution
|
|
388
|
+
|
|
389
|
+
The `cdkd local` family runs AWS workloads on the developer's machine
|
|
390
|
+
via Docker — Lambda functions, API Gateway routes, and ECS tasks —
|
|
391
|
+
without an AWS deploy. Modeled on `sam local *` but reuses cdkd's
|
|
392
|
+
synthesis / asset / construct-path plumbing — no `template.yaml` to
|
|
393
|
+
maintain, no `cdk synth | sam ...` round-trip.
|
|
394
|
+
|
|
395
|
+
| Subcommand | Emulates |
|
|
396
|
+
| --- | --- |
|
|
397
|
+
| `cdkd local invoke <target>` | One-shot Lambda invoke via the AWS Lambda Runtime Interface Emulator (RIE) |
|
|
398
|
+
| `cdkd local start-api` | Long-running HTTP server for REST v1 / HTTP API / Function URL routes |
|
|
399
|
+
| `cdkd local run-task <target>` | ECS RunTask — every container in a task definition started on a per-task docker network |
|
|
400
|
+
|
|
401
|
+
Requires Docker. Pass `--from-state` to substitute deployed physical
|
|
402
|
+
IDs into intrinsic-valued properties; without it, intrinsic values are
|
|
403
|
+
dropped with a per-key warning (matches `sam local *` semantics).
|
|
404
|
+
|
|
405
|
+
### `local invoke`
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
cdkd local invoke MyStack/MyApi/Handler # one-shot invoke
|
|
409
|
+
cdkd local invoke MyStack/Handler --event events/get.json
|
|
410
|
+
cdkd local invoke MyStack/Handler --from-state # recover deployed env vars
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
Supports every current AWS Lambda runtime (Node.js / Python / Ruby /
|
|
414
|
+
Java / .NET / `provided.al2023`), container Lambdas
|
|
415
|
+
(`DockerImageFunction` / `Code.ImageUri`) via local-build or ECR pull,
|
|
416
|
+
and same-stack Lambda Layers bind-mounted at `/opt`.
|
|
417
|
+
|
|
418
|
+
### `local start-api`
|
|
419
|
+
|
|
420
|
+
```bash
|
|
421
|
+
cdkd local start-api # one HTTP server per discovered API
|
|
422
|
+
cdkd local start-api --port 3000 # pin the first server's port
|
|
423
|
+
cdkd local start-api --api MyHttpApi # filter by logical id
|
|
424
|
+
cdkd local start-api --api MyStack/MyHttpApi # OR: CDK Construct path
|
|
425
|
+
cdkd local start-api --warm --watch # pre-start + hot reload
|
|
426
|
+
```
|
|
427
|
+
|
|
428
|
+
One server per discovered API — authorizers, CORS configs, and stage
|
|
429
|
+
variables stay scoped to the owning API. Supports REST v1 + HTTP API +
|
|
430
|
+
Function URL with AWS_PROXY integrations; Lambda TOKEN / REQUEST,
|
|
431
|
+
Cognito User Pool, and HTTP v2 JWT authorizers (JWKS-verified); CORS
|
|
432
|
+
preflight; hot reload via `--watch`.
|
|
433
|
+
|
|
434
|
+
### `local run-task`
|
|
435
|
+
|
|
436
|
+
```bash
|
|
437
|
+
cdkd local run-task MyStack/MyService/TaskDef
|
|
438
|
+
cdkd local run-task MyTaskDef --from-state # resolve deployed secrets / env intrinsics
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
Starts every container in the task definition on a per-task docker
|
|
442
|
+
network with the AWS-published ECS metadata sidecar
|
|
443
|
+
(`amazon/amazon-ecs-local-container-endpoints`). `DependsOn` /
|
|
444
|
+
`Secrets` / `Volumes` (Host / Docker) are honored; `Secrets[].ValueFrom`
|
|
445
|
+
is resolved from SecretsManager / SSM at startup.
|
|
446
|
+
|
|
447
|
+
See [docs/local-emulation.md](docs/local-emulation.md) for the full
|
|
448
|
+
reference — supported runtimes, target resolution, every flag, exit
|
|
449
|
+
codes, route precedence, container-pool semantics, networking model,
|
|
450
|
+
v1 scope notes.
|
|
451
|
+
|
|
379
452
|
## Importing existing resources
|
|
380
453
|
|
|
381
454
|
`cdkd import` adopts AWS resources that are already deployed (via
|
|
@@ -456,85 +529,6 @@ Two `orphan` variants at different granularities:
|
|
|
456
529
|
Both `cdkd destroy` (synth-driven) and `cdkd state destroy`
|
|
457
530
|
(state-driven, no synth) delete AWS resources + state.
|
|
458
531
|
|
|
459
|
-
## Stack-name prefix on physical names
|
|
460
|
-
|
|
461
|
-
By default cdkd creates AWS resources with the **exact name you
|
|
462
|
-
declared** in CDK code: `new iam.Role(this, 'CRRole', { roleName:
|
|
463
|
-
'my-role' })` in stack `MyStack` produces an AWS resource named
|
|
464
|
-
`my-role`. Consistent across every resource type. This is the
|
|
465
|
-
default since **v0.94.0** (closes [#299](https://github.com/go-to-k/cdkd/issues/299)).
|
|
466
|
-
|
|
467
|
-
Pre-v0.94.0 cdkd prepended the stack name to user-declared physical
|
|
468
|
-
names on a subset of types only (Pattern B providers: IAM Role /
|
|
469
|
-
User / Group / InstanceProfile / ELBv2 LoadBalancer / TargetGroup),
|
|
470
|
-
while Pattern A providers (Lambda, S3, SNS, SQS, DynamoDB, etc.) used
|
|
471
|
-
the user's name as-is. The inconsistency was opaque to users and
|
|
472
|
-
surfaced as failures in `cdkd export` (CFn IMPORT identifier
|
|
473
|
-
mismatch). Flipping the default brings every resource type into line
|
|
474
|
-
out of the box.
|
|
475
|
-
|
|
476
|
-
`cdkd deploy --prefix-user-supplied-names` opts BACK in to the
|
|
477
|
-
legacy prefixing on Pattern B providers (matching pre-v0.94.0 cdkd).
|
|
478
|
-
Useful when migrating an existing stack that was originally deployed
|
|
479
|
-
under the legacy default and you don't want to take a one-time
|
|
480
|
-
replacement on every Pattern B resource.
|
|
481
|
-
|
|
482
|
-
| | Default (no flag) | `--prefix-user-supplied-names` |
|
|
483
|
-
| --- | --- | --- |
|
|
484
|
-
| `new iam.Role({ roleName: 'my-role' })` | `my-role` | `MyStack-my-role` (legacy) |
|
|
485
|
-
| `new s3.Bucket({ bucketName: 'my-bucket' })` | `my-bucket` (always — Pattern A) | `my-bucket` (unchanged) |
|
|
486
|
-
| `new iam.Role(...)` (no `roleName`) | `MyStack-CRRole-<hash>` (auto-generated; prefix kept for uniqueness) | `MyStack-CRRole-<hash>` (unchanged) |
|
|
487
|
-
|
|
488
|
-
Resolution chain (highest wins): `--prefix-user-supplied-names`
|
|
489
|
-
CLI flag → `CDKD_PREFIX_USER_SUPPLIED_NAMES=true` env var →
|
|
490
|
-
`cdk.json` `context.cdkd.prefixUserSuppliedNames: true` → default
|
|
491
|
-
`false` (skip prefix).
|
|
492
|
-
|
|
493
|
-
The deprecated `--no-prefix-user-supplied-names` flag (plus the
|
|
494
|
-
`CDKD_NO_PREFIX_USER_SUPPLIED_NAMES` env var and `cdk.json
|
|
495
|
-
context.cdkd.noPrefixUserSuppliedNames`) is still accepted but now
|
|
496
|
-
matches the default; setting it emits a deprecation warning and is a
|
|
497
|
-
no-op. Remove it from your CLI invocations and config.
|
|
498
|
-
|
|
499
|
-
### Migration from pre-v0.94.0
|
|
500
|
-
|
|
501
|
-
This is a **breaking change**: upgrading from a pre-v0.94.0 cdkd to
|
|
502
|
-
v0.94.0+ flips the AWS-resource name cdkd produces on Pattern B
|
|
503
|
-
providers (IAM Role / User / Group / InstanceProfile / ELBv2 LB / TG)
|
|
504
|
-
with user-supplied physical names. The next `cdkd deploy` against an
|
|
505
|
-
existing stack will propose REPLACEMENT on every such resource —
|
|
506
|
-
the AWS resource has the prefixed name; the new template intent has
|
|
507
|
-
the un-prefixed name.
|
|
508
|
-
|
|
509
|
-
Pick one of:
|
|
510
|
-
|
|
511
|
-
1. **Accept the one-time replacement** (simplest; only safe when the
|
|
512
|
-
types involved tolerate replacement — IAM Roles get fresh ARNs,
|
|
513
|
-
ELBv2 LBs get fresh DNS names).
|
|
514
|
-
2. **Pin legacy prefixing**: pass `--prefix-user-supplied-names`,
|
|
515
|
-
set `CDKD_PREFIX_USER_SUPPLIED_NAMES=true`, or add
|
|
516
|
-
`"prefixUserSuppliedNames": true` under `cdk.json` `context.cdkd`.
|
|
517
|
-
3. **Drop the explicit physical name** in CDK code where you don't
|
|
518
|
-
actually need a stable name — `new iam.Role(...)` without
|
|
519
|
-
`roleName` always uses the auto-generated `MyStack-CRRole-<hash>`
|
|
520
|
-
form regardless of this flag.
|
|
521
|
-
|
|
522
|
-
A migration helper (`cdkd state rename-strip-prefix <stack>`) that
|
|
523
|
-
would let an existing stack adopt the new default without replacement
|
|
524
|
-
is tracked separately in [#300](https://github.com/go-to-k/cdkd/issues/300).
|
|
525
|
-
|
|
526
|
-
### Effect on `cdkd export`
|
|
527
|
-
|
|
528
|
-
[PR #285 `cdkd export`](https://github.com/go-to-k/cdkd/pull/285)
|
|
529
|
-
surfaced the pre-v0.94.0 inconsistency: the CFn IMPORT changeset's
|
|
530
|
-
identifier check would fail on a synth `RoleName: 'my-role'` vs the
|
|
531
|
-
AWS-deployed `MyStack-my-role`, so the export command overlays
|
|
532
|
-
`ResourceIdentifier` onto `Properties` to bridge the gap. The
|
|
533
|
-
overlay is still needed for stacks deployed under the legacy default
|
|
534
|
-
(or with `--prefix-user-supplied-names`); a fresh stack deployed
|
|
535
|
-
under the v0.94.0 default has matching names and the overlay is a
|
|
536
|
-
no-op for it.
|
|
537
|
-
|
|
538
532
|
## `--remove-protection`: one-shot bypass for protected resources
|
|
539
533
|
|
|
540
534
|
CDK's `new Stack(app, 'X', { terminationProtection: true })` is honored
|
|
@@ -594,182 +588,6 @@ cdkd publish-assets -a cdk.out # skip synth, use pre-synthesized assembly
|
|
|
594
588
|
See [docs/cli-reference.md](docs/cli-reference.md#publish-assets-synth--build--publish-no-deploy)
|
|
595
589
|
for stack-selection rules and concurrency knobs.
|
|
596
590
|
|
|
597
|
-
## `local invoke`: run Lambda functions locally
|
|
598
|
-
|
|
599
|
-
`cdkd local invoke <target>` runs a Lambda function from a CDK app on the
|
|
600
|
-
developer's machine, inside a Docker container that bundles the AWS
|
|
601
|
-
Lambda Runtime Interface Emulator (RIE). Modeled on `sam local invoke`
|
|
602
|
-
but reusing cdkd's synthesis / asset / construct-path plumbing — no
|
|
603
|
-
`template.yaml` to maintain, no `cdk synth | sam ...` round-trip.
|
|
604
|
-
|
|
605
|
-
Requires Docker. Supports every current AWS Lambda runtime
|
|
606
|
-
(`nodejs18.x` / `nodejs20.x` / `nodejs22.x` / `nodejs24.x` / `python3.11` /
|
|
607
|
-
`python3.12` / `python3.13` / `python3.14` / `ruby3.2` / `ruby3.3` /
|
|
608
|
-
`java8.al2` / `java11` / `java17` / `java21` / `dotnet6` / `dotnet8` /
|
|
609
|
-
`provided.al2` / `provided.al2023`). The deprecated `go1.x` runtime is
|
|
610
|
-
rejected with a migration pointer to `provided.al2023`. Java, .NET, and
|
|
611
|
-
`provided.*` Lambdas are **asset-backed only** — the Handler shape names
|
|
612
|
-
a compiled artifact (`package.Class::method` for Java's JVM class;
|
|
613
|
-
`Assembly::Namespace.Class::Method` for .NET's CLR assembly; an
|
|
614
|
-
arbitrary `bootstrap` binary for the OS-only `provided.*` runtimes), so
|
|
615
|
-
use `lambda.Code.fromAsset(<dir>)` with a directory containing the
|
|
616
|
-
compiled output (`.class` hierarchy / `.jar` / `.dll` / native binary);
|
|
617
|
-
inline `Code.ZipFile` is rejected with a clear routing message.
|
|
618
|
-
|
|
619
|
-
**Container Lambdas** — `lambda.DockerImageFunction(...)` /
|
|
620
|
-
`Code.ImageUri` is supported alongside ZIP Lambdas. cdkd reads the
|
|
621
|
-
function's local `Dockerfile` from `cdk.out` and runs `docker build`
|
|
622
|
-
locally before invoking. When no asset matches (typically: invoking a
|
|
623
|
-
stack deployed elsewhere), cdkd falls back to `docker pull` from
|
|
624
|
-
ECR — same-account / same-region only in v1; cross-account /
|
|
625
|
-
cross-region is not yet supported. `Architectures: [x86_64]` /
|
|
626
|
-
`[arm64]` are honored via `--platform` so an arm64 host running an
|
|
627
|
-
x86_64 Lambda doesn't hit emulation.
|
|
628
|
-
|
|
629
|
-
```bash
|
|
630
|
-
# Invoke by CDK display path (single-stack apps may omit the prefix)
|
|
631
|
-
cdkd local invoke MyStack/MyApi/Handler
|
|
632
|
-
cdkd local invoke MyStack:MyApiHandler1234ABCD # logical-id form
|
|
633
|
-
|
|
634
|
-
# Pass an event payload
|
|
635
|
-
cdkd local invoke MyStack/Handler --event events/get.json
|
|
636
|
-
echo '{"path":"/"}' | cdkd local invoke MyStack/Handler --event-stdin
|
|
637
|
-
|
|
638
|
-
# Override env vars (SAM-compatible shape: {"LogicalId":{"KEY":"VALUE"}}
|
|
639
|
-
# plus an optional top-level "Parameters" block applied to every invoke)
|
|
640
|
-
cdkd local invoke MyStack/Handler --env-vars env.json
|
|
641
|
-
|
|
642
|
-
# Skip docker pull when iterating
|
|
643
|
-
cdkd local invoke MyStack/Handler --no-pull
|
|
644
|
-
|
|
645
|
-
# Skip the local docker build for container Lambdas (Code.ImageUri).
|
|
646
|
-
# Reuses the deterministic cdkd-local-invoke-<hash> tag from a prior
|
|
647
|
-
# build. Errors clearly when the tag is missing.
|
|
648
|
-
cdkd local invoke MyStack/ContainerHandler --no-build
|
|
649
|
-
|
|
650
|
-
# Run with the deployed function's narrow execution role (otherwise the
|
|
651
|
-
# developer's shell credentials are forwarded — SAM-compatible default)
|
|
652
|
-
cdkd local invoke MyStack/Handler --assume-role arn:aws:iam::123456789012:role/MyApi-handler-role
|
|
653
|
-
|
|
654
|
-
# Attach a Node debugger
|
|
655
|
-
cdkd local invoke MyStack/Handler --debug-port 9229
|
|
656
|
-
|
|
657
|
-
# After `cdkd deploy`, recover intrinsic-valued env vars (Ref / Fn::GetAtt
|
|
658
|
-
# / Fn::Sub) from cdkd's S3 state instead of dropping them. Off by default
|
|
659
|
-
# — keeps the local-only / unscoped flow safe; opt in when you want the
|
|
660
|
-
# handler to see the deployed physical IDs (S3 bucket names, DDB table
|
|
661
|
-
# names, IAM role ARNs, ...). Disambiguate with `--stack-region <region>`
|
|
662
|
-
# when the same stack name has state in multiple regions.
|
|
663
|
-
cdkd local invoke MyStack/Handler --from-state
|
|
664
|
-
```
|
|
665
|
-
|
|
666
|
-
**Lambda Layers** — same-stack
|
|
667
|
-
`AWS::Lambda::LayerVersion` references in `Properties.Layers` are
|
|
668
|
-
resolved automatically and bind-mounted at `/opt` (read-only) inside
|
|
669
|
-
the container. Each layer's unzipped asset directory under `cdk.out/`
|
|
670
|
-
becomes one `-v <layerAssetPath>:/opt:ro` mount; multiple layers
|
|
671
|
-
stack via Docker overlay layering, and AWS's "last layer wins on
|
|
672
|
-
file collision" rule is preserved by keeping the template's input
|
|
673
|
-
order. Cross-stack / cross-account / cross-region layer ARNs (literal
|
|
674
|
-
ARN strings in `Properties.Layers`) are out of scope for v1 — cdkd
|
|
675
|
-
hard-errors with a clear pointer at the offending entry. Container
|
|
676
|
-
Lambdas (`Code.ImageUri`) silently ignore `Layers` (matches AWS:
|
|
677
|
-
container images bake layers at build time).
|
|
678
|
-
|
|
679
|
-
See [docs/cli-reference.md](docs/cli-reference.md#local-invoke-run-lambda-functions-locally)
|
|
680
|
-
for the full surface, target-resolution rules, and v1 scope notes.
|
|
681
|
-
|
|
682
|
-
## `local start-api`: long-running local API server
|
|
683
|
-
|
|
684
|
-
`cdkd local start-api` stands up a long-running local HTTP server that
|
|
685
|
-
maps the synthesized API Gateway routes (REST v1, HTTP API, Function
|
|
686
|
-
URL) to local Lambda invocations against the same RIE-backed Docker
|
|
687
|
-
containers `cdkd local invoke` uses. Modeled on `sam local start-api`
|
|
688
|
-
but reusing cdkd's synthesis / route-discovery plumbing.
|
|
689
|
-
|
|
690
|
-
```bash
|
|
691
|
-
# Auto-allocate one port PER discovered API (printed at startup)
|
|
692
|
-
cdkd local start-api
|
|
693
|
-
|
|
694
|
-
# Pin the FIRST server to port 3000; subsequent APIs get 3001, 3002, ...
|
|
695
|
-
cdkd local start-api --port 3000
|
|
696
|
-
|
|
697
|
-
# Restrict to a single API by its CDK logical id (HTTP API / REST API logical
|
|
698
|
-
# id, or the backing Lambda's logical id for Function URLs)
|
|
699
|
-
cdkd local start-api --api MyAdminApi
|
|
700
|
-
|
|
701
|
-
# Pre-warm one container per Lambda at server boot — eliminates first-request cold start
|
|
702
|
-
cdkd local start-api --warm
|
|
703
|
-
|
|
704
|
-
# Override env vars per-Lambda (SAM-shape file)
|
|
705
|
-
cdkd local start-api --env-vars env.json
|
|
706
|
-
|
|
707
|
-
# Pin the deployed execution role per Lambda (or globally with a bare ARN)
|
|
708
|
-
cdkd local start-api --assume-role MyApiHandler=arn:aws:iam::123:role/handler-role
|
|
709
|
-
|
|
710
|
-
# Hot reload — re-synth + re-discover routes when cdk.out/ or asset dirs change
|
|
711
|
-
cdkd local start-api --watch
|
|
712
|
-
|
|
713
|
-
# Select a specific API Gateway Stage (default: the first attached)
|
|
714
|
-
cdkd local start-api --stage prod
|
|
715
|
-
```
|
|
716
|
-
|
|
717
|
-
**One server per API** (since v0.81): every discovered API surface gets its
|
|
718
|
-
own HTTP server on its own port, so authorizers, CORS configs, and stage
|
|
719
|
-
variables stay scoped to the owning API and never bleed across APIs that
|
|
720
|
-
happen to share a path. `cdkd local start-api` prints one
|
|
721
|
-
`Server listening on http://<host>:<port> (<API> (<kind>))` line per
|
|
722
|
-
server at startup; pass `--api <id>` to launch only one of them.
|
|
723
|
-
|
|
724
|
-
Scope: REST v1 + HTTP API + Function URL with AWS_PROXY integrations.
|
|
725
|
-
Authorizers (Lambda TOKEN/REQUEST + Cognito User Pool + HTTP v2 JWT),
|
|
726
|
-
VPC-config Lambda warnings, CORS preflight, hot reload, and stage
|
|
727
|
-
variables are supported. WebSocket APIs are not.
|
|
728
|
-
|
|
729
|
-
**Authorizers**: `Authorization: Bearer <token>`-protected
|
|
730
|
-
routes are gated on the authorizer Lambda's response (TOKEN / REQUEST
|
|
731
|
-
authorizers, IAM-policy or HTTP v2 simple shape) or on a JWKS-based JWT
|
|
732
|
-
verification (Cognito User Pool authorizers, HTTP v2 JWT authorizers).
|
|
733
|
-
When the JWKS endpoint is unreachable from the dev machine, cdkd falls
|
|
734
|
-
back to **pass-through mode** (every JWT accepted, with a warn line at
|
|
735
|
-
startup) — local-dev-only fallback so a corporate proxy doesn't block
|
|
736
|
-
iteration. **Do NOT rely on this in any shared environment.**
|
|
737
|
-
|
|
738
|
-
**VPC-config Lambdas**: handlers with `Properties.VpcConfig`
|
|
739
|
-
still run locally, but the local container is NOT attached to the
|
|
740
|
-
deployed VPC's subnets — calls to private RDS / ElastiCache will fail.
|
|
741
|
-
cdkd warns at startup naming each affected Lambda; AWS SDK calls still
|
|
742
|
-
reach public AWS endpoints via the dev's network as usual.
|
|
743
|
-
|
|
744
|
-
**Hot reload (`--watch`)**: re-runs the synth → discover → spec-build
|
|
745
|
-
pipeline whenever `cdk.out/` or any of the routed Lambdas' asset
|
|
746
|
-
directories change. Routes added / removed / changed swap in
|
|
747
|
-
atomically without restarting the HTTP server; in-flight requests
|
|
748
|
-
complete against the old container pool while the new pool warms.
|
|
749
|
-
Synth failures are non-fatal — the previous version keeps serving and
|
|
750
|
-
a warn line names the failure. Off by default; pass `--watch` to
|
|
751
|
-
enable.
|
|
752
|
-
|
|
753
|
-
**CORS preflight**: HTTP API v2 OPTIONS preflight requests are
|
|
754
|
-
intercepted when the API has a `CorsConfiguration` block. The server
|
|
755
|
-
matches the request's `Origin` / `Access-Control-Request-Method` /
|
|
756
|
-
`Access-Control-Request-Headers` against the configured allowlist and
|
|
757
|
-
returns a `204 No Content` with the canonical `Access-Control-Allow-*`
|
|
758
|
-
headers. Preflight handling is skipped when the user has registered
|
|
759
|
-
an explicit OPTIONS method (their Lambda owns it). REST v1 CORS (Mock
|
|
760
|
-
OPTIONS method) is not auto-handled and stays out of scope; use the
|
|
761
|
-
deployed API for that case.
|
|
762
|
-
|
|
763
|
-
**Stage variables**: `event.stageVariables` is populated from the
|
|
764
|
-
selected Stage's `Variables` (REST v1) / `StageVariables` (HTTP API
|
|
765
|
-
v2) map. Default selection is the first Stage attached to each API;
|
|
766
|
-
pass `--stage <name>` to pick a Stage by `StageName`. Function URL
|
|
767
|
-
routes don't have a Stage — `event.stageVariables` stays `null`.
|
|
768
|
-
|
|
769
|
-
See [docs/cli-reference.md](docs/cli-reference.md#local-start-api-long-running-local-api-server)
|
|
770
|
-
for the full route-discovery rules, container-pool semantics, exit
|
|
771
|
-
codes, and per-authorizer-kind detection / response-shape details.
|
|
772
|
-
|
|
773
591
|
## State Management
|
|
774
592
|
|
|
775
593
|
State is stored in S3 with optimistic locking via S3 Conditional Writes
|
package/dist/cli.js
CHANGED
|
@@ -35003,7 +35003,7 @@ function discoverRoutes(stacks) {
|
|
|
35003
35003
|
routes.push(...discoverHttpApiRoute(logicalId, resource, template, stack.stackName));
|
|
35004
35004
|
break;
|
|
35005
35005
|
case "AWS::Lambda::Url":
|
|
35006
|
-
routes.push(...discoverFunctionUrl(logicalId, resource, stack.stackName));
|
|
35006
|
+
routes.push(...discoverFunctionUrl(logicalId, resource, template, stack.stackName));
|
|
35007
35007
|
break;
|
|
35008
35008
|
default: break;
|
|
35009
35009
|
}
|
|
@@ -35040,14 +35040,19 @@ function discoverRestV1Method(logicalId, resource, template, stackName) {
|
|
|
35040
35040
|
if (!restApiLogicalId) throw new Error(`${stackName}/${logicalId} (AWS::ApiGateway::Method): RestApiId must be a { Ref: '...' } reference (got ${shortJson$1(restApiId)}).`);
|
|
35041
35041
|
const resourceId = props["ResourceId"];
|
|
35042
35042
|
const path = buildRestV1Path(resourceId, restApiLogicalId, template, stackName, logicalId);
|
|
35043
|
+
const httpMethod = stringifyValue(props["HttpMethod"] ?? "ANY");
|
|
35044
|
+
const stage = pickRestV1Stage(restApiLogicalId, template);
|
|
35045
|
+
const restApiCdkPath = readApiCdkPath(restApiLogicalId, template);
|
|
35043
35046
|
return [{
|
|
35044
|
-
method:
|
|
35047
|
+
method: httpMethod,
|
|
35045
35048
|
pathPattern: path,
|
|
35046
35049
|
lambdaLogicalId,
|
|
35047
35050
|
source: "rest-v1",
|
|
35048
35051
|
apiVersion: "v1",
|
|
35049
|
-
stage
|
|
35052
|
+
stage,
|
|
35050
35053
|
apiLogicalId: restApiLogicalId,
|
|
35054
|
+
apiStackName: stackName,
|
|
35055
|
+
...restApiCdkPath !== void 0 && { apiCdkPath: restApiCdkPath },
|
|
35051
35056
|
declaredAt: `${stackName}/${logicalId}`
|
|
35052
35057
|
}];
|
|
35053
35058
|
}
|
|
@@ -35139,6 +35144,7 @@ function discoverHttpApiRoute(logicalId, resource, template, stackName) {
|
|
|
35139
35144
|
if (integrationProps["IntegrationSubtype"] !== void 0) throw new Error(`${stackName}/${logicalId} (AWS::ApiGatewayV2::Route): IntegrationSubtype '${stringifyValue(integrationProps["IntegrationSubtype"])}' is not supported (ApiGatewayV2 service integrations like SQS/EventBridge cannot run locally).`);
|
|
35140
35145
|
const lambdaLogicalId = resolveLambdaArnIntrinsic(integrationProps["IntegrationUri"], `${stackName}/${integrationLogicalId}.IntegrationUri`);
|
|
35141
35146
|
const { method, pathPattern } = parseRouteKey(routeKey);
|
|
35147
|
+
const apiCdkPath = readApiCdkPath(apiLogicalId, template);
|
|
35142
35148
|
return [{
|
|
35143
35149
|
method,
|
|
35144
35150
|
pathPattern,
|
|
@@ -35147,6 +35153,8 @@ function discoverHttpApiRoute(logicalId, resource, template, stackName) {
|
|
|
35147
35153
|
apiVersion: "v2",
|
|
35148
35154
|
stage: "$default",
|
|
35149
35155
|
apiLogicalId,
|
|
35156
|
+
apiStackName: stackName,
|
|
35157
|
+
...apiCdkPath !== void 0 && { apiCdkPath },
|
|
35150
35158
|
declaredAt: `${stackName}/${logicalId}`
|
|
35151
35159
|
}];
|
|
35152
35160
|
}
|
|
@@ -35159,23 +35167,38 @@ function discoverHttpApiRoute(logicalId, resource, template, stackName) {
|
|
|
35159
35167
|
* we cannot do locally, and RESPONSE_STREAM uses a streaming response shape
|
|
35160
35168
|
* (`InvokeWithResponseStream`) the RIE container does not implement.
|
|
35161
35169
|
*/
|
|
35162
|
-
function discoverFunctionUrl(logicalId, resource, stackName) {
|
|
35170
|
+
function discoverFunctionUrl(logicalId, resource, template, stackName) {
|
|
35163
35171
|
const props = resource.Properties ?? {};
|
|
35164
35172
|
const authType = props["AuthType"];
|
|
35165
35173
|
if (authType !== "NONE") throw new Error(`${stackName}/${logicalId} (AWS::Lambda::Url): AuthType '${String(authType)}' is not supported (only NONE — IAM auth requires SigV4 verification cdkd cannot emulate locally; deferred follow-up PR).`);
|
|
35166
35174
|
if (props["InvokeMode"] === "RESPONSE_STREAM") throw new Error(`${stackName}/${logicalId} (AWS::Lambda::Url): InvokeMode RESPONSE_STREAM is not supported (deferred follow-up PR).`);
|
|
35167
35175
|
const targetArn = props["TargetFunctionArn"];
|
|
35176
|
+
const lambdaLogicalId = resolveLambdaArnIntrinsic(targetArn, `${stackName}/${logicalId}.TargetFunctionArn`);
|
|
35177
|
+
const lambdaCdkPath = readApiCdkPath(lambdaLogicalId, template);
|
|
35168
35178
|
return [{
|
|
35169
35179
|
method: "ANY",
|
|
35170
35180
|
pathPattern: "/{proxy+}",
|
|
35171
|
-
lambdaLogicalId
|
|
35181
|
+
lambdaLogicalId,
|
|
35172
35182
|
source: "function-url",
|
|
35173
35183
|
apiVersion: "v2",
|
|
35174
35184
|
stage: "$default",
|
|
35185
|
+
apiStackName: stackName,
|
|
35186
|
+
...lambdaCdkPath !== void 0 && { apiCdkPath: lambdaCdkPath },
|
|
35175
35187
|
declaredAt: `${stackName}/${logicalId}`
|
|
35176
35188
|
}];
|
|
35177
35189
|
}
|
|
35178
35190
|
/**
|
|
35191
|
+
* Read the `aws:cdk:path` metadata of the resource at `logicalId`,
|
|
35192
|
+
* returning the empty string when the resource is missing or the
|
|
35193
|
+
* metadata isn't set. Hides the "may be missing for a hand-rolled
|
|
35194
|
+
* `cfn.Resource`" branch from every call site.
|
|
35195
|
+
*/
|
|
35196
|
+
function readApiCdkPath(logicalId, template) {
|
|
35197
|
+
const resource = template.Resources?.[logicalId];
|
|
35198
|
+
if (!resource) return void 0;
|
|
35199
|
+
return readCdkPath(resource) || void 0;
|
|
35200
|
+
}
|
|
35201
|
+
/**
|
|
35179
35202
|
* Local intrinsic resolver for `IntegrationUri` (and the equivalent
|
|
35180
35203
|
* `Uri` field on REST v1 Method.Integration). Delegates to the shared
|
|
35181
35204
|
* `resolveLambdaArnIntrinsic` in `intrinsic-lambda-arn.ts` (extracted in
|
|
@@ -37901,39 +37924,74 @@ function groupRoutesByServer(routes) {
|
|
|
37901
37924
|
/**
|
|
37902
37925
|
* Filter the route list to a single API by user-supplied identifier.
|
|
37903
37926
|
*
|
|
37904
|
-
*
|
|
37905
|
-
*
|
|
37906
|
-
*
|
|
37907
|
-
*
|
|
37908
|
-
*
|
|
37909
|
-
*
|
|
37910
|
-
*
|
|
37927
|
+
* Accepts four input forms — matches the rest of the `cdkd local *`
|
|
37928
|
+
* target-resolution family (`local invoke <target>` /
|
|
37929
|
+
* `local run-task <target>`) for consistency:
|
|
37930
|
+
*
|
|
37931
|
+
* 1. **Bare logical id** (`MyHttpApi`) — exact match on the parent
|
|
37932
|
+
* API's logical id, or on the backing Lambda's logical id for
|
|
37933
|
+
* Function URLs.
|
|
37934
|
+
* 2. **Stack-qualified logical id** (`MyStack:MyHttpApi`) — exact
|
|
37935
|
+
* match on `<stackName>:<logicalId>`. Useful in multi-stack apps
|
|
37936
|
+
* where the same bare logical id appears in two stacks.
|
|
37937
|
+
* 3. **CDK Construct path / display path** (`MyStack/MyHttpApi`) —
|
|
37938
|
+
* exact match on the resource's `aws:cdk:path` metadata.
|
|
37939
|
+
* 4. **CDK Construct path prefix** — when the input is a strict
|
|
37940
|
+
* ancestor of the resource's `aws:cdk:path` (i.e.
|
|
37941
|
+
* `cdkPath.startsWith(input + '/')`). Mirrors the prefix rule
|
|
37942
|
+
* `cdkd orphan` uses so an L2 wrapper path resolves to its L1
|
|
37943
|
+
* child (`MyStack/MyHttpApi` matches `MyStack/MyHttpApi/Resource`).
|
|
37944
|
+
*
|
|
37945
|
+
* Routes discovered before this field set was added (or routes where
|
|
37946
|
+
* the synthesized template doesn't carry `aws:cdk:path` metadata —
|
|
37947
|
+
* e.g. hand-rolled `cfn.Resource` defs) silently fall through to the
|
|
37948
|
+
* bare-logical-id-only path so the change is non-breaking.
|
|
37911
37949
|
*
|
|
37912
37950
|
* Returns an empty array when no route matches — the caller is
|
|
37913
37951
|
* responsible for surfacing a "no API matched" error with the list of
|
|
37914
37952
|
* available identifiers (see {@link availableApiIdentifiers}).
|
|
37915
37953
|
*/
|
|
37916
37954
|
function filterRoutesByApiIdentifier(routes, identifier) {
|
|
37917
|
-
return routes.filter((rwa) =>
|
|
37918
|
-
|
|
37919
|
-
|
|
37920
|
-
|
|
37921
|
-
|
|
37955
|
+
return routes.filter((rwa) => routeMatchesIdentifier(rwa.route, identifier));
|
|
37956
|
+
}
|
|
37957
|
+
/**
|
|
37958
|
+
* Predicate behind {@link filterRoutesByApiIdentifier} and
|
|
37959
|
+
* {@link availableApiIdentifiers}'s primary-form selection. Exported
|
|
37960
|
+
* for test coverage only — the production code path goes through
|
|
37961
|
+
* `filterRoutesByApiIdentifier`.
|
|
37962
|
+
*/
|
|
37963
|
+
function routeMatchesIdentifier(route, identifier) {
|
|
37964
|
+
const bareId = route.source === "function-url" ? route.lambdaLogicalId : route.apiLogicalId;
|
|
37965
|
+
if (bareId && bareId === identifier) return true;
|
|
37966
|
+
if (route.apiStackName) {
|
|
37967
|
+
if (bareId && identifier === `${route.apiStackName}:${bareId}`) return true;
|
|
37968
|
+
}
|
|
37969
|
+
if (route.apiCdkPath) {
|
|
37970
|
+
if (identifier === route.apiCdkPath) return true;
|
|
37971
|
+
if (route.apiCdkPath.startsWith(`${identifier}/`)) return true;
|
|
37972
|
+
}
|
|
37973
|
+
return false;
|
|
37922
37974
|
}
|
|
37923
37975
|
/**
|
|
37924
37976
|
* Enumerate every distinct API identifier in the route list, in the
|
|
37925
37977
|
* order they were discovered. Useful for the "available APIs" error
|
|
37926
37978
|
* message when `--api <id>` doesn't match.
|
|
37979
|
+
*
|
|
37980
|
+
* Returns the **primary form** per API (CDK Construct path when
|
|
37981
|
+
* available, else bare logical id) — the "available identifiers" hint
|
|
37982
|
+
* stays compact while pointing users at the form most likely to round-
|
|
37983
|
+
* trip across rename refactors.
|
|
37927
37984
|
*/
|
|
37928
37985
|
function availableApiIdentifiers(routes) {
|
|
37929
37986
|
const seen = /* @__PURE__ */ new Set();
|
|
37930
37987
|
const out = [];
|
|
37931
37988
|
for (const rwa of routes) {
|
|
37932
37989
|
const r = rwa.route;
|
|
37933
|
-
const
|
|
37934
|
-
|
|
37935
|
-
|
|
37936
|
-
|
|
37990
|
+
const bareId = r.source === "function-url" ? r.lambdaLogicalId : r.apiLogicalId ?? "<unknown>";
|
|
37991
|
+
const primary = r.apiCdkPath ?? bareId;
|
|
37992
|
+
if (!seen.has(primary)) {
|
|
37993
|
+
seen.add(primary);
|
|
37994
|
+
out.push(primary);
|
|
37937
37995
|
}
|
|
37938
37996
|
}
|
|
37939
37997
|
return out;
|
|
@@ -38932,7 +38990,7 @@ function parseDebugPort(raw) {
|
|
|
38932
38990
|
* Builder for the `start-api` subcommand. Wired up by `local.ts`.
|
|
38933
38991
|
*/
|
|
38934
38992
|
function createLocalStartApiCommand() {
|
|
38935
|
-
const startApi = new Command("start-api").description("Run a long-running local HTTP server that maps API Gateway routes (REST v1, HTTP API, Function URL) to Lambda invocations against the AWS Lambda Runtime Interface Emulator (Docker required). Supports Lambda TOKEN/REQUEST authorizers and Cognito User Pool / HTTP v2 JWT authorizers; when JWKS is unreachable, JWT authorizers fall back to pass-through (every token accepted) with a warn line — local dev fallback. VPC-config Lambdas run locally and surface a warn line at startup; their containers do NOT get attached to the deployed VPC subnets, so calls to private RDS / ElastiCache will fail.").addOption(new Option("--port <port>", "HTTP server port (default: auto-allocate)").default("0")).addOption(new Option("--host <host>", "Bind address").default("127.0.0.1")).addOption(new Option("--stack <name>", "Stack to start (single-stack apps auto-detect)")).addOption(new Option("--warm", "Pre-start one container per Lambda at server boot").default(false)).addOption(new Option("--per-lambda-concurrency <n>", "Pool size cap per Lambda (default 2, max 4)").default("2")).addOption(new Option("--no-pull", "Skip docker pull (cached image)")).addOption(new Option("--container-host <host>", "IP the host uses to bind/probe the RIE port (must be a numeric IP — `docker run -p <ip>:<port>:8080` rejects hostnames). Defaults to 127.0.0.1.").default("127.0.0.1")).addOption(new Option("--debug-port-base <port>", "Reserve a contiguous --debug-port range (one per Lambda)")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}, \"Parameters\": {...}})")).addOption(new Option("--assume-role <arn-or-pair>", "Assume the Lambda's execution role and forward STS-issued temp creds. Bare <arn> = global default; <LogicalId>=<arn> = per-Lambda override (repeatable). Per-Lambda > global > unset (developer creds passed through).").argParser((raw, prev) => parseAssumeRoleToken(raw, prev))).addOption(new Option("--watch", "Hot-reload: re-synth + re-discover routes when cdk.out/ or asset directories change. Off by default; the server keeps the previous version serving when synth fails mid-reload.").default(false)).addOption(new Option("--stage <name>", "Select an API Gateway Stage by its 'StageName'. Default: the first Stage attached to each API. Drives event.stageVariables for both REST v1 and HTTP API v2. NOTE: For HTTP API v2 routes, requestContext.stage is always '$default' regardless of this flag (AWS-side limitation — HTTP API only exposes one stage to the integration event); only event.stageVariables is affected for v2 routes. For REST v1 routes the selected StageName is also threaded into requestContext.stage.")).addOption(new Option("--api <id>", "Restrict to a single API surface
|
|
38993
|
+
const startApi = new Command("start-api").description("Run a long-running local HTTP server that maps API Gateway routes (REST v1, HTTP API, Function URL) to Lambda invocations against the AWS Lambda Runtime Interface Emulator (Docker required). Supports Lambda TOKEN/REQUEST authorizers and Cognito User Pool / HTTP v2 JWT authorizers; when JWKS is unreachable, JWT authorizers fall back to pass-through (every token accepted) with a warn line — local dev fallback. VPC-config Lambdas run locally and surface a warn line at startup; their containers do NOT get attached to the deployed VPC subnets, so calls to private RDS / ElastiCache will fail.").addOption(new Option("--port <port>", "HTTP server port (default: auto-allocate)").default("0")).addOption(new Option("--host <host>", "Bind address").default("127.0.0.1")).addOption(new Option("--stack <name>", "Stack to start (single-stack apps auto-detect)")).addOption(new Option("--warm", "Pre-start one container per Lambda at server boot").default(false)).addOption(new Option("--per-lambda-concurrency <n>", "Pool size cap per Lambda (default 2, max 4)").default("2")).addOption(new Option("--no-pull", "Skip docker pull (cached image)")).addOption(new Option("--container-host <host>", "IP the host uses to bind/probe the RIE port (must be a numeric IP — `docker run -p <ip>:<port>:8080` rejects hostnames). Defaults to 127.0.0.1.").default("127.0.0.1")).addOption(new Option("--debug-port-base <port>", "Reserve a contiguous --debug-port range (one per Lambda)")).addOption(new Option("--env-vars <file>", "JSON env-var overrides (SAM-compatible: {\"LogicalId\":{\"KEY\":\"VALUE\"}, \"Parameters\": {...}})")).addOption(new Option("--assume-role <arn-or-pair>", "Assume the Lambda's execution role and forward STS-issued temp creds. Bare <arn> = global default; <LogicalId>=<arn> = per-Lambda override (repeatable). Per-Lambda > global > unset (developer creds passed through).").argParser((raw, prev) => parseAssumeRoleToken(raw, prev))).addOption(new Option("--watch", "Hot-reload: re-synth + re-discover routes when cdk.out/ or asset directories change. Off by default; the server keeps the previous version serving when synth fails mid-reload.").default(false)).addOption(new Option("--stage <name>", "Select an API Gateway Stage by its 'StageName'. Default: the first Stage attached to each API. Drives event.stageVariables for both REST v1 and HTTP API v2. NOTE: For HTTP API v2 routes, requestContext.stage is always '$default' regardless of this flag (AWS-side limitation — HTTP API only exposes one stage to the integration event); only event.stageVariables is affected for v2 routes. For REST v1 routes the selected StageName is also threaded into requestContext.stage.")).addOption(new Option("--api <id>", "Restrict to a single API surface. Accepts the bare CDK logical id (e.g. 'MyHttpApi'), the stack-qualified logical id ('MyStack:MyHttpApi'), the full CDK Construct path ('MyStack/MyHttpApi/Resource'), or an ancestor Construct path that prefix-matches ('MyStack/MyHttpApi'). For Function URLs, the path forms reference the backing Lambda's aws:cdk:path. When unset, every discovered API gets its own server on its own port (basePort, basePort+1, ... when --port is set; auto-allocated otherwise).")).action(withErrorHandling(localStartApiCommand));
|
|
38936
38994
|
[
|
|
38937
38995
|
...commonOptions,
|
|
38938
38996
|
...appOptions,
|
|
@@ -41771,7 +41829,7 @@ function reorderArgs(argv) {
|
|
|
41771
41829
|
*/
|
|
41772
41830
|
async function main() {
|
|
41773
41831
|
const program = new Command();
|
|
41774
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.94.
|
|
41832
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.94.15");
|
|
41775
41833
|
program.addCommand(createBootstrapCommand());
|
|
41776
41834
|
program.addCommand(createSynthCommand());
|
|
41777
41835
|
program.addCommand(createListCommand());
|