@go-to-k/cdkd 0.68.0 → 0.69.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 +8 -0
- package/dist/cli.js +355 -11
- package/dist/cli.js.map +3 -3
- package/dist/go-to-k-cdkd-0.69.0.tgz +0 -0
- package/package.json +1 -1
- package/dist/go-to-k-cdkd-0.68.0.tgz +0 -0
package/README.md
CHANGED
|
@@ -607,6 +607,14 @@ cdkd local invoke MyStack/Handler --assume-role arn:aws:iam::123456789012:role/M
|
|
|
607
607
|
|
|
608
608
|
# Attach a Node debugger
|
|
609
609
|
cdkd local invoke MyStack/Handler --debug-port 9229
|
|
610
|
+
|
|
611
|
+
# After `cdkd deploy`, recover intrinsic-valued env vars (Ref / Fn::GetAtt
|
|
612
|
+
# / Fn::Sub) from cdkd's S3 state instead of dropping them. Off by default
|
|
613
|
+
# — keeps the local-only / unscoped flow safe; opt in when you want the
|
|
614
|
+
# handler to see the deployed physical IDs (S3 bucket names, DDB table
|
|
615
|
+
# names, IAM role ARNs, ...). Disambiguate with `--stack-region <region>`
|
|
616
|
+
# when the same stack name has state in multiple regions.
|
|
617
|
+
cdkd local invoke MyStack/Handler --from-state
|
|
610
618
|
```
|
|
611
619
|
|
|
612
620
|
See [docs/cli-reference.md](docs/cli-reference.md#local-invoke-run-lambda-functions-locally)
|
package/dist/cli.js
CHANGED
|
@@ -70177,6 +70177,183 @@ function isLiteralEnvValue(value) {
|
|
|
70177
70177
|
return typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
70178
70178
|
}
|
|
70179
70179
|
|
|
70180
|
+
// src/local-invoke/state-resolver.ts
|
|
70181
|
+
function substituteAgainstState(value, resources) {
|
|
70182
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
70183
|
+
return { kind: "literal", value };
|
|
70184
|
+
}
|
|
70185
|
+
if (value === null || typeof value !== "object") {
|
|
70186
|
+
return {
|
|
70187
|
+
kind: "unresolved",
|
|
70188
|
+
reason: `unsupported value type: ${value === null ? "null" : typeof value}`
|
|
70189
|
+
};
|
|
70190
|
+
}
|
|
70191
|
+
const obj = value;
|
|
70192
|
+
const keys = Object.keys(obj);
|
|
70193
|
+
if (keys.length !== 1) {
|
|
70194
|
+
return {
|
|
70195
|
+
kind: "unresolved",
|
|
70196
|
+
reason: `expected an intrinsic with one key, got ${keys.length} keys`
|
|
70197
|
+
};
|
|
70198
|
+
}
|
|
70199
|
+
const intrinsic = keys[0];
|
|
70200
|
+
const arg = obj[intrinsic];
|
|
70201
|
+
if (intrinsic === "Ref") {
|
|
70202
|
+
return resolveRef(arg, resources);
|
|
70203
|
+
}
|
|
70204
|
+
if (intrinsic === "Fn::GetAtt") {
|
|
70205
|
+
return resolveGetAtt(arg, resources);
|
|
70206
|
+
}
|
|
70207
|
+
if (intrinsic === "Fn::Sub") {
|
|
70208
|
+
return resolveSub(arg, resources);
|
|
70209
|
+
}
|
|
70210
|
+
return {
|
|
70211
|
+
kind: "unresolved",
|
|
70212
|
+
reason: `unsupported intrinsic '${intrinsic}' (only Ref, Fn::GetAtt, Fn::Sub are wired in --from-state v1)`
|
|
70213
|
+
};
|
|
70214
|
+
}
|
|
70215
|
+
function resolveRef(arg, resources) {
|
|
70216
|
+
if (typeof arg !== "string" || arg.length === 0) {
|
|
70217
|
+
return { kind: "unresolved", reason: `Ref expects a non-empty logical ID, got ${typeof arg}` };
|
|
70218
|
+
}
|
|
70219
|
+
const resource = resources[arg];
|
|
70220
|
+
if (!resource) {
|
|
70221
|
+
return {
|
|
70222
|
+
kind: "unresolved",
|
|
70223
|
+
reason: `Ref '${arg}': no record in cdkd state (was the resource deployed?)`
|
|
70224
|
+
};
|
|
70225
|
+
}
|
|
70226
|
+
return { kind: "literal", value: resource.physicalId };
|
|
70227
|
+
}
|
|
70228
|
+
function resolveGetAtt(arg, resources) {
|
|
70229
|
+
let logicalId;
|
|
70230
|
+
let attr;
|
|
70231
|
+
if (Array.isArray(arg) && arg.length === 2 && typeof arg[0] === "string") {
|
|
70232
|
+
logicalId = arg[0];
|
|
70233
|
+
if (typeof arg[1] !== "string") {
|
|
70234
|
+
return {
|
|
70235
|
+
kind: "unresolved",
|
|
70236
|
+
reason: `Fn::GetAtt's second arg must be a string attribute name, got ${typeof arg[1]} (nested intrinsics in attribute names are not supported in --from-state v1)`
|
|
70237
|
+
};
|
|
70238
|
+
}
|
|
70239
|
+
attr = arg[1];
|
|
70240
|
+
} else if (typeof arg === "string") {
|
|
70241
|
+
const dot = arg.indexOf(".");
|
|
70242
|
+
if (dot <= 0 || dot === arg.length - 1) {
|
|
70243
|
+
return {
|
|
70244
|
+
kind: "unresolved",
|
|
70245
|
+
reason: `Fn::GetAtt string form must be '<LogicalId>.<Attribute>', got '${arg}'`
|
|
70246
|
+
};
|
|
70247
|
+
}
|
|
70248
|
+
logicalId = arg.slice(0, dot);
|
|
70249
|
+
attr = arg.slice(dot + 1);
|
|
70250
|
+
} else {
|
|
70251
|
+
return {
|
|
70252
|
+
kind: "unresolved",
|
|
70253
|
+
reason: `Fn::GetAtt expects [LogicalId, Attribute] or 'LogicalId.Attribute', got ${Array.isArray(arg) ? `array of length ${arg.length}` : typeof arg}`
|
|
70254
|
+
};
|
|
70255
|
+
}
|
|
70256
|
+
const resource = resources[logicalId];
|
|
70257
|
+
if (!resource) {
|
|
70258
|
+
return {
|
|
70259
|
+
kind: "unresolved",
|
|
70260
|
+
reason: `Fn::GetAtt '${logicalId}.${attr}': no record in cdkd state`
|
|
70261
|
+
};
|
|
70262
|
+
}
|
|
70263
|
+
const cached = resource.attributes?.[attr];
|
|
70264
|
+
if (cached === void 0) {
|
|
70265
|
+
return {
|
|
70266
|
+
kind: "unresolved",
|
|
70267
|
+
reason: `Fn::GetAtt '${logicalId}.${attr}': attribute not captured in cdkd state at deploy time`
|
|
70268
|
+
};
|
|
70269
|
+
}
|
|
70270
|
+
if (typeof cached === "string" || typeof cached === "number" || typeof cached === "boolean") {
|
|
70271
|
+
return { kind: "literal", value: cached };
|
|
70272
|
+
}
|
|
70273
|
+
return { kind: "literal", value: JSON.stringify(cached) };
|
|
70274
|
+
}
|
|
70275
|
+
function resolveSub(arg, resources) {
|
|
70276
|
+
let template;
|
|
70277
|
+
let bindings = {};
|
|
70278
|
+
if (typeof arg === "string") {
|
|
70279
|
+
template = arg;
|
|
70280
|
+
} else if (Array.isArray(arg) && arg.length === 2 && typeof arg[0] === "string" && arg[1] !== null && typeof arg[1] === "object" && !Array.isArray(arg[1])) {
|
|
70281
|
+
template = arg[0];
|
|
70282
|
+
bindings = arg[1];
|
|
70283
|
+
} else {
|
|
70284
|
+
return {
|
|
70285
|
+
kind: "unresolved",
|
|
70286
|
+
reason: `Fn::Sub expects a string or [string, object], got ${Array.isArray(arg) ? "malformed array" : typeof arg}`
|
|
70287
|
+
};
|
|
70288
|
+
}
|
|
70289
|
+
const placeholderRegex = /\$\{([^}]+)\}/g;
|
|
70290
|
+
const placeholders = [];
|
|
70291
|
+
template.replace(placeholderRegex, (_, key) => {
|
|
70292
|
+
placeholders.push(key);
|
|
70293
|
+
return "";
|
|
70294
|
+
});
|
|
70295
|
+
const resolutions = /* @__PURE__ */ new Map();
|
|
70296
|
+
for (const placeholder of placeholders) {
|
|
70297
|
+
if (resolutions.has(placeholder))
|
|
70298
|
+
continue;
|
|
70299
|
+
if (placeholder in bindings) {
|
|
70300
|
+
const sub = substituteAgainstState(bindings[placeholder], resources);
|
|
70301
|
+
if (sub.kind !== "literal") {
|
|
70302
|
+
return {
|
|
70303
|
+
kind: "unresolved",
|
|
70304
|
+
reason: `Fn::Sub placeholder '\${${placeholder}}': ${sub.reason}`
|
|
70305
|
+
};
|
|
70306
|
+
}
|
|
70307
|
+
resolutions.set(placeholder, String(sub.value));
|
|
70308
|
+
continue;
|
|
70309
|
+
}
|
|
70310
|
+
const dot = placeholder.indexOf(".");
|
|
70311
|
+
if (dot === -1) {
|
|
70312
|
+
const sub = resolveRef(placeholder, resources);
|
|
70313
|
+
if (sub.kind !== "literal") {
|
|
70314
|
+
return {
|
|
70315
|
+
kind: "unresolved",
|
|
70316
|
+
reason: `Fn::Sub placeholder '\${${placeholder}}': ${sub.reason}`
|
|
70317
|
+
};
|
|
70318
|
+
}
|
|
70319
|
+
resolutions.set(placeholder, String(sub.value));
|
|
70320
|
+
} else {
|
|
70321
|
+
const sub = resolveGetAtt(placeholder, resources);
|
|
70322
|
+
if (sub.kind !== "literal") {
|
|
70323
|
+
return {
|
|
70324
|
+
kind: "unresolved",
|
|
70325
|
+
reason: `Fn::Sub placeholder '\${${placeholder}}': ${sub.reason}`
|
|
70326
|
+
};
|
|
70327
|
+
}
|
|
70328
|
+
resolutions.set(placeholder, String(sub.value));
|
|
70329
|
+
}
|
|
70330
|
+
}
|
|
70331
|
+
const out = template.replace(placeholderRegex, (_, key) => {
|
|
70332
|
+
return resolutions.get(key) ?? "";
|
|
70333
|
+
});
|
|
70334
|
+
return { kind: "literal", value: out };
|
|
70335
|
+
}
|
|
70336
|
+
function substituteEnvVarsFromState(templateEnv, resources) {
|
|
70337
|
+
const env = {};
|
|
70338
|
+
const audit = { resolvedKeys: [], unresolved: [] };
|
|
70339
|
+
if (!templateEnv)
|
|
70340
|
+
return { env, audit };
|
|
70341
|
+
for (const [key, value] of Object.entries(templateEnv)) {
|
|
70342
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
70343
|
+
env[key] = value;
|
|
70344
|
+
continue;
|
|
70345
|
+
}
|
|
70346
|
+
const result = substituteAgainstState(value, resources);
|
|
70347
|
+
if (result.kind === "literal") {
|
|
70348
|
+
env[key] = result.value;
|
|
70349
|
+
audit.resolvedKeys.push(key);
|
|
70350
|
+
} else {
|
|
70351
|
+
audit.unresolved.push({ key, reason: result.reason });
|
|
70352
|
+
}
|
|
70353
|
+
}
|
|
70354
|
+
return { env, audit };
|
|
70355
|
+
}
|
|
70356
|
+
|
|
70180
70357
|
// src/local-invoke/runtime-image.ts
|
|
70181
70358
|
var SUPPORTED_RUNTIMES = {
|
|
70182
70359
|
"nodejs18.x": { image: "public.ecr.aws/lambda/nodejs:18", fileExtension: ".js" },
|
|
@@ -70353,8 +70530,10 @@ async function waitForRieReady(host, port, timeoutMs = 5e3) {
|
|
|
70353
70530
|
while (Date.now() < deadline) {
|
|
70354
70531
|
try {
|
|
70355
70532
|
const ok = await httpProbe(host, port, 500);
|
|
70356
|
-
if (ok)
|
|
70533
|
+
if (ok) {
|
|
70534
|
+
await delay(250);
|
|
70357
70535
|
return;
|
|
70536
|
+
}
|
|
70358
70537
|
} catch (err) {
|
|
70359
70538
|
lastError = err;
|
|
70360
70539
|
}
|
|
@@ -70408,12 +70587,7 @@ async function invokeRie(host, port, event, timeoutMs) {
|
|
|
70408
70587
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
70409
70588
|
let response;
|
|
70410
70589
|
try {
|
|
70411
|
-
response = await
|
|
70412
|
-
method: "POST",
|
|
70413
|
-
headers: { "Content-Type": "application/json" },
|
|
70414
|
-
body,
|
|
70415
|
-
signal: controller.signal
|
|
70416
|
-
});
|
|
70590
|
+
response = await fetchWithStartupRetry(url, body, controller.signal);
|
|
70417
70591
|
} catch (err) {
|
|
70418
70592
|
if (err.name === "AbortError") {
|
|
70419
70593
|
throw new Error(
|
|
@@ -70432,8 +70606,32 @@ async function invokeRie(host, port, event, timeoutMs) {
|
|
|
70432
70606
|
}
|
|
70433
70607
|
return { payload, raw };
|
|
70434
70608
|
}
|
|
70609
|
+
async function fetchWithStartupRetry(url, body, signal) {
|
|
70610
|
+
const maxAttempts = 3;
|
|
70611
|
+
let lastError;
|
|
70612
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
70613
|
+
try {
|
|
70614
|
+
return await fetch(url, {
|
|
70615
|
+
method: "POST",
|
|
70616
|
+
headers: { "Content-Type": "application/json" },
|
|
70617
|
+
body,
|
|
70618
|
+
signal
|
|
70619
|
+
});
|
|
70620
|
+
} catch (err) {
|
|
70621
|
+
const name = err.name;
|
|
70622
|
+
if (name === "AbortError")
|
|
70623
|
+
throw err;
|
|
70624
|
+
lastError = err;
|
|
70625
|
+
if (attempt === maxAttempts)
|
|
70626
|
+
break;
|
|
70627
|
+
await delay(200);
|
|
70628
|
+
}
|
|
70629
|
+
}
|
|
70630
|
+
throw lastError;
|
|
70631
|
+
}
|
|
70435
70632
|
|
|
70436
70633
|
// src/cli/commands/local-invoke.ts
|
|
70634
|
+
init_aws_clients();
|
|
70437
70635
|
async function localInvokeCommand(target, options) {
|
|
70438
70636
|
const logger = getLogger();
|
|
70439
70637
|
if (options.verbose) {
|
|
@@ -70464,13 +70662,44 @@ async function localInvokeCommand(target, options) {
|
|
|
70464
70662
|
lambda.inlineCode ?? "",
|
|
70465
70663
|
resolveRuntimeFileExtension(lambda.runtime)
|
|
70466
70664
|
);
|
|
70665
|
+
let stateAudit;
|
|
70666
|
+
let templateEnv = getTemplateEnv(lambda.resource);
|
|
70667
|
+
let stateForRoleHint;
|
|
70668
|
+
if (options.fromState) {
|
|
70669
|
+
const loaded = await loadStateForStack(lambda.stack.stackName, lambda.stack.region, {
|
|
70670
|
+
...options.stackRegion !== void 0 && { stackRegion: options.stackRegion },
|
|
70671
|
+
...options.stateBucket !== void 0 && { stateBucket: options.stateBucket },
|
|
70672
|
+
statePrefix: options.statePrefix,
|
|
70673
|
+
...options.region !== void 0 && { region: options.region },
|
|
70674
|
+
...options.profile !== void 0 && { profile: options.profile }
|
|
70675
|
+
});
|
|
70676
|
+
if (loaded) {
|
|
70677
|
+
stateForRoleHint = loaded.state;
|
|
70678
|
+
const { env, audit } = substituteEnvVarsFromState(templateEnv, loaded.state.resources);
|
|
70679
|
+
templateEnv = env;
|
|
70680
|
+
stateAudit = audit;
|
|
70681
|
+
for (const key of audit.resolvedKeys) {
|
|
70682
|
+
logger.debug(`--from-state: substituted env var ${key} from cdkd state`);
|
|
70683
|
+
}
|
|
70684
|
+
for (const { key, reason } of audit.unresolved) {
|
|
70685
|
+
logger.warn(
|
|
70686
|
+
`--from-state: could not substitute env var ${key} (${reason}). Override it via --env-vars or it will be dropped.`
|
|
70687
|
+
);
|
|
70688
|
+
}
|
|
70689
|
+
}
|
|
70690
|
+
}
|
|
70467
70691
|
const overrides = readEnvOverridesFile(options.envVars);
|
|
70468
|
-
const envResult = resolveEnvVars(lambda.logicalId,
|
|
70692
|
+
const envResult = resolveEnvVars(lambda.logicalId, templateEnv, overrides);
|
|
70469
70693
|
for (const key of envResult.unresolved) {
|
|
70694
|
+
if (stateAudit && stateAudit.unresolved.some((u) => u.key === key))
|
|
70695
|
+
continue;
|
|
70470
70696
|
logger.warn(
|
|
70471
|
-
`Environment variable ${key} contains a CloudFormation intrinsic and was dropped. Override it with --env-vars (e.g. {"${lambda.logicalId}":{"${key}":"<literal>"}}) or
|
|
70697
|
+
`Environment variable ${key} contains a CloudFormation intrinsic and was dropped. Override it with --env-vars (e.g. {"${lambda.logicalId}":{"${key}":"<literal>"}}) or pass --from-state to recover deployed values.`
|
|
70472
70698
|
);
|
|
70473
70699
|
}
|
|
70700
|
+
if (options.fromState && !options.assumeRole && stateForRoleHint) {
|
|
70701
|
+
suggestAssumeRoleFromState(stateForRoleHint, lambda.logicalId);
|
|
70702
|
+
}
|
|
70474
70703
|
const event = await readEvent(options);
|
|
70475
70704
|
const image = resolveRuntimeImage(lambda.runtime);
|
|
70476
70705
|
const dockerEnv = {
|
|
@@ -70649,6 +70878,109 @@ function materializeInlineCode(handler, source, fileExtension) {
|
|
|
70649
70878
|
writeFileSync5(filePath, source, "utf-8");
|
|
70650
70879
|
return dir;
|
|
70651
70880
|
}
|
|
70881
|
+
async function loadStateForStack(stackName, synthRegion, opts) {
|
|
70882
|
+
const logger = getLogger();
|
|
70883
|
+
const region = opts.region ?? process.env["AWS_REGION"] ?? process.env["AWS_DEFAULT_REGION"] ?? synthRegion ?? "us-east-1";
|
|
70884
|
+
let stateBucket;
|
|
70885
|
+
try {
|
|
70886
|
+
stateBucket = await resolveStateBucketWithDefault(opts.stateBucket, region);
|
|
70887
|
+
} catch (err) {
|
|
70888
|
+
logger.warn(
|
|
70889
|
+
`--from-state: could not resolve state bucket: ${err instanceof Error ? err.message : String(err)}. Falling back to PR 1 warn-and-drop semantics.`
|
|
70890
|
+
);
|
|
70891
|
+
return void 0;
|
|
70892
|
+
}
|
|
70893
|
+
const awsClients = new AwsClients({
|
|
70894
|
+
...opts.region !== void 0 && { region: opts.region },
|
|
70895
|
+
...opts.profile !== void 0 && { profile: opts.profile }
|
|
70896
|
+
});
|
|
70897
|
+
setAwsClients(awsClients);
|
|
70898
|
+
try {
|
|
70899
|
+
const stateConfig = { bucket: stateBucket, prefix: opts.statePrefix };
|
|
70900
|
+
const stateBackend = new S3StateBackend(awsClients.s3, stateConfig, {
|
|
70901
|
+
...opts.region !== void 0 && { region: opts.region },
|
|
70902
|
+
...opts.profile !== void 0 && { profile: opts.profile }
|
|
70903
|
+
});
|
|
70904
|
+
await stateBackend.verifyBucketExists();
|
|
70905
|
+
const refs = (await stateBackend.listStacks()).filter((r) => r.stackName === stackName);
|
|
70906
|
+
if (refs.length === 0) {
|
|
70907
|
+
logger.warn(
|
|
70908
|
+
`--from-state: no cdkd state found for stack '${stackName}' in bucket '${stateBucket}'. Was it deployed via 'cdkd deploy'? Falling back to PR 1 warn-and-drop semantics.`
|
|
70909
|
+
);
|
|
70910
|
+
return void 0;
|
|
70911
|
+
}
|
|
70912
|
+
let targetRegion;
|
|
70913
|
+
if (opts.stackRegion) {
|
|
70914
|
+
const found = refs.find((r) => r.region === opts.stackRegion);
|
|
70915
|
+
if (!found) {
|
|
70916
|
+
const seen = refs.map((r) => r.region ?? "(legacy)").join(", ");
|
|
70917
|
+
logger.warn(
|
|
70918
|
+
`--from-state: stack '${stackName}' has no state in region '${opts.stackRegion}' (available: ${seen}). Falling back.`
|
|
70919
|
+
);
|
|
70920
|
+
return void 0;
|
|
70921
|
+
}
|
|
70922
|
+
targetRegion = opts.stackRegion;
|
|
70923
|
+
} else if (synthRegion && refs.some((r) => r.region === synthRegion)) {
|
|
70924
|
+
targetRegion = synthRegion;
|
|
70925
|
+
} else if (refs.length === 1) {
|
|
70926
|
+
targetRegion = refs[0].region ?? synthRegion ?? region;
|
|
70927
|
+
} else {
|
|
70928
|
+
const seen = refs.map((r) => r.region ?? "(legacy)").join(", ");
|
|
70929
|
+
logger.warn(
|
|
70930
|
+
`--from-state: stack '${stackName}' has state in multiple regions (${seen}). Re-run with --stack-region <region>. Falling back.`
|
|
70931
|
+
);
|
|
70932
|
+
return void 0;
|
|
70933
|
+
}
|
|
70934
|
+
const stateData = await stateBackend.getState(stackName, targetRegion);
|
|
70935
|
+
if (!stateData) {
|
|
70936
|
+
logger.warn(
|
|
70937
|
+
`--from-state: state record for '${stackName}' (${targetRegion}) returned empty. Falling back.`
|
|
70938
|
+
);
|
|
70939
|
+
return void 0;
|
|
70940
|
+
}
|
|
70941
|
+
logger.debug(`--from-state: loaded state for ${stackName} (${targetRegion})`);
|
|
70942
|
+
return { state: stateData.state, region: targetRegion };
|
|
70943
|
+
} finally {
|
|
70944
|
+
awsClients.destroy();
|
|
70945
|
+
}
|
|
70946
|
+
}
|
|
70947
|
+
function suggestAssumeRoleFromState(state, logicalId) {
|
|
70948
|
+
const logger = getLogger();
|
|
70949
|
+
const lambda = state.resources[logicalId];
|
|
70950
|
+
if (!lambda)
|
|
70951
|
+
return;
|
|
70952
|
+
const roleRef = lambda.properties?.["Role"] ?? lambda.observedProperties?.["Role"];
|
|
70953
|
+
let roleArn;
|
|
70954
|
+
if (typeof roleRef === "string" && roleRef.startsWith("arn:")) {
|
|
70955
|
+
roleArn = roleRef;
|
|
70956
|
+
} else if (typeof roleRef === "object" && roleRef !== null) {
|
|
70957
|
+
const refLogicalId = pickReferencedLogicalId(roleRef);
|
|
70958
|
+
if (refLogicalId) {
|
|
70959
|
+
const roleResource = state.resources[refLogicalId];
|
|
70960
|
+
const cached = roleResource?.attributes?.["Arn"];
|
|
70961
|
+
if (typeof cached === "string" && cached.startsWith("arn:")) {
|
|
70962
|
+
roleArn = cached;
|
|
70963
|
+
}
|
|
70964
|
+
}
|
|
70965
|
+
}
|
|
70966
|
+
if (roleArn) {
|
|
70967
|
+
logger.info(
|
|
70968
|
+
`Hint: the deployed function uses execution role ${roleArn}. Re-run with --assume-role <that-arn> to invoke under the deployed function's narrow permissions.`
|
|
70969
|
+
);
|
|
70970
|
+
}
|
|
70971
|
+
}
|
|
70972
|
+
function pickReferencedLogicalId(intrinsic) {
|
|
70973
|
+
if ("Ref" in intrinsic && typeof intrinsic["Ref"] === "string")
|
|
70974
|
+
return intrinsic["Ref"];
|
|
70975
|
+
if ("Fn::GetAtt" in intrinsic) {
|
|
70976
|
+
const arg = intrinsic["Fn::GetAtt"];
|
|
70977
|
+
if (Array.isArray(arg) && typeof arg[0] === "string")
|
|
70978
|
+
return arg[0];
|
|
70979
|
+
if (typeof arg === "string")
|
|
70980
|
+
return arg.split(".")[0];
|
|
70981
|
+
}
|
|
70982
|
+
return void 0;
|
|
70983
|
+
}
|
|
70652
70984
|
function createLocalCommand() {
|
|
70653
70985
|
const local = new Command14("local").description(
|
|
70654
70986
|
"Local Lambda execution against the AWS Lambda Runtime Interface Emulator (Docker required)"
|
|
@@ -70667,8 +70999,20 @@ function createLocalCommand() {
|
|
|
70667
70999
|
"--assume-role <arn>",
|
|
70668
71000
|
`Assume the Lambda's deployed execution role and forward STS-issued temp credentials to the container so the handler runs with the deployed function's narrow permissions (closes the "developer admin / function narrow" skew). Off by default \u2014 when omitted, the developer's shell credentials are forwarded unchanged (SAM-compatible default).`
|
|
70669
71001
|
)
|
|
71002
|
+
).addOption(
|
|
71003
|
+
new Option7(
|
|
71004
|
+
"--from-state",
|
|
71005
|
+
"Read cdkd S3 state for the target stack and substitute Ref / Fn::GetAtt / Fn::Sub in env vars with the deployed physical IDs / attributes. Off by default \u2014 keep PR 1 warn-and-drop semantics; turn on for stacks already deployed via cdkd deploy."
|
|
71006
|
+
).default(false)
|
|
71007
|
+
).addOption(
|
|
71008
|
+
new Option7(
|
|
71009
|
+
"--stack-region <region>",
|
|
71010
|
+
"Region of the cdkd state record to read (used with --from-state when the same stack name has state in multiple regions)."
|
|
71011
|
+
)
|
|
70670
71012
|
).action(withErrorHandling(localInvokeCommand));
|
|
70671
|
-
[...commonOptions, ...appOptions, ...contextOptions].forEach(
|
|
71013
|
+
[...commonOptions, ...appOptions, ...contextOptions, ...stateOptions].forEach(
|
|
71014
|
+
(opt) => invoke.addOption(opt)
|
|
71015
|
+
);
|
|
70672
71016
|
invoke.addOption(deprecatedRegionOption);
|
|
70673
71017
|
local.addCommand(invoke);
|
|
70674
71018
|
return local;
|
|
@@ -70703,7 +71047,7 @@ function reorderArgs(argv) {
|
|
|
70703
71047
|
}
|
|
70704
71048
|
async function main() {
|
|
70705
71049
|
const program = new Command15();
|
|
70706
|
-
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.
|
|
71050
|
+
program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.69.0");
|
|
70707
71051
|
program.addCommand(createBootstrapCommand());
|
|
70708
71052
|
program.addCommand(createSynthCommand());
|
|
70709
71053
|
program.addCommand(createListCommand());
|