@go-to-k/cdkd 0.280.16 → 0.280.18

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/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import { $ as requireConfigString, $t as processStackMessages, A as red, At as runDockerStreaming, B as isTerminationProtectionPropagationError, Bt as resolveSkipPrefix, C as isStatefulRecreateTargetSync, Ct as parseBootstrapMarker, D as cyan, Dn as normalizeAwsError, Dt as formatDockerLoginError, E as bold, Et as buildDockerImage, F as ProviderRegistry, Ft as getDefaultStateBucketName, G as normalizeAwsTagsToCfn, Gt as warnDeprecatedNoPrefixCliFlag, H as cfnRefValueFromPhysicalId, Ht as resolveStateBucketWithDefaultAndSource, I as findActionableSilentDrops, It as getLegacyStateBucketName, J as configStringRefusal, Jt as MIGRATE_TMP_PREFIX, K as resolveExplicitPhysicalId, Kt as CFN_TEMPLATE_BODY_LIMIT, L as CloudControlProvider, Lt as resolveApp, M as IAMRoleProvider, Mt as getDockerImageBySourceHash, N as collectInlinePolicyNamesManagedBySiblings, Nt as Synthesizer, O as gray, On as withErrorHandling, Ot as getDockerCmd, P as clearOnUpdateRemoval, Pt as synthesisStatusMessage, Q as requireConfigObject, Qt as AssemblyReader, R as slowCcOperationTimeoutMs, Rt as resolveAutoAssetStorage, S as MULTI_REGION_RECREATE_BLOCKED_TYPES, Sn as StackTerminationProtectionError, St as getBootstrapMarkerKey, T as formatResourceLine, Tt as validateContainerRepoName, U as refStateLookupFromResource, Ut as resolveUseCdkBootstrapAssets, V as IntrinsicFunctionResolver, Vt as resolveStateBucketWithDefault, W as WAFv2WebACLProvider, Wt as stateBucketExistenceConfirmed, X as replayWarn, Xt as uploadCfnTemplate, Y as readConfigString, Yt as findLargeInlineResources, Z as requireConfigArray, Zt as expectedOwnerParam, _ as createPreDeleteFinalSnapshot, _n as PartialFailureError, _t as loadPublishableAssetManifest, a as DeploymentEventsStore, an as setAwsClients, at as isThrottlingError, b as unsupportedFinalSnapshotError, bn as ResourceUpdateNotSupportedError, bt as BOOTSTRAP_MARKER_PREFIX, c as replayFailedOperations, ct as LockManager, d as IMPLICIT_DELETE_DEPENDENCIES, dn as LocalInvokeBuildError$1, dt as shouldRetainResource, et as applyRoleArnIfSet, f as computeImplicitDeleteEdges, fn as LocalMigrateError, ft as AssetPublisher, g as ccRoutedFinalSnapshotError, gn as NestedStackChildDirectDestroyError, gt as createAssetRedirectResolver, h as buildFinalSnapshotIdentifier, hn as MissingCdkCliError, ht as buildAssetRedirectMap, i as DeploymentEventsReader, in as resetAwsClients, it as isRetryableTransientError, j as yellow, jt as AssetManifestLoader, k as green, kt as runDockerForeground, l as replayRollback, lt as S3StateBackend, m as PRE_DELETE_SNAPSHOT_TYPES, mt as WorkGraph, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as AwsClients, nt as describeTypeWithThrottleRetry, o as planFailedOps, ot as DagBuilder, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as LocalStartServiceError, pt as stringifyValue, q as assertRegionMatch, qt as CFN_TEMPLATE_URL_LIMIT, r as DeployEngine, rn as getAwsClients, rt as withRetry, s as planRollback, sn as CdkdError, st as TemplateParser, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as resolveBucketRegion, tt as DiffCalculator, u as withResourceDeadline, un as DeployCancelledError, ut as rebuildClientForBucketRegion, v as isFinalSnapshotError, vn as ProvisioningError, vt as rewriteTemplateAssetReferences, w as renderStatefulReason, wt as validateAssetBucketName, x as extractDeploymentEventError, xn as StackHasActiveImportsError, xt as ensureAssetStorage, y as refusesFinalSnapshot, yn as ResourceTimeoutError, yt as AssetModeResolver, z as disableInstanceApiTermination, zt as resolveCaptureObservedState } from "./deploy-engine-1eZqhWKB.js";
2
+ import { $ as requireConfigString, $t as processStackMessages, A as red, At as runDockerStreaming, B as isTerminationProtectionPropagationError, Bt as resolveSkipPrefix, C as isStatefulRecreateTargetSync, Ct as parseBootstrapMarker, D as cyan, Dn as normalizeAwsError, Dt as formatDockerLoginError, E as bold, Et as buildDockerImage, F as ProviderRegistry, Ft as getDefaultStateBucketName, G as normalizeAwsTagsToCfn, Gt as warnDeprecatedNoPrefixCliFlag, H as cfnRefValueFromPhysicalId, Ht as resolveStateBucketWithDefaultAndSource, I as findActionableSilentDrops, It as getLegacyStateBucketName, J as configStringRefusal, Jt as MIGRATE_TMP_PREFIX, K as resolveExplicitPhysicalId, Kt as CFN_TEMPLATE_BODY_LIMIT, L as CloudControlProvider, Lt as resolveApp, M as IAMRoleProvider, Mt as getDockerImageBySourceHash, N as collectInlinePolicyNamesManagedBySiblings, Nt as Synthesizer, O as gray, On as withErrorHandling, Ot as getDockerCmd, P as clearOnUpdateRemoval, Pt as synthesisStatusMessage, Q as requireConfigObject, Qt as AssemblyReader, R as slowCcOperationTimeoutMs, Rt as resolveAutoAssetStorage, S as MULTI_REGION_RECREATE_BLOCKED_TYPES, Sn as StackTerminationProtectionError, St as getBootstrapMarkerKey, T as formatResourceLine, Tt as validateContainerRepoName, U as refStateLookupFromResource, Ut as resolveUseCdkBootstrapAssets, V as IntrinsicFunctionResolver, Vt as resolveStateBucketWithDefault, W as WAFv2WebACLProvider, Wt as stateBucketExistenceConfirmed, X as replayWarn, Xt as uploadCfnTemplate, Y as readConfigString, Yt as findLargeInlineResources, Z as requireConfigArray, Zt as expectedOwnerParam, _ as createPreDeleteFinalSnapshot, _n as PartialFailureError, _t as loadPublishableAssetManifest, a as DeploymentEventsStore, an as setAwsClients, at as isThrottlingError, b as unsupportedFinalSnapshotError, bn as ResourceUpdateNotSupportedError, bt as BOOTSTRAP_MARKER_PREFIX, c as replayFailedOperations, ct as LockManager, d as IMPLICIT_DELETE_DEPENDENCIES, dn as LocalInvokeBuildError$1, dt as shouldRetainResource, et as applyRoleArnIfSet, f as computeImplicitDeleteEdges, fn as LocalMigrateError, ft as AssetPublisher, g as ccRoutedFinalSnapshotError, gn as NestedStackChildDirectDestroyError, gt as createAssetRedirectResolver, h as buildFinalSnapshotIdentifier, hn as MissingCdkCliError, ht as buildAssetRedirectMap, i as DeploymentEventsReader, in as resetAwsClients, it as isRetryableTransientError, j as yellow, jt as AssetManifestLoader, k as green, kt as runDockerForeground, l as replayRollback, lt as S3StateBackend, m as PRE_DELETE_SNAPSHOT_TYPES, mt as WorkGraph, n as DEFAULT_RESOURCE_WARN_AFTER_MS, nn as AwsClients, nt as describeTypeWithThrottleRetry, o as planFailedOps, ot as DagBuilder, p as ATOMIC_FINAL_SNAPSHOT_TYPES, pn as LocalStartServiceError, pt as stringifyValue, q as assertRegionMatch, qt as CFN_TEMPLATE_URL_LIMIT, r as DeployEngine, rn as getAwsClients, rt as withRetry, s as planRollback, sn as CdkdError, st as TemplateParser, t as DEFAULT_RESOURCE_TIMEOUT_MS, tn as resolveBucketRegion, tt as DiffCalculator, u as withResourceDeadline, un as DeployCancelledError, ut as rebuildClientForBucketRegion, v as isFinalSnapshotError, vn as ProvisioningError, vt as rewriteTemplateAssetReferences, w as renderStatefulReason, wt as validateAssetBucketName, x as extractDeploymentEventError, xn as StackHasActiveImportsError, xt as ensureAssetStorage, y as refusesFinalSnapshot, yn as ResourceTimeoutError, yt as AssetModeResolver, z as disableInstanceApiTermination, zt as resolveCaptureObservedState } from "./deploy-engine-mvkiu-7h.js";
3
3
  import { a as getLiveRenderer, c as PATTERN_B_RESOURCE_TYPES, d as generateResourceNameWithFallback, f as withSkipPrefix, i as runStackBuffered, n as getLogger, o as PATTERN_B_NAME_OPTIONS, p as withStackName, s as PATTERN_B_NAME_PROPERTIES, u as generateResourceName } from "./logger-BYMEE-BS.js";
4
- import { t as ASGProvider } from "./asg-provider-4pPwAL_v.js";
4
+ import { t as ASGProvider } from "./asg-provider-C6cQkX3h.js";
5
5
  import { AsyncLocalStorage } from "node:async_hooks";
6
6
  import { createHash, randomBytes, randomUUID } from "node:crypto";
7
7
  import { CopyObjectCommand, CreateBucketCommand, DeleteBucketAnalyticsConfigurationCommand, DeleteBucketCommand, DeleteBucketCorsCommand, DeleteBucketEncryptionCommand, DeleteBucketIntelligentTieringConfigurationCommand, DeleteBucketInventoryConfigurationCommand, DeleteBucketLifecycleCommand, DeleteBucketMetricsConfigurationCommand, DeleteBucketOwnershipControlsCommand, DeleteBucketPolicyCommand, DeleteBucketReplicationCommand, DeleteBucketTaggingCommand, DeleteBucketWebsiteCommand, DeleteObjectsCommand, GetBucketAccelerateConfigurationCommand, GetBucketCorsCommand, GetBucketEncryptionCommand, GetBucketLifecycleConfigurationCommand, GetBucketLocationCommand, GetBucketLoggingCommand, GetBucketNotificationConfigurationCommand, GetBucketOwnershipControlsCommand, GetBucketPolicyCommand, GetBucketReplicationCommand, GetBucketTaggingCommand, GetBucketVersioningCommand, GetBucketWebsiteCommand, GetObjectCommand, GetObjectLockConfigurationCommand, GetPublicAccessBlockCommand, HeadBucketCommand, ListBucketAnalyticsConfigurationsCommand, ListBucketIntelligentTieringConfigurationsCommand, ListBucketInventoryConfigurationsCommand, ListBucketMetricsConfigurationsCommand, ListObjectVersionsCommand, ListObjectsV2Command, NoSuchBucket, PutBucketAccelerateConfigurationCommand, PutBucketAnalyticsConfigurationCommand, PutBucketCorsCommand, PutBucketEncryptionCommand, PutBucketIntelligentTieringConfigurationCommand, PutBucketInventoryConfigurationCommand, PutBucketLifecycleConfigurationCommand, PutBucketLoggingCommand, PutBucketMetricsConfigurationCommand, PutBucketNotificationConfigurationCommand, PutBucketOwnershipControlsCommand, PutBucketPolicyCommand, PutBucketReplicationCommand, PutBucketTaggingCommand, PutBucketVersioningCommand, PutBucketWebsiteCommand, PutObjectCommand, PutObjectLockConfigurationCommand, PutPublicAccessBlockCommand, S3Client, S3ServiceException } from "@aws-sdk/client-s3";
@@ -22462,6 +22462,7 @@ var ApiGatewayV2Provider = class {
22462
22462
  const apiId = properties["ApiId"];
22463
22463
  const integrationType = properties["IntegrationType"];
22464
22464
  if (!apiId || !integrationType) throw new ProvisioningError(`ApiId and IntegrationType are required for API Gateway V2 Integration ${logicalId}`, resourceType, logicalId);
22465
+ this.warnTlsConfigIgnoredIfPublic(logicalId, properties);
22465
22466
  try {
22466
22467
  const integrationId = (await this.getClient().send(new CreateIntegrationCommand({
22467
22468
  ApiId: apiId,
@@ -22630,6 +22631,30 @@ var ApiGatewayV2Provider = class {
22630
22631
  * each uses `properties.ApiId` (passed through PR G's signature
22631
22632
  * extension) to issue the appropriate `Get*` call.
22632
22633
  */
22634
+ /**
22635
+ * `TlsConfig` is meaningful ONLY for a private (VPC-Link) integration.
22636
+ * On a public one (`ConnectionType` absent or `INTERNET`) AWS SILENTLY
22637
+ * IGNORES the field — it is absent from both the `CreateIntegration`
22638
+ * echo and the `GetIntegration` read-back (direct A/B, 2026-08-11
22639
+ * us-east-1) — so the provider can never read it back and a template
22640
+ * that declares it would report permanent phantom drift on the
22641
+ * `properties`-fallback baseline, with `--revert` re-sending a value AWS
22642
+ * discards on every run (issue #1602). The path is scoped by the
22643
+ * resource's own `ConnectionType` via the optional `properties` bag: a
22644
+ * VPC_LINK integration keeps full `TlsConfig` drift coverage (AWS
22645
+ * returns the field there). Without a USABLE properties bag the answer
22646
+ * stays empty — comparing is the safe default, since hiding a private
22647
+ * integration's real `TlsConfig` drift is worse than a false positive on
22648
+ * a caller that could not supply the bag. An EMPTY bag counts as unusable
22649
+ * for the same reason: the caller passes `resource.properties ?? {}`, so
22650
+ * `{}` means "nothing recorded", not "ConnectionType is absent, therefore
22651
+ * public" (an empty bag also has no `TlsConfig` key for the comparator to
22652
+ * walk, so refusing to declare costs nothing).
22653
+ */
22654
+ getDriftUnknownPaths(resourceType, properties) {
22655
+ if (resourceType === "AWS::ApiGatewayV2::Integration" && properties !== void 0 && Object.keys(properties).length > 0 && properties["ConnectionType"] !== "VPC_LINK") return ["TlsConfig"];
22656
+ return [];
22657
+ }
22633
22658
  async readCurrentState(physicalId, _logicalId, resourceType, properties) {
22634
22659
  switch (resourceType) {
22635
22660
  case "AWS::ApiGatewayV2::Api": return this.readApi(physicalId);
@@ -22707,7 +22732,7 @@ var ApiGatewayV2Provider = class {
22707
22732
  if (resp.IntegrationSubtype !== void 0) result["IntegrationSubtype"] = resp.IntegrationSubtype;
22708
22733
  if (resp.PassthroughBehavior !== void 0) result["PassthroughBehavior"] = resp.PassthroughBehavior;
22709
22734
  if (resp.RequestTemplates !== void 0) result["RequestTemplates"] = resp.RequestTemplates;
22710
- if (resp.ResponseParameters !== void 0) result["ResponseParameters"] = this.toCfnResponseParameters(resp.ResponseParameters);
22735
+ if (resp.ResponseParameters !== void 0) result["ResponseParameters"] = this.toCfnResponseParameters(resp.ResponseParameters, properties?.["ResponseParameters"]);
22711
22736
  if (resp.TemplateSelectionExpression !== void 0) result["TemplateSelectionExpression"] = resp.TemplateSelectionExpression;
22712
22737
  if (resp.TlsConfig !== void 0) result["TlsConfig"] = resp.TlsConfig;
22713
22738
  return result;
@@ -23082,6 +23107,7 @@ var ApiGatewayV2Provider = class {
23082
23107
  changed = true;
23083
23108
  }
23084
23109
  if (properties["TlsConfig"] != null && !this.deepEqual(properties["TlsConfig"], previousProperties["TlsConfig"])) {
23110
+ this.warnTlsConfigIgnoredIfPublic(logicalId, properties);
23085
23111
  input.TlsConfig = properties["TlsConfig"];
23086
23112
  changed = true;
23087
23113
  }
@@ -23435,6 +23461,22 @@ var ApiGatewayV2Provider = class {
23435
23461
  return merged;
23436
23462
  }
23437
23463
  /**
23464
+ * Announce a `TlsConfig` that AWS will silently discard (issue #1602):
23465
+ * the field is only honored on a private (VPC-Link) integration, and on a
23466
+ * public one (`ConnectionType` absent or `INTERNET`) AWS accepts the value
23467
+ * and drops it — it never appears on the `GetIntegration` read-back
23468
+ * (direct A/B, 2026-08-11 us-east-1). cdkd still sends the field (parity:
23469
+ * CloudFormation forwards it too and AWS accepts it), but the never-silent
23470
+ * policy requires saying so. The matching drift false positive is closed
23471
+ * by {@link getDriftUnknownPaths}. A WARN on both create and update, never
23472
+ * a throw — the update path can be a state-record replay
23473
+ * (`rollback-executor.ts` / `drift --revert`) where a refusal would leave
23474
+ * the resource un-revertable.
23475
+ */
23476
+ warnTlsConfigIgnoredIfPublic(logicalId, properties) {
23477
+ if (properties["TlsConfig"] != null && properties["ConnectionType"] !== "VPC_LINK") this.logger.warn(`AWS::ApiGatewayV2::Integration ${logicalId}: TlsConfig is only honored on a VPC_LINK (private) integration — AWS silently ignores it on a public one, and the value will never be readable back from AWS (issue #1602).`);
23478
+ }
23479
+ /**
23438
23480
  * `ResponseParameters` is the one `AWS::ApiGatewayV2::Integration` property
23439
23481
  * whose CFn shape does NOT match the SDK's, so forwarding it verbatim is not
23440
23482
  * an option (issue #609). CloudFormation models it as
@@ -23493,26 +23535,87 @@ var ApiGatewayV2Provider = class {
23493
23535
  * baseline carries the CFn list-of-pairs, and every `cdkd drift` run would
23494
23536
  * report permanent phantom drift on a resource nobody touched.
23495
23537
  *
23496
- * The pairs are sorted by `Destination` because this turns an UNORDERED SDK
23497
- * map into an ARRAY, and `calculateResourceDrift` compares arrays
23498
- * POSITIONALLY — AWS does not guarantee map readback order, so an unsorted
23499
- * rebuild would trade the shape-mismatch phantom drift for an ordering one
23500
- * (and `--revert` would re-push an identical value forever). Same reasoning
23501
- * as `src/analyzer/drift-normalize.ts`, applied at the source instead.
23502
- */
23503
- toCfnResponseParameters(value) {
23538
+ * Order matters because this turns an UNORDERED SDK map into an ARRAY, and
23539
+ * `calculateResourceDrift` compares arrays POSITIONALLY AWS does not
23540
+ * guarantee map readback order, so an arbitrary rebuild would trade the
23541
+ * shape-mismatch phantom drift for an ordering one (and `--revert` would
23542
+ * re-push an identical value forever). Declared destinations therefore keep
23543
+ * their DECLARED position and anything else is sorted; both are stable
23544
+ * across reads. Same reasoning as `src/analyzer/drift-normalize.ts`,
23545
+ * applied at the source instead.
23546
+ *
23547
+ * `declared` (the state-recorded desired `ResponseParameters` block, when
23548
+ * the caller has it) makes the inverse MIRROR the template's spelling per
23549
+ * status code (issue #1602): {@link toSdkResponseParameters} accepts an
23550
+ * ALREADY-FLAT block (`{ "<status>": { "<Destination>": "<Source>" } }` —
23551
+ * the SDK spelling a hand-written template may use) and passes it through,
23552
+ * which made the pair non-injective — this method always rebuilt the CFn
23553
+ * list shape, so a flat-spelled baseline could never compare equal to the
23554
+ * read-back and reported permanent phantom drift. For a status code whose
23555
+ * DECLARED block matches the flat-passthrough branch of
23556
+ * {@link toSdkResponseParameters} (an object without an array
23557
+ * `ResponseParameters` member), the read-back now stays flat too; every
23558
+ * other status code (declared CFn shape, declared absent, no `declared` at
23559
+ * all) keeps the CFn list shape. Object comparison is key-based, so the
23560
+ * flat map round-trips without the sort the list shape needs.
23561
+ *
23562
+ * The CFn arm mirrors the declared block too, in the two ways the
23563
+ * `properties`-fallback baseline is compared on: ENTRY ORDER (the
23564
+ * comparator walks arrays positionally, and the template's order is
23565
+ * whatever the author wrote — sorting only the read side is the mistake
23566
+ * `drift-normalize.ts`'s header records) and SCALAR TYPE (CFn types
23567
+ * `Source` as free-form and cdkd coerces a number to a string on the way
23568
+ * out, so re-emitting AWS's string against a numeric declaration is
23569
+ * permanent phantom drift). Declared destinations keep their declared
23570
+ * position and their declared scalar whenever the value still agrees with
23571
+ * AWS; a destination AWS returns that the template never declared is
23572
+ * appended in sorted order, so the result stays deterministic across reads.
23573
+ */
23574
+ toCfnResponseParameters(value, declared) {
23504
23575
  if (value == null) return void 0;
23576
+ const declaredBlocks = declared != null && typeof declared === "object" && !Array.isArray(declared) ? declared : void 0;
23505
23577
  const out = {};
23506
23578
  for (const [statusCode, flat] of Object.entries(value)) {
23507
23579
  if (flat == null || typeof flat !== "object" || Array.isArray(flat)) continue;
23508
- out[statusCode] = { ResponseParameters: Object.entries(flat).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([destination, source]) => ({
23509
- Destination: destination,
23510
- Source: source
23511
- })) };
23580
+ const declaredBlock = declaredBlocks?.[statusCode];
23581
+ const declaredIsBlock = declaredBlock != null && typeof declaredBlock === "object" && !Array.isArray(declaredBlock);
23582
+ const declaredEntries = declaredIsBlock ? declaredBlock["ResponseParameters"] : void 0;
23583
+ if (declaredIsBlock && !Array.isArray(declaredEntries)) {
23584
+ out[statusCode] = flat;
23585
+ continue;
23586
+ }
23587
+ out[statusCode] = { ResponseParameters: this.mirrorDeclaredResponsePairs(flat, declaredEntries) };
23512
23588
  }
23513
23589
  return out;
23514
23590
  }
23515
23591
  /**
23592
+ * Rebuild one status code's CFn list-of-pairs from AWS's flat map, keeping
23593
+ * the DECLARED entries' order and scalar types where they still agree with
23594
+ * AWS. See {@link toCfnResponseParameters} for why both matter.
23595
+ */
23596
+ mirrorDeclaredResponsePairs(flat, declaredEntries) {
23597
+ const remaining = new Map(Object.entries(flat));
23598
+ const pairs = [];
23599
+ if (Array.isArray(declaredEntries)) for (const entry of declaredEntries) {
23600
+ if (entry == null || typeof entry !== "object" || Array.isArray(entry)) continue;
23601
+ const destination = entry["Destination"];
23602
+ if (typeof destination !== "string" || !remaining.has(destination)) continue;
23603
+ const awsSource = remaining.get(destination);
23604
+ remaining.delete(destination);
23605
+ const declaredSource = entry["Source"];
23606
+ const declaredDenotesSame = (typeof declaredSource === "number" || typeof declaredSource === "boolean") && String(declaredSource) === awsSource;
23607
+ pairs.push({
23608
+ Destination: destination,
23609
+ Source: declaredDenotesSame ? declaredSource : awsSource
23610
+ });
23611
+ }
23612
+ for (const [destination, source] of [...remaining.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) pairs.push({
23613
+ Destination: destination,
23614
+ Source: source
23615
+ });
23616
+ return pairs;
23617
+ }
23618
+ /**
23516
23619
  * Convert CloudFormation Tags (Array<{Key, Value}>) to SDK Tags (Record<string, string>).
23517
23620
  */
23518
23621
  cfnTagsToRecord(tags) {
@@ -34561,6 +34664,7 @@ const MUTABLE_GRAPHQL_API_CONFIG_PROPERTIES = [
34561
34664
  */
34562
34665
  var AppSyncProvider = class {
34563
34666
  client;
34667
+ s3Client;
34564
34668
  providerRegion = process.env["AWS_REGION"];
34565
34669
  logger = getLogger().child("AppSyncProvider");
34566
34670
  /**
@@ -34611,7 +34715,12 @@ var AppSyncProvider = class {
34611
34715
  "ServiceRoleArn",
34612
34716
  "DynamoDBConfig",
34613
34717
  "LambdaConfig",
34614
- "HttpConfig"
34718
+ "HttpConfig",
34719
+ "ElasticsearchConfig",
34720
+ "EventBridgeConfig",
34721
+ "MetricsConfig",
34722
+ "OpenSearchServiceConfig",
34723
+ "RelationalDatabaseConfig"
34615
34724
  ])],
34616
34725
  ["AWS::AppSync::Resolver", /* @__PURE__ */ new Set([
34617
34726
  "ApiId",
@@ -34623,7 +34732,14 @@ var AppSyncProvider = class {
34623
34732
  "Kind",
34624
34733
  "PipelineConfig",
34625
34734
  "Runtime",
34626
- "Code"
34735
+ "Code",
34736
+ "CachingConfig",
34737
+ "CodeS3Location",
34738
+ "MaxBatchSize",
34739
+ "MetricsConfig",
34740
+ "RequestMappingTemplateS3Location",
34741
+ "ResponseMappingTemplateS3Location",
34742
+ "SyncConfig"
34627
34743
  ])],
34628
34744
  ["AWS::AppSync::ApiKey", /* @__PURE__ */ new Set([
34629
34745
  "ApiId",
@@ -34631,11 +34747,23 @@ var AppSyncProvider = class {
34631
34747
  "Expires"
34632
34748
  ])]
34633
34749
  ]);
34634
- unhandledByDesign = /* @__PURE__ */ new Map([["AWS::AppSync::DataSource", /* @__PURE__ */ new Map([["ElasticsearchConfig", "Legacy Elasticsearch alias; use OpenSearchServiceConfig (AppSync deprecated the Elasticsearch DataSource type in favor of OpenSearch)"]])]]);
34635
34750
  getClient() {
34636
34751
  if (!this.client) this.client = new AppSyncClient(this.providerRegion ? { region: this.providerRegion } : {});
34637
34752
  return this.client;
34638
34753
  }
34754
+ /**
34755
+ * Lazy S3 client for the `*S3Location` properties (Resolver
34756
+ * `CodeS3Location` / `RequestMappingTemplateS3Location` /
34757
+ * `ResponseMappingTemplateS3Location`, GraphQLSchema
34758
+ * `DefinitionS3Location`) — CloudFormation reads the S3 object and inlines
34759
+ * its body, and cdkd mirrors that. Same construction as
34760
+ * `StepFunctionsProvider.getS3Client` (the sibling `DefinitionS3Location`
34761
+ * implementation).
34762
+ */
34763
+ getS3Client() {
34764
+ if (!this.s3Client) this.s3Client = new S3Client(this.providerRegion ? { region: this.providerRegion } : {});
34765
+ return this.s3Client;
34766
+ }
34639
34767
  async create(logicalId, resourceType, properties, context) {
34640
34768
  switch (resourceType) {
34641
34769
  case "AWS::AppSync::GraphQLApi": return this.createGraphQLApi(logicalId, resourceType, properties, context);
@@ -34654,11 +34782,16 @@ var AppSyncProvider = class {
34654
34782
  * `XrayEnabled` / `LogConfig`) + `TagResource` / `UntagResource`
34655
34783
  * for `Tags` diff. `Name` is immutable on AWS.
34656
34784
  * - `DataSource` → `UpdateDataSourceCommand` (`Description` /
34657
- * `ServiceRoleArn` / `DynamoDBConfig` / `LambdaConfig` / `HttpConfig`).
34785
+ * `ServiceRoleArn` / the per-type configs — `DynamoDBConfig` /
34786
+ * `LambdaConfig` / `HttpConfig` / `ElasticsearchConfig` /
34787
+ * `OpenSearchServiceConfig` / `EventBridgeConfig` /
34788
+ * `RelationalDatabaseConfig` — / `MetricsConfig`).
34658
34789
  * `ApiId` / `Name` / `Type` are immutable identity fields.
34659
34790
  * - `Resolver` → `UpdateResolverCommand` (`DataSourceName` /
34660
34791
  * `RequestMappingTemplate` / `ResponseMappingTemplate` / `Kind` /
34661
- * `PipelineConfig` / `Runtime` / `Code`). `ApiId` / `TypeName` /
34792
+ * `PipelineConfig` / `Runtime` / `Code` / `CachingConfig` /
34793
+ * `SyncConfig` / `MaxBatchSize` / `MetricsConfig` + the three
34794
+ * `*S3Location` fetch-and-inline pairs). `ApiId` / `TypeName` /
34662
34795
  * `FieldName` are immutable identity fields.
34663
34796
  * - `ApiKey` → `UpdateApiKeyCommand` (`Description` / `Expires`).
34664
34797
  * `ApiId` is immutable; the AWS-generated key id is immutable.
@@ -34750,7 +34883,23 @@ var AppSyncProvider = class {
34750
34883
  async updateGraphQLSchema(logicalId, physicalId, resourceType, properties, previousProperties) {
34751
34884
  const newDef = properties["Definition"];
34752
34885
  const oldDef = previousProperties["Definition"];
34753
- if (newDef === void 0 || newDef === oldDef) return {
34886
+ const newLoc = properties["DefinitionS3Location"];
34887
+ const oldLoc = previousProperties["DefinitionS3Location"];
34888
+ let definition;
34889
+ if (newDef !== void 0) {
34890
+ if (newLoc !== void 0) this.logger.warn(`${resourceType} ${logicalId}: both Definition and DefinitionS3Location are set — using the inline Definition and ignoring DefinitionS3Location (CFn treats them as alternatives)`);
34891
+ if (newDef === oldDef) return {
34892
+ physicalId,
34893
+ wasReplaced: false
34894
+ };
34895
+ definition = newDef;
34896
+ } else if (newLoc !== void 0) {
34897
+ if (this.deepEqual(newLoc, oldLoc)) return {
34898
+ physicalId,
34899
+ wasReplaced: false
34900
+ };
34901
+ definition = await this.fetchS3LocationBody(newLoc, resourceType, logicalId, "DefinitionS3Location");
34902
+ } else return {
34754
34903
  physicalId,
34755
34904
  wasReplaced: false
34756
34905
  };
@@ -34758,7 +34907,7 @@ var AppSyncProvider = class {
34758
34907
  try {
34759
34908
  await this.getClient().send(new StartSchemaCreationCommand({
34760
34909
  apiId,
34761
- definition: Buffer.from(newDef, "utf-8")
34910
+ definition: Buffer.from(definition, "utf-8")
34762
34911
  }));
34763
34912
  } catch (error) {
34764
34913
  throw this.wrapUpdateError(error, resourceType, logicalId, physicalId, "GraphqlSchema");
@@ -34781,17 +34930,18 @@ var AppSyncProvider = class {
34781
34930
  const [apiId, name] = physicalId.split("|");
34782
34931
  if (!apiId || !name) throw new ProvisioningError(`Invalid DataSource physical ID format: ${physicalId}`, resourceType, logicalId, physicalId);
34783
34932
  const type = properties["Type"] ?? previousProperties["Type"];
34784
- const newDesc = properties["Description"];
34785
- const oldDesc = previousProperties["Description"];
34786
- const newRole = properties["ServiceRoleArn"];
34787
- const oldRole = previousProperties["ServiceRoleArn"];
34788
- const newDDB = properties["DynamoDBConfig"];
34789
- const oldDDB = previousProperties["DynamoDBConfig"];
34790
- const newLambda = properties["LambdaConfig"];
34791
- const oldLambda = previousProperties["LambdaConfig"];
34792
- const newHttp = properties["HttpConfig"];
34793
- const oldHttp = previousProperties["HttpConfig"];
34794
- if (!(newDesc !== oldDesc || newRole !== oldRole || !this.deepEqual(newDDB, oldDDB) || !this.deepEqual(newLambda, oldLambda) || !this.deepEqual(newHttp, oldHttp))) return {
34933
+ if (![
34934
+ "Description",
34935
+ "ServiceRoleArn",
34936
+ "DynamoDBConfig",
34937
+ "LambdaConfig",
34938
+ "HttpConfig",
34939
+ "ElasticsearchConfig",
34940
+ "OpenSearchServiceConfig",
34941
+ "EventBridgeConfig",
34942
+ "RelationalDatabaseConfig",
34943
+ "MetricsConfig"
34944
+ ].some((key) => (key in properties || key in previousProperties) && !this.deepEqual(properties[key], previousProperties[key]))) return {
34795
34945
  physicalId,
34796
34946
  wasReplaced: false
34797
34947
  };
@@ -34800,15 +34950,7 @@ var AppSyncProvider = class {
34800
34950
  name,
34801
34951
  type
34802
34952
  };
34803
- if (newDesc !== void 0) input.description = newDesc;
34804
- if (newRole !== void 0) input.serviceRoleArn = newRole;
34805
- if (newDDB !== void 0) input.dynamodbConfig = {
34806
- tableName: newDDB["TableName"],
34807
- awsRegion: newDDB["AwsRegion"],
34808
- useCallerCredentials: newDDB["UseCallerCredentials"]
34809
- };
34810
- if (newLambda !== void 0) input.lambdaConfig = { lambdaFunctionArn: newLambda["LambdaFunctionArn"] };
34811
- if (newHttp !== void 0) input.httpConfig = { endpoint: newHttp["Endpoint"] };
34953
+ this.applyDataSourceConfig(input, resourceType, logicalId, properties);
34812
34954
  try {
34813
34955
  await this.getClient().send(new UpdateDataSourceCommand(input));
34814
34956
  } catch (error) {
@@ -34839,8 +34981,15 @@ var AppSyncProvider = class {
34839
34981
  "Kind",
34840
34982
  "PipelineConfig",
34841
34983
  "Runtime",
34842
- "Code"
34843
- ].some((key) => !this.deepEqual(properties[key], previousProperties[key]))) return {
34984
+ "Code",
34985
+ "CachingConfig",
34986
+ "SyncConfig",
34987
+ "MaxBatchSize",
34988
+ "MetricsConfig",
34989
+ "CodeS3Location",
34990
+ "RequestMappingTemplateS3Location",
34991
+ "ResponseMappingTemplateS3Location"
34992
+ ].some((key) => (key in properties || key in previousProperties) && !this.deepEqual(properties[key], previousProperties[key]))) return {
34844
34993
  physicalId,
34845
34994
  wasReplaced: false
34846
34995
  };
@@ -34849,20 +34998,7 @@ var AppSyncProvider = class {
34849
34998
  typeName,
34850
34999
  fieldName
34851
35000
  };
34852
- const effectiveKind = properties["Kind"] ?? previousProperties["Kind"] ?? "UNIT";
34853
- if (effectiveKind === "UNIT" && properties["DataSourceName"] !== void 0) input.dataSourceName = properties["DataSourceName"];
34854
- if (properties["RequestMappingTemplate"] !== void 0) input.requestMappingTemplate = properties["RequestMappingTemplate"];
34855
- if (properties["ResponseMappingTemplate"] !== void 0) input.responseMappingTemplate = properties["ResponseMappingTemplate"];
34856
- if (properties["Kind"] !== void 0) input.kind = properties["Kind"];
34857
- if (effectiveKind === "PIPELINE" && properties["PipelineConfig"] !== void 0) input.pipelineConfig = { functions: properties["PipelineConfig"]["Functions"] };
34858
- if (properties["Runtime"] !== void 0) {
34859
- const runtime = properties["Runtime"];
34860
- input.runtime = {
34861
- name: runtime["Name"],
34862
- runtimeVersion: runtime["RuntimeVersion"]
34863
- };
34864
- }
34865
- if (properties["Code"] !== void 0) input.code = properties["Code"];
35001
+ await this.applyResolverConfig(input, resourceType, logicalId, properties, previousProperties, true);
34866
35002
  try {
34867
35003
  await this.getClient().send(new UpdateResolverCommand(input));
34868
35004
  } catch (error) {
@@ -34983,6 +35119,11 @@ var AppSyncProvider = class {
34983
35119
  */
34984
35120
  getDriftUnknownPaths(resourceType) {
34985
35121
  if (resourceType === "AWS::AppSync::GraphQLSchema") return ["DefinitionS3Location"];
35122
+ if (resourceType === "AWS::AppSync::Resolver") return [
35123
+ "CodeS3Location",
35124
+ "RequestMappingTemplateS3Location",
35125
+ "ResponseMappingTemplateS3Location"
35126
+ ];
34986
35127
  return [];
34987
35128
  }
34988
35129
  /**
@@ -34995,15 +35136,15 @@ var AppSyncProvider = class {
34995
35136
  * possibly-malformed value"). An ABSENT container keeps returning
34996
35137
  * `undefined`, which is a legitimate "not configured".
34997
35138
  */
34998
- asObject(value, path, options) {
35139
+ asObject(value, path, options, resourceType = "AWS::AppSync::GraphQLApi") {
34999
35140
  if (value == null) return void 0;
35000
35141
  if (typeof value === "object" && !Array.isArray(value)) return value;
35001
- const message = `AWS::AppSync::GraphQLApi ${path} must be an object, got ${Array.isArray(value) ? "array" : typeof value} — cdkd refuses to drop it silently`;
35142
+ const message = `${resourceType} ${path} must be an object, got ${Array.isArray(value) ? "array" : typeof value} — cdkd refuses to drop it silently`;
35002
35143
  if (options?.onUnusable) {
35003
35144
  options.onUnusable(message);
35004
35145
  return;
35005
35146
  }
35006
- throw new ProvisioningError(message, "AWS::AppSync::GraphQLApi", options?.logicalId ?? "");
35147
+ throw new ProvisioningError(message, resourceType, options?.logicalId ?? "");
35007
35148
  }
35008
35149
  /**
35009
35150
  * NOTE: `awsRegion` / `defaultAction` are passed through VERBATIM, including
@@ -35181,6 +35322,211 @@ var AppSyncProvider = class {
35181
35322
  throw this.wrapUpdateError(error, resourceType, logicalId, apiId, isUpdate ? "GraphqlApi (env vars)" : "GraphqlApi (env vars, post-create)");
35182
35323
  }
35183
35324
  }
35325
+ /** CFn `CachingConfig` (`Ttl` / `CachingKeys`) -> SDK `cachingConfig`. */
35326
+ toSdkCachingConfig(value, logicalId) {
35327
+ const cfn = this.asObject(value, "CachingConfig", { logicalId }, "AWS::AppSync::Resolver");
35328
+ if (!cfn) return void 0;
35329
+ const out = { ttl: cfn["Ttl"] };
35330
+ if (cfn["CachingKeys"] !== void 0) out.cachingKeys = requireConfigArray(cfn["CachingKeys"], "AWS::AppSync::Resolver CachingConfig.CachingKeys");
35331
+ return out;
35332
+ }
35333
+ /**
35334
+ * CFn `SyncConfig` (`ConflictDetection` / `ConflictHandler` /
35335
+ * `LambdaConflictHandlerConfig.LambdaConflictHandlerArn`) -> SDK
35336
+ * `syncConfig`.
35337
+ */
35338
+ toSdkSyncConfig(value, logicalId) {
35339
+ const cfn = this.asObject(value, "SyncConfig", { logicalId }, "AWS::AppSync::Resolver");
35340
+ if (!cfn) return void 0;
35341
+ const out = {};
35342
+ if (cfn["ConflictDetection"] !== void 0) out.conflictDetection = cfn["ConflictDetection"];
35343
+ if (cfn["ConflictHandler"] !== void 0) out.conflictHandler = cfn["ConflictHandler"];
35344
+ const lambdaHandler = this.asObject(cfn["LambdaConflictHandlerConfig"], "SyncConfig.LambdaConflictHandlerConfig", { logicalId }, "AWS::AppSync::Resolver");
35345
+ if (lambdaHandler) out.lambdaConflictHandlerConfig = { lambdaConflictHandlerArn: lambdaHandler["LambdaConflictHandlerArn"] };
35346
+ return out;
35347
+ }
35348
+ /**
35349
+ * Parse the `s3://bucket/key` URL the AppSync `*S3Location` string
35350
+ * properties carry.
35351
+ *
35352
+ * CDK's asset bindings (`appsync.Code.fromAsset`, `aws-s3-assets` Asset
35353
+ * `s3ObjectUrl`) emit exactly this form, and the CloudFormation examples
35354
+ * for these properties use it too. Anything else (an `https://` object
35355
+ * URL, a bare bucket, a non-string) is refused with a clear error naming
35356
+ * the property — never silently dropped.
35357
+ */
35358
+ parseS3LocationUrl(location, resourceType, logicalId, propertyPath) {
35359
+ if (typeof location !== "string") throw new ProvisioningError(`${resourceType} ${propertyPath} must be a string s3://bucket/key URL, got ${Array.isArray(location) ? "array" : typeof location} — check for an unresolved intrinsic`, resourceType, logicalId);
35360
+ const match = /^s3:\/\/([^/]+)\/(.+)$/.exec(location);
35361
+ if (!match || !match[1] || !match[2]) throw new ProvisioningError(`${resourceType} ${propertyPath} must be an s3://bucket/key URL (got '${location}')`, resourceType, logicalId);
35362
+ return {
35363
+ bucket: match[1],
35364
+ key: match[2]
35365
+ };
35366
+ }
35367
+ /**
35368
+ * Fetch an `*S3Location` object body as a UTF-8 string.
35369
+ *
35370
+ * CloudFormation's behavior for these properties is to download the S3
35371
+ * object and pass its CONTENT as the corresponding inline member
35372
+ * (`code` / `requestMappingTemplate` / `responseMappingTemplate` /
35373
+ * `definition`); the URL itself never reaches AppSync. Mirrors
35374
+ * `StepFunctionsProvider.fetchDefinitionFromS3`, including the
35375
+ * empty-body refusal (AWS would reject '' with a generic validation
35376
+ * error naming nothing).
35377
+ */
35378
+ async fetchS3LocationBody(location, resourceType, logicalId, propertyPath) {
35379
+ const { bucket, key } = this.parseS3LocationUrl(location, resourceType, logicalId, propertyPath);
35380
+ this.logger.debug(`Fetching ${propertyPath} for ${logicalId} from s3://${bucket}/${key}`);
35381
+ let body;
35382
+ try {
35383
+ const resp = await this.getS3Client().send(new GetObjectCommand({
35384
+ Bucket: bucket,
35385
+ Key: key
35386
+ }));
35387
+ if (!resp.Body) throw new Error(`object returned no body`);
35388
+ body = await resp.Body.transformToString();
35389
+ } catch (error) {
35390
+ if (error instanceof ProvisioningError) throw error;
35391
+ throw new ProvisioningError(`Failed to fetch ${propertyPath} s3://${bucket}/${key} for ${logicalId}: ${error instanceof Error ? error.message : String(error)}`, resourceType, logicalId, void 0, error instanceof Error ? error : void 0);
35392
+ }
35393
+ if (body.length === 0) throw new ProvisioningError(`${propertyPath} object s3://${bucket}/${key} returned an empty body`, resourceType, logicalId);
35394
+ return body;
35395
+ }
35396
+ /**
35397
+ * Resolve an inline-or-S3 property pair to the inline SDK value.
35398
+ *
35399
+ * Precedence: the INLINE property wins when both are present, matching
35400
+ * the repo's established S3Location precedent (StepFunctions
35401
+ * `DefinitionString` over `DefinitionS3Location`, AppSync GraphQLSchema
35402
+ * `Definition` over `DefinitionS3Location`) — CFn treats the pair as
35403
+ * alternatives and CDK's bindings only ever emit one, so both-present is
35404
+ * a hand-authored template; it is honored inline-first with a loud
35405
+ * warning rather than refused.
35406
+ */
35407
+ async resolveInlineOrS3Template(inline, s3Location, inlineKey, s3Key, resourceType, logicalId) {
35408
+ if (inline !== void 0) {
35409
+ if (s3Location !== void 0) this.logger.warn(`${resourceType} ${logicalId}: both ${inlineKey} and ${s3Key} are set — using the inline ${inlineKey} and ignoring ${s3Key} (CFn treats them as alternatives)`);
35410
+ return inline;
35411
+ }
35412
+ if (s3Location !== void 0) return this.fetchS3LocationBody(s3Location, resourceType, logicalId, s3Key);
35413
+ }
35414
+ /**
35415
+ * Fill the mutable members shared by `CreateResolver` and `UpdateResolver`
35416
+ * from the CFn property bag — the single mapper both paths use so they
35417
+ * cannot diverge (the `applyGraphQLApiConfig` pattern).
35418
+ *
35419
+ * REMOVAL semantics: unlike `UpdateGraphqlApi` (where an omitted member is
35420
+ * "no change" and removal needs reset sentinels), `UpdateResolver` is a
35421
+ * FULL-REPLACE write — the desired bag is mapped in its entirety and an
35422
+ * omitted member is cleared server-side, so "absent in template => omitted
35423
+ * from the update input => cleared by AppSync" and no sentinel is needed.
35424
+ * That semantic is pinned live by the integ fixture's REMOVAL phase
35425
+ * (tests/integration/appsync/verify.sh drops the resolver's MetricsConfig
35426
+ * and asserts AWS reset it while a sibling DataSource keeps its own).
35427
+ * MetricsConfig is the one member A/B'd against real AWS; the other
35428
+ * members rest on the same UpdateResolver full-replace API contract, not
35429
+ * on a per-member live probe (the #1160 doctrine's per-member A/B was
35430
+ * deliberately not paid here — recorded in the PR body).
35431
+ *
35432
+ * Resolver shape is `Kind`-discriminated:
35433
+ * - Kind=UNIT -> DataSourceName applies, PipelineConfig is N/A.
35434
+ * - Kind=PIPELINE -> PipelineConfig applies, DataSourceName is N/A
35435
+ * (AWS rejects the call if it's set).
35436
+ * The gate applies ONLY on update (`forUpdate`), where the effective Kind
35437
+ * falls back to the state-recorded previous value and finally AWS's
35438
+ * default 'UNIT' — an update must not re-send the other kind's member on a
35439
+ * live resolver. On CREATE both members forward UNCONDITIONALLY when
35440
+ * present: gating there would silently drop a crossed shape (the very
35441
+ * silent-drop class this batch closes) where CFn parity is AWS's own loud
35442
+ * rejection — the same forward-don't-gate doctrine as
35443
+ * `applyDataSourceConfig`'s per-type sub-configs.
35444
+ */
35445
+ async applyResolverConfig(input, resourceType, logicalId, properties, previousProperties, forUpdate = false) {
35446
+ const effectiveKind = properties["Kind"] ?? previousProperties?.["Kind"] ?? "UNIT";
35447
+ if ((!forUpdate || effectiveKind === "UNIT") && properties["DataSourceName"] !== void 0) input.dataSourceName = properties["DataSourceName"];
35448
+ if (properties["Kind"] !== void 0) input.kind = properties["Kind"];
35449
+ if ((!forUpdate || effectiveKind === "PIPELINE") && properties["PipelineConfig"] !== void 0) input.pipelineConfig = { functions: properties["PipelineConfig"]["Functions"] };
35450
+ if (properties["Runtime"] !== void 0) {
35451
+ const runtime = properties["Runtime"];
35452
+ input.runtime = {
35453
+ name: runtime["Name"],
35454
+ runtimeVersion: runtime["RuntimeVersion"]
35455
+ };
35456
+ }
35457
+ const requestTemplate = await this.resolveInlineOrS3Template(properties["RequestMappingTemplate"], properties["RequestMappingTemplateS3Location"], "RequestMappingTemplate", "RequestMappingTemplateS3Location", resourceType, logicalId);
35458
+ if (requestTemplate !== void 0) input.requestMappingTemplate = requestTemplate;
35459
+ const responseTemplate = await this.resolveInlineOrS3Template(properties["ResponseMappingTemplate"], properties["ResponseMappingTemplateS3Location"], "ResponseMappingTemplate", "ResponseMappingTemplateS3Location", resourceType, logicalId);
35460
+ if (responseTemplate !== void 0) input.responseMappingTemplate = responseTemplate;
35461
+ const code = await this.resolveInlineOrS3Template(properties["Code"], properties["CodeS3Location"], "Code", "CodeS3Location", resourceType, logicalId);
35462
+ if (code !== void 0) input.code = code;
35463
+ const caching = this.toSdkCachingConfig(properties["CachingConfig"], logicalId);
35464
+ if (caching) input.cachingConfig = caching;
35465
+ const sync = this.toSdkSyncConfig(properties["SyncConfig"], logicalId);
35466
+ if (sync) input.syncConfig = sync;
35467
+ if (properties["MaxBatchSize"] !== void 0) input.maxBatchSize = properties["MaxBatchSize"];
35468
+ if (properties["MetricsConfig"] !== void 0) input.metricsConfig = properties["MetricsConfig"];
35469
+ }
35470
+ /**
35471
+ * Fill the mutable members shared by `CreateDataSource` and
35472
+ * `UpdateDataSource` from the CFn property bag — single mapper for both
35473
+ * paths (the `applyGraphQLApiConfig` pattern).
35474
+ *
35475
+ * REMOVAL semantics: `UpdateDataSource` is a FULL-REPLACE write like
35476
+ * `UpdateResolver` — an omitted member is cleared server-side, so removal
35477
+ * needs no reset sentinel (see `applyResolverConfig`'s note; pinned live
35478
+ * by the integ fixture's REMOVAL phase).
35479
+ *
35480
+ * The per-type sub-configs (`DynamoDBConfig` / `LambdaConfig` /
35481
+ * `HttpConfig` / `ElasticsearchConfig` / `OpenSearchServiceConfig` /
35482
+ * `EventBridgeConfig` / `RelationalDatabaseConfig`) are mutually exclusive
35483
+ * on the `Type` discriminator. The write side deliberately FORWARDS
35484
+ * whichever block the template carries rather than gating on `Type`:
35485
+ * gating would silently drop a mismatched block (the very silent-drop
35486
+ * class this batch closes), while AWS rejects a crossed shape with a loud
35487
+ * validation error. The read side (`readDataSource`) gates on the
35488
+ * response's own discriminator instead.
35489
+ */
35490
+ applyDataSourceConfig(input, resourceType, logicalId, properties) {
35491
+ if (properties["Description"] !== void 0) input.description = properties["Description"];
35492
+ if (properties["ServiceRoleArn"] !== void 0) input.serviceRoleArn = properties["ServiceRoleArn"];
35493
+ const dynamo = this.asObject(properties["DynamoDBConfig"], "DynamoDBConfig", { logicalId }, resourceType);
35494
+ if (dynamo) input.dynamodbConfig = {
35495
+ tableName: dynamo["TableName"],
35496
+ awsRegion: dynamo["AwsRegion"],
35497
+ ...dynamo["UseCallerCredentials"] !== void 0 ? { useCallerCredentials: dynamo["UseCallerCredentials"] } : {}
35498
+ };
35499
+ const lambda = this.asObject(properties["LambdaConfig"], "LambdaConfig", { logicalId }, resourceType);
35500
+ if (lambda) input.lambdaConfig = { lambdaFunctionArn: lambda["LambdaFunctionArn"] };
35501
+ const http = this.asObject(properties["HttpConfig"], "HttpConfig", { logicalId }, resourceType);
35502
+ if (http) input.httpConfig = { endpoint: http["Endpoint"] };
35503
+ const es = this.asObject(properties["ElasticsearchConfig"], "ElasticsearchConfig", { logicalId }, resourceType);
35504
+ if (es) input.elasticsearchConfig = {
35505
+ endpoint: es["Endpoint"],
35506
+ awsRegion: es["AwsRegion"]
35507
+ };
35508
+ const openSearch = this.asObject(properties["OpenSearchServiceConfig"], "OpenSearchServiceConfig", { logicalId }, resourceType);
35509
+ if (openSearch) input.openSearchServiceConfig = {
35510
+ endpoint: openSearch["Endpoint"],
35511
+ awsRegion: openSearch["AwsRegion"]
35512
+ };
35513
+ const eventBridge = this.asObject(properties["EventBridgeConfig"], "EventBridgeConfig", { logicalId }, resourceType);
35514
+ if (eventBridge) input.eventBridgeConfig = { eventBusArn: eventBridge["EventBusArn"] };
35515
+ const relational = this.asObject(properties["RelationalDatabaseConfig"], "RelationalDatabaseConfig", { logicalId }, resourceType);
35516
+ if (relational) {
35517
+ const rdsConfig = { relationalDatabaseSourceType: relational["RelationalDatabaseSourceType"] };
35518
+ const rdsEndpoint = this.asObject(relational["RdsHttpEndpointConfig"], "RelationalDatabaseConfig.RdsHttpEndpointConfig", { logicalId }, resourceType);
35519
+ if (rdsEndpoint) rdsConfig.rdsHttpEndpointConfig = {
35520
+ awsRegion: rdsEndpoint["AwsRegion"],
35521
+ dbClusterIdentifier: rdsEndpoint["DbClusterIdentifier"],
35522
+ ...rdsEndpoint["DatabaseName"] !== void 0 ? { databaseName: rdsEndpoint["DatabaseName"] } : {},
35523
+ ...rdsEndpoint["Schema"] !== void 0 ? { schema: rdsEndpoint["Schema"] } : {},
35524
+ ...rdsEndpoint["AwsSecretStoreArn"] !== void 0 ? { awsSecretStoreArn: rdsEndpoint["AwsSecretStoreArn"] } : {}
35525
+ };
35526
+ input.relationalDatabaseConfig = rdsConfig;
35527
+ }
35528
+ if (properties["MetricsConfig"] !== void 0) input.metricsConfig = properties["MetricsConfig"];
35529
+ }
35184
35530
  async createGraphQLApi(logicalId, resourceType, properties, context) {
35185
35531
  this.logger.debug(`Creating GraphQL API ${logicalId}`);
35186
35532
  const name = properties["Name"];
@@ -35265,11 +35611,19 @@ var AppSyncProvider = class {
35265
35611
  const definition = properties["Definition"];
35266
35612
  const definitionS3Location = properties["DefinitionS3Location"];
35267
35613
  try {
35268
- if (definition) await this.getClient().send(new StartSchemaCreationCommand({
35269
- apiId,
35270
- definition: new TextEncoder().encode(definition)
35271
- }));
35272
- else if (definitionS3Location) this.logger.warn(`S3-based schema definition for ${logicalId} - using inline only`);
35614
+ if (definition) {
35615
+ if (definitionS3Location !== void 0) this.logger.warn(`${resourceType} ${logicalId}: both Definition and DefinitionS3Location are set — using the inline Definition and ignoring DefinitionS3Location (CFn treats them as alternatives)`);
35616
+ await this.getClient().send(new StartSchemaCreationCommand({
35617
+ apiId,
35618
+ definition: new TextEncoder().encode(definition)
35619
+ }));
35620
+ } else if (definitionS3Location !== void 0) {
35621
+ const body = await this.fetchS3LocationBody(definitionS3Location, resourceType, logicalId, "DefinitionS3Location");
35622
+ await this.getClient().send(new StartSchemaCreationCommand({
35623
+ apiId,
35624
+ definition: new TextEncoder().encode(body)
35625
+ }));
35626
+ }
35273
35627
  this.logger.debug(`Successfully started schema creation for ${logicalId}`);
35274
35628
  return {
35275
35629
  physicalId: apiId,
@@ -35292,18 +35646,7 @@ var AppSyncProvider = class {
35292
35646
  name,
35293
35647
  type
35294
35648
  };
35295
- if (properties["Description"]) input.description = properties["Description"];
35296
- if (properties["ServiceRoleArn"]) input.serviceRoleArn = properties["ServiceRoleArn"];
35297
- if (properties["DynamoDBConfig"]) {
35298
- const config = properties["DynamoDBConfig"];
35299
- input.dynamodbConfig = {
35300
- tableName: config["TableName"],
35301
- awsRegion: config["AwsRegion"],
35302
- useCallerCredentials: config["UseCallerCredentials"]
35303
- };
35304
- }
35305
- if (properties["LambdaConfig"]) input.lambdaConfig = { lambdaFunctionArn: properties["LambdaConfig"]["LambdaFunctionArn"] };
35306
- if (properties["HttpConfig"]) input.httpConfig = { endpoint: properties["HttpConfig"]["Endpoint"] };
35649
+ this.applyDataSourceConfig(input, resourceType, logicalId, properties);
35307
35650
  await this.getClient().send(new CreateDataSourceCommand(input));
35308
35651
  const physicalId = `${apiId}|${name}`;
35309
35652
  this.logger.debug(`Successfully created DataSource ${logicalId}: ${physicalId}`);
@@ -35354,19 +35697,7 @@ var AppSyncProvider = class {
35354
35697
  typeName,
35355
35698
  fieldName
35356
35699
  };
35357
- if (properties["DataSourceName"]) input.dataSourceName = properties["DataSourceName"];
35358
- if (properties["RequestMappingTemplate"]) input.requestMappingTemplate = properties["RequestMappingTemplate"];
35359
- if (properties["ResponseMappingTemplate"]) input.responseMappingTemplate = properties["ResponseMappingTemplate"];
35360
- if (properties["Kind"]) input.kind = properties["Kind"];
35361
- if (properties["PipelineConfig"]) input.pipelineConfig = { functions: properties["PipelineConfig"]["Functions"] };
35362
- if (properties["Runtime"]) {
35363
- const runtime = properties["Runtime"];
35364
- input.runtime = {
35365
- name: runtime["Name"],
35366
- runtimeVersion: runtime["RuntimeVersion"]
35367
- };
35368
- }
35369
- if (properties["Code"]) input.code = properties["Code"];
35700
+ await this.applyResolverConfig(input, resourceType, logicalId, properties);
35370
35701
  await this.getClient().send(new CreateResolverCommand(input));
35371
35702
  const physicalId = `${apiId}|${typeName}|${fieldName}`;
35372
35703
  this.logger.debug(`Successfully created Resolver ${logicalId}: ${physicalId}`);
@@ -35465,10 +35796,13 @@ var AppSyncProvider = class {
35465
35796
  * CDK's `aws:*` auto-tags are filtered out and the result key is
35466
35797
  * omitted when no user tags remain.
35467
35798
  * - `DataSource` → `GetDataSource` (Name, Type, Description,
35468
- * ServiceRoleArn, DynamoDBConfig, LambdaConfig, HttpConfig). The
35799
+ * ServiceRoleArn, the per-type configs — DynamoDBConfig, LambdaConfig,
35800
+ * HttpConfig, ElasticsearchConfig, OpenSearchServiceConfig,
35801
+ * EventBridgeConfig, RelationalDatabaseConfig — and MetricsConfig). The
35469
35802
  * `ApiId` cdkd holds is recovered from the `apiId|name` physicalId.
35470
35803
  * - `Resolver` → `GetResolver` (TypeName, FieldName, DataSourceName,
35471
- * request/response templates, Kind, PipelineConfig, Runtime, Code).
35804
+ * request/response templates, Kind, PipelineConfig, Runtime, Code,
35805
+ * CachingConfig, SyncConfig, MaxBatchSize, MetricsConfig).
35472
35806
  * - `ApiKey` → `ListApiKeys` filtered by id (no `GetApiKey` SDK call;
35473
35807
  * AppSync only exposes list-based access). Surfaces Description and
35474
35808
  * Expires.
@@ -35646,6 +35980,35 @@ var AppSyncProvider = class {
35646
35980
  }
35647
35981
  if (ds.lambdaConfig?.lambdaFunctionArn !== void 0) result["LambdaConfig"] = { LambdaFunctionArn: ds.lambdaConfig.lambdaFunctionArn };
35648
35982
  if (ds.httpConfig?.endpoint !== void 0) result["HttpConfig"] = { Endpoint: ds.httpConfig.endpoint };
35983
+ if (ds.elasticsearchConfig) {
35984
+ const es = {};
35985
+ if (ds.elasticsearchConfig.endpoint !== void 0) es["Endpoint"] = ds.elasticsearchConfig.endpoint;
35986
+ if (ds.elasticsearchConfig.awsRegion !== void 0) es["AwsRegion"] = ds.elasticsearchConfig.awsRegion;
35987
+ if (Object.keys(es).length > 0) result["ElasticsearchConfig"] = es;
35988
+ }
35989
+ if (ds.openSearchServiceConfig) {
35990
+ const os = {};
35991
+ if (ds.openSearchServiceConfig.endpoint !== void 0) os["Endpoint"] = ds.openSearchServiceConfig.endpoint;
35992
+ if (ds.openSearchServiceConfig.awsRegion !== void 0) os["AwsRegion"] = ds.openSearchServiceConfig.awsRegion;
35993
+ if (Object.keys(os).length > 0) result["OpenSearchServiceConfig"] = os;
35994
+ }
35995
+ if (ds.eventBridgeConfig?.eventBusArn !== void 0) result["EventBridgeConfig"] = { EventBusArn: ds.eventBridgeConfig.eventBusArn };
35996
+ if (ds.relationalDatabaseConfig) {
35997
+ const rds = {};
35998
+ if (ds.relationalDatabaseConfig.relationalDatabaseSourceType !== void 0) rds["RelationalDatabaseSourceType"] = ds.relationalDatabaseConfig.relationalDatabaseSourceType;
35999
+ const endpoint = ds.relationalDatabaseConfig.rdsHttpEndpointConfig;
36000
+ if (endpoint) {
36001
+ const ep = {};
36002
+ if (endpoint.awsRegion !== void 0) ep["AwsRegion"] = endpoint.awsRegion;
36003
+ if (endpoint.dbClusterIdentifier !== void 0) ep["DbClusterIdentifier"] = endpoint.dbClusterIdentifier;
36004
+ if (endpoint.databaseName !== void 0) ep["DatabaseName"] = endpoint.databaseName;
36005
+ if (endpoint.schema !== void 0) ep["Schema"] = endpoint.schema;
36006
+ if (endpoint.awsSecretStoreArn !== void 0) ep["AwsSecretStoreArn"] = endpoint.awsSecretStoreArn;
36007
+ if (Object.keys(ep).length > 0) rds["RdsHttpEndpointConfig"] = ep;
36008
+ }
36009
+ if (Object.keys(rds).length > 0) result["RelationalDatabaseConfig"] = rds;
36010
+ }
36011
+ if (ds.metricsConfig !== void 0) result["MetricsConfig"] = ds.metricsConfig;
35649
36012
  return result;
35650
36013
  }
35651
36014
  async readResolver(physicalId) {
@@ -35680,6 +36043,21 @@ var AppSyncProvider = class {
35680
36043
  result["RequestMappingTemplate"] = resolver.requestMappingTemplate ?? "";
35681
36044
  result["ResponseMappingTemplate"] = resolver.responseMappingTemplate ?? "";
35682
36045
  }
36046
+ if (resolver.cachingConfig) {
36047
+ const caching = {};
36048
+ if (resolver.cachingConfig.ttl !== void 0) caching["Ttl"] = resolver.cachingConfig.ttl;
36049
+ if (resolver.cachingConfig.cachingKeys !== void 0) caching["CachingKeys"] = [...resolver.cachingConfig.cachingKeys];
36050
+ if (Object.keys(caching).length > 0) result["CachingConfig"] = caching;
36051
+ }
36052
+ if (resolver.syncConfig) {
36053
+ const sync = {};
36054
+ if (resolver.syncConfig.conflictDetection !== void 0) sync["ConflictDetection"] = resolver.syncConfig.conflictDetection;
36055
+ if (resolver.syncConfig.conflictHandler !== void 0) sync["ConflictHandler"] = resolver.syncConfig.conflictHandler;
36056
+ if (resolver.syncConfig.lambdaConflictHandlerConfig?.lambdaConflictHandlerArn !== void 0) sync["LambdaConflictHandlerConfig"] = { LambdaConflictHandlerArn: resolver.syncConfig.lambdaConflictHandlerConfig.lambdaConflictHandlerArn };
36057
+ if (Object.keys(sync).length > 0) result["SyncConfig"] = sync;
36058
+ }
36059
+ if (resolver.maxBatchSize !== void 0) result["MaxBatchSize"] = resolver.maxBatchSize;
36060
+ if (resolver.metricsConfig !== void 0) result["MetricsConfig"] = resolver.metricsConfig;
35683
36061
  return result;
35684
36062
  }
35685
36063
  async readApiKey(physicalId) {
@@ -51937,7 +52315,7 @@ async function runDriftForStack(stackName, region, stateBackend, providerRegistr
51937
52315
  });
51938
52316
  continue;
51939
52317
  }
51940
- const ignorePaths = provider.getDriftUnknownPaths ? provider.getDriftUnknownPaths(resource.resourceType) : [];
52318
+ const ignorePaths = provider.getDriftUnknownPaths ? provider.getDriftUnknownPaths(resource.resourceType, resource.properties ?? {}) : [];
51941
52319
  const unorderedPaths = provider.getDriftUnorderedPaths ? provider.getDriftUnorderedPaths(resource.resourceType) : [];
51942
52320
  const useObserved = resource.observedProperties !== void 0;
51943
52321
  const baseline = useObserved ? resource.observedProperties : resource.properties ?? {};
@@ -69419,7 +69797,7 @@ function createMigrateCommand() {
69419
69797
  */
69420
69798
  function buildProgram() {
69421
69799
  const program = new Command();
69422
- program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.280.16");
69800
+ program.name("cdkd").description("CDK Direct - Deploy AWS CDK apps directly via SDK/Cloud Control API").version("0.280.18");
69423
69801
  program.hook("preAction", (_thisCommand, actionCommand) => {
69424
69802
  const { profile } = actionCommand.optsWithGlobals();
69425
69803
  if (profile !== void 0) process.env["AWS_PROFILE"] = profile;