import type { CloudAvailability, CloudDescriptor, CloudProvider, CloudResource, CloudServiceAdapter, CloudServiceDescriptor, CloudServiceType, CloudServiceStatus, CloudStatus, CosmosContainer, CosmosItem, CosmosQueryResult, CreateDatabaseSnapshotInput, CreateKubernetesFargateProfileInput, CreateKubernetesNodegroupInput, CreateResourceInput, AppConfigConfigurationProfile, AppConfigDeployment, AppConfigDeploymentStrategy, AppConfigEnvironment, AppConfigHostedConfigurationVersion, ApiGatewayLambdaRouteInput, ApiGatewayLambdaRouteResult, ApiGatewayTestRouteInput, ApiGatewayTestRouteResult, ApiGatewayMethodTestInput, ApiGatewayMethodTestResult, ApiGatewayRoutesResult, ApiGatewayDeleteRouteInput, ApiGatewayCorsInput, ApiGatewayCorsResult, S3LambdaTriggerInput, S3LambdaTriggerOverview, S3BucketWebsiteConfiguration, EventBridgeLambdaTriggerInput, EventBridgeLambdaTriggerOverview, EventBridgeRuleInput, EventBridgeRuleOverview, EventBridgePutEventInput, EventBridgePutEventResult, CloudFormationChangeSet, CloudFormationChangeSetInput, SqsLambdaEventSourceMappingInput, SqsLambdaEventSourceMappingOverview, KinesisPutRecordInput, KinesisRecord, LambdaEventSourceMappingInput, LambdaEventSourceMappingOverview, SqsMessage, SqsMoveTaskInput, SqsMoveTaskResult, SqsReceiveMessagesInput, SqsSendMessageInput, DatabaseSnapshot, KubernetesFargateProfile, KubernetesNodegroup, LogsInsightsQueryInput, LogsInsightsQueryResult, LogsFilterInput, KmsDecryptInput, KmsDecryptResult, KmsEncryptInput, KmsEncryptResult, PublishNotificationInput, PublishNotificationResult, SnsSubscriptionInput, SnsSubscriptionOverview, ResourceQuery, ServerlessInvokeResult, SqlConnectionInput, SqlDatabase, SqlQueryResult, SqlTable, RuntimeReachability, ServiceSchema, StorageObjectDownload, StorageObjectList, UpdateResourceInput, CreateUserPoolGroupInput, UserPoolGroup, UserPoolSignInTestInput, UserPoolSignInTestResult, UserPoolClientSettings, DistributionInvalidation, UpdateUserPoolClientOAuthInput, UserPoolUserAction, } from '../cloud-spi/types' import type { ChildCollection, ChildItem, CollectionPage, DocumentStoreAdapter, ItemStoreAdapter, ItemQuery, PageQuery, } from '../cloud-spi/childCollections' import {NotSupportedError} from '../cloud-spi/errors' import {CloudAdapterRegistry} from '../registry/CloudAdapterRegistry' import type {StartWorkflowExecutionInput, WorkflowExecutionDetail, WorkflowExecutionHistoryPage, WorkflowExecutionSummary} from '../cloud-spi/workflowExecutions' import type {CodeBuildBuild, StartCodeBuildInput} from '../cloud-spi/codebuild' import type {PipelineExecutionSummary, PipelineState} from '../cloud-spi/codepipeline' import type {CodeDeployDeployment, StartCodeDeployDeploymentInput} from '../cloud-spi/codedeploy' import {SERVICE_CATALOG_ENTRIES, displayNameFor, routeFor} from '../cloud-spi/serviceCatalog' import {toHttpError} from '../cloud-spi/errors' import {mapAwsSdkError} from '../adapter-aws/awsErrors' import {endpointFor, runtimeProbes, type RuntimeProbe} from './runtimeProbe' /** * Status probes are real network calls and the console polls them on a short * interval, so results are memoized briefly. Without this, asking for * per-service detail turns one page load into a probe per registered service. */ const STATUS_TTL_MS = 5_000 export class CloudProxyService { private readonly runtimeCache = new Map() private readonly serviceStatusCache = new Map() private readonly probes: Record /** * `probes` is injectable so tests can exercise status without reaching the * network — the probes hit real runtime endpoints, which are present on a * developer machine and absent in CI. */ constructor( private readonly registry: CloudAdapterRegistry, probes: Record = runtimeProbes, ) { this.probes = probes } clouds(): CloudDescriptor[] { return [ {id: 'aws', displayName: 'AWS', availability: 'available'}, {id: 'azure', displayName: 'Azure', availability: 'available'}, {id: 'gcp', displayName: 'GCP', availability: 'available'}, ] } /** * Derived from the service catalog and the adapter registry — never from a * hardcoded per-cloud list. Registering an adapter is therefore the only * thing needed to make a service appear as available in the UI. */ services(cloud: CloudProvider): CloudServiceDescriptor[] { return SERVICE_CATALOG_ENTRIES.map((entry) => { const adapter = this.registry.get(cloud, entry.service) const override = adapter?.descriptorOverride?.() ?? {} const derived: CloudAvailability = entry.legacyAvailability?.[cloud] ?? (adapter ? 'available' : 'coming_soon') const availability = override.availability ?? derived const displayName = override.displayName ?? displayNameFor(entry, cloud) return { cloud, service: entry.service, displayName, availability, reason: override.reason ?? unavailableReason(availability, cloud, displayName), route: routeFor(entry, cloud), iconKey: entry.iconKey, group: entry.group, order: entry.order, } }) } /** * Only a registered adapter can describe a service. Returning a static * schema for an unregistered pair used to make the UI render a table that * then failed on every request. */ schema(cloud: CloudProvider, service: CloudServiceType): ServiceSchema | null { return this.registry.get(cloud, service)?.schema() ?? null } /** * Cloud-level reachability, probed against the runtime itself rather than * inferred from one adapter's list call. * * Per-service detail is opt-in: the sidebar polls this every few seconds, so * fanning out across every service by default would hammer the runtime. */ async status(cloud: CloudProvider, options: {includeServices?: boolean} = {}): Promise { const services = this.registry.servicesFor(cloud) const cached = await this.probeRuntime(cloud) const base: CloudStatus = { cloud, adapterRegistered: services.length > 0, runtime: cached.runtime, endpoint: endpointFor(cloud), checkedAt: cached.checkedAt, error: cached.error, } if (!options.includeServices) return base return { ...base, services: await Promise.all(services.map((service) => this.serviceStatus(cloud, service))), } } /** Health of a single service, so the UI can gate on the service it is showing. */ async serviceStatus(cloud: CloudProvider, service: CloudServiceType): Promise { const cacheKey = `${cloud}:${service}` const cached = this.serviceStatusCache.get(cacheKey) if (cached && Date.now() - cached.at < STATUS_TTL_MS) return cached.value const value = await this.measureServiceStatus(cloud, service) this.serviceStatusCache.set(cacheKey, {at: Date.now(), value}) return value } private async measureServiceStatus(cloud: CloudProvider, service: CloudServiceType): Promise { const endpoint = endpointFor(cloud) const adapter = this.registry.get(cloud, service) const checkedAt = new Date().toISOString() if (!adapter) { return { cloud, service, adapterRegistered: false, runtime: 'coming_soon', endpoint, checkedAt, latencyMs: null, error: null, errorCode: null, } } const startedAt = performance.now() try { // list() is a valid probe for every current adapter; health() overrides it. if (adapter.health) await adapter.health() else await adapter.list() return { cloud, service, adapterRegistered: true, runtime: 'reachable', endpoint, checkedAt, latencyMs: Math.round(performance.now() - startedAt), error: null, errorCode: null, } } catch (error) { // The mapped code is what lets the UI distinguish "the runtime does not // implement this" from "the runtime is down". const {body} = toHttpError(error, mapAwsSdkError) return { cloud, service, adapterRegistered: true, runtime: 'unavailable', endpoint, checkedAt, latencyMs: Math.round(performance.now() - startedAt), error: body.detail ?? body.message, errorCode: body.code, } } } private async probeRuntime(cloud: CloudProvider): Promise<{runtime: RuntimeReachability; checkedAt: string; error: string | null}> { const cached = this.runtimeCache.get(cloud) if (cached && Date.now() - cached.at < STATUS_TTL_MS) return cached.value const checkedAt = new Date().toISOString() let value: {runtime: RuntimeReachability; checkedAt: string; error: string | null} try { await this.probes[cloud]() value = {runtime: 'reachable', checkedAt, error: null} } catch (error) { value = { runtime: 'unavailable', checkedAt, error: error instanceof Error ? error.message : 'Runtime check failed', } } this.runtimeCache.set(cloud, {at: Date.now(), value}) return value } async listResources(cloud: CloudProvider, service: CloudServiceType, query: ResourceQuery): Promise { return this.requireAdapter(cloud, service).list(query) } async getResource(cloud: CloudProvider, service: CloudServiceType, id: string): Promise { return this.requireAdapter(cloud, service).get(id) } async createResource(cloud: CloudProvider, service: CloudServiceType, input: CreateResourceInput): Promise { return this.requireAdapter(cloud, service).create(input) } async createApiGatewayLambdaRoute(cloud: CloudProvider, apiId: string, input: ApiGatewayLambdaRouteInput): Promise { const adapter = this.requireAdapter(cloud, 'apigateway') if (!adapter.createApiGatewayLambdaRoute) { throw new NotSupportedError(`API Gateway route creation is not supported for ${cloud}`) } return adapter.createApiGatewayLambdaRoute(apiId, input) } async testApiGatewayRoute(cloud: CloudProvider, apiId: string, input: ApiGatewayTestRouteInput): Promise { const adapter = this.requireAdapter(cloud, 'apigateway') if (!adapter.testApiGatewayRoute) { throw new NotSupportedError(`API Gateway route testing is not supported for ${cloud}`) } return adapter.testApiGatewayRoute(apiId, input) } async testApiGatewayMethod(cloud: CloudProvider, apiId: string, input: ApiGatewayMethodTestInput): Promise { const adapter = this.requireAdapter(cloud, 'apigateway') if (!adapter.testApiGatewayMethod) { throw new NotSupportedError(`API Gateway method testing is not supported for ${cloud}`) } return adapter.testApiGatewayMethod(apiId, input) } async listApiGatewayRoutes(cloud: CloudProvider, apiId: string): Promise { const adapter = this.requireAdapter(cloud, 'apigateway') if (!adapter.listApiGatewayRoutes) { throw new NotSupportedError(`API Gateway route discovery is not supported for ${cloud}`) } return adapter.listApiGatewayRoutes(apiId) } async deleteApiGatewayRoute(cloud: CloudProvider, apiId: string, input: ApiGatewayDeleteRouteInput): Promise { const adapter = this.requireAdapter(cloud, 'apigateway') if (!adapter.deleteApiGatewayRoute) throw new NotSupportedError(`API Gateway route deletion is not supported for ${cloud}`) await adapter.deleteApiGatewayRoute(apiId, input) } async enableApiGatewayCors(cloud: CloudProvider, apiId: string, input: ApiGatewayCorsInput): Promise { const adapter = this.requireAdapter(cloud, 'apigateway') if (!adapter.enableApiGatewayCors) throw new NotSupportedError(`API Gateway CORS is not supported for ${cloud}`) return adapter.enableApiGatewayCors(apiId, input) } async createS3LambdaTrigger(cloud: CloudProvider, bucketName: string, input: S3LambdaTriggerInput): Promise { const adapter = this.requireAdapter(cloud, 'storage') if (!adapter.createS3LambdaTrigger) throw new NotSupportedError(`S3 Lambda triggers are not supported for ${cloud}`) return adapter.createS3LambdaTrigger(bucketName, input) } async listS3LambdaTriggers(cloud: CloudProvider, bucketName: string): Promise { const adapter = this.requireAdapter(cloud, 'storage') if (!adapter.listS3LambdaTriggers) throw new NotSupportedError(`S3 Lambda triggers are not supported for ${cloud}`) return adapter.listS3LambdaTriggers(bucketName) } async deleteS3LambdaTrigger(cloud: CloudProvider, bucketName: string, triggerId: string): Promise { const adapter = this.requireAdapter(cloud, 'storage') if (!adapter.deleteS3LambdaTrigger) throw new NotSupportedError(`S3 Lambda triggers are not supported for ${cloud}`) await adapter.deleteS3LambdaTrigger(bucketName, triggerId) } async getS3BucketWebsite(cloud: CloudProvider, bucketName: string): Promise { const adapter = this.requireAdapter(cloud, 'storage') if (!adapter.getS3BucketWebsite) throw new NotSupportedError(`S3 website hosting is not supported for ${cloud}`) return adapter.getS3BucketWebsite(bucketName) } async putS3BucketWebsite(cloud: CloudProvider, bucketName: string, configuration: S3BucketWebsiteConfiguration): Promise { const adapter = this.requireAdapter(cloud, 'storage') if (!adapter.putS3BucketWebsite) throw new NotSupportedError(`S3 website hosting is not supported for ${cloud}`) await adapter.putS3BucketWebsite(bucketName, configuration) } async deleteS3BucketWebsite(cloud: CloudProvider, bucketName: string): Promise { const adapter = this.requireAdapter(cloud, 'storage') if (!adapter.deleteS3BucketWebsite) throw new NotSupportedError(`S3 website hosting is not supported for ${cloud}`) await adapter.deleteS3BucketWebsite(bucketName) } async createEventBridgeLambdaTrigger(cloud: CloudProvider, input: EventBridgeLambdaTriggerInput): Promise { const adapter = this.requireAdapter(cloud, 'serverless') if (!adapter.createEventBridgeLambdaTrigger) throw new NotSupportedError(`EventBridge Lambda triggers are not supported for ${cloud}`) return adapter.createEventBridgeLambdaTrigger(input) } async listEventBridgeLambdaTriggers(cloud: CloudProvider, functionName: string): Promise { const adapter = this.requireAdapter(cloud, 'serverless') if (!adapter.listEventBridgeLambdaTriggers) throw new NotSupportedError(`EventBridge Lambda triggers are not supported for ${cloud}`) return adapter.listEventBridgeLambdaTriggers(functionName) } async deleteEventBridgeLambdaTrigger(cloud: CloudProvider, functionName: string, triggerId: string): Promise { const adapter = this.requireAdapter(cloud, 'serverless') if (!adapter.deleteEventBridgeLambdaTrigger) throw new NotSupportedError(`EventBridge Lambda triggers are not supported for ${cloud}`) await adapter.deleteEventBridgeLambdaTrigger(functionName, triggerId) } async listEventBridgeRules(cloud: CloudProvider, eventBusId: string): Promise { const adapter = this.requireAdapter(cloud, 'events') if (!adapter.listEventBridgeRules) throw new NotSupportedError(`EventBridge rules are not supported for ${cloud}`) return adapter.listEventBridgeRules(eventBusId) } async createEventBridgeRule(cloud: CloudProvider, eventBusId: string, input: EventBridgeRuleInput): Promise { const adapter = this.requireAdapter(cloud, 'events') if (!adapter.createEventBridgeRule) throw new NotSupportedError(`EventBridge rules are not supported for ${cloud}`) return adapter.createEventBridgeRule(eventBusId, input) } async deleteEventBridgeRule(cloud: CloudProvider, eventBusId: string, ruleName: string): Promise { const adapter = this.requireAdapter(cloud, 'events') if (!adapter.deleteEventBridgeRule) throw new NotSupportedError(`EventBridge rules are not supported for ${cloud}`) await adapter.deleteEventBridgeRule(eventBusId, ruleName) } async putEventBridgeEvent(cloud: CloudProvider, eventBusId: string, input: EventBridgePutEventInput): Promise { const adapter = this.requireAdapter(cloud, 'events') if (!adapter.putEventBridgeEvent) throw new NotSupportedError(`Event publishing is not supported for ${cloud}`) return adapter.putEventBridgeEvent(eventBusId, input) } async listCloudFormationChangeSets(cloud: CloudProvider, stackName: string): Promise { const adapter = this.requireAdapter(cloud, 'iac') if (!adapter.listCloudFormationChangeSets) throw new NotSupportedError(`CloudFormation change sets are not supported for ${cloud}`) return adapter.listCloudFormationChangeSets(stackName) } async createCloudFormationChangeSet(cloud: CloudProvider, stackName: string, input: CloudFormationChangeSetInput): Promise { const adapter = this.requireAdapter(cloud, 'iac') if (!adapter.createCloudFormationChangeSet) throw new NotSupportedError(`CloudFormation change sets are not supported for ${cloud}`) return adapter.createCloudFormationChangeSet(stackName, input) } async executeCloudFormationChangeSet(cloud: CloudProvider, stackName: string, changeSetName: string): Promise { const adapter = this.requireAdapter(cloud, 'iac') if (!adapter.executeCloudFormationChangeSet) throw new NotSupportedError(`CloudFormation change sets are not supported for ${cloud}`) await adapter.executeCloudFormationChangeSet(stackName, changeSetName) } async deleteCloudFormationChangeSet(cloud: CloudProvider, stackName: string, changeSetName: string): Promise { const adapter = this.requireAdapter(cloud, 'iac') if (!adapter.deleteCloudFormationChangeSet) throw new NotSupportedError(`CloudFormation change sets are not supported for ${cloud}`) await adapter.deleteCloudFormationChangeSet(stackName, changeSetName) } async updateCloudFormationTerminationProtection(cloud: CloudProvider, stackName: string, enabled: boolean): Promise { const adapter = this.requireAdapter(cloud, 'iac') if (!adapter.updateCloudFormationTerminationProtection) throw new NotSupportedError(`CloudFormation termination protection is not supported for ${cloud}`) await adapter.updateCloudFormationTerminationProtection(stackName, enabled) } async createSqsLambdaEventSourceMapping(cloud: CloudProvider, input: SqsLambdaEventSourceMappingInput): Promise { const adapter = this.requireAdapter(cloud, 'serverless') if (!adapter.createSqsLambdaEventSourceMapping) throw new NotSupportedError(`SQS Lambda event source mappings are not supported for ${cloud}`) return adapter.createSqsLambdaEventSourceMapping(input) } async listSqsLambdaEventSourceMappings(cloud: CloudProvider, functionName: string): Promise { const adapter = this.requireAdapter(cloud, 'serverless') if (!adapter.listSqsLambdaEventSourceMappings) throw new NotSupportedError(`SQS Lambda event source mappings are not supported for ${cloud}`) return adapter.listSqsLambdaEventSourceMappings(functionName) } async deleteSqsLambdaEventSourceMapping(cloud: CloudProvider, functionName: string, mappingId: string): Promise { const adapter = this.requireAdapter(cloud, 'serverless') if (!adapter.deleteSqsLambdaEventSourceMapping) throw new NotSupportedError(`SQS Lambda event source mappings are not supported for ${cloud}`) await adapter.deleteSqsLambdaEventSourceMapping(functionName, mappingId) } async putKinesisRecord(cloud: CloudProvider, streamName: string, input: KinesisPutRecordInput): Promise<{sequenceNumber: string; shardId: string}> { const adapter = this.requireAdapter(cloud, 'kinesis') if (!adapter.putKinesisRecord) throw new NotSupportedError(`Kinesis record production is not supported for ${cloud}`) return adapter.putKinesisRecord(streamName, input) } async listKinesisRecords(cloud: CloudProvider, streamName: string): Promise { const adapter = this.requireAdapter(cloud, 'kinesis') if (!adapter.listKinesisRecords) throw new NotSupportedError(`Kinesis record inspection is not supported for ${cloud}`) return adapter.listKinesisRecords(streamName) } async createLambdaEventSourceMapping(cloud: CloudProvider, input: LambdaEventSourceMappingInput): Promise { const adapter = this.requireAdapter(cloud, 'serverless') if (!adapter.createLambdaEventSourceMapping) throw new NotSupportedError(`Lambda event source mappings are not supported for ${cloud}`) return adapter.createLambdaEventSourceMapping(input) } async listLambdaEventSourceMappings(cloud: CloudProvider, filter?: {functionName?: string; eventSourceArn?: string}): Promise { const adapter = this.requireAdapter(cloud, 'serverless') if (!adapter.listLambdaEventSourceMappings) throw new NotSupportedError(`Lambda event source mappings are not supported for ${cloud}`) return adapter.listLambdaEventSourceMappings(filter) } async updateLambdaEventSourceMapping(cloud: CloudProvider, mappingId: string, input: {batchSize?: number; enabled?: boolean}): Promise { const adapter = this.requireAdapter(cloud, 'serverless') if (!adapter.updateLambdaEventSourceMapping) throw new NotSupportedError(`Lambda event source mappings are not supported for ${cloud}`) return adapter.updateLambdaEventSourceMapping(mappingId, input) } async deleteLambdaEventSourceMapping(cloud: CloudProvider, mappingId: string): Promise { const adapter = this.requireAdapter(cloud, 'serverless') if (!adapter.deleteLambdaEventSourceMapping) throw new NotSupportedError(`Lambda event source mappings are not supported for ${cloud}`) await adapter.deleteLambdaEventSourceMapping(mappingId) } async sendSqsMessage(cloud: CloudProvider, queueId: string, input: SqsSendMessageInput): Promise<{messageId: string}> { const adapter = this.requireAdapter(cloud, 'messaging') if (!adapter.sendSqsMessage) throw new NotSupportedError(`SQS message sending is not supported for ${cloud}`) return adapter.sendSqsMessage(queueId, input) } async receiveSqsMessages(cloud: CloudProvider, queueId: string, input: SqsReceiveMessagesInput): Promise { const adapter = this.requireAdapter(cloud, 'messaging') if (!adapter.receiveSqsMessages) throw new NotSupportedError(`SQS message receiving is not supported for ${cloud}`) return adapter.receiveSqsMessages(queueId, input) } async deleteSqsMessage(cloud: CloudProvider, queueId: string, receiptHandle: string): Promise { const adapter = this.requireAdapter(cloud, 'messaging') if (!adapter.deleteSqsMessage) throw new NotSupportedError(`SQS message deletion is not supported for ${cloud}`) await adapter.deleteSqsMessage(queueId, receiptHandle) } async purgeSqsQueue(cloud: CloudProvider, queueId: string): Promise { const adapter = this.requireAdapter(cloud, 'messaging') if (!adapter.purgeSqsQueue) throw new NotSupportedError(`SQS queue purging is not supported for ${cloud}`) await adapter.purgeSqsQueue(queueId) } async startSqsMoveTask(cloud: CloudProvider, queueId: string, input: SqsMoveTaskInput): Promise { const adapter = this.requireAdapter(cloud, 'messaging') if (!adapter.startSqsMoveTask) throw new NotSupportedError(`SQS message moving is not supported for ${cloud}`) return adapter.startSqsMoveTask(queueId, input) } async updateResource(cloud: CloudProvider, service: CloudServiceType, id: string, input: UpdateResourceInput): Promise { const adapter = this.requireAdapter(cloud, service) if (!adapter.update) throw new NotSupportedError(`Resource updates are not supported for ${cloud}/${service}`) return adapter.update(id, input) } async dependencies(cloud: CloudProvider, service: CloudServiceType): Promise> { const adapter = this.requireAdapter(cloud, service) if (!adapter.dependencies) throw new NotSupportedError(`Form dependencies are not supported for ${cloud}/${service}`) return adapter.dependencies() } async deleteResource(cloud: CloudProvider, service: CloudServiceType, id: string): Promise { await this.requireAdapter(cloud, service).delete(id) } async startResource(cloud: CloudProvider, service: CloudServiceType, id: string): Promise { const adapter = this.requireAdapter(cloud, service) if (!adapter.start) throw new NotSupportedError(`Resource start is not supported for ${cloud}/${service}`) await adapter.start(id) } async stopResource(cloud: CloudProvider, service: CloudServiceType, id: string): Promise { const adapter = this.requireAdapter(cloud, service) if (!adapter.stop) throw new NotSupportedError(`Resource stop is not supported for ${cloud}/${service}`) await adapter.stop(id) } async rebootResource(cloud: CloudProvider, service: CloudServiceType, id: string): Promise { const adapter = this.requireAdapter(cloud, service) if (!adapter.reboot) throw new NotSupportedError(`Resource reboot is not supported for ${cloud}/${service}`) await adapter.reboot(id) } async encryptKms(cloud: CloudProvider, keyId: string, input: KmsEncryptInput): Promise { const adapter = this.requireAdapter(cloud, 'kms') if (!adapter.encrypt) throw new NotSupportedError(`${cloud}/kms encrypt is not supported`) return adapter.encrypt(keyId, input) } async decryptKms(cloud: CloudProvider, keyId: string, input: KmsDecryptInput): Promise { const adapter = this.requireAdapter(cloud, 'kms') if (!adapter.decrypt) throw new NotSupportedError(`${cloud}/kms decrypt is not supported`) return adapter.decrypt(keyId, input) } async invokeResource( cloud: CloudProvider, service: CloudServiceType, id: string, payload: string, ): Promise { const adapter = this.requireAdapter(cloud, service) if (!adapter.invoke) throw new NotSupportedError(`${cloud}/${service} invoke is not supported`) return adapter.invoke(id, payload) } async publishNotification(cloud: CloudProvider, topicArn: string, input: PublishNotificationInput): Promise { const adapter = this.requireAdapter(cloud, 'notifications') if (!adapter.publish) throw new NotSupportedError(`Publishing notifications is not supported for ${cloud}`) return adapter.publish(topicArn, input) } async createSnsSubscription(cloud: CloudProvider, topicArn: string, input: SnsSubscriptionInput): Promise { const adapter = this.requireAdapter(cloud, 'notifications') if (!adapter.createSnsSubscription) throw new NotSupportedError(`SNS subscriptions are not supported for ${cloud}`) return adapter.createSnsSubscription(topicArn, input) } async listSnsSubscriptions(cloud: CloudProvider, topicArn: string): Promise { const adapter = this.requireAdapter(cloud, 'notifications') if (!adapter.listSnsSubscriptions) throw new NotSupportedError(`SNS subscriptions are not supported for ${cloud}`) return adapter.listSnsSubscriptions(topicArn) } async deleteSnsSubscription(cloud: CloudProvider, subscriptionArn: string): Promise { const adapter = this.requireAdapter(cloud, 'notifications') if (!adapter.deleteSnsSubscription) throw new NotSupportedError(`SNS subscriptions are not supported for ${cloud}`) await adapter.deleteSnsSubscription(subscriptionArn) } async listObjects(cloud: CloudProvider, service: CloudServiceType, resourceId: string, prefix?: string): Promise { const adapter = this.requireAdapter(cloud, service) if (!adapter.listObjects) throw new NotSupportedError(`Object listing is not supported for ${cloud}/${service}`) return adapter.listObjects(resourceId, prefix) } async putObject(cloud: CloudProvider, service: CloudServiceType, resourceId: string, key: string, body: Uint8Array, contentType: string): Promise { const adapter = this.requireAdapter(cloud, service) if (!adapter.putObject) throw new NotSupportedError(`Object upload is not supported for ${cloud}/${service}`) await adapter.putObject(resourceId, key, body, contentType) } async getObject(cloud: CloudProvider, service: CloudServiceType, resourceId: string, key: string): Promise { const adapter = this.requireAdapter(cloud, service) if (!adapter.getObject) throw new NotSupportedError(`Object download is not supported for ${cloud}/${service}`) return adapter.getObject(resourceId, key) } async deleteObject(cloud: CloudProvider, service: CloudServiceType, resourceId: string, key: string): Promise { const adapter = this.requireAdapter(cloud, service) if (!adapter.deleteObject) throw new NotSupportedError(`Object delete is not supported for ${cloud}/${service}`) await adapter.deleteObject(resourceId, key) } async copyObject(cloud: CloudProvider, service: CloudServiceType, srcResourceId: string, srcKey: string, destKey: string, destResourceId?: string): Promise { const adapter = this.requireAdapter(cloud, service) if (!adapter.copyObject) throw new NotSupportedError(`Object copy is not supported for ${cloud}/${service}`) await adapter.copyObject(srcResourceId, srcKey, destKey, destResourceId) } // Child collections. `requireDocuments`/`requireItems` reject the wrong // shape rather than the missing method, so a flat store asked for // collections gets a 501 that names the actual problem. async listChildCollections(cloud: CloudProvider, service: CloudServiceType, resourceId: string, page: PageQuery): Promise> { return this.requireDocuments(cloud, service).listCollections(resourceId, page) } async createChildCollection(cloud: CloudProvider, service: CloudServiceType, resourceId: string, input: CreateResourceInput): Promise { const documents = this.requireDocuments(cloud, service) if (!documents.createCollection) throw new NotSupportedError(`Creating collections is not supported for ${cloud}/${service}`) return documents.createCollection(resourceId, input) } async deleteChildCollection(cloud: CloudProvider, service: CloudServiceType, resourceId: string, collectionId: string): Promise { const documents = this.requireDocuments(cloud, service) if (!documents.deleteCollection) throw new NotSupportedError(`Deleting collections is not supported for ${cloud}/${service}`) await documents.deleteCollection(resourceId, collectionId) } async listCollectionItems(cloud: CloudProvider, service: CloudServiceType, resourceId: string, collectionId: string, page: PageQuery): Promise> { return this.requireDocuments(cloud, service).listItems(resourceId, collectionId, page) } async putCollectionItem(cloud: CloudProvider, service: CloudServiceType, resourceId: string, collectionId: string, body: Record): Promise { const documents = this.requireDocuments(cloud, service) if (!documents.putItem) throw new NotSupportedError(`Writing items is not supported for ${cloud}/${service}`) return documents.putItem(resourceId, collectionId, body) } async deleteCollectionItem(cloud: CloudProvider, service: CloudServiceType, resourceId: string, collectionId: string, itemId: string, partitionKey?: string | null): Promise { const documents = this.requireDocuments(cloud, service) if (!documents.deleteItem) throw new NotSupportedError(`Deleting items is not supported for ${cloud}/${service}`) await documents.deleteItem(resourceId, collectionId, itemId, partitionKey) } async queryCollectionItems(cloud: CloudProvider, service: CloudServiceType, resourceId: string, collectionId: string, query: ItemQuery, page: PageQuery): Promise> { const documents = this.requireDocuments(cloud, service) if (!documents.queryItems) throw new NotSupportedError(`Querying items is not supported for ${cloud}/${service}`) return documents.queryItems(resourceId, collectionId, query, page) } async listFlatItems(cloud: CloudProvider, service: CloudServiceType, resourceId: string, page: PageQuery): Promise> { return this.requireItems(cloud, service).listItems(resourceId, page) } async getFlatItem(cloud: CloudProvider, service: CloudServiceType, resourceId: string, itemId: string): Promise { const items = this.requireItems(cloud, service) if (!items.getItem) throw new NotSupportedError(`Reading one item is not supported for ${cloud}/${service}`) return items.getItem(resourceId, itemId) } async putFlatItem(cloud: CloudProvider, service: CloudServiceType, resourceId: string, body: Record): Promise { const items = this.requireItems(cloud, service) if (!items.putItem) throw new NotSupportedError(`Writing items is not supported for ${cloud}/${service}`) return items.putItem(resourceId, body) } async deleteFlatItem(cloud: CloudProvider, service: CloudServiceType, resourceId: string, itemId: string, partitionKey?: string | null): Promise { const items = this.requireItems(cloud, service) if (!items.deleteItem) throw new NotSupportedError(`Deleting items is not supported for ${cloud}/${service}`) await items.deleteItem(resourceId, itemId, partitionKey) } async queryFlatItems(cloud: CloudProvider, service: CloudServiceType, resourceId: string, query: ItemQuery, page: PageQuery): Promise> { const items = this.requireItems(cloud, service) if (!items.queryItems) throw new NotSupportedError(`Querying items is not supported for ${cloud}/${service}`) return items.queryItems(resourceId, query, page) } private requireDocuments(cloud: CloudProvider, service: CloudServiceType): DocumentStoreAdapter { const adapter = this.requireAdapter(cloud, service) if (!adapter.documents) throw new NotSupportedError(`Nested collections are not supported for ${cloud}/${service}`) return adapter.documents } private requireItems(cloud: CloudProvider, service: CloudServiceType): ItemStoreAdapter { const adapter = this.requireAdapter(cloud, service) if (!adapter.items) throw new NotSupportedError(`Flat items are not supported for ${cloud}/${service}`) return adapter.items } async listCosmosContainers(cloud: CloudProvider, databaseId: string): Promise { const adapter = this.requireAdapter(cloud, 'nosql') if (!adapter.listCosmosContainers) throw new NotSupportedError(`Cosmos containers are not supported for ${cloud}/database`) return adapter.listCosmosContainers(databaseId) } async createCosmosContainer(cloud: CloudProvider, databaseId: string, input: CreateResourceInput): Promise { const adapter = this.requireAdapter(cloud, 'nosql') if (!adapter.createCosmosContainer) throw new NotSupportedError(`Cosmos container creation is not supported for ${cloud}/database`) return adapter.createCosmosContainer(databaseId, input) } async deleteCosmosContainer(cloud: CloudProvider, databaseId: string, containerId: string): Promise { const adapter = this.requireAdapter(cloud, 'nosql') if (!adapter.deleteCosmosContainer) throw new NotSupportedError(`Cosmos container deletion is not supported for ${cloud}/database`) await adapter.deleteCosmosContainer(databaseId, containerId) } async listCosmosItems(cloud: CloudProvider, databaseId: string, containerId: string): Promise { const adapter = this.requireAdapter(cloud, 'nosql') if (!adapter.listCosmosItems) throw new NotSupportedError(`Cosmos items are not supported for ${cloud}/database`) return adapter.listCosmosItems(databaseId, containerId) } async upsertCosmosItem(cloud: CloudProvider, databaseId: string, containerId: string, document: Record): Promise { const adapter = this.requireAdapter(cloud, 'nosql') if (!adapter.upsertCosmosItem) throw new NotSupportedError(`Cosmos item upsert is not supported for ${cloud}/database`) return adapter.upsertCosmosItem(databaseId, containerId, document) } async deleteCosmosItem(cloud: CloudProvider, databaseId: string, containerId: string, itemId: string, partitionKey?: string | null): Promise { const adapter = this.requireAdapter(cloud, 'nosql') if (!adapter.deleteCosmosItem) throw new NotSupportedError(`Cosmos item deletion is not supported for ${cloud}/database`) await adapter.deleteCosmosItem(databaseId, containerId, itemId, partitionKey) } async queryCosmosItems(cloud: CloudProvider, databaseId: string, containerId: string, query: string): Promise { const adapter = this.requireAdapter(cloud, 'nosql') if (!adapter.queryCosmosItems) throw new NotSupportedError(`Cosmos query is not supported for ${cloud}/database`) return adapter.queryCosmosItems(databaseId, containerId, query) } async listDatabaseSnapshots(cloud: CloudProvider, instanceIdentifier?: string): Promise { const adapter = this.requireAdapter(cloud, 'database') if (!adapter.listDatabaseSnapshots) throw new NotSupportedError(`Snapshot listing is not supported for ${cloud}/database`) return adapter.listDatabaseSnapshots(instanceIdentifier) } async createDatabaseSnapshot(cloud: CloudProvider, input: CreateDatabaseSnapshotInput): Promise { const adapter = this.requireAdapter(cloud, 'database') if (!adapter.createDatabaseSnapshot) throw new NotSupportedError(`Snapshot creation is not supported for ${cloud}/database`) return adapter.createDatabaseSnapshot(input) } async listDatabaseOrderableInstanceClasses(cloud: CloudProvider, engine?: string): Promise { const adapter = this.requireAdapter(cloud, 'database') if (!adapter.listDatabaseOrderableInstanceClasses) throw new NotSupportedError(`Orderable instance class listing is not supported for ${cloud}/database`) return adapter.listDatabaseOrderableInstanceClasses(engine) } async listSqlDatabases(cloud: CloudProvider, serverId: string, connection: SqlConnectionInput): Promise { const adapter = this.requireAdapter(cloud, 'database') if (!adapter.listSqlDatabases) throw new NotSupportedError(`SQL database browsing is not supported for ${cloud}/database`) return adapter.listSqlDatabases(serverId, connection) } async listSqlTables(cloud: CloudProvider, serverId: string, connection: SqlConnectionInput): Promise { const adapter = this.requireAdapter(cloud, 'database') if (!adapter.listSqlTables) throw new NotSupportedError(`SQL table browsing is not supported for ${cloud}/database`) return adapter.listSqlTables(serverId, connection) } async querySql(cloud: CloudProvider, serverId: string, connection: SqlConnectionInput, query: string): Promise { const adapter = this.requireAdapter(cloud, 'database') if (!adapter.querySql) throw new NotSupportedError(`SQL query is not supported for ${cloud}/database`) return adapter.querySql(serverId, connection, query) } async queryLogs(cloud: CloudProvider, logGroupName: string, input: LogsInsightsQueryInput): Promise { const adapter = this.requireAdapter(cloud, 'logs') if (!adapter.queryLogs) throw new NotSupportedError(`Logs Insights query is not supported for ${cloud}/logs`) return adapter.queryLogs(logGroupName, input) } async filterLogEvents(cloud: CloudProvider, logGroupName: string, input: LogsFilterInput, page: PageQuery = {}): Promise> { const adapter = this.requireAdapter(cloud, 'logs') if (!adapter.filterLogEvents) throw new NotSupportedError(`Log event filtering is not supported for ${cloud}/logs`) return adapter.filterLogEvents(logGroupName, input, page) } async updateUserPoolUser(cloud: CloudProvider, poolId: string, username: string, input: UserPoolUserAction): Promise { const adapter = this.requireAdapter(cloud, 'userpools') if (!adapter.updateUserPoolUser) throw new NotSupportedError(`User actions are not supported for ${cloud}/userpools`) await adapter.updateUserPoolUser(poolId, username, input) } async setUserPoolUserGroups(cloud: CloudProvider, poolId: string, username: string, groups: string[]): Promise { const adapter = this.requireAdapter(cloud, 'userpools') if (!adapter.setUserPoolUserGroups) throw new NotSupportedError(`User groups are not supported for ${cloud}/userpools`) await adapter.setUserPoolUserGroups(poolId, username, groups) } async listUserPoolGroups(cloud: CloudProvider, poolId: string): Promise { const adapter = this.requireAdapter(cloud, 'userpools') if (!adapter.listUserPoolGroups) throw new NotSupportedError(`Groups are not supported for ${cloud}/userpools`) return adapter.listUserPoolGroups(poolId) } async createUserPoolGroup(cloud: CloudProvider, poolId: string, input: CreateUserPoolGroupInput): Promise { const adapter = this.requireAdapter(cloud, 'userpools') if (!adapter.createUserPoolGroup) throw new NotSupportedError(`Group creation is not supported for ${cloud}/userpools`) return adapter.createUserPoolGroup(poolId, input) } async deleteUserPoolGroup(cloud: CloudProvider, poolId: string, groupName: string): Promise { const adapter = this.requireAdapter(cloud, 'userpools') if (!adapter.deleteUserPoolGroup) throw new NotSupportedError(`Group deletion is not supported for ${cloud}/userpools`) await adapter.deleteUserPoolGroup(poolId, groupName) } async testUserPoolSignIn(cloud: CloudProvider, poolId: string, input: UserPoolSignInTestInput): Promise { const adapter = this.requireAdapter(cloud, 'userpools') if (!adapter.testUserPoolSignIn) throw new NotSupportedError(`Test sign-in is not supported for ${cloud}/userpools`) return adapter.testUserPoolSignIn(poolId, input) } async setDistributionEnabled(cloud: CloudProvider, distributionId: string, enabled: boolean): Promise { const adapter = this.requireAdapter(cloud, 'cdn') if (!adapter.setDistributionEnabled) throw new NotSupportedError(`Enabling distributions is not supported for ${cloud}/cdn`) return adapter.setDistributionEnabled(distributionId, enabled) } async createDistributionInvalidation(cloud: CloudProvider, distributionId: string, paths: string[]): Promise { const adapter = this.requireAdapter(cloud, 'cdn') if (!adapter.createDistributionInvalidation) throw new NotSupportedError(`Invalidations are not supported for ${cloud}/cdn`) return adapter.createDistributionInvalidation(distributionId, paths) } async describeUserPoolClient(cloud: CloudProvider, poolId: string, clientId: string): Promise { const adapter = this.requireAdapter(cloud, 'userpools') if (!adapter.describeUserPoolClient) throw new NotSupportedError(`App client settings are not supported for ${cloud}/userpools`) return adapter.describeUserPoolClient(poolId, clientId) } async updateUserPoolClientOAuth(cloud: CloudProvider, poolId: string, clientId: string, input: UpdateUserPoolClientOAuthInput): Promise { const adapter = this.requireAdapter(cloud, 'userpools') if (!adapter.updateUserPoolClientOAuth) throw new NotSupportedError(`App client settings are not supported for ${cloud}/userpools`) return adapter.updateUserPoolClientOAuth(poolId, clientId, input) } async listKubernetesNodegroups(cloud: CloudProvider, clusterId: string): Promise { const adapter = this.requireAdapter(cloud, 'k8s') if (!adapter.listKubernetesNodegroups) throw new NotSupportedError(`Nodegroups are not supported for ${cloud}/k8s`) return adapter.listKubernetesNodegroups(clusterId) } async createKubernetesNodegroup(cloud: CloudProvider, clusterId: string, input: CreateKubernetesNodegroupInput): Promise { const adapter = this.requireAdapter(cloud, 'k8s') if (!adapter.createKubernetesNodegroup) throw new NotSupportedError(`Nodegroup creation is not supported for ${cloud}/k8s`) return adapter.createKubernetesNodegroup(clusterId, input) } async deleteKubernetesNodegroup(cloud: CloudProvider, clusterId: string, nodegroupId: string): Promise { const adapter = this.requireAdapter(cloud, 'k8s') if (!adapter.deleteKubernetesNodegroup) throw new NotSupportedError(`Nodegroup deletion is not supported for ${cloud}/k8s`) await adapter.deleteKubernetesNodegroup(clusterId, nodegroupId) } async listKubernetesFargateProfiles(cloud: CloudProvider, clusterId: string): Promise { const adapter = this.requireAdapter(cloud, 'k8s') if (!adapter.listKubernetesFargateProfiles) throw new NotSupportedError(`Fargate profiles are not supported for ${cloud}/k8s`) return adapter.listKubernetesFargateProfiles(clusterId) } async createKubernetesFargateProfile(cloud: CloudProvider, clusterId: string, input: CreateKubernetesFargateProfileInput): Promise { const adapter = this.requireAdapter(cloud, 'k8s') if (!adapter.createKubernetesFargateProfile) throw new NotSupportedError(`Fargate profile creation is not supported for ${cloud}/k8s`) return adapter.createKubernetesFargateProfile(clusterId, input) } async deleteKubernetesFargateProfile(cloud: CloudProvider, clusterId: string, profileId: string): Promise { const adapter = this.requireAdapter(cloud, 'k8s') if (!adapter.deleteKubernetesFargateProfile) throw new NotSupportedError(`Fargate profile deletion is not supported for ${cloud}/k8s`) await adapter.deleteKubernetesFargateProfile(clusterId, profileId) } async clearEmailInbox(cloud: CloudProvider): Promise { const adapter = this.requireAdapter(cloud, 'email') if (!adapter.clearEmailInbox) throw new NotSupportedError(`Inbox clearing is not supported for ${cloud}/email`) await adapter.clearEmailInbox() } private appConfigAdapter(cloud: CloudProvider): CloudServiceAdapter { const adapter = this.requireAdapter(cloud, 'configuration') const method = adapter.listAppConfigEnvironments if (!method) throw new NotSupportedError(`AppConfig resources are not supported for ${cloud}/configuration`) return adapter } async listAppConfigEnvironments(cloud: CloudProvider, applicationId: string): Promise { return this.appConfigAdapter(cloud).listAppConfigEnvironments!(applicationId) } async createAppConfigEnvironment(cloud: CloudProvider, applicationId: string, input: CreateResourceInput): Promise { return this.appConfigAdapter(cloud).createAppConfigEnvironment!(applicationId, input) } async deleteAppConfigEnvironment(cloud: CloudProvider, applicationId: string, environmentId: string): Promise { await this.appConfigAdapter(cloud).deleteAppConfigEnvironment!(applicationId, environmentId) } async listAppConfigConfigurationProfiles(cloud: CloudProvider, applicationId: string): Promise { return this.appConfigAdapter(cloud).listAppConfigConfigurationProfiles!(applicationId) } async createAppConfigConfigurationProfile(cloud: CloudProvider, applicationId: string, input: CreateResourceInput): Promise { return this.appConfigAdapter(cloud).createAppConfigConfigurationProfile!(applicationId, input) } async deleteAppConfigConfigurationProfile(cloud: CloudProvider, applicationId: string, profileId: string): Promise { await this.appConfigAdapter(cloud).deleteAppConfigConfigurationProfile!(applicationId, profileId) } async listAppConfigHostedConfigurationVersions(cloud: CloudProvider, applicationId: string, profileId: string): Promise { return this.appConfigAdapter(cloud).listAppConfigHostedConfigurationVersions!(applicationId, profileId) } async getAppConfigHostedConfigurationVersion(cloud: CloudProvider, applicationId: string, profileId: string, versionNumber: number): Promise { return this.appConfigAdapter(cloud).getAppConfigHostedConfigurationVersion!(applicationId, profileId, versionNumber) } async createAppConfigHostedConfigurationVersion(cloud: CloudProvider, applicationId: string, profileId: string, input: CreateResourceInput): Promise { return this.appConfigAdapter(cloud).createAppConfigHostedConfigurationVersion!(applicationId, profileId, input) } async deleteAppConfigHostedConfigurationVersion(cloud: CloudProvider, applicationId: string, profileId: string, versionNumber: number): Promise { await this.appConfigAdapter(cloud).deleteAppConfigHostedConfigurationVersion!(applicationId, profileId, versionNumber) } async listAppConfigDeploymentStrategies(cloud: CloudProvider): Promise { return this.appConfigAdapter(cloud).listAppConfigDeploymentStrategies!() } async createAppConfigDeploymentStrategy(cloud: CloudProvider, input: CreateResourceInput): Promise { return this.appConfigAdapter(cloud).createAppConfigDeploymentStrategy!(input) } async deleteAppConfigDeploymentStrategy(cloud: CloudProvider, strategyId: string): Promise { await this.appConfigAdapter(cloud).deleteAppConfigDeploymentStrategy!(strategyId) } async startAppConfigDeployment(cloud: CloudProvider, applicationId: string, environmentId: string, input: CreateResourceInput): Promise { return this.appConfigAdapter(cloud).startAppConfigDeployment!(applicationId, environmentId, input) } async getAppConfigDeployment(cloud: CloudProvider, applicationId: string, environmentId: string, deploymentNumber: number): Promise { return this.appConfigAdapter(cloud).getAppConfigDeployment!(applicationId, environmentId, deploymentNumber) } async startWorkflowExecution(cloud: CloudProvider, stateMachineArn: string, input: StartWorkflowExecutionInput): Promise<{executionArn: string; startDate: Date}> { return this.workflowAdapter(cloud).startExecution(stateMachineArn, input) } async listWorkflowExecutions(cloud: CloudProvider, stateMachineArn: string): Promise { return this.workflowAdapter(cloud).listExecutions(stateMachineArn) } async describeWorkflowExecution(cloud: CloudProvider, executionArn: string): Promise { return this.workflowAdapter(cloud).describeExecution(executionArn) } async getWorkflowExecutionHistory(cloud: CloudProvider, executionArn: string, nextToken?: string): Promise { return this.workflowAdapter(cloud).getExecutionHistory(executionArn, nextToken) } async stopWorkflowExecution(cloud: CloudProvider, executionArn: string): Promise { await this.workflowAdapter(cloud).stopExecution(executionArn) } async startCodeBuild(cloud: CloudProvider, projectName: string, input: StartCodeBuildInput): Promise { return this.codeBuildAdapter(cloud).startBuild!(projectName, input) } async listCodeBuilds(cloud: CloudProvider, projectName: string): Promise { return this.codeBuildAdapter(cloud).listBuilds!(projectName) } async getCodeBuild(cloud: CloudProvider, id: string): Promise { return this.codeBuildAdapter(cloud).getBuild!(id) } async startPipeline(cloud: CloudProvider, id: string): Promise<{id: string}> { return this.codePipelineAdapter(cloud).startPipeline!(id) } async listPipelineExecutions(cloud: CloudProvider, id: string): Promise { return this.codePipelineAdapter(cloud).listPipelineExecutions!(id) } async getPipelineState(cloud: CloudProvider, id: string): Promise { return this.codePipelineAdapter(cloud).getPipelineState!(id) } async startCodeDeployDeployment(cloud: CloudProvider, applicationName: string, deploymentGroupName: string, input: StartCodeDeployDeploymentInput): Promise { return this.codeDeployAdapter(cloud).startCodeDeployDeployment!(applicationName, deploymentGroupName, input) } async listCodeDeployDeployments(cloud: CloudProvider, applicationName: string, deploymentGroupName: string): Promise { return this.codeDeployAdapter(cloud).listCodeDeployDeployments!(applicationName, deploymentGroupName) } async getCodeDeployDeployment(cloud: CloudProvider, id: string): Promise { return this.codeDeployAdapter(cloud).getCodeDeployDeployment!(id) } async stopCodeDeployDeployment(cloud: CloudProvider, id: string, rollback: boolean): Promise { await this.codeDeployAdapter(cloud).stopCodeDeployDeployment!(id, rollback) } private workflowAdapter(cloud: CloudProvider) { const adapter = this.requireAdapter(cloud, 'workflows') if (!('startExecution' in adapter) || !('listExecutions' in adapter) || !('describeExecution' in adapter) || !('getExecutionHistory' in adapter) || !('stopExecution' in adapter)) { throw new NotSupportedError(`Workflow executions are not supported for ${cloud}`) } return adapter as import('../adapter-aws/AwsStepFunctionsAdapter').AwsStepFunctionsAdapter } private codeBuildAdapter(cloud: CloudProvider) { const adapter = this.requireAdapter(cloud, 'codebuild') if (!adapter.startBuild || !adapter.listBuilds || !adapter.getBuild) { throw new NotSupportedError(`CodeBuild builds are not supported for ${cloud}`) } return adapter } private codePipelineAdapter(cloud: CloudProvider) { const adapter = this.requireAdapter(cloud, 'codepipeline') if (!adapter.startPipeline || !adapter.listPipelineExecutions || !adapter.getPipelineState) { throw new NotSupportedError(`CodePipeline executions are not supported for ${cloud}`) } return adapter } private codeDeployAdapter(cloud: CloudProvider) { const adapter = this.requireAdapter(cloud, 'codedeploy') if (!adapter.startCodeDeployDeployment || !adapter.listCodeDeployDeployments || !adapter.getCodeDeployDeployment || !adapter.stopCodeDeployDeployment) { throw new NotSupportedError(`CodeDeploy deployments are not supported for ${cloud}`) } return adapter } private requireAdapter(cloud: CloudProvider, service: CloudServiceType) { const adapter = this.registry.get(cloud, service) if (!adapter) throw new NotSupportedError(`No adapter registered for ${cloud}/${service}`) return adapter } } /** Keeps the promise that every coming_soon descriptor explains itself. */ function unavailableReason( availability: CloudAvailability, cloud: CloudProvider, displayName: string, ): string | undefined { if (availability === 'available') return undefined return `No ${cloud.toUpperCase()} adapter is registered for ${displayName} yet.` }