@go-to-k/cdkd 0.0.3 → 0.1.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.
Binary file
package/dist/index.js CHANGED
@@ -649,7 +649,7 @@ Caused by: ${error.cause.message}`;
649
649
  init_aws_clients();
650
650
 
651
651
  // src/synthesis/synthesizer.ts
652
- import { mkdirSync } from "node:fs";
652
+ import { existsSync as existsSync3, mkdirSync, statSync } from "node:fs";
653
653
  import { resolve as resolve3 } from "node:path";
654
654
  import { GetCallerIdentityCommand, STSClient as STSClient2 } from "@aws-sdk/client-sts";
655
655
 
@@ -1846,6 +1846,14 @@ var Synthesizer = class {
1846
1846
  * 5. Return assembly with stacks
1847
1847
  */
1848
1848
  async synthesize(options) {
1849
+ const appPath = resolve3(options.app);
1850
+ if (existsSync3(appPath) && statSync(appPath).isDirectory()) {
1851
+ this.logger.debug(`Using pre-synthesized cloud assembly at ${appPath}`);
1852
+ const manifest = this.assemblyReader.readManifest(appPath);
1853
+ const stacks = this.assemblyReader.getAllStacks(appPath, manifest);
1854
+ this.logger.debug(`Loaded ${stacks.length} stack(s) from pre-synthesized assembly`);
1855
+ return { manifest, assemblyDir: appPath, stacks };
1856
+ }
1849
1857
  const outputDir = resolve3(options.output || "cdk.out");
1850
1858
  mkdirSync(outputDir, { recursive: true });
1851
1859
  const userCdkJson = loadUserCdkJson();
@@ -1933,7 +1941,7 @@ function setsEqual(a, b) {
1933
1941
  import { readFileSync as readFileSync4 } from "node:fs";
1934
1942
 
1935
1943
  // src/assets/file-asset-publisher.ts
1936
- import { createReadStream, statSync } from "node:fs";
1944
+ import { createReadStream, statSync as statSync2 } from "node:fs";
1937
1945
  import { join as join4, basename } from "node:path";
1938
1946
  import { S3Client as S3Client2, HeadObjectCommand, PutObjectCommand } from "@aws-sdk/client-s3";
1939
1947
  var FileAssetPublisher = class {
@@ -2003,7 +2011,7 @@ var FileAssetPublisher = class {
2003
2011
  * Upload a single file to S3
2004
2012
  */
2005
2013
  async uploadFile(client, filePath, bucket, key) {
2006
- const stat = statSync(filePath);
2014
+ const stat = statSync2(filePath);
2007
2015
  const stream = createReadStream(filePath);
2008
2016
  await client.send(
2009
2017
  new PutObjectCommand({
@@ -2025,7 +2033,7 @@ var FileAssetPublisher = class {
2025
2033
  archive.on("data", (chunk) => chunks.push(chunk));
2026
2034
  archive.on("end", () => resolve4(Buffer.concat(chunks)));
2027
2035
  archive.on("error", reject);
2028
- const stat = statSync(dirPath);
2036
+ const stat = statSync2(dirPath);
2029
2037
  if (stat.isDirectory()) {
2030
2038
  archive.directory(dirPath, false);
2031
2039
  } else {
@@ -3132,6 +3140,11 @@ var TemplateParser = class {
3132
3140
  // src/analyzer/dag-builder.ts
3133
3141
  import graphlib from "graphlib";
3134
3142
  var { Graph, alg } = graphlib;
3143
+ var IAM_ROLE_POLICY_TYPES = /* @__PURE__ */ new Set([
3144
+ "AWS::IAM::Policy",
3145
+ "AWS::IAM::RolePolicy",
3146
+ "AWS::IAM::ManagedPolicy"
3147
+ ]);
3135
3148
  var DagBuilder = class {
3136
3149
  logger = getLogger().child("DagBuilder");
3137
3150
  parser = new TemplateParser();
@@ -3172,6 +3185,7 @@ var DagBuilder = class {
3172
3185
  }
3173
3186
  }
3174
3187
  this.logger.debug(`Dependency graph built: ${resourceIds.length} nodes, ${edgeCount} edges`);
3188
+ edgeCount += this.addCustomResourcePolicyEdges(graph, template);
3175
3189
  if (!alg.isAcyclic(graph)) {
3176
3190
  const cycles = this.findCycles(graph);
3177
3191
  throw new DependencyError(
@@ -3314,6 +3328,123 @@ var DagBuilder = class {
3314
3328
  const deps = this.getAllDependencies(graph, resourceA);
3315
3329
  return deps.has(resourceB);
3316
3330
  }
3331
+ /**
3332
+ * Add implicit edges from IAM::Policy resources to Custom Resources whose
3333
+ * ServiceToken Lambda's execution role those policies attach to.
3334
+ *
3335
+ * Returns the number of edges added.
3336
+ */
3337
+ addCustomResourcePolicyEdges(graph, template) {
3338
+ const rolePolicies = this.buildRolePoliciesMap(template);
3339
+ if (rolePolicies.size === 0) {
3340
+ return 0;
3341
+ }
3342
+ let added = 0;
3343
+ for (const logicalId of this.parser.getResourceIds(template)) {
3344
+ const resource = this.parser.getResource(template, logicalId);
3345
+ if (!resource || !this.isCustomResourceType(resource.Type)) {
3346
+ continue;
3347
+ }
3348
+ const serviceToken = (resource.Properties ?? {})["ServiceToken"];
3349
+ const lambdaId = this.extractLogicalIdFromReference(serviceToken);
3350
+ if (!lambdaId)
3351
+ continue;
3352
+ const lambdaResource = this.parser.getResource(template, lambdaId);
3353
+ if (!lambdaResource || lambdaResource.Type !== "AWS::Lambda::Function") {
3354
+ continue;
3355
+ }
3356
+ const roleId = this.extractLogicalIdFromReference((lambdaResource.Properties ?? {})["Role"]);
3357
+ if (!roleId)
3358
+ continue;
3359
+ const policies = rolePolicies.get(roleId);
3360
+ if (!policies)
3361
+ continue;
3362
+ for (const policyId of policies) {
3363
+ if (policyId === logicalId)
3364
+ continue;
3365
+ if (!graph.hasNode(policyId))
3366
+ continue;
3367
+ if (graph.hasEdge(policyId, logicalId))
3368
+ continue;
3369
+ graph.setEdge(policyId, logicalId);
3370
+ added++;
3371
+ this.logger.debug(
3372
+ `Added implicit edge (custom resource policy): ${policyId} -> ${logicalId}`
3373
+ );
3374
+ }
3375
+ }
3376
+ if (added > 0) {
3377
+ this.logger.debug(`Added ${added} implicit edges for custom resource policies`);
3378
+ }
3379
+ return added;
3380
+ }
3381
+ isCustomResourceType(type) {
3382
+ return type === "AWS::CloudFormation::CustomResource" || type.startsWith("Custom::");
3383
+ }
3384
+ /**
3385
+ * Build a map of roleLogicalId -> Set<policyLogicalId> by scanning the
3386
+ * template for IAM::Policy / IAM::RolePolicy / IAM::ManagedPolicy resources
3387
+ * that attach to a role by Ref/GetAtt.
3388
+ */
3389
+ buildRolePoliciesMap(template) {
3390
+ const map = /* @__PURE__ */ new Map();
3391
+ for (const [policyId, resource] of Object.entries(template.Resources)) {
3392
+ if (!IAM_ROLE_POLICY_TYPES.has(resource.Type))
3393
+ continue;
3394
+ for (const roleId of this.extractAttachedRoleIds(resource)) {
3395
+ let set = map.get(roleId);
3396
+ if (!set) {
3397
+ set = /* @__PURE__ */ new Set();
3398
+ map.set(roleId, set);
3399
+ }
3400
+ set.add(policyId);
3401
+ }
3402
+ }
3403
+ return map;
3404
+ }
3405
+ /**
3406
+ * Extract the logical IDs of IAM::Role resources that a policy resource
3407
+ * attaches to. Supports both `Roles: [Ref]` (IAM::Policy / IAM::ManagedPolicy)
3408
+ * and `RoleName: Ref` (IAM::RolePolicy) shapes.
3409
+ */
3410
+ extractAttachedRoleIds(resource) {
3411
+ const ids = [];
3412
+ const props = resource.Properties ?? {};
3413
+ const roles = props["Roles"];
3414
+ if (Array.isArray(roles)) {
3415
+ for (const entry of roles) {
3416
+ const id = this.extractLogicalIdFromReference(entry);
3417
+ if (id)
3418
+ ids.push(id);
3419
+ }
3420
+ }
3421
+ const roleName = props["RoleName"];
3422
+ const roleNameId = this.extractLogicalIdFromReference(roleName);
3423
+ if (roleNameId)
3424
+ ids.push(roleNameId);
3425
+ return ids;
3426
+ }
3427
+ /**
3428
+ * Extract a resource logical ID from a direct Ref or Fn::GetAtt expression.
3429
+ * Returns undefined for literals or intrinsics we can't statically resolve
3430
+ * (Fn::Join, Fn::ImportValue, etc.) — callers should skip in that case.
3431
+ */
3432
+ extractLogicalIdFromReference(value) {
3433
+ if (typeof value !== "object" || value === null)
3434
+ return void 0;
3435
+ const obj = value;
3436
+ if ("Ref" in obj && typeof obj["Ref"] === "string") {
3437
+ const ref = obj["Ref"];
3438
+ return ref.startsWith("AWS::") ? void 0 : ref;
3439
+ }
3440
+ if ("Fn::GetAtt" in obj) {
3441
+ const getAtt = obj["Fn::GetAtt"];
3442
+ if (Array.isArray(getAtt) && typeof getAtt[0] === "string") {
3443
+ return getAtt[0];
3444
+ }
3445
+ }
3446
+ return void 0;
3447
+ }
3317
3448
  };
3318
3449
 
3319
3450
  // src/analyzer/replacement-rules.ts