alchemy 2.0.0-beta.64 → 2.0.0-beta.66
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/bin/exec.js +433 -181
- package/bin/exec.js.map +1 -1
- package/lib/AWS/ApiGateway/common.d.ts.map +1 -1
- package/lib/AWS/ApiGateway/common.js +8 -1
- package/lib/AWS/ApiGateway/common.js.map +1 -1
- package/lib/AWS/Bedrock/LanguageModel.d.ts +208 -0
- package/lib/AWS/Bedrock/LanguageModel.d.ts.map +1 -0
- package/lib/AWS/Bedrock/LanguageModel.js +508 -0
- package/lib/AWS/Bedrock/LanguageModel.js.map +1 -0
- package/lib/AWS/Bedrock/LanguageModelHttp.d.ts +10 -0
- package/lib/AWS/Bedrock/LanguageModelHttp.d.ts.map +1 -0
- package/lib/AWS/Bedrock/LanguageModelHttp.js +26 -0
- package/lib/AWS/Bedrock/LanguageModelHttp.js.map +1 -0
- package/lib/AWS/Bedrock/index.d.ts +2 -0
- package/lib/AWS/Bedrock/index.d.ts.map +1 -1
- package/lib/AWS/Bedrock/index.js +2 -0
- package/lib/AWS/Bedrock/index.js.map +1 -1
- package/lib/AWS/CloudFront/Distribution.d.ts +24 -0
- package/lib/AWS/CloudFront/Distribution.d.ts.map +1 -1
- package/lib/AWS/CloudFront/Distribution.js +89 -4
- package/lib/AWS/CloudFront/Distribution.js.map +1 -1
- package/lib/AWS/CostExplorer/AnomalyMonitor.d.ts +1 -1
- package/lib/AWS/CostExplorer/AnomalyMonitor.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/AnomalySubscription.d.ts +1 -1
- package/lib/AWS/CostExplorer/AnomalySubscription.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/BindingHttp.d.ts +3 -3
- package/lib/AWS/CostExplorer/BindingHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/CostCategory.d.ts +1 -1
- package/lib/AWS/CostExplorer/CostCategory.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/GetAnomaliesHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/GetAnomaliesHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/GetApproximateUsageRecordsHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/GetApproximateUsageRecordsHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/GetCommitmentPurchaseAnalysisHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/GetCommitmentPurchaseAnalysisHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/GetCostAndUsageComparisonsHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/GetCostAndUsageComparisonsHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/GetCostAndUsageHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/GetCostAndUsageHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/GetCostAndUsageWithResourcesHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/GetCostAndUsageWithResourcesHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/GetCostCategoriesHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/GetCostCategoriesHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/GetCostComparisonDriversHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/GetCostComparisonDriversHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/GetCostForecastHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/GetCostForecastHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/GetDimensionValuesHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/GetDimensionValuesHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/GetReservationCoverageHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/GetReservationCoverageHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/GetReservationPurchaseRecommendationHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/GetReservationPurchaseRecommendationHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/GetReservationUtilizationHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/GetReservationUtilizationHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/GetRightsizingRecommendationHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/GetRightsizingRecommendationHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/GetSavingsPlanPurchaseRecommendationDetailsHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/GetSavingsPlanPurchaseRecommendationDetailsHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/GetSavingsPlansCoverageHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/GetSavingsPlansCoverageHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/GetSavingsPlansPurchaseRecommendationHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/GetSavingsPlansPurchaseRecommendationHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/GetSavingsPlansUtilizationDetailsHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/GetSavingsPlansUtilizationDetailsHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/GetSavingsPlansUtilizationHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/GetSavingsPlansUtilizationHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/GetTagsHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/GetTagsHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/GetUsageForecastHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/GetUsageForecastHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/ListCommitmentPurchaseAnalysesHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/ListCommitmentPurchaseAnalysesHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/ListCostAllocationTagBackfillHistoryHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/ListCostAllocationTagBackfillHistoryHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/ListCostAllocationTagsHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/ListCostAllocationTagsHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/ListCostCategoryResourceAssociationsHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/ListCostCategoryResourceAssociationsHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/ListSavingsPlansPurchaseRecommendationGenerationHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/ListSavingsPlansPurchaseRecommendationGenerationHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/ProvideAnomalyFeedbackHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/ProvideAnomalyFeedbackHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/StartCommitmentPurchaseAnalysisHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/StartCommitmentPurchaseAnalysisHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/StartCostAllocationTagBackfillHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/StartCostAllocationTagBackfillHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/StartSavingsPlansPurchaseRecommendationGenerationHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/StartSavingsPlansPurchaseRecommendationGenerationHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/UpdateCostAllocationTagsStatusHttp.d.ts +1 -1
- package/lib/AWS/CostExplorer/UpdateCostAllocationTagsStatusHttp.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/common.d.ts +5 -3
- package/lib/AWS/CostExplorer/common.d.ts.map +1 -1
- package/lib/AWS/CostExplorer/common.js +16 -1
- package/lib/AWS/CostExplorer/common.js.map +1 -1
- package/lib/AWS/DataSync/Task.d.ts.map +1 -1
- package/lib/AWS/DataSync/Task.js +16 -2
- package/lib/AWS/DataSync/Task.js.map +1 -1
- package/lib/AWS/EC2/InternetGateway.d.ts.map +1 -1
- package/lib/AWS/EC2/InternetGateway.js +8 -3
- package/lib/AWS/EC2/InternetGateway.js.map +1 -1
- package/lib/AWS/EC2/LingeringEnis.d.ts +26 -0
- package/lib/AWS/EC2/LingeringEnis.d.ts.map +1 -0
- package/lib/AWS/EC2/LingeringEnis.js +128 -0
- package/lib/AWS/EC2/LingeringEnis.js.map +1 -0
- package/lib/AWS/EC2/SecurityGroup.d.ts.map +1 -1
- package/lib/AWS/EC2/SecurityGroup.js +14 -14
- package/lib/AWS/EC2/SecurityGroup.js.map +1 -1
- package/lib/AWS/EC2/Subnet.d.ts.map +1 -1
- package/lib/AWS/EC2/Subnet.js +13 -21
- package/lib/AWS/EC2/Subnet.js.map +1 -1
- package/lib/AWS/EC2/Vpc.d.ts.map +1 -1
- package/lib/AWS/EC2/Vpc.js +15 -2
- package/lib/AWS/EC2/Vpc.js.map +1 -1
- package/lib/AWS/EC2/hosted.d.ts.map +1 -1
- package/lib/AWS/EC2/hosted.js +10 -2
- package/lib/AWS/EC2/hosted.js.map +1 -1
- package/lib/AWS/ECR/Image.d.ts +133 -13
- package/lib/AWS/ECR/Image.d.ts.map +1 -1
- package/lib/AWS/ECR/ImageSource.d.ts.map +1 -1
- package/lib/AWS/ECR/ImageSource.js +10 -2
- package/lib/AWS/ECR/ImageSource.js.map +1 -1
- package/lib/AWS/ECR/Repository.d.ts +2 -0
- package/lib/AWS/ECR/Repository.d.ts.map +1 -1
- package/lib/AWS/ECR/Repository.js +28 -3
- package/lib/AWS/ECR/Repository.js.map +1 -1
- package/lib/AWS/ECS/Service.d.ts +27 -1
- package/lib/AWS/ECS/Service.d.ts.map +1 -1
- package/lib/AWS/ECS/Service.js +184 -22
- package/lib/AWS/ECS/Service.js.map +1 -1
- package/lib/AWS/ECS/Task.d.ts +32 -8
- package/lib/AWS/ECS/Task.d.ts.map +1 -1
- package/lib/AWS/ECS/Task.js +62 -29
- package/lib/AWS/ECS/Task.js.map +1 -1
- package/lib/AWS/EKS/Addon.d.ts.map +1 -1
- package/lib/AWS/EKS/Addon.js +15 -3
- package/lib/AWS/EKS/Addon.js.map +1 -1
- package/lib/AWS/EKS/Deployment.d.ts +10 -1
- package/lib/AWS/EKS/Deployment.d.ts.map +1 -1
- package/lib/AWS/EKS/Deployment.js +20 -6
- package/lib/AWS/EKS/Deployment.js.map +1 -1
- package/lib/AWS/EKS/HyperPod.d.ts +57 -0
- package/lib/AWS/EKS/HyperPod.d.ts.map +1 -0
- package/lib/AWS/EKS/HyperPod.js +46 -0
- package/lib/AWS/EKS/HyperPod.js.map +1 -0
- package/lib/AWS/EKS/Job.d.ts +10 -1
- package/lib/AWS/EKS/Job.d.ts.map +1 -1
- package/lib/AWS/EKS/Job.js +26 -8
- package/lib/AWS/EKS/Job.js.map +1 -1
- package/lib/AWS/EKS/internal/client.d.ts +4 -3
- package/lib/AWS/EKS/internal/client.d.ts.map +1 -1
- package/lib/AWS/EKS/internal/client.js +33 -2
- package/lib/AWS/EKS/internal/client.js.map +1 -1
- package/lib/AWS/Lambda/Function.d.ts +15 -0
- package/lib/AWS/Lambda/Function.d.ts.map +1 -1
- package/lib/AWS/Lambda/Function.js +68 -30
- package/lib/AWS/Lambda/Function.js.map +1 -1
- package/lib/AWS/Lambda/HttpServer.d.ts.map +1 -1
- package/lib/AWS/Lambda/HttpServer.js +7 -1
- package/lib/AWS/Lambda/HttpServer.js.map +1 -1
- package/lib/AWS/Lambda/LayerVersion.d.ts +176 -0
- package/lib/AWS/Lambda/LayerVersion.d.ts.map +1 -0
- package/lib/AWS/Lambda/LayerVersion.js +288 -0
- package/lib/AWS/Lambda/LayerVersion.js.map +1 -0
- package/lib/AWS/Lambda/MicrovmBundle.d.ts.map +1 -1
- package/lib/AWS/Lambda/MicrovmBundle.js +10 -2
- package/lib/AWS/Lambda/MicrovmBundle.js.map +1 -1
- package/lib/AWS/Lambda/index.d.ts +1 -0
- package/lib/AWS/Lambda/index.d.ts.map +1 -1
- package/lib/AWS/Lambda/index.js +1 -0
- package/lib/AWS/Lambda/index.js.map +1 -1
- package/lib/AWS/Providers.d.ts.map +1 -1
- package/lib/AWS/Providers.js +9 -1
- package/lib/AWS/Providers.js.map +1 -1
- package/lib/AWS/SES/ActiveReceiptRuleSet.d.ts +40 -0
- package/lib/AWS/SES/ActiveReceiptRuleSet.d.ts.map +1 -0
- package/lib/AWS/SES/ActiveReceiptRuleSet.js +58 -0
- package/lib/AWS/SES/ActiveReceiptRuleSet.js.map +1 -0
- package/lib/AWS/SES/ReceiptFilter.d.ts +67 -0
- package/lib/AWS/SES/ReceiptFilter.d.ts.map +1 -0
- package/lib/AWS/SES/ReceiptFilter.js +92 -0
- package/lib/AWS/SES/ReceiptFilter.js.map +1 -0
- package/lib/AWS/SES/ReceiptRule.d.ts +133 -0
- package/lib/AWS/SES/ReceiptRule.d.ts.map +1 -0
- package/lib/AWS/SES/ReceiptRule.js +178 -0
- package/lib/AWS/SES/ReceiptRule.js.map +1 -0
- package/lib/AWS/SES/ReceiptRuleSet.d.ts +55 -0
- package/lib/AWS/SES/ReceiptRuleSet.d.ts.map +1 -0
- package/lib/AWS/SES/ReceiptRuleSet.js +112 -0
- package/lib/AWS/SES/ReceiptRuleSet.js.map +1 -0
- package/lib/AWS/SES/SendBounce.d.ts +37 -0
- package/lib/AWS/SES/SendBounce.d.ts.map +1 -0
- package/lib/AWS/SES/SendBounce.js +3 -0
- package/lib/AWS/SES/SendBounce.js.map +1 -0
- package/lib/AWS/SES/SendBounceHttp.d.ts +4 -0
- package/lib/AWS/SES/SendBounceHttp.d.ts.map +1 -0
- package/lib/AWS/SES/SendBounceHttp.js +10 -0
- package/lib/AWS/SES/SendBounceHttp.js.map +1 -0
- package/lib/AWS/SES/index.d.ts +6 -0
- package/lib/AWS/SES/index.d.ts.map +1 -1
- package/lib/AWS/SES/index.js +6 -0
- package/lib/AWS/SES/index.js.map +1 -1
- package/lib/AWS/SageMaker/Cluster.d.ts +327 -0
- package/lib/AWS/SageMaker/Cluster.d.ts.map +1 -0
- package/lib/AWS/SageMaker/Cluster.js +469 -0
- package/lib/AWS/SageMaker/Cluster.js.map +1 -0
- package/lib/AWS/SageMaker/ClusterSchedulerConfig.d.ts +96 -0
- package/lib/AWS/SageMaker/ClusterSchedulerConfig.d.ts.map +1 -0
- package/lib/AWS/SageMaker/ClusterSchedulerConfig.js +270 -0
- package/lib/AWS/SageMaker/ClusterSchedulerConfig.js.map +1 -0
- package/lib/AWS/SageMaker/ComputeQuota.d.ts +106 -0
- package/lib/AWS/SageMaker/ComputeQuota.d.ts.map +1 -0
- package/lib/AWS/SageMaker/ComputeQuota.js +268 -0
- package/lib/AWS/SageMaker/ComputeQuota.js.map +1 -0
- package/lib/AWS/SageMaker/index.d.ts +3 -0
- package/lib/AWS/SageMaker/index.d.ts.map +1 -1
- package/lib/AWS/SageMaker/index.js +3 -0
- package/lib/AWS/SageMaker/index.js.map +1 -1
- package/lib/AWS/StateStore/State.d.ts +11 -0
- package/lib/AWS/StateStore/State.d.ts.map +1 -1
- package/lib/AWS/StateStore/State.js +117 -38
- package/lib/AWS/StateStore/State.js.map +1 -1
- package/lib/Apply.d.ts +37 -1
- package/lib/Apply.d.ts.map +1 -1
- package/lib/Apply.js +270 -194
- package/lib/Apply.js.map +1 -1
- package/lib/Axiom/Telemetry.d.ts +66 -0
- package/lib/Axiom/Telemetry.d.ts.map +1 -0
- package/lib/Axiom/Telemetry.js +69 -0
- package/lib/Axiom/Telemetry.js.map +1 -0
- package/lib/Axiom/index.d.ts +1 -0
- package/lib/Axiom/index.d.ts.map +1 -1
- package/lib/Axiom/index.js +1 -0
- package/lib/Axiom/index.js.map +1 -1
- package/lib/Bundle/Bundle.d.ts +1 -1
- package/lib/Bundle/Bundle.d.ts.map +1 -1
- package/lib/Bundle/Bundle.js +14 -6
- package/lib/Bundle/Bundle.js.map +1 -1
- package/lib/Bundle/BundleAnalyzerPlugin.d.ts +1 -1
- package/lib/Bundle/BundleAnalyzerPlugin.d.ts.map +1 -1
- package/lib/Bundle/BundleAnalyzerPlugin.js +10 -5
- package/lib/Bundle/BundleAnalyzerPlugin.js.map +1 -1
- package/lib/Bundle/PurePlugin.d.ts +29 -7
- package/lib/Bundle/PurePlugin.d.ts.map +1 -1
- package/lib/Bundle/PurePlugin.js +71 -40
- package/lib/Bundle/PurePlugin.js.map +1 -1
- package/lib/Cli/checkVersion.d.ts +11 -5
- package/lib/Cli/checkVersion.d.ts.map +1 -1
- package/lib/Cli/checkVersion.js +73 -12
- package/lib/Cli/checkVersion.js.map +1 -1
- package/lib/Cli/commands/cloudflare.d.ts +1 -1
- package/lib/Cli/commands/deploy.d.ts.map +1 -1
- package/lib/Cli/commands/deploy.js +3 -11
- package/lib/Cli/commands/deploy.js.map +1 -1
- package/lib/Cli/commands/logs.d.ts.map +1 -1
- package/lib/Cli/commands/logs.js +3 -1
- package/lib/Cli/commands/logs.js.map +1 -1
- package/lib/Cli/commands/nuke.d.ts.map +1 -1
- package/lib/Cli/commands/nuke.js +231 -37
- package/lib/Cli/commands/nuke.js.map +1 -1
- package/lib/Cli/commands/tail.d.ts.map +1 -1
- package/lib/Cli/commands/tail.js +3 -1
- package/lib/Cli/commands/tail.js.map +1 -1
- package/lib/Cli/main.d.ts.map +1 -1
- package/lib/Cli/main.js +5 -4
- package/lib/Cli/main.js.map +1 -1
- package/lib/Cloudflare/AI/Search.d.ts +4 -15
- package/lib/Cloudflare/AI/Search.d.ts.map +1 -1
- package/lib/Cloudflare/AI/Search.js +4 -6
- package/lib/Cloudflare/AI/Search.js.map +1 -1
- package/lib/Cloudflare/AI/SearchInstance.d.ts +7 -26
- package/lib/Cloudflare/AI/SearchInstance.d.ts.map +1 -1
- package/lib/Cloudflare/AI/SearchInstance.js +7 -26
- package/lib/Cloudflare/AI/SearchInstance.js.map +1 -1
- package/lib/Cloudflare/ApiToken/AccountApiToken.d.ts +3 -0
- package/lib/Cloudflare/ApiToken/AccountApiToken.d.ts.map +1 -1
- package/lib/Cloudflare/ApiToken/AccountApiToken.js +3 -0
- package/lib/Cloudflare/ApiToken/AccountApiToken.js.map +1 -1
- package/lib/Cloudflare/Auth/AuthProvider.d.ts +2 -1
- package/lib/Cloudflare/Auth/AuthProvider.d.ts.map +1 -1
- package/lib/Cloudflare/Auth/AuthProvider.js +8 -3
- package/lib/Cloudflare/Auth/AuthProvider.js.map +1 -1
- package/lib/Cloudflare/Auth/OAuthClient.d.ts +5 -0
- package/lib/Cloudflare/Auth/OAuthClient.d.ts.map +1 -1
- package/lib/Cloudflare/Auth/OAuthClient.js +50 -4
- package/lib/Cloudflare/Auth/OAuthClient.js.map +1 -1
- package/lib/Cloudflare/Containers/Container.d.ts +81 -3
- package/lib/Cloudflare/Containers/Container.d.ts.map +1 -1
- package/lib/Cloudflare/Containers/Container.js +70 -0
- package/lib/Cloudflare/Containers/Container.js.map +1 -1
- package/lib/Cloudflare/Containers/ContainerApplication.d.ts +22 -1
- package/lib/Cloudflare/Containers/ContainerApplication.d.ts.map +1 -1
- package/lib/Cloudflare/Containers/ContainerApplication.js +4 -1
- package/lib/Cloudflare/Containers/ContainerApplication.js.map +1 -1
- package/lib/Cloudflare/Containers/ContainerBundle.d.ts.map +1 -1
- package/lib/Cloudflare/Containers/ContainerBundle.js +10 -2
- package/lib/Cloudflare/Containers/ContainerBundle.js.map +1 -1
- package/lib/Cloudflare/Containers/ContainerProvider.d.ts.map +1 -1
- package/lib/Cloudflare/Containers/ContainerProvider.js +43 -0
- package/lib/Cloudflare/Containers/ContainerProvider.js.map +1 -1
- package/lib/Cloudflare/Credentials.d.ts +21 -1
- package/lib/Cloudflare/Credentials.d.ts.map +1 -1
- package/lib/Cloudflare/Credentials.js +52 -9
- package/lib/Cloudflare/Credentials.js.map +1 -1
- package/lib/Cloudflare/D1/ApplyMigrations.d.ts +30 -5
- package/lib/Cloudflare/D1/ApplyMigrations.d.ts.map +1 -1
- package/lib/Cloudflare/D1/ApplyMigrations.js +36 -26
- package/lib/Cloudflare/D1/ApplyMigrations.js.map +1 -1
- package/lib/Cloudflare/D1/Database.d.ts +18 -1
- package/lib/Cloudflare/D1/Database.d.ts.map +1 -1
- package/lib/Cloudflare/D1/Database.js +134 -2
- package/lib/Cloudflare/D1/Database.js.map +1 -1
- package/lib/Cloudflare/D1/LocalD1Gateway.d.ts +39 -0
- package/lib/Cloudflare/D1/LocalD1Gateway.d.ts.map +1 -0
- package/lib/Cloudflare/D1/LocalD1Gateway.js +100 -0
- package/lib/Cloudflare/D1/LocalD1Gateway.js.map +1 -0
- package/lib/Cloudflare/D1/QueryDatabaseHttpClient.d.ts +31 -0
- package/lib/Cloudflare/D1/QueryDatabaseHttpClient.d.ts.map +1 -1
- package/lib/Cloudflare/D1/QueryDatabaseHttpClient.js +13 -6
- package/lib/Cloudflare/D1/QueryDatabaseHttpClient.js.map +1 -1
- package/lib/Cloudflare/D1/QueryDatabaseLocal.d.ts.map +1 -1
- package/lib/Cloudflare/D1/QueryDatabaseLocal.js +21 -2
- package/lib/Cloudflare/D1/QueryDatabaseLocal.js.map +1 -1
- package/lib/Cloudflare/Email/Address.d.ts.map +1 -1
- package/lib/Cloudflare/Email/Address.js +15 -1
- package/lib/Cloudflare/Email/Address.js.map +1 -1
- package/lib/Cloudflare/Email/SendBinding.d.ts.map +1 -1
- package/lib/Cloudflare/Email/SendBinding.js +1 -0
- package/lib/Cloudflare/Email/SendBinding.js.map +1 -1
- package/lib/Cloudflare/Email/SendEmail.d.ts +12 -0
- package/lib/Cloudflare/Email/SendEmail.d.ts.map +1 -1
- package/lib/Cloudflare/Email/SendEmail.js +1 -0
- package/lib/Cloudflare/Email/SendEmail.js.map +1 -1
- package/lib/Cloudflare/Flagship/App.d.ts.map +1 -1
- package/lib/Cloudflare/Flagship/App.js +8 -3
- package/lib/Cloudflare/Flagship/App.js.map +1 -1
- package/lib/Cloudflare/KV/LocalKVGateway.d.ts +15 -0
- package/lib/Cloudflare/KV/LocalKVGateway.d.ts.map +1 -0
- package/lib/Cloudflare/KV/LocalKVGateway.js +64 -0
- package/lib/Cloudflare/KV/LocalKVGateway.js.map +1 -0
- package/lib/Cloudflare/KV/Namespace.d.ts +9 -1
- package/lib/Cloudflare/KV/Namespace.d.ts.map +1 -1
- package/lib/Cloudflare/KV/Namespace.js +45 -1
- package/lib/Cloudflare/KV/Namespace.js.map +1 -1
- package/lib/Cloudflare/KV/NamespaceLocal.d.ts +16 -5
- package/lib/Cloudflare/KV/NamespaceLocal.d.ts.map +1 -1
- package/lib/Cloudflare/KV/NamespaceLocal.js +21 -4
- package/lib/Cloudflare/KV/NamespaceLocal.js.map +1 -1
- package/lib/Cloudflare/KV/ReadNamespaceLocal.d.ts.map +1 -1
- package/lib/Cloudflare/KV/ReadNamespaceLocal.js +5 -1
- package/lib/Cloudflare/KV/ReadNamespaceLocal.js.map +1 -1
- package/lib/Cloudflare/KV/ReadWriteNamespaceLocal.d.ts.map +1 -1
- package/lib/Cloudflare/KV/ReadWriteNamespaceLocal.js +5 -1
- package/lib/Cloudflare/KV/ReadWriteNamespaceLocal.js.map +1 -1
- package/lib/Cloudflare/KV/WriteNamespaceLocal.d.ts.map +1 -1
- package/lib/Cloudflare/KV/WriteNamespaceLocal.js +5 -1
- package/lib/Cloudflare/KV/WriteNamespaceLocal.js.map +1 -1
- package/lib/Cloudflare/LocalGateway.d.ts +25 -0
- package/lib/Cloudflare/LocalGateway.d.ts.map +1 -0
- package/lib/Cloudflare/LocalGateway.js +70 -0
- package/lib/Cloudflare/LocalGateway.js.map +1 -0
- package/lib/Cloudflare/LocalRuntime.d.ts +14 -1
- package/lib/Cloudflare/LocalRuntime.d.ts.map +1 -1
- package/lib/Cloudflare/LocalRuntime.js +16 -1
- package/lib/Cloudflare/LocalRuntime.js.map +1 -1
- package/lib/Cloudflare/Logs.d.ts.map +1 -1
- package/lib/Cloudflare/Logs.js +18 -6
- package/lib/Cloudflare/Logs.js.map +1 -1
- package/lib/Cloudflare/Providers.d.ts +1 -1
- package/lib/Cloudflare/Providers.d.ts.map +1 -1
- package/lib/Cloudflare/Providers.js +6 -2
- package/lib/Cloudflare/Providers.js.map +1 -1
- package/lib/Cloudflare/Queues/Consumer.d.ts +16 -3
- package/lib/Cloudflare/Queues/Consumer.d.ts.map +1 -1
- package/lib/Cloudflare/Queues/Consumer.js +51 -3
- package/lib/Cloudflare/Queues/Consumer.js.map +1 -1
- package/lib/Cloudflare/Queues/Queue.d.ts +4 -3
- package/lib/Cloudflare/Queues/Queue.d.ts.map +1 -1
- package/lib/Cloudflare/Queues/Queue.js +7 -17
- package/lib/Cloudflare/Queues/Queue.js.map +1 -1
- package/lib/Cloudflare/Queues/QueueBinding.d.ts.map +1 -1
- package/lib/Cloudflare/Queues/QueueBinding.js +19 -0
- package/lib/Cloudflare/Queues/QueueBinding.js.map +1 -1
- package/lib/Cloudflare/Queues/QueueShim.d.ts +55 -0
- package/lib/Cloudflare/Queues/QueueShim.d.ts.map +1 -0
- package/lib/Cloudflare/Queues/QueueShim.js +177 -0
- package/lib/Cloudflare/Queues/QueueShim.js.map +1 -0
- package/lib/Cloudflare/R2/Bucket.d.ts +14 -1
- package/lib/Cloudflare/R2/Bucket.d.ts.map +1 -1
- package/lib/Cloudflare/R2/Bucket.js +53 -1
- package/lib/Cloudflare/R2/Bucket.js.map +1 -1
- package/lib/Cloudflare/R2/BucketBinding.d.ts +10 -0
- package/lib/Cloudflare/R2/BucketBinding.d.ts.map +1 -1
- package/lib/Cloudflare/R2/BucketBinding.js +14 -3
- package/lib/Cloudflare/R2/BucketBinding.js.map +1 -1
- package/lib/Cloudflare/R2/BucketLocal.d.ts +13 -6
- package/lib/Cloudflare/R2/BucketLocal.d.ts.map +1 -1
- package/lib/Cloudflare/R2/BucketLocal.js +19 -6
- package/lib/Cloudflare/R2/BucketLocal.js.map +1 -1
- package/lib/Cloudflare/R2/LocalR2Gateway.d.ts +15 -0
- package/lib/Cloudflare/R2/LocalR2Gateway.d.ts.map +1 -0
- package/lib/Cloudflare/R2/LocalR2Gateway.js +65 -0
- package/lib/Cloudflare/R2/LocalR2Gateway.js.map +1 -0
- package/lib/Cloudflare/R2/ReadBucketLocal.d.ts.map +1 -1
- package/lib/Cloudflare/R2/ReadBucketLocal.js +5 -1
- package/lib/Cloudflare/R2/ReadBucketLocal.js.map +1 -1
- package/lib/Cloudflare/R2/ReadWriteBucketLocal.d.ts.map +1 -1
- package/lib/Cloudflare/R2/ReadWriteBucketLocal.js +5 -1
- package/lib/Cloudflare/R2/ReadWriteBucketLocal.js.map +1 -1
- package/lib/Cloudflare/R2/WriteBucketLocal.d.ts.map +1 -1
- package/lib/Cloudflare/R2/WriteBucketLocal.js +5 -1
- package/lib/Cloudflare/R2/WriteBucketLocal.js.map +1 -1
- package/lib/Cloudflare/Ruleset/AccountEntrypoint.d.ts.map +1 -1
- package/lib/Cloudflare/Ruleset/AccountEntrypoint.js +5 -1
- package/lib/Cloudflare/Ruleset/AccountEntrypoint.js.map +1 -1
- package/lib/Cloudflare/Ruleset/Ruleset.d.ts.map +1 -1
- package/lib/Cloudflare/Ruleset/Ruleset.js +19 -6
- package/lib/Cloudflare/Ruleset/Ruleset.js.map +1 -1
- package/lib/Cloudflare/Rum/Site.js +10 -2
- package/lib/Cloudflare/Rum/Site.js.map +1 -1
- package/lib/Cloudflare/StateStore/Api.d.ts +32 -3
- package/lib/Cloudflare/StateStore/Api.js +1 -1
- package/lib/Cloudflare/StateStore/Api.js.map +1 -1
- package/lib/Cloudflare/StateStore/State.d.ts +1 -1
- package/lib/Cloudflare/StateStore/State.d.ts.map +1 -1
- package/lib/Cloudflare/Tunnel/Tunnel.d.ts.map +1 -1
- package/lib/Cloudflare/Tunnel/Tunnel.js +20 -1
- package/lib/Cloudflare/Tunnel/Tunnel.js.map +1 -1
- package/lib/Cloudflare/Website/StaticSite.d.ts +34 -7
- package/lib/Cloudflare/Website/StaticSite.d.ts.map +1 -1
- package/lib/Cloudflare/Website/StaticSite.js +108 -24
- package/lib/Cloudflare/Website/StaticSite.js.map +1 -1
- package/lib/Cloudflare/Website/Vite.d.ts +33 -19
- package/lib/Cloudflare/Website/Vite.d.ts.map +1 -1
- package/lib/Cloudflare/Website/Vite.js +33 -19
- package/lib/Cloudflare/Website/Vite.js.map +1 -1
- package/lib/Cloudflare/Workers/Assets.d.ts +26 -8
- package/lib/Cloudflare/Workers/Assets.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Assets.js +46 -1
- package/lib/Cloudflare/Workers/Assets.js.map +1 -1
- package/lib/Cloudflare/Workers/Compatibility.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Compatibility.js +26 -7
- package/lib/Cloudflare/Workers/Compatibility.js.map +1 -1
- package/lib/Cloudflare/Workers/DurableObject.d.ts +22 -0
- package/lib/Cloudflare/Workers/DurableObject.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/DurableObject.js +22 -0
- package/lib/Cloudflare/Workers/DurableObject.js.map +1 -1
- package/lib/Cloudflare/Workers/DurableObjectBridge.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/DurableObjectBridge.js +20 -5
- package/lib/Cloudflare/Workers/DurableObjectBridge.js.map +1 -1
- package/lib/Cloudflare/Workers/InferEnv.d.ts +4 -2
- package/lib/Cloudflare/Workers/InferEnv.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts +7 -3
- package/lib/Cloudflare/Workers/LocalWorkerProvider.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/LocalWorkerProvider.js +520 -302
- package/lib/Cloudflare/Workers/LocalWorkerProvider.js.map +1 -1
- package/lib/Cloudflare/Workers/Worker.d.ts +619 -18
- package/lib/Cloudflare/Workers/Worker.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/Worker.js +222 -2
- package/lib/Cloudflare/Workers/Worker.js.map +1 -1
- package/lib/Cloudflare/Workers/WorkerAsyncBindings.d.ts +13 -1
- package/lib/Cloudflare/Workers/WorkerAsyncBindings.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/WorkerAsyncBindings.js +171 -21
- package/lib/Cloudflare/Workers/WorkerAsyncBindings.js.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBinding.d.ts +130 -28
- package/lib/Cloudflare/Workers/WorkerBinding.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBinding.js.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBridge.d.ts +6 -2
- package/lib/Cloudflare/Workers/WorkerBridge.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBridge.js +18 -2
- package/lib/Cloudflare/Workers/WorkerBridge.js.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBundle.d.ts +20 -1
- package/lib/Cloudflare/Workers/WorkerBundle.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/WorkerBundle.js +15 -1
- package/lib/Cloudflare/Workers/WorkerBundle.js.map +1 -1
- package/lib/Cloudflare/Workers/WorkerProvider.d.ts +186 -11
- package/lib/Cloudflare/Workers/WorkerProvider.d.ts.map +1 -1
- package/lib/Cloudflare/Workers/WorkerProvider.js +1775 -240
- package/lib/Cloudflare/Workers/WorkerProvider.js.map +1 -1
- package/lib/Cloudflare/Workflows/WorkflowBridge.d.ts +9 -2
- package/lib/Cloudflare/Workflows/WorkflowBridge.d.ts.map +1 -1
- package/lib/Cloudflare/Workflows/WorkflowBridge.js +19 -9
- package/lib/Cloudflare/Workflows/WorkflowBridge.js.map +1 -1
- package/lib/Cloudflare/Workflows/index.d.ts +1 -1
- package/lib/Cloudflare/Workflows/index.d.ts.map +1 -1
- package/lib/Cloudflare/Workflows/index.js +1 -1
- package/lib/Cloudflare/Workflows/index.js.map +1 -1
- package/lib/Command/Build.d.ts +2 -2
- package/lib/Command/Build.d.ts.map +1 -1
- package/lib/Command/Build.js +4 -7
- package/lib/Command/Build.js.map +1 -1
- package/lib/Command/Command.d.ts +34 -4
- package/lib/Command/Command.d.ts.map +1 -1
- package/lib/Command/Command.js +125 -30
- package/lib/Command/Command.js.map +1 -1
- package/lib/Command/Dev.d.ts +2 -2
- package/lib/Command/Dev.d.ts.map +1 -1
- package/lib/Command/Dev.js +51 -73
- package/lib/Command/Dev.js.map +1 -1
- package/lib/Command/Exec.d.ts +11 -2
- package/lib/Command/Exec.d.ts.map +1 -1
- package/lib/Command/Exec.js +9 -0
- package/lib/Command/Exec.js.map +1 -1
- package/lib/Command/Memo.d.ts +9 -1
- package/lib/Command/Memo.d.ts.map +1 -1
- package/lib/Command/Memo.js +14 -2
- package/lib/Command/Memo.js.map +1 -1
- package/lib/Command/Providers.d.ts +1 -1
- package/lib/Command/Providers.d.ts.map +1 -1
- package/lib/Command/Redaction.d.ts +10 -0
- package/lib/Command/Redaction.d.ts.map +1 -0
- package/lib/Command/Redaction.js +119 -0
- package/lib/Command/Redaction.js.map +1 -0
- package/lib/Deploy.d.ts +2 -1
- package/lib/Deploy.d.ts.map +1 -1
- package/lib/Destroy.d.ts +2 -1
- package/lib/Destroy.d.ts.map +1 -1
- package/lib/Destroy.js +1 -9
- package/lib/Destroy.js.map +1 -1
- package/lib/Docker/Container.d.ts +41 -1
- package/lib/Docker/Container.d.ts.map +1 -1
- package/lib/Docker/Container.js +54 -12
- package/lib/Docker/Container.js.map +1 -1
- package/lib/Docker/Context.d.ts +84 -0
- package/lib/Docker/Context.d.ts.map +1 -0
- package/lib/Docker/Context.js +145 -0
- package/lib/Docker/Context.js.map +1 -0
- package/lib/Docker/Docker.d.ts +192 -14
- package/lib/Docker/Docker.d.ts.map +1 -1
- package/lib/Docker/Docker.js +149 -28
- package/lib/Docker/Docker.js.map +1 -1
- package/lib/Docker/Image.d.ts +12 -0
- package/lib/Docker/Image.d.ts.map +1 -1
- package/lib/Docker/Image.js +24 -7
- package/lib/Docker/Image.js.map +1 -1
- package/lib/Docker/Network.d.ts +7 -0
- package/lib/Docker/Network.d.ts.map +1 -1
- package/lib/Docker/Network.js +16 -7
- package/lib/Docker/Network.js.map +1 -1
- package/lib/Docker/Providers.d.ts +1 -1
- package/lib/Docker/Providers.d.ts.map +1 -1
- package/lib/Docker/Providers.js +13 -1
- package/lib/Docker/Providers.js.map +1 -1
- package/lib/Docker/RemoteImage.d.ts +12 -0
- package/lib/Docker/RemoteImage.d.ts.map +1 -1
- package/lib/Docker/RemoteImage.js +22 -8
- package/lib/Docker/RemoteImage.js.map +1 -1
- package/lib/Docker/Service.d.ts +445 -0
- package/lib/Docker/Service.d.ts.map +1 -0
- package/lib/Docker/Service.js +578 -0
- package/lib/Docker/Service.js.map +1 -0
- package/lib/Docker/ServiceImage.d.ts +78 -0
- package/lib/Docker/ServiceImage.d.ts.map +1 -0
- package/lib/Docker/ServiceImage.js +232 -0
- package/lib/Docker/ServiceImage.js.map +1 -0
- package/lib/Docker/Swarm.d.ts +117 -0
- package/lib/Docker/Swarm.d.ts.map +1 -0
- package/lib/Docker/Swarm.js +149 -0
- package/lib/Docker/Swarm.js.map +1 -0
- package/lib/Docker/Volume.d.ts +11 -0
- package/lib/Docker/Volume.d.ts.map +1 -1
- package/lib/Docker/Volume.js +21 -6
- package/lib/Docker/Volume.js.map +1 -1
- package/lib/Docker/index.d.ts +3 -0
- package/lib/Docker/index.d.ts.map +1 -1
- package/lib/Docker/index.js +3 -0
- package/lib/Docker/index.js.map +1 -1
- package/lib/Http.d.ts +8 -1
- package/lib/Http.d.ts.map +1 -1
- package/lib/Http.js +7 -2
- package/lib/Http.js.map +1 -1
- package/lib/Local/LocalProvider.d.ts +182 -0
- package/lib/Local/LocalProvider.d.ts.map +1 -0
- package/lib/Local/LocalProvider.js +226 -0
- package/lib/Local/LocalProvider.js.map +1 -0
- package/lib/Local/ProviderLayer.d.ts +43 -2
- package/lib/Local/ProviderLayer.d.ts.map +1 -1
- package/lib/Local/ProviderLayer.js +80 -2
- package/lib/Local/ProviderLayer.js.map +1 -1
- package/lib/Local/RpcProvider.d.ts +7 -0
- package/lib/Local/RpcProvider.d.ts.map +1 -1
- package/lib/Local/RpcProvider.js +8 -3
- package/lib/Local/RpcProvider.js.map +1 -1
- package/lib/Local/RpcProviderProxy.d.ts.map +1 -1
- package/lib/Local/RpcProviderProxy.js +3 -0
- package/lib/Local/RpcProviderProxy.js.map +1 -1
- package/lib/Local/RpcServer.d.ts +8 -1
- package/lib/Local/RpcServer.d.ts.map +1 -1
- package/lib/Local/RpcServer.js +4 -1
- package/lib/Local/RpcServer.js.map +1 -1
- package/lib/Output.d.ts +4 -1
- package/lib/Output.d.ts.map +1 -1
- package/lib/Output.js.map +1 -1
- package/lib/Plan.d.ts +34 -0
- package/lib/Plan.d.ts.map +1 -1
- package/lib/Plan.js +127 -40
- package/lib/Plan.js.map +1 -1
- package/lib/Planetscale/AuthProvider.d.ts.map +1 -1
- package/lib/Planetscale/AuthProvider.js +6 -2
- package/lib/Planetscale/AuthProvider.js.map +1 -1
- package/lib/Planetscale/Branch.d.ts.map +1 -1
- package/lib/Planetscale/Branch.js +36 -6
- package/lib/Planetscale/Branch.js.map +1 -1
- package/lib/Planetscale/MySQL/MySQLDatabase.d.ts.map +1 -1
- package/lib/Planetscale/MySQL/MySQLDatabase.js +31 -6
- package/lib/Planetscale/MySQL/MySQLDatabase.js.map +1 -1
- package/lib/Planetscale/OAuthClient.d.ts +7 -1
- package/lib/Planetscale/OAuthClient.d.ts.map +1 -1
- package/lib/Planetscale/OAuthClient.js +51 -4
- package/lib/Planetscale/OAuthClient.js.map +1 -1
- package/lib/Planetscale/Postgres/PostgresDatabase.d.ts.map +1 -1
- package/lib/Planetscale/Postgres/PostgresDatabase.js +30 -5
- package/lib/Planetscale/Postgres/PostgresDatabase.js.map +1 -1
- package/lib/Platform.d.ts.map +1 -1
- package/lib/Platform.js +25 -14
- package/lib/Platform.js.map +1 -1
- package/lib/Prisma/App.d.ts +80 -0
- package/lib/Prisma/App.d.ts.map +1 -0
- package/lib/Prisma/App.js +238 -0
- package/lib/Prisma/App.js.map +1 -0
- package/lib/Prisma/AuthProvider.d.ts +28 -0
- package/lib/Prisma/AuthProvider.d.ts.map +1 -0
- package/lib/Prisma/AuthProvider.js +131 -0
- package/lib/Prisma/AuthProvider.js.map +1 -0
- package/lib/Prisma/Branch.d.ts +75 -0
- package/lib/Prisma/Branch.d.ts.map +1 -0
- package/lib/Prisma/Branch.js +216 -0
- package/lib/Prisma/Branch.js.map +1 -0
- package/lib/Prisma/Client.d.ts +178 -0
- package/lib/Prisma/Client.d.ts.map +1 -0
- package/lib/Prisma/Client.js +562 -0
- package/lib/Prisma/Client.js.map +1 -0
- package/lib/Prisma/Compute.d.ts +583 -0
- package/lib/Prisma/Compute.d.ts.map +1 -0
- package/lib/Prisma/Compute.js +1543 -0
- package/lib/Prisma/Compute.js.map +1 -0
- package/lib/Prisma/ComputeArchive.d.ts +63 -0
- package/lib/Prisma/ComputeArchive.d.ts.map +1 -0
- package/lib/Prisma/ComputeArchive.js +227 -0
- package/lib/Prisma/ComputeArchive.js.map +1 -0
- package/lib/Prisma/ComputeBuild.d.ts +106 -0
- package/lib/Prisma/ComputeBuild.d.ts.map +1 -0
- package/lib/Prisma/ComputeBuild.js +987 -0
- package/lib/Prisma/ComputeBuild.js.map +1 -0
- package/lib/Prisma/ComputeLifecycle.d.ts +84 -0
- package/lib/Prisma/ComputeLifecycle.d.ts.map +1 -0
- package/lib/Prisma/ComputeLifecycle.js +186 -0
- package/lib/Prisma/ComputeLifecycle.js.map +1 -0
- package/lib/Prisma/Connect.d.ts +99 -0
- package/lib/Prisma/Connect.d.ts.map +1 -0
- package/lib/Prisma/Connect.js +187 -0
- package/lib/Prisma/Connect.js.map +1 -0
- package/lib/Prisma/Connection.d.ts +202 -0
- package/lib/Prisma/Connection.d.ts.map +1 -0
- package/lib/Prisma/Connection.js +346 -0
- package/lib/Prisma/Connection.js.map +1 -0
- package/lib/Prisma/CustomDomain.d.ts +81 -0
- package/lib/Prisma/CustomDomain.d.ts.map +1 -0
- package/lib/Prisma/CustomDomain.js +175 -0
- package/lib/Prisma/CustomDomain.js.map +1 -0
- package/lib/Prisma/Database.d.ts +208 -0
- package/lib/Prisma/Database.d.ts.map +1 -0
- package/lib/Prisma/Database.js +405 -0
- package/lib/Prisma/Database.js.map +1 -0
- package/lib/Prisma/Deployment.d.ts +135 -0
- package/lib/Prisma/Deployment.d.ts.map +1 -0
- package/lib/Prisma/Deployment.js +348 -0
- package/lib/Prisma/Deployment.js.map +1 -0
- package/lib/Prisma/EnvironmentVariable.d.ts +89 -0
- package/lib/Prisma/EnvironmentVariable.d.ts.map +1 -0
- package/lib/Prisma/EnvironmentVariable.js +213 -0
- package/lib/Prisma/EnvironmentVariable.js.map +1 -0
- package/lib/Prisma/Internal/AppIdentity.d.ts +10 -0
- package/lib/Prisma/Internal/AppIdentity.d.ts.map +1 -0
- package/lib/Prisma/Internal/AppIdentity.js +6 -0
- package/lib/Prisma/Internal/AppIdentity.js.map +1 -0
- package/lib/Prisma/Internal/AppPromotion.d.ts +19 -0
- package/lib/Prisma/Internal/AppPromotion.d.ts.map +1 -0
- package/lib/Prisma/Internal/AppPromotion.js +107 -0
- package/lib/Prisma/Internal/AppPromotion.js.map +1 -0
- package/lib/Prisma/Internal/ArchivePlatform.d.ts +42 -0
- package/lib/Prisma/Internal/ArchivePlatform.d.ts.map +1 -0
- package/lib/Prisma/Internal/ArchivePlatform.js +214 -0
- package/lib/Prisma/Internal/ArchivePlatform.js.map +1 -0
- package/lib/Prisma/Internal/ArtifactFile.d.ts +58 -0
- package/lib/Prisma/Internal/ArtifactFile.d.ts.map +1 -0
- package/lib/Prisma/Internal/ArtifactFile.js +134 -0
- package/lib/Prisma/Internal/ArtifactFile.js.map +1 -0
- package/lib/Prisma/Internal/ArtifactUpload.d.ts +5 -0
- package/lib/Prisma/Internal/ArtifactUpload.d.ts.map +1 -0
- package/lib/Prisma/Internal/ArtifactUpload.js +51 -0
- package/lib/Prisma/Internal/ArtifactUpload.js.map +1 -0
- package/lib/Prisma/Internal/BundlePaths.d.ts +4 -0
- package/lib/Prisma/Internal/BundlePaths.d.ts.map +1 -0
- package/lib/Prisma/Internal/BundlePaths.js +16 -0
- package/lib/Prisma/Internal/BundlePaths.js.map +1 -0
- package/lib/Prisma/Internal/CleanupFailure.d.ts +8 -0
- package/lib/Prisma/Internal/CleanupFailure.d.ts.map +1 -0
- package/lib/Prisma/Internal/CleanupFailure.js +8 -0
- package/lib/Prisma/Internal/CleanupFailure.js.map +1 -0
- package/lib/Prisma/Internal/DatabaseSecrets.d.ts +35 -0
- package/lib/Prisma/Internal/DatabaseSecrets.d.ts.map +1 -0
- package/lib/Prisma/Internal/DatabaseSecrets.js +85 -0
- package/lib/Prisma/Internal/DatabaseSecrets.js.map +1 -0
- package/lib/Prisma/Internal/DeploymentActions.d.ts +5 -0
- package/lib/Prisma/Internal/DeploymentActions.d.ts.map +1 -0
- package/lib/Prisma/Internal/DeploymentActions.js +16 -0
- package/lib/Prisma/Internal/DeploymentActions.js.map +1 -0
- package/lib/Prisma/Internal/DeploymentIdentity.d.ts +8 -0
- package/lib/Prisma/Internal/DeploymentIdentity.d.ts.map +1 -0
- package/lib/Prisma/Internal/DeploymentIdentity.js +12 -0
- package/lib/Prisma/Internal/DeploymentIdentity.js.map +1 -0
- package/lib/Prisma/Internal/DeploymentObserve.d.ts +3 -0
- package/lib/Prisma/Internal/DeploymentObserve.d.ts.map +1 -0
- package/lib/Prisma/Internal/DeploymentObserve.js +2 -0
- package/lib/Prisma/Internal/DeploymentObserve.js.map +1 -0
- package/lib/Prisma/Internal/EnvName.d.ts +7 -0
- package/lib/Prisma/Internal/EnvName.d.ts.map +1 -0
- package/lib/Prisma/Internal/EnvName.js +23 -0
- package/lib/Prisma/Internal/EnvName.js.map +1 -0
- package/lib/Prisma/Internal/HttpClient.d.ts +21 -0
- package/lib/Prisma/Internal/HttpClient.d.ts.map +1 -0
- package/lib/Prisma/Internal/HttpClient.js +23 -0
- package/lib/Prisma/Internal/HttpClient.js.map +1 -0
- package/lib/Prisma/Operations.d.ts +143 -0
- package/lib/Prisma/Operations.d.ts.map +1 -0
- package/lib/Prisma/Operations.js +75 -0
- package/lib/Prisma/Operations.js.map +1 -0
- package/lib/Prisma/Postgres.d.ts +24 -0
- package/lib/Prisma/Postgres.d.ts.map +1 -0
- package/lib/Prisma/Postgres.js +23 -0
- package/lib/Prisma/Postgres.js.map +1 -0
- package/lib/Prisma/PostgresOrigin.d.ts +31 -0
- package/lib/Prisma/PostgresOrigin.d.ts.map +1 -0
- package/lib/Prisma/PostgresOrigin.js +30 -0
- package/lib/Prisma/PostgresOrigin.js.map +1 -0
- package/lib/Prisma/PrismaDevDatabase.d.ts +25 -0
- package/lib/Prisma/PrismaDevDatabase.d.ts.map +1 -0
- package/lib/Prisma/PrismaDevDatabase.js +280 -0
- package/lib/Prisma/PrismaDevDatabase.js.map +1 -0
- package/lib/Prisma/PrismaEnvironment.d.ts +14 -0
- package/lib/Prisma/PrismaEnvironment.d.ts.map +1 -0
- package/lib/Prisma/PrismaEnvironment.js +50 -0
- package/lib/Prisma/PrismaEnvironment.js.map +1 -0
- package/lib/Prisma/PrismaLogs.d.ts +42 -0
- package/lib/Prisma/PrismaLogs.d.ts.map +1 -0
- package/lib/Prisma/PrismaLogs.js +318 -0
- package/lib/Prisma/PrismaLogs.js.map +1 -0
- package/lib/Prisma/Project.d.ts +117 -0
- package/lib/Prisma/Project.d.ts.map +1 -0
- package/lib/Prisma/Project.js +361 -0
- package/lib/Prisma/Project.js.map +1 -0
- package/lib/Prisma/Providers.d.ts +61 -0
- package/lib/Prisma/Providers.d.ts.map +1 -0
- package/lib/Prisma/Providers.js +282 -0
- package/lib/Prisma/Providers.js.map +1 -0
- package/lib/Prisma/Refs.d.ts +19 -0
- package/lib/Prisma/Refs.d.ts.map +1 -0
- package/lib/Prisma/Refs.js +25 -0
- package/lib/Prisma/Refs.js.map +1 -0
- package/lib/Prisma/SourceRepository.d.ts +104 -0
- package/lib/Prisma/SourceRepository.d.ts.map +1 -0
- package/lib/Prisma/SourceRepository.js +221 -0
- package/lib/Prisma/SourceRepository.js.map +1 -0
- package/lib/Prisma/Types.d.ts +539 -0
- package/lib/Prisma/Types.d.ts.map +1 -0
- package/lib/Prisma/Types.js +13 -0
- package/lib/Prisma/Types.js.map +1 -0
- package/lib/Prisma/index.d.ts +25 -0
- package/lib/Prisma/index.d.ts.map +1 -0
- package/lib/Prisma/index.js +24 -0
- package/lib/Prisma/index.js.map +1 -0
- package/lib/Provider.d.ts +77 -5
- package/lib/Provider.d.ts.map +1 -1
- package/lib/Provider.js +54 -30
- package/lib/Provider.js.map +1 -1
- package/lib/ProviderMode.d.ts +93 -0
- package/lib/ProviderMode.d.ts.map +1 -0
- package/lib/ProviderMode.js +77 -0
- package/lib/ProviderMode.js.map +1 -0
- package/lib/Random.d.ts +1 -1
- package/lib/Random.d.ts.map +1 -1
- package/lib/Resource.d.ts +22 -1
- package/lib/Resource.d.ts.map +1 -1
- package/lib/Resource.js +31 -0
- package/lib/Resource.js.map +1 -1
- package/lib/RuntimeContext.d.ts +7 -0
- package/lib/RuntimeContext.d.ts.map +1 -1
- package/lib/RuntimeContext.js.map +1 -1
- package/lib/SQL/D1.d.ts +1 -1
- package/lib/SQL/D1.js +1 -1
- package/lib/SQL/Postgres.d.ts +1 -1
- package/lib/SQL/Postgres.js +1 -1
- package/lib/Server/Process.d.ts +8 -0
- package/lib/Server/Process.d.ts.map +1 -1
- package/lib/Server/Process.js +29 -0
- package/lib/Server/Process.js.map +1 -1
- package/lib/State/HttpStateApi.d.ts +11 -11
- package/lib/State/InMemoryState.d.ts.map +1 -1
- package/lib/State/InMemoryState.js +2 -0
- package/lib/State/InMemoryState.js.map +1 -1
- package/lib/State/ResourceState.d.ts +18 -0
- package/lib/State/ResourceState.d.ts.map +1 -1
- package/lib/Sync.d.ts.map +1 -1
- package/lib/Sync.js +8 -2
- package/lib/Sync.js.map +1 -1
- package/lib/Telemetry.d.ts +150 -0
- package/lib/Telemetry.d.ts.map +1 -0
- package/lib/Telemetry.js +512 -0
- package/lib/Telemetry.js.map +1 -0
- package/lib/Test/Alchemy.d.ts +1 -1
- package/lib/Test/Alchemy.d.ts.map +1 -1
- package/lib/Test/Alchemy.js +4 -2
- package/lib/Test/Alchemy.js.map +1 -1
- package/lib/Test/Core.d.ts +2 -2
- package/lib/Test/Core.d.ts.map +1 -1
- package/lib/Test/Core.js +1 -8
- package/lib/Test/Core.js.map +1 -1
- package/lib/Test/Vitest.d.ts +58 -0
- package/lib/Test/Vitest.d.ts.map +1 -0
- package/lib/Test/Vitest.js +114 -0
- package/lib/Test/Vitest.js.map +1 -0
- package/lib/Util/Clank.d.ts +11 -6
- package/lib/Util/Clank.d.ts.map +1 -1
- package/lib/Util/Clank.js +22 -9
- package/lib/Util/Clank.js.map +1 -1
- package/lib/Util/proxy-chain.d.ts +5 -0
- package/lib/Util/proxy-chain.d.ts.map +1 -1
- package/lib/Util/proxy-chain.js +52 -4
- package/lib/Util/proxy-chain.js.map +1 -1
- package/lib/Util/zip.d.ts +11 -0
- package/lib/Util/zip.d.ts.map +1 -1
- package/lib/Util/zip.js +30 -0
- package/lib/Util/zip.js.map +1 -1
- package/lib/index.d.ts +3 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -0
- package/lib/index.js.map +1 -1
- package/package.json +59 -18
- package/src/AWS/ApiGateway/common.ts +9 -0
- package/src/AWS/Bedrock/LanguageModel.ts +883 -0
- package/src/AWS/Bedrock/LanguageModelHttp.ts +36 -0
- package/src/AWS/Bedrock/index.ts +2 -0
- package/src/AWS/CloudFront/Distribution.ts +116 -6
- package/src/AWS/CostExplorer/common.ts +23 -2
- package/src/AWS/DataSync/Task.ts +30 -15
- package/src/AWS/EC2/InternetGateway.ts +8 -3
- package/src/AWS/EC2/LingeringEnis.ts +169 -0
- package/src/AWS/EC2/SecurityGroup.ts +24 -28
- package/src/AWS/EC2/Subnet.ts +23 -34
- package/src/AWS/EC2/Vpc.ts +35 -21
- package/src/AWS/EC2/hosted.ts +10 -2
- package/src/AWS/ECR/ImageSource.ts +10 -2
- package/src/AWS/ECR/Repository.ts +39 -4
- package/src/AWS/ECS/Service.ts +322 -22
- package/src/AWS/ECS/Task.ts +82 -32
- package/src/AWS/EKS/Addon.ts +17 -4
- package/src/AWS/EKS/Deployment.ts +36 -7
- package/src/AWS/EKS/HyperPod.ts +97 -0
- package/src/AWS/EKS/Job.ts +42 -9
- package/src/AWS/EKS/internal/client.ts +40 -3
- package/src/AWS/Lambda/Function.ts +106 -46
- package/src/AWS/Lambda/HttpServer.ts +7 -1
- package/src/AWS/Lambda/LayerVersion.ts +490 -0
- package/src/AWS/Lambda/MicrovmBundle.ts +10 -2
- package/src/AWS/Lambda/index.ts +1 -0
- package/src/AWS/Providers.ts +16 -0
- package/src/AWS/SES/ActiveReceiptRuleSet.ts +88 -0
- package/src/AWS/SES/ReceiptFilter.ts +169 -0
- package/src/AWS/SES/ReceiptRule.ts +300 -0
- package/src/AWS/SES/ReceiptRuleSet.ts +164 -0
- package/src/AWS/SES/SendBounce.ts +44 -0
- package/src/AWS/SES/SendBounceHttp.ts +13 -0
- package/src/AWS/SES/index.ts +6 -0
- package/src/AWS/SageMaker/Cluster.ts +775 -0
- package/src/AWS/SageMaker/ClusterSchedulerConfig.ts +430 -0
- package/src/AWS/SageMaker/ComputeQuota.ts +426 -0
- package/src/AWS/SageMaker/index.ts +3 -0
- package/src/AWS/StateStore/State.ts +159 -42
- package/src/Apply.ts +199 -83
- package/src/Axiom/Telemetry.ts +119 -0
- package/src/Axiom/index.ts +1 -0
- package/src/Bundle/Bundle.ts +17 -7
- package/src/Bundle/BundleAnalyzerPlugin.ts +9 -4
- package/src/Bundle/PurePlugin.ts +109 -46
- package/src/Cli/checkVersion.ts +102 -16
- package/src/Cli/commands/deploy.ts +3 -14
- package/src/Cli/commands/logs.ts +6 -1
- package/src/Cli/commands/nuke.ts +295 -56
- package/src/Cli/commands/tail.ts +6 -1
- package/src/Cli/main.ts +5 -4
- package/src/Cloudflare/AI/Search.ts +4 -18
- package/src/Cloudflare/AI/SearchInstance.ts +7 -26
- package/src/Cloudflare/ApiToken/AccountApiToken.ts +3 -0
- package/src/Cloudflare/Auth/AuthProvider.ts +17 -3
- package/src/Cloudflare/Auth/OAuthClient.ts +58 -2
- package/src/Cloudflare/Containers/Container.ts +92 -3
- package/src/Cloudflare/Containers/ContainerApplication.ts +31 -5
- package/src/Cloudflare/Containers/ContainerBundle.ts +10 -2
- package/src/Cloudflare/Containers/ContainerProvider.ts +62 -0
- package/src/Cloudflare/Credentials.ts +69 -9
- package/src/Cloudflare/D1/ApplyMigrations.ts +72 -77
- package/src/Cloudflare/D1/Database.ts +154 -2
- package/src/Cloudflare/D1/LocalD1Gateway.ts +150 -0
- package/src/Cloudflare/D1/QueryDatabaseHttpClient.ts +49 -11
- package/src/Cloudflare/D1/QueryDatabaseLocal.ts +44 -2
- package/src/Cloudflare/Email/Address.ts +18 -1
- package/src/Cloudflare/Email/SendBinding.ts +1 -0
- package/src/Cloudflare/Email/SendEmail.ts +13 -0
- package/src/Cloudflare/Flagship/App.ts +18 -6
- package/src/Cloudflare/KV/LocalKVGateway.ts +86 -0
- package/src/Cloudflare/KV/Namespace.ts +47 -1
- package/src/Cloudflare/KV/NamespaceLocal.ts +29 -6
- package/src/Cloudflare/KV/ReadNamespaceLocal.ts +5 -1
- package/src/Cloudflare/KV/ReadWriteNamespaceLocal.ts +5 -1
- package/src/Cloudflare/KV/WriteNamespaceLocal.ts +5 -1
- package/src/Cloudflare/LocalGateway.ts +86 -0
- package/src/Cloudflare/LocalRuntime.ts +21 -1
- package/src/Cloudflare/Logs.ts +58 -28
- package/src/Cloudflare/Providers.ts +4 -2
- package/src/Cloudflare/Queues/Consumer.ts +70 -2
- package/src/Cloudflare/Queues/Queue.ts +7 -16
- package/src/Cloudflare/Queues/QueueBinding.ts +21 -0
- package/src/Cloudflare/Queues/QueueShim.ts +193 -0
- package/src/Cloudflare/R2/Bucket.ts +55 -1
- package/src/Cloudflare/R2/BucketBinding.ts +17 -3
- package/src/Cloudflare/R2/BucketLocal.ts +25 -8
- package/src/Cloudflare/R2/LocalR2Gateway.ts +84 -0
- package/src/Cloudflare/R2/ReadBucketLocal.ts +7 -1
- package/src/Cloudflare/R2/ReadWriteBucketLocal.ts +7 -1
- package/src/Cloudflare/R2/WriteBucketLocal.ts +7 -1
- package/src/Cloudflare/Ruleset/AccountEntrypoint.ts +5 -1
- package/src/Cloudflare/Ruleset/Ruleset.ts +20 -6
- package/src/Cloudflare/Rum/Site.ts +11 -4
- package/src/Cloudflare/StateStore/Api.ts +1 -1
- package/src/Cloudflare/Tunnel/Tunnel.ts +23 -1
- package/src/Cloudflare/Website/StaticSite.ts +117 -28
- package/src/Cloudflare/Website/Vite.ts +33 -19
- package/src/Cloudflare/Workers/Assets.ts +53 -1
- package/src/Cloudflare/Workers/Compatibility.ts +27 -7
- package/src/Cloudflare/Workers/DurableObject.ts +22 -0
- package/src/Cloudflare/Workers/DurableObjectBridge.ts +29 -10
- package/src/Cloudflare/Workers/InferEnv.ts +74 -59
- package/src/Cloudflare/Workers/LocalWorkerProvider.ts +662 -405
- package/src/Cloudflare/Workers/Worker.ts +683 -33
- package/src/Cloudflare/Workers/WorkerAsyncBindings.ts +209 -25
- package/src/Cloudflare/Workers/WorkerBinding.ts +67 -3
- package/src/Cloudflare/Workers/WorkerBridge.ts +26 -4
- package/src/Cloudflare/Workers/WorkerBundle.ts +37 -2
- package/src/Cloudflare/Workers/WorkerProvider.ts +2287 -281
- package/src/Cloudflare/Workflows/WorkflowBridge.ts +36 -18
- package/src/Cloudflare/Workflows/index.ts +1 -1
- package/src/Command/Build.ts +8 -8
- package/src/Command/Command.ts +217 -42
- package/src/Command/Dev.ts +79 -107
- package/src/Command/Exec.ts +11 -2
- package/src/Command/Memo.ts +27 -3
- package/src/Command/Redaction.ts +181 -0
- package/src/Destroy.ts +1 -10
- package/src/Docker/Container.ts +85 -16
- package/src/Docker/Context.ts +236 -0
- package/src/Docker/Docker.ts +395 -43
- package/src/Docker/Image.ts +28 -7
- package/src/Docker/Network.ts +38 -14
- package/src/Docker/Providers.ts +16 -1
- package/src/Docker/RemoteImage.ts +23 -7
- package/src/Docker/Service.ts +1119 -0
- package/src/Docker/ServiceImage.ts +331 -0
- package/src/Docker/Swarm.ts +226 -0
- package/src/Docker/Volume.ts +25 -6
- package/src/Docker/index.ts +3 -0
- package/src/Http.ts +15 -2
- package/src/Local/LocalProvider.ts +518 -0
- package/src/Local/ProviderLayer.ts +113 -11
- package/src/Local/RpcProvider.ts +10 -5
- package/src/Local/RpcProviderProxy.ts +4 -1
- package/src/Local/RpcServer.ts +20 -2
- package/src/Output.ts +19 -11
- package/src/Plan.ts +182 -54
- package/src/Planetscale/AuthProvider.ts +15 -2
- package/src/Planetscale/Branch.ts +39 -6
- package/src/Planetscale/MySQL/MySQLDatabase.ts +34 -6
- package/src/Planetscale/OAuthClient.ts +59 -3
- package/src/Planetscale/Postgres/PostgresDatabase.ts +33 -5
- package/src/Platform.ts +29 -17
- package/src/Prisma/App.ts +420 -0
- package/src/Prisma/AuthProvider.ts +260 -0
- package/src/Prisma/Branch.ts +396 -0
- package/src/Prisma/Client.ts +1337 -0
- package/src/Prisma/Compute.ts +2876 -0
- package/src/Prisma/ComputeArchive.ts +432 -0
- package/src/Prisma/ComputeBuild.ts +1652 -0
- package/src/Prisma/ComputeLifecycle.ts +391 -0
- package/src/Prisma/Connect.ts +410 -0
- package/src/Prisma/Connection.ts +626 -0
- package/src/Prisma/CustomDomain.ts +363 -0
- package/src/Prisma/Database.ts +836 -0
- package/src/Prisma/Deployment.ts +655 -0
- package/src/Prisma/EnvironmentVariable.ts +401 -0
- package/src/Prisma/Internal/AppIdentity.ts +19 -0
- package/src/Prisma/Internal/AppPromotion.ts +172 -0
- package/src/Prisma/Internal/ArchivePlatform.ts +320 -0
- package/src/Prisma/Internal/ArtifactFile.ts +242 -0
- package/src/Prisma/Internal/ArtifactUpload.ts +86 -0
- package/src/Prisma/Internal/BundlePaths.ts +28 -0
- package/src/Prisma/Internal/CleanupFailure.ts +17 -0
- package/src/Prisma/Internal/DatabaseSecrets.ts +175 -0
- package/src/Prisma/Internal/DeploymentActions.ts +59 -0
- package/src/Prisma/Internal/DeploymentIdentity.ts +25 -0
- package/src/Prisma/Internal/DeploymentObserve.ts +6 -0
- package/src/Prisma/Internal/EnvName.ts +24 -0
- package/src/Prisma/Internal/HttpClient.ts +30 -0
- package/src/Prisma/Operations.ts +259 -0
- package/src/Prisma/Postgres.ts +24 -0
- package/src/Prisma/PostgresOrigin.ts +52 -0
- package/src/Prisma/PrismaDevDatabase.ts +395 -0
- package/src/Prisma/PrismaEnvironment.ts +89 -0
- package/src/Prisma/PrismaLogs.ts +434 -0
- package/src/Prisma/Project.ts +681 -0
- package/src/Prisma/Providers.ts +411 -0
- package/src/Prisma/Refs.ts +69 -0
- package/src/Prisma/SourceRepository.ts +432 -0
- package/src/Prisma/Types.ts +611 -0
- package/src/Prisma/index.ts +24 -0
- package/src/Provider.ts +175 -39
- package/src/ProviderMode.ts +123 -0
- package/src/Resource.ts +64 -1
- package/src/RuntimeContext.ts +7 -0
- package/src/SQL/D1.ts +1 -1
- package/src/SQL/Postgres.ts +1 -1
- package/src/Server/Process.ts +33 -0
- package/src/State/InMemoryState.ts +2 -0
- package/src/State/ResourceState.ts +18 -0
- package/src/Sync.ts +14 -2
- package/src/Telemetry.ts +736 -0
- package/src/Test/Alchemy.ts +4 -2
- package/src/Test/Core.ts +1 -8
- package/src/Test/Vitest.ts +244 -0
- package/src/Util/Clank.ts +37 -11
- package/src/Util/proxy-chain.ts +73 -4
- package/src/Util/zip.ts +39 -0
- package/src/index.ts +3 -0
- package/lib/SQL/index.d.ts +0 -4
- package/lib/SQL/index.d.ts.map +0 -1
- package/lib/SQL/index.js +0 -4
- package/lib/SQL/index.js.map +0 -1
- package/src/SQL/index.ts +0 -3
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import * as durableObjectsApi from "@distilled.cloud/cloudflare/durable-objects";
|
|
2
|
+
import * as rulesets from "@distilled.cloud/cloudflare/rulesets";
|
|
2
3
|
import * as workers from "@distilled.cloud/cloudflare/workers";
|
|
3
4
|
import * as wfp from "@distilled.cloud/cloudflare/workers-for-platforms";
|
|
4
5
|
import * as zones from "@distilled.cloud/cloudflare/zones";
|
|
6
|
+
import * as Config from "effect/Config";
|
|
5
7
|
import * as Data from "effect/Data";
|
|
6
8
|
import * as Effect from "effect/Effect";
|
|
9
|
+
import * as Layer from "effect/Layer";
|
|
10
|
+
import * as Option from "effect/Option";
|
|
7
11
|
import * as Path from "effect/Path";
|
|
8
12
|
import * as Predicate from "effect/Predicate";
|
|
9
13
|
import * as Redacted from "effect/Redacted";
|
|
10
14
|
import * as Schedule from "effect/Schedule";
|
|
11
15
|
import * as Stream from "effect/Stream";
|
|
16
|
+
import { isHttpClientError } from "effect/unstable/http/HttpClientError";
|
|
12
17
|
import * as crypto from "node:crypto";
|
|
13
18
|
import { Unowned } from "../../AdoptPolicy.ts";
|
|
14
19
|
import * as Artifacts from "../../Artifacts.ts";
|
|
@@ -19,22 +24,39 @@ import * as ProviderLayer from "../../Local/ProviderLayer.ts";
|
|
|
19
24
|
import * as Provider from "../../Provider.ts";
|
|
20
25
|
import { type ResourceBinding } from "../../Resource.ts";
|
|
21
26
|
import { Stack } from "../../Stack.ts";
|
|
27
|
+
import { cachedFunction } from "../../Util/cached-function.ts";
|
|
22
28
|
import { sha256Object } from "../../Util/sha256.ts";
|
|
23
29
|
import { CloudflareEnvironment } from "../CloudflareEnvironment.ts";
|
|
30
|
+
import { localRuntimeServices } from "../LocalRuntime.ts";
|
|
24
31
|
import { CloudflareLogs } from "../Logs.ts";
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
32
|
+
import { resolveZoneId } from "../Zone/lookup.ts";
|
|
33
|
+
import {
|
|
34
|
+
mergeAssetsConfigFiles,
|
|
35
|
+
readAssets,
|
|
36
|
+
readAssetsConfigFiles,
|
|
37
|
+
uploadAssets,
|
|
38
|
+
} from "./Assets.ts";
|
|
27
39
|
import { getCompatibility } from "./Compatibility.ts";
|
|
28
40
|
import { isDurableObjectExport } from "./DurableObject.ts";
|
|
29
41
|
import { LocalWorkerProvider } from "./LocalWorkerProvider.ts";
|
|
30
42
|
import {
|
|
43
|
+
isSelfUrl,
|
|
31
44
|
Worker,
|
|
32
45
|
type ViteOptions,
|
|
33
46
|
type WorkerProps,
|
|
34
47
|
type WorkerRouteConfig,
|
|
48
|
+
type WorkerVersionAffinity,
|
|
35
49
|
} from "./Worker.ts";
|
|
36
|
-
import {
|
|
37
|
-
|
|
50
|
+
import {
|
|
51
|
+
getCacheBinding,
|
|
52
|
+
getCronBindings,
|
|
53
|
+
isContainerDecl,
|
|
54
|
+
} from "./WorkerAsyncBindings.ts";
|
|
55
|
+
import type {
|
|
56
|
+
WireWorkerBinding,
|
|
57
|
+
WorkerBinding,
|
|
58
|
+
WorkerSettingsBinding,
|
|
59
|
+
} from "./WorkerBinding.ts";
|
|
38
60
|
import { isPythonMain, readPythonWorkerBundle } from "./PythonWorkerBundle.ts";
|
|
39
61
|
import { readPrebuiltWorkerBundle, WorkerBundle } from "./WorkerBundle.ts";
|
|
40
62
|
import { isWorkerLoader } from "./WorkerLoader.ts";
|
|
@@ -120,6 +142,433 @@ export const resolveNamespaceName = (
|
|
|
120
142
|
return (namespace as { name?: string }).name;
|
|
121
143
|
};
|
|
122
144
|
|
|
145
|
+
/**
|
|
146
|
+
* A Worker's `version` configuration is invalid — a prop that can't be
|
|
147
|
+
* combined with `version.parent` (script-level settings belong to the
|
|
148
|
+
* parent), a locally-hosted Durable Object / Workflow class on a version
|
|
149
|
+
* worker, an out-of-range `traffic`, or a gradual rollout that requires
|
|
150
|
+
* changes the versions API can't carry (assets, DO migrations).
|
|
151
|
+
*/
|
|
152
|
+
export class WorkerVersionConfigError extends Data.TaggedError(
|
|
153
|
+
"WorkerVersionConfigError",
|
|
154
|
+
)<{
|
|
155
|
+
message: string;
|
|
156
|
+
}> {}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Resolve the parent script *name* from a resolved `version.parent` prop or
|
|
160
|
+
* persisted props. The engine resolves a passed {@link Worker} (or
|
|
161
|
+
* `Worker.ref(...)`) to its Attributes object — possibly stables-only during
|
|
162
|
+
* planning, but `workerName` is always a stable — so the value is either the
|
|
163
|
+
* script name string, that attributes object, or `undefined`.
|
|
164
|
+
*
|
|
165
|
+
* @internal
|
|
166
|
+
*/
|
|
167
|
+
export const resolveVersionParentName = (
|
|
168
|
+
version: WorkerProps["version"],
|
|
169
|
+
): string | undefined => {
|
|
170
|
+
const parent = version?.parent;
|
|
171
|
+
if (parent == null) return undefined;
|
|
172
|
+
if (typeof parent === "string") return parent;
|
|
173
|
+
const workerName = (parent as { workerName?: unknown }).workerName;
|
|
174
|
+
return typeof workerName === "string" ? workerName : undefined;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* The traffic percentage a *self-owned* Worker's new version should receive,
|
|
179
|
+
* or `undefined` for the default full cutover. Only a `version` prop without
|
|
180
|
+
* a `parent` participates — version workers handle traffic separately.
|
|
181
|
+
*
|
|
182
|
+
* @internal
|
|
183
|
+
*/
|
|
184
|
+
const getSelfRolloutTraffic = (news: WorkerProps): number | undefined => {
|
|
185
|
+
if (!news.version || news.version.parent != null) return undefined;
|
|
186
|
+
const traffic = news.version.traffic;
|
|
187
|
+
return traffic === undefined || traffic >= 100 ? undefined : traffic;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
const validateTraffic = (traffic: number | undefined) =>
|
|
191
|
+
traffic !== undefined &&
|
|
192
|
+
(!Number.isFinite(traffic) || traffic < 0 || traffic > 100)
|
|
193
|
+
? Effect.fail(
|
|
194
|
+
new WorkerVersionConfigError({
|
|
195
|
+
message: `version.traffic must be a percentage between 0 and 100, got ${traffic}`,
|
|
196
|
+
}),
|
|
197
|
+
)
|
|
198
|
+
: Effect.void;
|
|
199
|
+
|
|
200
|
+
/** The request header Cloudflare hashes to pin a request to a version. */
|
|
201
|
+
const AFFINITY_HEADER = "Cloudflare-Workers-Version-Key";
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* `version.affinity` normalized to a single key source plus the optional
|
|
205
|
+
* IP fallback.
|
|
206
|
+
*
|
|
207
|
+
* @internal exported for unit testing.
|
|
208
|
+
*/
|
|
209
|
+
export interface ResolvedVersionAffinity {
|
|
210
|
+
source:
|
|
211
|
+
| { kind: "cookie" | "header"; name: string }
|
|
212
|
+
| { kind: "ip" }
|
|
213
|
+
| { kind: "key"; expression: string };
|
|
214
|
+
ipFallback: boolean;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Cookie / header names are interpolated into a double-quoted Rules-language
|
|
218
|
+
// string literal — restrict them to the token characters real-world names
|
|
219
|
+
// use so a name can never terminate the literal or smuggle expression text.
|
|
220
|
+
const AFFINITY_NAME_PATTERN = /^[A-Za-z0-9_.-]+$/;
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Validate `version.affinity` and normalize it to its key source: exactly
|
|
224
|
+
* one of `cookie` / `header` / `key`, or `ip: true` alone; `ip` combines
|
|
225
|
+
* with `cookie` / `header` as the absent-source fallback.
|
|
226
|
+
*
|
|
227
|
+
* @internal exported for unit testing.
|
|
228
|
+
*/
|
|
229
|
+
export const resolveVersionAffinity = (
|
|
230
|
+
affinity: WorkerVersionAffinity,
|
|
231
|
+
): Effect.Effect<ResolvedVersionAffinity, WorkerVersionConfigError> =>
|
|
232
|
+
Effect.gen(function* () {
|
|
233
|
+
const declared = [
|
|
234
|
+
...(affinity.cookie !== undefined ? ["cookie"] : []),
|
|
235
|
+
...(affinity.header !== undefined ? ["header"] : []),
|
|
236
|
+
...(affinity.key !== undefined ? ["key"] : []),
|
|
237
|
+
];
|
|
238
|
+
if (declared.length > 1) {
|
|
239
|
+
return yield* Effect.fail(
|
|
240
|
+
new WorkerVersionConfigError({
|
|
241
|
+
message: `version.affinity accepts exactly one key source, got ${declared.join(" and ")}. Combine sources with a raw \`key\` expression instead.`,
|
|
242
|
+
}),
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
if (declared.length === 0 && affinity.ip !== true) {
|
|
246
|
+
return yield* Effect.fail(
|
|
247
|
+
new WorkerVersionConfigError({
|
|
248
|
+
message:
|
|
249
|
+
"version.affinity requires a key source: set `cookie`, `header`, `key`, or `ip: true`.",
|
|
250
|
+
}),
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
if (affinity.key !== undefined && affinity.ip === true) {
|
|
254
|
+
return yield* Effect.fail(
|
|
255
|
+
new WorkerVersionConfigError({
|
|
256
|
+
message:
|
|
257
|
+
"version.affinity: `ip` is the fallback for an absent `cookie`/`header` — a raw `key` expression has no absence condition to fall back from. Fold `ip.src` into the expression instead.",
|
|
258
|
+
}),
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
for (const [prop, name] of [
|
|
262
|
+
["cookie", affinity.cookie],
|
|
263
|
+
["header", affinity.header],
|
|
264
|
+
] as const) {
|
|
265
|
+
if (name !== undefined && !AFFINITY_NAME_PATTERN.test(name)) {
|
|
266
|
+
return yield* Effect.fail(
|
|
267
|
+
new WorkerVersionConfigError({
|
|
268
|
+
message: `version.affinity.${prop} '${name}' is not a valid ${prop} name: expected only letters, digits, '_', '.', and '-'.`,
|
|
269
|
+
}),
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
const source: ResolvedVersionAffinity["source"] =
|
|
274
|
+
affinity.cookie !== undefined
|
|
275
|
+
? { kind: "cookie", name: affinity.cookie }
|
|
276
|
+
: affinity.header !== undefined
|
|
277
|
+
? // Rules-language header map keys are lowercase.
|
|
278
|
+
{ kind: "header", name: affinity.header.toLowerCase() }
|
|
279
|
+
: affinity.key !== undefined
|
|
280
|
+
? { kind: "key", expression: affinity.key }
|
|
281
|
+
: { kind: "ip" };
|
|
282
|
+
return {
|
|
283
|
+
source,
|
|
284
|
+
ipFallback:
|
|
285
|
+
affinity.ip === true &&
|
|
286
|
+
(source.kind === "cookie" || source.kind === "header"),
|
|
287
|
+
};
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
/** A hostname a Worker serves on within one zone. */
|
|
291
|
+
interface AffinityZoneHost {
|
|
292
|
+
host: string;
|
|
293
|
+
/** `true` when `host` came from a route pattern containing `*`. */
|
|
294
|
+
wildcard: boolean;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* The `http.host` clause scoping a zone's affinity rules to the Worker's
|
|
299
|
+
* own hostnames, so unrelated zone traffic — and other Workers' rollouts
|
|
300
|
+
* on the same zone — never get this Worker's version key.
|
|
301
|
+
*
|
|
302
|
+
* @internal exported for unit testing.
|
|
303
|
+
*/
|
|
304
|
+
export const affinityHostExpression = (
|
|
305
|
+
hosts: readonly AffinityZoneHost[],
|
|
306
|
+
): string => {
|
|
307
|
+
const dedupe = (values: string[]) => [...new Set(values)].sort();
|
|
308
|
+
const exact = dedupe(hosts.filter((h) => !h.wildcard).map((h) => h.host));
|
|
309
|
+
const wild = dedupe(hosts.filter((h) => h.wildcard).map((h) => h.host));
|
|
310
|
+
const clauses = [
|
|
311
|
+
...(exact.length === 1
|
|
312
|
+
? [`http.host eq "${exact[0]}"`]
|
|
313
|
+
: exact.length > 1
|
|
314
|
+
? [`http.host in {${exact.map((h) => `"${h}"`).join(" ")}}`]
|
|
315
|
+
: []),
|
|
316
|
+
...wild.map((h) => `http.host wildcard "${h}"`),
|
|
317
|
+
];
|
|
318
|
+
return clauses.length === 1 ? clauses[0] : `(${clauses.join(" or ")})`;
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
interface AffinityRuleSpec {
|
|
322
|
+
description: string;
|
|
323
|
+
expression: string;
|
|
324
|
+
/** Rules-language expression producing the header value. */
|
|
325
|
+
value: string;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const affinityRulePrefix = (scriptName: string) =>
|
|
329
|
+
`alchemy:worker:${scriptName}:affinity`;
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* The transform rules pinning one zone's traffic: a primary rule filling
|
|
333
|
+
* the version-key header from the configured source, plus — for
|
|
334
|
+
* `cookie`/`header` sources with `ip: true` — a fallback rule keying
|
|
335
|
+
* requests that lack the source by client IP.
|
|
336
|
+
*
|
|
337
|
+
* @internal exported for unit testing.
|
|
338
|
+
*/
|
|
339
|
+
export const buildAffinityZoneRules = (
|
|
340
|
+
scriptName: string,
|
|
341
|
+
affinity: ResolvedVersionAffinity,
|
|
342
|
+
hosts: readonly AffinityZoneHost[],
|
|
343
|
+
): AffinityRuleSpec[] => {
|
|
344
|
+
const prefix = affinityRulePrefix(scriptName);
|
|
345
|
+
const hostExpr = affinityHostExpression(hosts);
|
|
346
|
+
const { source } = affinity;
|
|
347
|
+
if (source.kind === "ip" || source.kind === "key") {
|
|
348
|
+
return [
|
|
349
|
+
{
|
|
350
|
+
description: `${prefix}:key`,
|
|
351
|
+
expression: hostExpr,
|
|
352
|
+
value: source.kind === "ip" ? "to_string(ip.src)" : source.expression,
|
|
353
|
+
},
|
|
354
|
+
];
|
|
355
|
+
}
|
|
356
|
+
const field =
|
|
357
|
+
source.kind === "cookie"
|
|
358
|
+
? `http.request.cookies["${source.name}"]`
|
|
359
|
+
: `http.request.headers["${source.name}"]`;
|
|
360
|
+
return [
|
|
361
|
+
{
|
|
362
|
+
description: `${prefix}:key`,
|
|
363
|
+
expression: `${hostExpr} and len(${field}) > 0`,
|
|
364
|
+
value: `${field}[0]`,
|
|
365
|
+
},
|
|
366
|
+
...(affinity.ipFallback
|
|
367
|
+
? [
|
|
368
|
+
{
|
|
369
|
+
// An absent cookie/header is a *missing* value in the Rules
|
|
370
|
+
// language, and every comparison on missing evaluates false —
|
|
371
|
+
// `len(field) == 0` can never match. `not (len(field) > 0)`
|
|
372
|
+
// is the complement that does: missing → false → `not` → true.
|
|
373
|
+
description: `${prefix}:ip`,
|
|
374
|
+
expression: `${hostExpr} and not (len(${field}) > 0)`,
|
|
375
|
+
value: "to_string(ip.src)",
|
|
376
|
+
},
|
|
377
|
+
]
|
|
378
|
+
: []),
|
|
379
|
+
];
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* A Worker's `domain` configuration is invalid — a hostname appears in more
|
|
384
|
+
* than one role (name/aliases/redirects), or a redirect targets itself.
|
|
385
|
+
*/
|
|
386
|
+
export class WorkerDomainConfigError extends Data.TaggedError(
|
|
387
|
+
"WorkerDomainConfigError",
|
|
388
|
+
)<{
|
|
389
|
+
message: string;
|
|
390
|
+
}> {}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* The resolved shape of `WorkerProps.workersDev`: `enabled` drives the
|
|
394
|
+
* stable `<name>.<account>.workers.dev` URL, `previewsEnabled` the
|
|
395
|
+
* per-version preview URLs. The two toggles are independent on the
|
|
396
|
+
* Cloudflare API.
|
|
397
|
+
*
|
|
398
|
+
* @internal exported for unit testing.
|
|
399
|
+
*/
|
|
400
|
+
export interface ResolvedWorkersDev {
|
|
401
|
+
enabled: boolean;
|
|
402
|
+
previewsEnabled: boolean;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Resolve the `workersDev` prop to its full shape. `true` / omitted means
|
|
407
|
+
* "default workers.dev behavior" (stable URL + version previews), `false`
|
|
408
|
+
* disables both, and the object form fills unset toggles with `true`.
|
|
409
|
+
*
|
|
410
|
+
* @internal exported for unit testing.
|
|
411
|
+
*/
|
|
412
|
+
export const resolveWorkersDev = (
|
|
413
|
+
workersDev: WorkerProps["workersDev"],
|
|
414
|
+
): ResolvedWorkersDev => {
|
|
415
|
+
if (workersDev === undefined || workersDev === true) {
|
|
416
|
+
return { enabled: true, previewsEnabled: true };
|
|
417
|
+
}
|
|
418
|
+
if (workersDev === false) {
|
|
419
|
+
return { enabled: false, previewsEnabled: false };
|
|
420
|
+
}
|
|
421
|
+
return {
|
|
422
|
+
enabled: workersDev.enabled ?? true,
|
|
423
|
+
previewsEnabled: workersDev.previewsEnabled ?? true,
|
|
424
|
+
};
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* The resolved shape of `WorkerProps.domain`: the canonical hostname plus
|
|
429
|
+
* alias and redirect hostname lists, all punycode-normalized and
|
|
430
|
+
* de-duplicated.
|
|
431
|
+
*
|
|
432
|
+
* @internal exported for unit testing.
|
|
433
|
+
*/
|
|
434
|
+
export interface ResolvedWorkerDomain {
|
|
435
|
+
name: string;
|
|
436
|
+
aliases: string[];
|
|
437
|
+
redirects: string[];
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Convert non-ASCII hostnames (emoji, IDN, etc.) to punycode so the
|
|
441
|
+
// Cloudflare API receives the form it stores domains in. `new URL(...)`
|
|
442
|
+
// does IDNA via WHATWG URL parsing — `📦.alchemy.run` → `xn--5z8h.alchemy.run`.
|
|
443
|
+
const toPunycode = (hostname: string): string => {
|
|
444
|
+
try {
|
|
445
|
+
return new URL(`https://${hostname}`).hostname;
|
|
446
|
+
} catch {
|
|
447
|
+
return hostname;
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Resolve the `domain` prop to its full shape — a bare string is shorthand
|
|
453
|
+
* for `{ name }`. Hostnames are punycode-normalized and de-duplicated;
|
|
454
|
+
* a hostname may only play one role, so aliases/redirects that repeat the
|
|
455
|
+
* canonical name (or each other) fail with a typed error.
|
|
456
|
+
*
|
|
457
|
+
* @internal exported for unit testing.
|
|
458
|
+
*/
|
|
459
|
+
export const resolveWorkerDomain = (
|
|
460
|
+
// `string[]` is the pre-redesign prop shape — it can still reach us from
|
|
461
|
+
// persisted `olds` written by older providers (read's classification).
|
|
462
|
+
domain: WorkerProps["domain"] | string[],
|
|
463
|
+
): Effect.Effect<ResolvedWorkerDomain | undefined, WorkerDomainConfigError> =>
|
|
464
|
+
Effect.gen(function* () {
|
|
465
|
+
if (domain === undefined || domain === null) return undefined;
|
|
466
|
+
// Legacy array form: the first hostname was the primary custom domain
|
|
467
|
+
// (`url = domains[0]` back then), the rest map to aliases. A legacy
|
|
468
|
+
// empty array was the explicit detach-all — no domain.
|
|
469
|
+
const config =
|
|
470
|
+
typeof domain === "string"
|
|
471
|
+
? { name: domain }
|
|
472
|
+
: Array.isArray(domain)
|
|
473
|
+
? domain.length > 0
|
|
474
|
+
? { name: domain[0], aliases: domain.slice(1) }
|
|
475
|
+
: undefined
|
|
476
|
+
: domain;
|
|
477
|
+
if (config === undefined) return undefined;
|
|
478
|
+
const name = toPunycode(config.name);
|
|
479
|
+
const aliases = Array.from(new Set((config.aliases ?? []).map(toPunycode)));
|
|
480
|
+
const redirects = Array.from(
|
|
481
|
+
new Set((config.redirects ?? []).map(toPunycode)),
|
|
482
|
+
);
|
|
483
|
+
const overlap = [
|
|
484
|
+
...aliases.filter((h) => h === name),
|
|
485
|
+
...redirects.filter((h) => h === name || aliases.includes(h)),
|
|
486
|
+
];
|
|
487
|
+
if (overlap.length > 0) {
|
|
488
|
+
return yield* Effect.fail(
|
|
489
|
+
new WorkerDomainConfigError({
|
|
490
|
+
message: `Each hostname may play only one role in a Worker's domain config; ${[...new Set(overlap)].map((h) => `'${h}'`).join(", ")} appears in more than one of name/aliases/redirects.`,
|
|
491
|
+
}),
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
return { name, aliases, redirects };
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
const isWorkersDevHostname = (hostname: string) =>
|
|
498
|
+
hostname.endsWith(".workers.dev");
|
|
499
|
+
|
|
500
|
+
// Hostnames that only appear in local-dev state (the dev server's
|
|
501
|
+
// localhost/LAN URLs), never as attachable custom domains.
|
|
502
|
+
const isLocalDevHostname = (hostname: string) =>
|
|
503
|
+
hostname === "localhost" ||
|
|
504
|
+
hostname === "::1" ||
|
|
505
|
+
/^\d+\.\d+\.\d+\.\d+$/.test(hostname);
|
|
506
|
+
|
|
507
|
+
const urlHostname = (url: string): string => {
|
|
508
|
+
try {
|
|
509
|
+
return new URL(url).hostname;
|
|
510
|
+
} catch {
|
|
511
|
+
return url;
|
|
512
|
+
}
|
|
513
|
+
};
|
|
514
|
+
|
|
515
|
+
/** The `rules` payload shape of a zone phase-entrypoint PUT. */
|
|
516
|
+
type PutZoneRedirectRules = rulesets.PutPhasForZoneRequest["rules"];
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* The *custom domain* hostnames recorded in a Worker's persisted legacy
|
|
520
|
+
* `domains` state — minus the workers.dev (stable or preview) and local-dev
|
|
521
|
+
* entries that shared the list in older formats.
|
|
522
|
+
*
|
|
523
|
+
* @internal exported for unit testing.
|
|
524
|
+
*/
|
|
525
|
+
export const stateCustomDomains = (
|
|
526
|
+
domains: readonly unknown[] | undefined,
|
|
527
|
+
): string[] =>
|
|
528
|
+
normalizeStateDomains(domains).filter(
|
|
529
|
+
(hostname) =>
|
|
530
|
+
!isWorkersDevHostname(hostname) && !isLocalDevHostname(hostname),
|
|
531
|
+
);
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* The Worker's persisted domain configuration: the `domain` attribute for
|
|
535
|
+
* state written by the current format, else re-derived from the legacy
|
|
536
|
+
* `domains` list (first custom hostname = canonical name, rest = aliases;
|
|
537
|
+
* legacy state had no redirects).
|
|
538
|
+
*
|
|
539
|
+
* @internal exported for unit testing.
|
|
540
|
+
*/
|
|
541
|
+
export const stateWorkerDomain = (
|
|
542
|
+
output: object | undefined,
|
|
543
|
+
): ResolvedWorkerDomain | undefined => {
|
|
544
|
+
const state = output as
|
|
545
|
+
| {
|
|
546
|
+
domain?: {
|
|
547
|
+
name?: unknown;
|
|
548
|
+
aliases?: unknown[];
|
|
549
|
+
redirects?: unknown[];
|
|
550
|
+
} | null;
|
|
551
|
+
domains?: unknown[];
|
|
552
|
+
}
|
|
553
|
+
| undefined;
|
|
554
|
+
const domain = state?.domain;
|
|
555
|
+
if (domain && typeof domain.name === "string") {
|
|
556
|
+
return {
|
|
557
|
+
name: domain.name,
|
|
558
|
+
aliases: (domain.aliases ?? []).filter(
|
|
559
|
+
(h): h is string => typeof h === "string",
|
|
560
|
+
),
|
|
561
|
+
redirects: (domain.redirects ?? []).filter(
|
|
562
|
+
(h): h is string => typeof h === "string",
|
|
563
|
+
),
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
const legacy = stateCustomDomains(state?.domains);
|
|
567
|
+
return legacy.length > 0
|
|
568
|
+
? { name: legacy[0], aliases: legacy.slice(1), redirects: [] }
|
|
569
|
+
: undefined;
|
|
570
|
+
};
|
|
571
|
+
|
|
123
572
|
// Workers for Platforms "user workers" live inside a dispatch namespace and
|
|
124
573
|
// use a parallel family of script endpoints (`/workers/dispatch/namespaces/
|
|
125
574
|
// :namespace/scripts/...`). The request/response shapes are identical to the
|
|
@@ -176,7 +625,8 @@ const getScriptSettings = (
|
|
|
176
625
|
const isBindingTargetNotFound = (
|
|
177
626
|
e:
|
|
178
627
|
| Effect.Error<ReturnType<typeof workers.putScript>>
|
|
179
|
-
| Effect.Error<ReturnType<typeof wfp.putDispatchNamespaceScript
|
|
628
|
+
| Effect.Error<ReturnType<typeof wfp.putDispatchNamespaceScript>>
|
|
629
|
+
| Effect.Error<ReturnType<typeof workers.createScriptVersion>>,
|
|
180
630
|
): boolean =>
|
|
181
631
|
e._tag === "SecretsStoreBindingNotFound" ||
|
|
182
632
|
e._tag === "KVNamespaceNotFound" ||
|
|
@@ -193,6 +643,20 @@ const isBindingTargetNotFound = (
|
|
|
193
643
|
const bindingTargetNotFoundRetrySchedule = () =>
|
|
194
644
|
Schedule.max([Schedule.fixed("2 seconds"), Schedule.recurs(10)]);
|
|
195
645
|
|
|
646
|
+
/**
|
|
647
|
+
* Script PUT is an idempotent upsert, so a pure transport failure (the
|
|
648
|
+
* request died before any response — e.g. Cloudflare closing a keep-alive
|
|
649
|
+
* socket that idled while slow upstream resources provisioned earlier in the
|
|
650
|
+
* deploy) is safe to replay. Errors that carry a response are real API
|
|
651
|
+
* verdicts and are NOT retried here.
|
|
652
|
+
*/
|
|
653
|
+
const isScriptPutTransportError = (e: { _tag?: string }): boolean =>
|
|
654
|
+
isHttpClientError(e) && e.reason._tag === "TransportError";
|
|
655
|
+
|
|
656
|
+
const retryableScriptPut = (
|
|
657
|
+
e: Parameters<typeof isBindingTargetNotFound>[0],
|
|
658
|
+
): boolean => isBindingTargetNotFound(e) || isScriptPutTransportError(e);
|
|
659
|
+
|
|
196
660
|
/**
|
|
197
661
|
* Upsert a Worker script, routing to the dispatch-namespace endpoint when
|
|
198
662
|
* `dispatchNamespace` is set. The metadata/files contract is identical, and
|
|
@@ -221,7 +685,7 @@ const putWorkerScript = (params: {
|
|
|
221
685
|
})
|
|
222
686
|
.pipe(
|
|
223
687
|
Effect.retry({
|
|
224
|
-
while:
|
|
688
|
+
while: retryableScriptPut,
|
|
225
689
|
schedule: bindingTargetNotFoundRetrySchedule(),
|
|
226
690
|
}),
|
|
227
691
|
);
|
|
@@ -235,7 +699,7 @@ const putWorkerScript = (params: {
|
|
|
235
699
|
})
|
|
236
700
|
.pipe(
|
|
237
701
|
Effect.retry({
|
|
238
|
-
while:
|
|
702
|
+
while: retryableScriptPut,
|
|
239
703
|
schedule: bindingTargetNotFoundRetrySchedule(),
|
|
240
704
|
}),
|
|
241
705
|
);
|
|
@@ -265,14 +729,14 @@ const deleteWorkerScript = (
|
|
|
265
729
|
});
|
|
266
730
|
|
|
267
731
|
/**
|
|
268
|
-
* Normalize a Worker's persisted `domains` state to
|
|
269
|
-
*
|
|
270
|
-
* `{ id, hostname, zoneId }` object; beta.45+
|
|
271
|
-
* strings
|
|
272
|
-
*
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
732
|
+
* Normalize a Worker's persisted *legacy* `domains` state to bare
|
|
733
|
+
* hostnames. Alchemy <= beta.44 stored each custom domain as a
|
|
734
|
+
* `{ id, hostname, zoneId }` object; beta.45+ stored `https://<hostname>`
|
|
735
|
+
* URL strings (with the workers.dev URL mixed in); current state stores the
|
|
736
|
+
* `domain` config object instead of a `domains` list. All legacy
|
|
737
|
+
* generations coerce to hostnames so the diff never throws on older state
|
|
738
|
+
* (#546). Entries that fit no generation are dropped rather than turned
|
|
739
|
+
* into a bogus hostname that would skew the diff.
|
|
276
740
|
*
|
|
277
741
|
* @internal exported for unit testing.
|
|
278
742
|
*/
|
|
@@ -280,11 +744,79 @@ export const normalizeStateDomains = (
|
|
|
280
744
|
domains: readonly unknown[] | undefined,
|
|
281
745
|
): string[] =>
|
|
282
746
|
(domains ?? []).flatMap((u) => {
|
|
283
|
-
if (typeof u === "string")
|
|
747
|
+
if (typeof u === "string") {
|
|
748
|
+
if (u.includes("://")) {
|
|
749
|
+
try {
|
|
750
|
+
return [new URL(u).hostname];
|
|
751
|
+
} catch {
|
|
752
|
+
return [];
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
return u.length > 0 ? [u] : [];
|
|
756
|
+
}
|
|
284
757
|
const hostname = (u as { hostname?: unknown } | null)?.hostname;
|
|
285
|
-
return typeof hostname === "string" ? [
|
|
758
|
+
return typeof hostname === "string" ? [hostname] : [];
|
|
286
759
|
});
|
|
287
760
|
|
|
761
|
+
/**
|
|
762
|
+
* Custom domains Alchemy is responsible for on this Worker — either declared
|
|
763
|
+
* on props (`domain`) or already persisted as non-`workers.dev` URLs in state.
|
|
764
|
+
* Used by `read` to skip `listDomains` when the surface is unmanaged (#926).
|
|
765
|
+
*
|
|
766
|
+
* @internal exported for unit testing.
|
|
767
|
+
*/
|
|
768
|
+
export const shouldObserveWorkerDomains = (
|
|
769
|
+
olds: Pick<WorkerProps, "domain"> | undefined,
|
|
770
|
+
output: object | undefined,
|
|
771
|
+
): boolean =>
|
|
772
|
+
olds?.domain !== undefined || stateWorkerDomain(output) !== undefined;
|
|
773
|
+
|
|
774
|
+
/**
|
|
775
|
+
* Zone routes Alchemy is responsible for on this Worker. Used by `read` to
|
|
776
|
+
* skip account-wide zone/route fan-out when the surface is unmanaged (#926).
|
|
777
|
+
*
|
|
778
|
+
* @internal exported for unit testing.
|
|
779
|
+
*/
|
|
780
|
+
export const shouldObserveWorkerRoutes = (
|
|
781
|
+
olds: Pick<WorkerProps, "routes"> | undefined,
|
|
782
|
+
output: Pick<Worker["Attributes"], "routes"> | undefined,
|
|
783
|
+
): boolean => olds?.routes !== undefined || (output?.routes?.length ?? 0) > 0;
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* Cron triggers Alchemy is responsible for on this Worker. Used by `read` to
|
|
787
|
+
* skip `getScriptSchedule` when the surface is unmanaged (#926). Effect-native
|
|
788
|
+
* `cron()` bindings persist into `output.crons` after the first reconcile, so
|
|
789
|
+
* subsequent reads still observe them.
|
|
790
|
+
*
|
|
791
|
+
* @internal exported for unit testing.
|
|
792
|
+
*/
|
|
793
|
+
export const shouldObserveWorkerCrons = (
|
|
794
|
+
olds: Pick<WorkerProps, "crons"> | undefined,
|
|
795
|
+
output: Pick<Worker["Attributes"], "crons"> | undefined,
|
|
796
|
+
): boolean => olds?.crons !== undefined || (output?.crons?.length ?? 0) > 0;
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
* Optional override for the account's stable `workers.dev` subdomain
|
|
800
|
+
* (`<subdomain>` in `https://<script>.<subdomain>.workers.dev`). When set,
|
|
801
|
+
* Worker URL construction skips `GET /accounts/{id}/workers/subdomain`.
|
|
802
|
+
*/
|
|
803
|
+
const CLOUDFLARE_WORKERS_SUBDOMAIN = Config.string(
|
|
804
|
+
"CLOUDFLARE_WORKERS_SUBDOMAIN",
|
|
805
|
+
).pipe(
|
|
806
|
+
Config.map((value) => value.trim()),
|
|
807
|
+
Config.option,
|
|
808
|
+
);
|
|
809
|
+
|
|
810
|
+
const WORKERS_SUBDOMAIN_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* Max concurrent `GET /zones/{id}/workers/routes` calls when observing the
|
|
814
|
+
* routes attached to a Worker. Route listing is per-zone, so this fans out
|
|
815
|
+
* with the number of zones the Worker has routes in; unbounded fan-out here
|
|
816
|
+
* is what trips Cloudflare account-level 429 / code 971 throttling (#926).
|
|
817
|
+
*/
|
|
818
|
+
const WORKER_ROUTE_LIST_CONCURRENCY = 8;
|
|
819
|
+
|
|
288
820
|
type MetadataHashValue =
|
|
289
821
|
| string
|
|
290
822
|
| number
|
|
@@ -374,6 +906,13 @@ interface WorkerMetadataHashInput {
|
|
|
374
906
|
readonly bindings: readonly ResourceBinding<Worker["Binding"]>[];
|
|
375
907
|
readonly accountId: string;
|
|
376
908
|
readonly stack: { readonly name: string; readonly stage: string };
|
|
909
|
+
/**
|
|
910
|
+
* The resolved URL a `Worker.URL` (`self_url`) binding lowers into.
|
|
911
|
+
* Included in the hash so external URL changes (an account-subdomain
|
|
912
|
+
* rename, a custom-domain reorder) redeploy the Worker with the fresh
|
|
913
|
+
* value even though props and binding data are unchanged.
|
|
914
|
+
*/
|
|
915
|
+
readonly selfUrl?: string;
|
|
377
916
|
}
|
|
378
917
|
|
|
379
918
|
// The asset router config the resource declares (htmlHandling,
|
|
@@ -404,9 +943,11 @@ const resolveWorkerMetadataHash = ({
|
|
|
404
943
|
bindings,
|
|
405
944
|
accountId,
|
|
406
945
|
stack,
|
|
946
|
+
selfUrl,
|
|
407
947
|
}: WorkerMetadataHashInput): Effect.Effect<string> =>
|
|
408
948
|
resolveMetadataHashValue({
|
|
409
949
|
accountId,
|
|
950
|
+
selfUrl,
|
|
410
951
|
stack: { name: stack.name, stage: stack.stage },
|
|
411
952
|
compatibility: getCompatibility(props),
|
|
412
953
|
env: props.env,
|
|
@@ -420,15 +961,33 @@ const resolveWorkerMetadataHash = ({
|
|
|
420
961
|
logpush: props.logpush,
|
|
421
962
|
observability: props.observability,
|
|
422
963
|
placement: props.placement,
|
|
423
|
-
subdomain: props.subdomain,
|
|
424
964
|
tags: props.tags,
|
|
425
|
-
|
|
965
|
+
workersDev: resolveWorkersDev(props.workersDev),
|
|
966
|
+
// Reduce `version.parent` to the parent's script name: the resolved
|
|
967
|
+
// parent is a full attributes object whose *other* fields (hash, url,
|
|
968
|
+
// ...) change on every parent deploy, which would spuriously re-version
|
|
969
|
+
// this Worker even though nothing about the version itself changed.
|
|
970
|
+
version: props.version
|
|
971
|
+
? {
|
|
972
|
+
parent: resolveVersionParentName(props.version),
|
|
973
|
+
traffic: props.version.traffic,
|
|
974
|
+
alias: props.version.alias,
|
|
975
|
+
affinity: props.version.affinity,
|
|
976
|
+
message: props.version.message,
|
|
977
|
+
tag: props.version.tag,
|
|
978
|
+
}
|
|
979
|
+
: undefined,
|
|
426
980
|
}).pipe(Effect.flatMap((metadata) => sha256Object({ metadata })));
|
|
427
981
|
|
|
428
982
|
export const WorkerProvider = () =>
|
|
429
|
-
ProviderLayer.
|
|
983
|
+
ProviderLayer.dual(Worker, {
|
|
430
984
|
live: () => LiveWorkerProvider(),
|
|
431
|
-
local
|
|
985
|
+
// The local runtime deps (workerd, WorkerProxy, LocalRuntimeState)
|
|
986
|
+
// compose INTO the local variant so a live deploy only constructs them
|
|
987
|
+
// if the local provider is actually demanded (e.g. deleting a local
|
|
988
|
+
// dev worker's state row). See ProviderLayer.dual.
|
|
989
|
+
local: () =>
|
|
990
|
+
LocalWorkerProvider().pipe(Layer.provide(localRuntimeServices())),
|
|
432
991
|
});
|
|
433
992
|
|
|
434
993
|
export const LiveWorkerProvider = () =>
|
|
@@ -454,57 +1013,79 @@ export const LiveWorkerProvider = () =>
|
|
|
454
1013
|
// const listZones = yield* zones.listZones;
|
|
455
1014
|
const telemetry = yield* CloudflareLogs;
|
|
456
1015
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
1016
|
+
// Account subdomain is invariant for the life of a provider layer —
|
|
1017
|
+
// memoize so N Workers (or repeated plan/deploy reads) don't each hit
|
|
1018
|
+
// GET /accounts/{id}/workers/subdomain and trip Cloudflare's 429/971
|
|
1019
|
+
// throttle (#926). `CLOUDFLARE_WORKERS_SUBDOMAIN` bypasses the lookup
|
|
1020
|
+
// entirely when the operator already knows the value.
|
|
1021
|
+
const getAccountSubdomain = yield* cachedFunction((accountId: string) =>
|
|
1022
|
+
Effect.gen(function* () {
|
|
1023
|
+
const configured = yield* CLOUDFLARE_WORKERS_SUBDOMAIN;
|
|
1024
|
+
if (Option.isSome(configured) && configured.value !== "") {
|
|
1025
|
+
if (!WORKERS_SUBDOMAIN_PATTERN.test(configured.value)) {
|
|
1026
|
+
return yield* Effect.die(
|
|
1027
|
+
new Error(
|
|
1028
|
+
`Invalid CLOUDFLARE_WORKERS_SUBDOMAIN "${configured.value}": ` +
|
|
1029
|
+
"expected a workers.dev subdomain label (lowercase letters, digits, hyphens)",
|
|
1030
|
+
),
|
|
1031
|
+
);
|
|
1032
|
+
}
|
|
1033
|
+
return configured.value;
|
|
1034
|
+
}
|
|
1035
|
+
const result = yield* workers.getSubdomain({ accountId });
|
|
1036
|
+
return result.subdomain;
|
|
1037
|
+
}),
|
|
1038
|
+
);
|
|
1039
|
+
|
|
1040
|
+
// Converge the script's workers.dev settings via `POST /subdomain`.
|
|
1041
|
+
// The two toggles are independent on the Cloudflare API: `enabled`
|
|
1042
|
+
// drives the stable `<name>.<account>.workers.dev` URL and
|
|
1043
|
+
// `previews_enabled` the per-version preview URLs.
|
|
470
1044
|
const setWorkerSubdomain = Effect.fn(function* (
|
|
471
1045
|
name: string,
|
|
472
|
-
|
|
1046
|
+
desired: ResolvedWorkersDev,
|
|
473
1047
|
) {
|
|
474
1048
|
const { accountId } = yield* yield* CloudflareEnvironment;
|
|
475
1049
|
return yield* workers.createScriptSubdomain({
|
|
476
1050
|
accountId,
|
|
477
1051
|
scriptName: name,
|
|
478
|
-
enabled,
|
|
479
|
-
previewsEnabled:
|
|
1052
|
+
enabled: desired.enabled,
|
|
1053
|
+
previewsEnabled: desired.previewsEnabled,
|
|
480
1054
|
});
|
|
481
1055
|
});
|
|
482
1056
|
|
|
483
|
-
// Convert non-ASCII hostnames (emoji, IDN, etc.) to punycode so the
|
|
484
|
-
// Cloudflare API receives the form it stores domains in. `new URL(...)`
|
|
485
|
-
// does IDNA via WHATWG URL parsing — `📦.alchemy.run` → `xn--5z8h.alchemy.run`.
|
|
486
|
-
const toPunycode = (hostname: string): string => {
|
|
487
|
-
try {
|
|
488
|
-
return new URL(`https://${hostname}`).hostname;
|
|
489
|
-
} catch {
|
|
490
|
-
return hostname;
|
|
491
|
-
}
|
|
492
|
-
};
|
|
493
|
-
|
|
494
|
-
const normalizeDomains = (
|
|
495
|
-
domain: string | string[] | undefined,
|
|
496
|
-
): string[] =>
|
|
497
|
-
domain === undefined
|
|
498
|
-
? []
|
|
499
|
-
: Array.from(
|
|
500
|
-
new Set(
|
|
501
|
-
(Array.isArray(domain) ? domain : [domain]).map(toPunycode),
|
|
502
|
-
),
|
|
503
|
-
);
|
|
504
|
-
|
|
505
1057
|
const normalizeCrons = (crons: string[] | undefined): string[] =>
|
|
506
1058
|
Array.from(new Set(crons ?? []));
|
|
507
1059
|
|
|
1060
|
+
const hasSelfUrlBinding = (
|
|
1061
|
+
bindings: readonly ResourceBinding<Worker["Binding"]>[],
|
|
1062
|
+
) =>
|
|
1063
|
+
bindings.some((b) =>
|
|
1064
|
+
(b.data.bindings ?? []).some((item) => item.type === "self_url"),
|
|
1065
|
+
);
|
|
1066
|
+
|
|
1067
|
+
// Resolve the URL this Worker will be served at — the same formula that
|
|
1068
|
+
// produces the `url` attribute (first custom domain in user order, else
|
|
1069
|
+
// the workers.dev URL) — usable BEFORE the script upload, so `Worker.URL`
|
|
1070
|
+
// bindings can be lowered into plain_text bindings and VITE_*-prefixed
|
|
1071
|
+
// env entries can be inlined into the client bundle at build time.
|
|
1072
|
+
const resolveSelfUrl = Effect.fn(function* (
|
|
1073
|
+
name: string,
|
|
1074
|
+
props: WorkerProps,
|
|
1075
|
+
accountId: string,
|
|
1076
|
+
) {
|
|
1077
|
+
const domain = yield* resolveWorkerDomain(props.domain);
|
|
1078
|
+
if (domain) {
|
|
1079
|
+
return `https://${domain.name}`;
|
|
1080
|
+
}
|
|
1081
|
+
if (!resolveWorkersDev(props.workersDev).enabled) {
|
|
1082
|
+
return yield* Effect.die(
|
|
1083
|
+
`Worker "${name}" binds its own URL (Worker.URL) but has none: the stable workers.dev URL is disabled (workersDev) and no custom domain is configured.`,
|
|
1084
|
+
);
|
|
1085
|
+
}
|
|
1086
|
+
return `https://${name}.${yield* getAccountSubdomain(accountId)}.workers.dev`;
|
|
1087
|
+
});
|
|
1088
|
+
|
|
508
1089
|
const getWorkerCrons = Effect.fn(function* (scriptName: string) {
|
|
509
1090
|
const { accountId } = yield* yield* CloudflareEnvironment;
|
|
510
1091
|
return yield* workers
|
|
@@ -726,6 +1307,315 @@ export const LiveWorkerProvider = () =>
|
|
|
726
1307
|
return applied;
|
|
727
1308
|
});
|
|
728
1309
|
|
|
1310
|
+
// The preview URL of the Worker's *current* version:
|
|
1311
|
+
// `https://<version-prefix>-<name>.<subdomain>.workers.dev`, where the
|
|
1312
|
+
// prefix is the first 8 characters of the version id (read from the
|
|
1313
|
+
// latest deployment's majority version). Resolves to `undefined` when
|
|
1314
|
+
// no deployment is visible yet — callers treat the preview URL as
|
|
1315
|
+
// best-effort. Only consulted in previews-only mode
|
|
1316
|
+
// (`workersDev: { enabled: false, previewsEnabled: true }`), where it
|
|
1317
|
+
// is the Worker's only workers.dev surface.
|
|
1318
|
+
const getPreviewUrl = Effect.fn(function* (
|
|
1319
|
+
scriptName: string,
|
|
1320
|
+
accountSubdomain: string,
|
|
1321
|
+
) {
|
|
1322
|
+
const { accountId } = yield* yield* CloudflareEnvironment;
|
|
1323
|
+
const deployments = yield* workers
|
|
1324
|
+
.listScriptDeployments({ accountId, scriptName })
|
|
1325
|
+
.pipe(
|
|
1326
|
+
Effect.map((response) => response.deployments ?? []),
|
|
1327
|
+
Effect.catch(() => Effect.succeed([])),
|
|
1328
|
+
);
|
|
1329
|
+
const latest = [...deployments].sort((a, b) =>
|
|
1330
|
+
b.createdOn.localeCompare(a.createdOn),
|
|
1331
|
+
)[0];
|
|
1332
|
+
const version = latest?.versions?.reduce(
|
|
1333
|
+
(max, candidate) =>
|
|
1334
|
+
max === undefined || candidate.percentage > max.percentage
|
|
1335
|
+
? candidate
|
|
1336
|
+
: max,
|
|
1337
|
+
undefined as { percentage: number; versionId: string } | undefined,
|
|
1338
|
+
);
|
|
1339
|
+
if (!version?.versionId) return undefined;
|
|
1340
|
+
return `https://${version.versionId.slice(0, 8)}-${scriptName}.${accountSubdomain}.workers.dev`;
|
|
1341
|
+
});
|
|
1342
|
+
|
|
1343
|
+
/**
|
|
1344
|
+
* Assemble every URL a deployed, script-owning Worker serves at, most
|
|
1345
|
+
* significant first — the first entry becomes the `url` attribute:
|
|
1346
|
+
*
|
|
1347
|
+
* 1. `https://<domain.name>` — the canonical custom domain
|
|
1348
|
+
* 2. aliases, in declared order
|
|
1349
|
+
* 3. the stable workers.dev URL (`workersDev.enabled`)
|
|
1350
|
+
* 4. preview URLs of the version this deploy uploaded (gradual
|
|
1351
|
+
* rollouts), or — in previews-only mode — the current version's
|
|
1352
|
+
* preview URL, which is then the only workers.dev surface
|
|
1353
|
+
*
|
|
1354
|
+
* Redirect hostnames never appear: they don't serve the Worker.
|
|
1355
|
+
* (Version workers don't use this — their `urls` are their preview
|
|
1356
|
+
* URLs, assembled in `putWorkerVersion`.)
|
|
1357
|
+
*/
|
|
1358
|
+
const computeWorkerUrls = Effect.fn(function* (params: {
|
|
1359
|
+
scriptName: string;
|
|
1360
|
+
workersDev: ResolvedWorkersDev;
|
|
1361
|
+
domain: ResolvedWorkerDomain | undefined;
|
|
1362
|
+
/** the version uploaded by this deploy (self-rollout), if any */
|
|
1363
|
+
uploadedVersionId?: string;
|
|
1364
|
+
/** user-provided `version.alias` attached to the uploaded version */
|
|
1365
|
+
uploadedVersionAlias?: string;
|
|
1366
|
+
}) {
|
|
1367
|
+
const { workersDev, domain, scriptName } = params;
|
|
1368
|
+
const urls: string[] = [];
|
|
1369
|
+
if (domain) {
|
|
1370
|
+
urls.push(
|
|
1371
|
+
`https://${domain.name}`,
|
|
1372
|
+
...domain.aliases.map((hostname) => `https://${hostname}`),
|
|
1373
|
+
);
|
|
1374
|
+
}
|
|
1375
|
+
if (workersDev.enabled || workersDev.previewsEnabled) {
|
|
1376
|
+
const { accountId } = yield* yield* CloudflareEnvironment;
|
|
1377
|
+
const accountSubdomain = yield* getAccountSubdomain(accountId);
|
|
1378
|
+
if (workersDev.enabled) {
|
|
1379
|
+
urls.push(`https://${scriptName}.${accountSubdomain}.workers.dev`);
|
|
1380
|
+
}
|
|
1381
|
+
if (workersDev.previewsEnabled) {
|
|
1382
|
+
if (params.uploadedVersionId !== undefined) {
|
|
1383
|
+
if (params.uploadedVersionAlias !== undefined) {
|
|
1384
|
+
urls.push(
|
|
1385
|
+
`https://${params.uploadedVersionAlias}-${scriptName}.${accountSubdomain}.workers.dev`,
|
|
1386
|
+
);
|
|
1387
|
+
}
|
|
1388
|
+
urls.push(
|
|
1389
|
+
`https://${params.uploadedVersionId.split("-")[0]}-${scriptName}.${accountSubdomain}.workers.dev`,
|
|
1390
|
+
);
|
|
1391
|
+
} else if (!workersDev.enabled) {
|
|
1392
|
+
const previewUrl = yield* getPreviewUrl(
|
|
1393
|
+
scriptName,
|
|
1394
|
+
accountSubdomain,
|
|
1395
|
+
);
|
|
1396
|
+
if (previewUrl) urls.push(previewUrl);
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
return urls;
|
|
1401
|
+
});
|
|
1402
|
+
|
|
1403
|
+
/**
|
|
1404
|
+
* Converge the redirect rules for a Worker's `domain.redirects` in
|
|
1405
|
+
* each affected zone's `http_request_dynamic_redirect` phase
|
|
1406
|
+
* entrypoint. Rules are identified by a
|
|
1407
|
+
* `alchemy:worker:<script>:redirect:<host>` description; only our own
|
|
1408
|
+
* rules are added/removed — every other rule in the shared entrypoint
|
|
1409
|
+
* ruleset passes through untouched. Zones that previously held our
|
|
1410
|
+
* rules but no longer should (removed redirects, removed domain) are
|
|
1411
|
+
* cleaned the same way.
|
|
1412
|
+
*/
|
|
1413
|
+
const reconcileRedirectRules = Effect.fn(function* (params: {
|
|
1414
|
+
scriptName: string;
|
|
1415
|
+
domain: ResolvedWorkerDomain | undefined;
|
|
1416
|
+
/** hostname → zoneId for every currently-attached custom domain */
|
|
1417
|
+
zoneIdByHostname: ReadonlyMap<string, string>;
|
|
1418
|
+
/** redirect hostnames from previous state (for zone cleanup) */
|
|
1419
|
+
previousRedirects: readonly string[];
|
|
1420
|
+
}) {
|
|
1421
|
+
const prefix = `alchemy:worker:${params.scriptName}:redirect:`;
|
|
1422
|
+
const desired = params.domain?.redirects ?? [];
|
|
1423
|
+
const targetName = params.domain?.name;
|
|
1424
|
+
// Zones to touch: every zone hosting a desired redirect, plus every
|
|
1425
|
+
// zone that hosted one before (so removals converge). Hostnames
|
|
1426
|
+
// whose zone we can't resolve (e.g. the domain was already
|
|
1427
|
+
// detached) are skipped — their rules become unreachable dead
|
|
1428
|
+
// config at worst, never a failed destroy.
|
|
1429
|
+
const zoneIds = new Set<string>();
|
|
1430
|
+
for (const hostname of [...desired, ...params.previousRedirects]) {
|
|
1431
|
+
const zoneId = params.zoneIdByHostname.get(hostname);
|
|
1432
|
+
if (zoneId) zoneIds.add(zoneId);
|
|
1433
|
+
}
|
|
1434
|
+
if (zoneIds.size === 0) return;
|
|
1435
|
+
for (const zoneId of zoneIds) {
|
|
1436
|
+
const entrypoint = yield* rulesets
|
|
1437
|
+
.getPhasForZone({
|
|
1438
|
+
zoneId,
|
|
1439
|
+
rulesetPhase: "http_request_dynamic_redirect",
|
|
1440
|
+
})
|
|
1441
|
+
.pipe(Effect.catch(() => Effect.succeed(undefined)));
|
|
1442
|
+
const existingRules = entrypoint?.rules ?? [];
|
|
1443
|
+
const ourDesired = desired
|
|
1444
|
+
.filter(
|
|
1445
|
+
(hostname) => params.zoneIdByHostname.get(hostname) === zoneId,
|
|
1446
|
+
)
|
|
1447
|
+
.map((hostname) => ({
|
|
1448
|
+
action: "redirect" as const,
|
|
1449
|
+
expression: `http.host eq "${hostname}"`,
|
|
1450
|
+
description: `${prefix}${hostname}`,
|
|
1451
|
+
enabled: true,
|
|
1452
|
+
actionParameters: {
|
|
1453
|
+
fromValue: {
|
|
1454
|
+
statusCode: 301,
|
|
1455
|
+
preserveQueryString: true,
|
|
1456
|
+
targetUrl: {
|
|
1457
|
+
expression: `concat("https://${targetName}", http.request.uri.path)`,
|
|
1458
|
+
},
|
|
1459
|
+
},
|
|
1460
|
+
},
|
|
1461
|
+
}));
|
|
1462
|
+
const isOurs = (rule: { description?: string | null }) =>
|
|
1463
|
+
(rule.description ?? "").startsWith(prefix);
|
|
1464
|
+
const ourExisting = existingRules.filter(isOurs);
|
|
1465
|
+
const converged =
|
|
1466
|
+
ourExisting.length === ourDesired.length &&
|
|
1467
|
+
ourDesired.every((rule) =>
|
|
1468
|
+
ourExisting.some(
|
|
1469
|
+
(existing) =>
|
|
1470
|
+
existing.description === rule.description &&
|
|
1471
|
+
existing.expression === rule.expression,
|
|
1472
|
+
),
|
|
1473
|
+
);
|
|
1474
|
+
if (converged) continue;
|
|
1475
|
+
// Pass foreign rules through untouched (minus the read-only
|
|
1476
|
+
// fields the PUT schema doesn't accept) and replace our own set.
|
|
1477
|
+
const foreign = existingRules
|
|
1478
|
+
.filter((rule) => !isOurs(rule))
|
|
1479
|
+
.map((rule) => {
|
|
1480
|
+
const {
|
|
1481
|
+
lastUpdated: _lastUpdated,
|
|
1482
|
+
version: _version,
|
|
1483
|
+
...rest
|
|
1484
|
+
} = rule as Record<string, unknown> & {
|
|
1485
|
+
lastUpdated?: string;
|
|
1486
|
+
version?: string;
|
|
1487
|
+
};
|
|
1488
|
+
return rest;
|
|
1489
|
+
});
|
|
1490
|
+
yield* rulesets.putPhasForZone({
|
|
1491
|
+
zoneId,
|
|
1492
|
+
rulesetPhase: "http_request_dynamic_redirect",
|
|
1493
|
+
rules: [...foreign, ...ourDesired] as PutZoneRedirectRules,
|
|
1494
|
+
});
|
|
1495
|
+
}
|
|
1496
|
+
});
|
|
1497
|
+
|
|
1498
|
+
/**
|
|
1499
|
+
* Converge the version-affinity transform rules (`version.affinity`)
|
|
1500
|
+
* in each serving zone's `http_request_late_transform` phase
|
|
1501
|
+
* entrypoint: `rewrite` rules that fill the
|
|
1502
|
+
* `Cloudflare-Workers-Version-Key` header from the configured source,
|
|
1503
|
+
* scoped to the Worker's hostnames in that zone. Rules are identified
|
|
1504
|
+
* by an `alchemy:worker:<script>:affinity` description prefix; only
|
|
1505
|
+
* our own rules are added/updated/removed — every other rule in the
|
|
1506
|
+
* shared entrypoint passes through untouched. Zones that previously
|
|
1507
|
+
* held our rules but no longer should (affinity removed, zone no
|
|
1508
|
+
* longer served) are cleaned the same way. Returns the zone ids now
|
|
1509
|
+
* holding rules, for persistence in `affinityZoneIds`.
|
|
1510
|
+
*/
|
|
1511
|
+
const reconcileAffinityRules = Effect.fn(function* (params: {
|
|
1512
|
+
scriptName: string;
|
|
1513
|
+
/** `undefined` removes every rule (affinity removed / delete). */
|
|
1514
|
+
desired:
|
|
1515
|
+
| {
|
|
1516
|
+
affinity: ResolvedVersionAffinity;
|
|
1517
|
+
hostsByZone: ReadonlyMap<string, AffinityZoneHost[]>;
|
|
1518
|
+
}
|
|
1519
|
+
| undefined;
|
|
1520
|
+
/** zone ids holding rules per previous state (for cleanup). */
|
|
1521
|
+
previousZoneIds: readonly string[];
|
|
1522
|
+
}) {
|
|
1523
|
+
const prefix = affinityRulePrefix(params.scriptName);
|
|
1524
|
+
const zoneIds = new Set([
|
|
1525
|
+
...(params.desired?.hostsByZone.keys() ?? []),
|
|
1526
|
+
...params.previousZoneIds,
|
|
1527
|
+
]);
|
|
1528
|
+
for (const zoneId of zoneIds) {
|
|
1529
|
+
const hosts = params.desired?.hostsByZone.get(zoneId);
|
|
1530
|
+
const ourDesired =
|
|
1531
|
+
params.desired !== undefined && hosts !== undefined
|
|
1532
|
+
? buildAffinityZoneRules(
|
|
1533
|
+
params.scriptName,
|
|
1534
|
+
params.desired.affinity,
|
|
1535
|
+
hosts,
|
|
1536
|
+
)
|
|
1537
|
+
: [];
|
|
1538
|
+
const entrypoint = yield* rulesets
|
|
1539
|
+
.getPhasForZone({
|
|
1540
|
+
zoneId,
|
|
1541
|
+
rulesetPhase: "http_request_late_transform",
|
|
1542
|
+
})
|
|
1543
|
+
.pipe(Effect.catch(() => Effect.succeed(undefined)));
|
|
1544
|
+
const existingRules = entrypoint?.rules ?? [];
|
|
1545
|
+
const isOurs = (rule: { description?: string | null }) =>
|
|
1546
|
+
(rule.description ?? "").startsWith(prefix);
|
|
1547
|
+
// The header-value expression of an existing rewrite rule, for
|
|
1548
|
+
// convergence comparison. `headers` is an untyped wire record:
|
|
1549
|
+
// `{ "<Header-Name>": { operation, expression | value } }`.
|
|
1550
|
+
const existingValue = (rule: (typeof existingRules)[number]) => {
|
|
1551
|
+
const headers =
|
|
1552
|
+
"actionParameters" in rule
|
|
1553
|
+
? (
|
|
1554
|
+
rule.actionParameters as
|
|
1555
|
+
| { headers?: Record<string, unknown> | null }
|
|
1556
|
+
| null
|
|
1557
|
+
| undefined
|
|
1558
|
+
)?.headers
|
|
1559
|
+
: undefined;
|
|
1560
|
+
const header = headers?.[AFFINITY_HEADER] as
|
|
1561
|
+
| { expression?: unknown }
|
|
1562
|
+
| undefined;
|
|
1563
|
+
return typeof header?.expression === "string"
|
|
1564
|
+
? header.expression
|
|
1565
|
+
: undefined;
|
|
1566
|
+
};
|
|
1567
|
+
const ourExisting = existingRules.filter(isOurs);
|
|
1568
|
+
const converged =
|
|
1569
|
+
ourExisting.length === ourDesired.length &&
|
|
1570
|
+
ourDesired.every((rule) =>
|
|
1571
|
+
ourExisting.some(
|
|
1572
|
+
(existing) =>
|
|
1573
|
+
existing.description === rule.description &&
|
|
1574
|
+
existing.expression === rule.expression &&
|
|
1575
|
+
existingValue(existing) === rule.value,
|
|
1576
|
+
),
|
|
1577
|
+
);
|
|
1578
|
+
if (converged) continue;
|
|
1579
|
+
// Pass foreign rules through untouched (minus the read-only
|
|
1580
|
+
// fields the PUT schema doesn't accept) and replace our own set.
|
|
1581
|
+
const foreign = existingRules
|
|
1582
|
+
.filter((rule) => !isOurs(rule))
|
|
1583
|
+
.map((rule) => {
|
|
1584
|
+
const {
|
|
1585
|
+
lastUpdated: _lastUpdated,
|
|
1586
|
+
version: _version,
|
|
1587
|
+
...rest
|
|
1588
|
+
} = rule as Record<string, unknown> & {
|
|
1589
|
+
lastUpdated?: string;
|
|
1590
|
+
version?: string;
|
|
1591
|
+
};
|
|
1592
|
+
return rest;
|
|
1593
|
+
});
|
|
1594
|
+
yield* rulesets.putPhasForZone({
|
|
1595
|
+
zoneId,
|
|
1596
|
+
rulesetPhase: "http_request_late_transform",
|
|
1597
|
+
rules: [
|
|
1598
|
+
...foreign,
|
|
1599
|
+
...ourDesired.map((rule) => ({
|
|
1600
|
+
action: "rewrite" as const,
|
|
1601
|
+
description: rule.description,
|
|
1602
|
+
enabled: true,
|
|
1603
|
+
expression: rule.expression,
|
|
1604
|
+
actionParameters: {
|
|
1605
|
+
headers: {
|
|
1606
|
+
[AFFINITY_HEADER]: {
|
|
1607
|
+
operation: "set",
|
|
1608
|
+
expression: rule.value,
|
|
1609
|
+
},
|
|
1610
|
+
},
|
|
1611
|
+
},
|
|
1612
|
+
})),
|
|
1613
|
+
] as PutZoneRedirectRules,
|
|
1614
|
+
});
|
|
1615
|
+
}
|
|
1616
|
+
return [...(params.desired?.hostsByZone.keys() ?? [])].sort();
|
|
1617
|
+
});
|
|
1618
|
+
|
|
729
1619
|
type NormalizedWorkerRoute = {
|
|
730
1620
|
pattern: string;
|
|
731
1621
|
zoneId: string;
|
|
@@ -805,28 +1695,39 @@ export const LiveWorkerProvider = () =>
|
|
|
805
1695
|
Effect.catch(() => Effect.succeed([])),
|
|
806
1696
|
),
|
|
807
1697
|
),
|
|
808
|
-
|
|
1698
|
+
// Bounded: this issues one request per zone, so a Worker with routes
|
|
1699
|
+
// spread across many zones would otherwise burst the account's whole
|
|
1700
|
+
// API budget in a single tick (#926).
|
|
1701
|
+
{ concurrency: WORKER_ROUTE_LIST_CONCURRENCY },
|
|
809
1702
|
);
|
|
810
1703
|
|
|
811
1704
|
return Effect.map(routesByZone, (routes) => routes.flat());
|
|
812
1705
|
};
|
|
813
1706
|
|
|
814
|
-
// Observe
|
|
815
|
-
//
|
|
816
|
-
//
|
|
817
|
-
//
|
|
818
|
-
//
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
1707
|
+
// Observe the routes attached to `scriptName` in the zones Alchemy
|
|
1708
|
+
// already associates with this Worker.
|
|
1709
|
+
//
|
|
1710
|
+
// This used to enumerate every zone on the account and then list routes
|
|
1711
|
+
// in each one. Routes are zone-scoped with no account-level enumeration
|
|
1712
|
+
// API, so that costs O(zones) requests on *every* Worker read — on an
|
|
1713
|
+
// account with a few hundred zones a couple of plan/deploy cycles
|
|
1714
|
+
// exhausts Cloudflare's per-user API budget and unrelated calls start
|
|
1715
|
+
// coming back 429 / code 971 (#926).
|
|
1716
|
+
//
|
|
1717
|
+
// `reconcileRoutes` already scopes itself to the zones implied by
|
|
1718
|
+
// `desired ∪ previous` rather than sweeping the account, so `read` now
|
|
1719
|
+
// uses the same bounded zone set: the zones of the routes already
|
|
1720
|
+
// recorded in state. The blind spot this introduces — a route added out
|
|
1721
|
+
// of band in a zone this Worker has never had a route in — is the one
|
|
1722
|
+
// `reconcileRoutes` already has.
|
|
1723
|
+
const readWorkerRoutes = (
|
|
1724
|
+
scriptName: string,
|
|
1725
|
+
knownRoutes: Worker["Attributes"]["routes"] | undefined,
|
|
1726
|
+
) =>
|
|
1727
|
+
listWorkerRoutesInZones(
|
|
1728
|
+
scriptName,
|
|
1729
|
+
(knownRoutes ?? []).map((route) => route.zoneId),
|
|
1730
|
+
);
|
|
830
1731
|
|
|
831
1732
|
// Converge the zone routes attached to `scriptName` to `desired`.
|
|
832
1733
|
// Observed cloud state (not `previous`) is the diff baseline —
|
|
@@ -1170,7 +2071,10 @@ export const LiveWorkerProvider = () =>
|
|
|
1170
2071
|
crypto.createHash("sha256").update(script).digest("hex"),
|
|
1171
2072
|
);
|
|
1172
2073
|
|
|
1173
|
-
const viteBuild = Effect.fn(function* (
|
|
2074
|
+
const viteBuild = Effect.fn(function* (
|
|
2075
|
+
props: WorkerProps,
|
|
2076
|
+
selfUrl?: string,
|
|
2077
|
+
) {
|
|
1174
2078
|
const compatibility = getCompatibility(props);
|
|
1175
2079
|
// Loaded lazily: `./Vite.ts` pulls in `@distilled.cloud/cloudflare-vite-plugin`
|
|
1176
2080
|
// (~0.5s), which is only needed for vite-based workers at build time —
|
|
@@ -1190,22 +2094,44 @@ export const LiveWorkerProvider = () =>
|
|
|
1190
2094
|
: Redacted.isRedacted(value) &&
|
|
1191
2095
|
typeof Redacted.value(value) === "string"
|
|
1192
2096
|
? Redacted.value(value)
|
|
1193
|
-
: //
|
|
1194
|
-
//
|
|
1195
|
-
//
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
2097
|
+
: // `Worker.URL` (bare tag or called) — resolved to
|
|
2098
|
+
// this Worker's own URL. The bare tag is
|
|
2099
|
+
// Effect-shaped, so check before `Effect.isEffect`.
|
|
2100
|
+
isSelfUrl(value)
|
|
2101
|
+
? selfUrl
|
|
2102
|
+
: // A `WorkerLoader` is a real Effect that also carries
|
|
2103
|
+
// the `~alchemy/Kind` marker — it is a binding, not a
|
|
2104
|
+
// runnable env value. Check it before `Effect.isEffect`
|
|
2105
|
+
// so we don't execute it as an inlined env entry.
|
|
2106
|
+
isWorkerLoader(value)
|
|
2107
|
+
? undefined
|
|
2108
|
+
: // A `Cloudflare.Container` declaration is likewise
|
|
2109
|
+
// Effect-shaped but is a binding (DO namespace +
|
|
2110
|
+
// ContainerApplication) — yielding it would resolve
|
|
2111
|
+
// the started-instance tag, which only exists inside
|
|
2112
|
+
// a Durable Object (#997).
|
|
2113
|
+
isContainerDecl(value)
|
|
2114
|
+
? undefined
|
|
2115
|
+
: Effect.isEffect(value)
|
|
2116
|
+
? yield* value as any as Effect.Effect<any>
|
|
2117
|
+
: undefined,
|
|
1202
2118
|
];
|
|
1203
2119
|
}),
|
|
1204
2120
|
),
|
|
1205
2121
|
)).filter(([_, value]) => value !== undefined),
|
|
1206
2122
|
),
|
|
1207
2123
|
{
|
|
1208
|
-
|
|
2124
|
+
// A relative `vite.main` is documented to resolve from the Vite
|
|
2125
|
+
// root. The rolldown plugin resolves the worker entry with no
|
|
2126
|
+
// importer (i.e. against `process.cwd()`), which breaks when the
|
|
2127
|
+
// deploy runs from a different directory (e.g. a monorepo infra
|
|
2128
|
+
// package) — absolutize before handing it over (#796).
|
|
2129
|
+
main: props.vite?.main
|
|
2130
|
+
? path.resolve(
|
|
2131
|
+
props.vite.rootDir ?? process.cwd(),
|
|
2132
|
+
props.vite.main,
|
|
2133
|
+
)
|
|
2134
|
+
: undefined,
|
|
1209
2135
|
compatibilityDate: compatibility.date,
|
|
1210
2136
|
compatibilityFlags: compatibility.flags,
|
|
1211
2137
|
viteEnvironments: props.vite?.viteEnvironments,
|
|
@@ -1293,7 +2219,7 @@ export const LiveWorkerProvider = () =>
|
|
|
1293
2219
|
const prepareAssetsAndBundle = (
|
|
1294
2220
|
id: string,
|
|
1295
2221
|
props: WorkerProps,
|
|
1296
|
-
opts: { skipAssetsRead?: boolean } = {},
|
|
2222
|
+
opts: { skipAssetsRead?: boolean; selfUrl?: string } = {},
|
|
1297
2223
|
) =>
|
|
1298
2224
|
Effect.gen(function* () {
|
|
1299
2225
|
if (props.script !== undefined) {
|
|
@@ -1317,7 +2243,29 @@ export const LiveWorkerProvider = () =>
|
|
|
1317
2243
|
};
|
|
1318
2244
|
}
|
|
1319
2245
|
if (props.vite) {
|
|
1320
|
-
return yield* viteBuild(props);
|
|
2246
|
+
return yield* viteBuild(props, opts.selfUrl);
|
|
2247
|
+
}
|
|
2248
|
+
// Assets-only Worker: no entry module at all. The script PUT goes
|
|
2249
|
+
// out with no modules and no main_module — Cloudflare's asset
|
|
2250
|
+
// layer serves every request and applies `notFoundHandling`
|
|
2251
|
+
// (including SPA fallback) itself, exactly like Wrangler's
|
|
2252
|
+
// assets-only deploys.
|
|
2253
|
+
if (props.main === undefined) {
|
|
2254
|
+
if (!props.assets) {
|
|
2255
|
+
return yield* Effect.die(
|
|
2256
|
+
new Error(
|
|
2257
|
+
`Worker "${id}" has no main, script, or assets. Provide an entry module (main / script) or an assets directory to deploy an assets-only Worker.`,
|
|
2258
|
+
),
|
|
2259
|
+
);
|
|
2260
|
+
}
|
|
2261
|
+
return {
|
|
2262
|
+
assets: opts.skipAssetsRead
|
|
2263
|
+
? undefined
|
|
2264
|
+
: yield* prepareAssets(props.assets),
|
|
2265
|
+
bundle: undefined,
|
|
2266
|
+
input: undefined,
|
|
2267
|
+
additionalWorkspaces: undefined,
|
|
2268
|
+
};
|
|
1321
2269
|
}
|
|
1322
2270
|
const [assets, bundle] = yield* Effect.all(
|
|
1323
2271
|
[
|
|
@@ -1360,10 +2308,527 @@ export const LiveWorkerProvider = () =>
|
|
|
1360
2308
|
output: Worker["Attributes"] | undefined,
|
|
1361
2309
|
) => {
|
|
1362
2310
|
if (!Predicate.hasProperty(assets, "hash")) return undefined;
|
|
1363
|
-
const { directory
|
|
1364
|
-
return { config, hash, skip: hash === output?.hash?.assets };
|
|
2311
|
+
const { directory, hash, ...config } = assets;
|
|
2312
|
+
return { directory, config, hash, skip: hash === output?.hash?.assets };
|
|
1365
2313
|
};
|
|
1366
2314
|
|
|
2315
|
+
/**
|
|
2316
|
+
* Append the standard Alchemy runtime bindings plus the user's `env`
|
|
2317
|
+
* entries (routed by shape: `Redacted` → secret_text, string →
|
|
2318
|
+
* plain_text, everything else → json) to a metadata binding list.
|
|
2319
|
+
* Shared between the full script upload and the version upload.
|
|
2320
|
+
*/
|
|
2321
|
+
const appendAlchemyAndEnvBindings = (
|
|
2322
|
+
metadataBindings: WorkerBinding[],
|
|
2323
|
+
news: WorkerProps,
|
|
2324
|
+
accountId: string,
|
|
2325
|
+
workerName: string,
|
|
2326
|
+
) => {
|
|
2327
|
+
metadataBindings.push(
|
|
2328
|
+
{
|
|
2329
|
+
type: "plain_text",
|
|
2330
|
+
name: "ALCHEMY_PHASE",
|
|
2331
|
+
text: "runtime",
|
|
2332
|
+
},
|
|
2333
|
+
{
|
|
2334
|
+
type: "plain_text",
|
|
2335
|
+
name: "ALCHEMY_WORKER_NAME",
|
|
2336
|
+
text: workerName,
|
|
2337
|
+
},
|
|
2338
|
+
{
|
|
2339
|
+
type: "plain_text",
|
|
2340
|
+
name: "ALCHEMY_STACK_NAME",
|
|
2341
|
+
text: stack.name,
|
|
2342
|
+
},
|
|
2343
|
+
{
|
|
2344
|
+
type: "plain_text",
|
|
2345
|
+
name: "ALCHEMY_STAGE",
|
|
2346
|
+
text: stack.stage,
|
|
2347
|
+
},
|
|
2348
|
+
{
|
|
2349
|
+
type: "plain_text",
|
|
2350
|
+
name: "ALCHEMY_CLOUDFLARE_ACCOUNT_ID",
|
|
2351
|
+
text: accountId,
|
|
2352
|
+
},
|
|
2353
|
+
);
|
|
2354
|
+
// Add environment variables as metadata bindings
|
|
2355
|
+
if (news.env) {
|
|
2356
|
+
for (const [key, value] of Object.entries(news.env)) {
|
|
2357
|
+
if (value === undefined) continue;
|
|
2358
|
+
if (metadataBindings.some((b) => b.name === key)) continue;
|
|
2359
|
+
if (Redacted.isRedacted(value)) {
|
|
2360
|
+
const unredacted = Redacted.value(value);
|
|
2361
|
+
metadataBindings.push({
|
|
2362
|
+
type: "secret_text",
|
|
2363
|
+
name: key,
|
|
2364
|
+
text:
|
|
2365
|
+
typeof unredacted === "string"
|
|
2366
|
+
? unredacted
|
|
2367
|
+
: JSON.stringify(unredacted),
|
|
2368
|
+
});
|
|
2369
|
+
} else if (typeof value === "string") {
|
|
2370
|
+
metadataBindings.push({
|
|
2371
|
+
type: "plain_text",
|
|
2372
|
+
name: key,
|
|
2373
|
+
text: value,
|
|
2374
|
+
});
|
|
2375
|
+
} else {
|
|
2376
|
+
metadataBindings.push({
|
|
2377
|
+
type: "json",
|
|
2378
|
+
name: key,
|
|
2379
|
+
json: value,
|
|
2380
|
+
});
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
2383
|
+
}
|
|
2384
|
+
};
|
|
2385
|
+
|
|
2386
|
+
/**
|
|
2387
|
+
* Create a deployment routing `traffic`% to `versionId`, with the
|
|
2388
|
+
* remainder staying on the currently-live version (the
|
|
2389
|
+
* highest-percentage version of the script's latest deployment).
|
|
2390
|
+
* `traffic >= 100` — or a script with no other live version to split
|
|
2391
|
+
* against — deploys the new version at 100%.
|
|
2392
|
+
*/
|
|
2393
|
+
const deployVersionTraffic = Effect.fn(function* (params: {
|
|
2394
|
+
accountId: string;
|
|
2395
|
+
scriptName: string;
|
|
2396
|
+
versionId: string;
|
|
2397
|
+
traffic: number;
|
|
2398
|
+
message: string | undefined;
|
|
2399
|
+
}) {
|
|
2400
|
+
const { accountId, scriptName, versionId, traffic } = params;
|
|
2401
|
+
const split = yield* Effect.gen(function* () {
|
|
2402
|
+
if (traffic >= 100) {
|
|
2403
|
+
return [{ versionId, percentage: 100 }];
|
|
2404
|
+
}
|
|
2405
|
+
const { deployments } = yield* workers.listScriptDeployments({
|
|
2406
|
+
accountId,
|
|
2407
|
+
scriptName,
|
|
2408
|
+
});
|
|
2409
|
+
// Deployments are returned newest-first; the live version is the
|
|
2410
|
+
// highest-percentage version of the most recent deployment.
|
|
2411
|
+
const stable = deployments[0]?.versions
|
|
2412
|
+
.filter((v) => v.versionId !== versionId)
|
|
2413
|
+
.sort((a, b) => b.percentage - a.percentage)[0];
|
|
2414
|
+
if (!stable) {
|
|
2415
|
+
// Nothing to split against (first deployment of this script).
|
|
2416
|
+
return [{ versionId, percentage: 100 }];
|
|
2417
|
+
}
|
|
2418
|
+
return [
|
|
2419
|
+
{ versionId, percentage: traffic },
|
|
2420
|
+
{ versionId: stable.versionId, percentage: 100 - traffic },
|
|
2421
|
+
];
|
|
2422
|
+
});
|
|
2423
|
+
const deployment = yield* workers.createScriptDeployment({
|
|
2424
|
+
accountId,
|
|
2425
|
+
scriptName,
|
|
2426
|
+
strategy: "percentage",
|
|
2427
|
+
versions: split,
|
|
2428
|
+
annotations: params.message
|
|
2429
|
+
? { workersMessage: params.message }
|
|
2430
|
+
: undefined,
|
|
2431
|
+
});
|
|
2432
|
+
return deployment.id;
|
|
2433
|
+
});
|
|
2434
|
+
|
|
2435
|
+
/**
|
|
2436
|
+
* Resolve the preview-URL alias for a version worker: the
|
|
2437
|
+
* user-provided `version.alias` (validated against Cloudflare's
|
|
2438
|
+
* naming rules), or an alias derived from the stack, stage, and
|
|
2439
|
+
* logical id — stable across deploys, so the aliased preview URL
|
|
2440
|
+
* (`<alias>-<script>.<subdomain>.workers.dev`) is a durable link
|
|
2441
|
+
* that always points at the latest uploaded version. Returns
|
|
2442
|
+
* `undefined` when the parent's script name leaves no room in the
|
|
2443
|
+
* 63-character DNS label and no explicit alias was given.
|
|
2444
|
+
*/
|
|
2445
|
+
const resolveVersionAlias = Effect.fn(function* (
|
|
2446
|
+
id: string,
|
|
2447
|
+
news: WorkerProps,
|
|
2448
|
+
parentName: string,
|
|
2449
|
+
) {
|
|
2450
|
+
// `<alias>-<script name>` is a single DNS label (≤ 63 chars).
|
|
2451
|
+
const budget = 63 - parentName.length - 1;
|
|
2452
|
+
const userAlias = news.version?.alias;
|
|
2453
|
+
if (userAlias !== undefined) {
|
|
2454
|
+
if (!/^[a-z][a-z0-9-]*$/.test(userAlias)) {
|
|
2455
|
+
return yield* Effect.fail(
|
|
2456
|
+
new WorkerVersionConfigError({
|
|
2457
|
+
message: `version.alias '${userAlias}' is invalid: aliases must start with a lowercase letter and contain only lowercase letters, digits, and dashes.`,
|
|
2458
|
+
}),
|
|
2459
|
+
);
|
|
2460
|
+
}
|
|
2461
|
+
if (userAlias.length > budget) {
|
|
2462
|
+
return yield* Effect.fail(
|
|
2463
|
+
new WorkerVersionConfigError({
|
|
2464
|
+
message: `version.alias '${userAlias}' is too long: '<alias>-${parentName}' must fit in a 63-character DNS label, leaving ${Math.max(budget, 0)} characters for the alias.`,
|
|
2465
|
+
}),
|
|
2466
|
+
);
|
|
2467
|
+
}
|
|
2468
|
+
return userAlias;
|
|
2469
|
+
}
|
|
2470
|
+
if (budget < 4) {
|
|
2471
|
+
return undefined;
|
|
2472
|
+
}
|
|
2473
|
+
// Deterministic per (stack, stage, id) — survives updates AND
|
|
2474
|
+
// replacements so the aliased preview URL never moves. A short
|
|
2475
|
+
// hash disambiguates different stacks/stages whose readable
|
|
2476
|
+
// prefixes collide after truncation.
|
|
2477
|
+
const hash = yield* Effect.sync(() =>
|
|
2478
|
+
crypto
|
|
2479
|
+
.createHash("sha256")
|
|
2480
|
+
.update(`${stack.name}/${stack.stage}/${id}`)
|
|
2481
|
+
.digest("hex")
|
|
2482
|
+
.slice(0, 6),
|
|
2483
|
+
);
|
|
2484
|
+
const readable = `${stack.stage}-${id}`
|
|
2485
|
+
.toLowerCase()
|
|
2486
|
+
.replace(/[^a-z0-9-]+/g, "-")
|
|
2487
|
+
.replace(/^-+|-+$/g, "");
|
|
2488
|
+
const alias = [readable, hash]
|
|
2489
|
+
.filter((part) => part.length > 0)
|
|
2490
|
+
.join("-")
|
|
2491
|
+
.slice(0, budget)
|
|
2492
|
+
.replace(/-+$/, "");
|
|
2493
|
+
return /^[a-z]/.test(alias) ? alias : `v${alias}`.slice(0, budget);
|
|
2494
|
+
});
|
|
2495
|
+
|
|
2496
|
+
/**
|
|
2497
|
+
* The parent's workers.dev preview settings + account subdomain,
|
|
2498
|
+
* fetched once per version reconcile. Preview URLs (aliased and
|
|
2499
|
+
* versioned) only serve when the parent has previews enabled.
|
|
2500
|
+
*/
|
|
2501
|
+
const resolveVersionPreviewContext = Effect.fn(function* (
|
|
2502
|
+
accountId: string,
|
|
2503
|
+
scriptName: string,
|
|
2504
|
+
) {
|
|
2505
|
+
const subdomain = yield* workers
|
|
2506
|
+
.getScriptSubdomain({ accountId, scriptName })
|
|
2507
|
+
.pipe(
|
|
2508
|
+
Effect.orElseSucceed<workers.GetScriptSubdomainResponse>(() => ({
|
|
2509
|
+
enabled: false,
|
|
2510
|
+
previewsEnabled: false,
|
|
2511
|
+
})),
|
|
2512
|
+
);
|
|
2513
|
+
const accountSubdomain = yield* getAccountSubdomain(accountId);
|
|
2514
|
+
return {
|
|
2515
|
+
previewsEnabled: subdomain.previewsEnabled === true,
|
|
2516
|
+
accountSubdomain,
|
|
2517
|
+
};
|
|
2518
|
+
});
|
|
2519
|
+
|
|
2520
|
+
/**
|
|
2521
|
+
* Reject props that can't ride along on a *version worker*
|
|
2522
|
+
* (`version.parent` set). A version carries only code, bindings, and
|
|
2523
|
+
* compatibility settings; everything script-level belongs to the
|
|
2524
|
+
* parent and must not be mutated from a version resource. Locally
|
|
2525
|
+
* hosted Durable Object / Workflow classes are rejected because their
|
|
2526
|
+
* migrations would apply to the parent script when the version
|
|
2527
|
+
* deploys.
|
|
2528
|
+
*/
|
|
2529
|
+
const validateVersionWorkerProps = Effect.fn(function* (
|
|
2530
|
+
news: WorkerProps,
|
|
2531
|
+
bindings: ResourceBinding<Worker["Binding"]>[],
|
|
2532
|
+
parentName: string,
|
|
2533
|
+
) {
|
|
2534
|
+
const forbidden = (
|
|
2535
|
+
[
|
|
2536
|
+
["name", news.name],
|
|
2537
|
+
["assets", news.assets],
|
|
2538
|
+
["namespace", news.namespace],
|
|
2539
|
+
["crons", news.crons],
|
|
2540
|
+
["domain", news.domain],
|
|
2541
|
+
["routes", news.routes],
|
|
2542
|
+
["tags", news.tags],
|
|
2543
|
+
["logpush", news.logpush],
|
|
2544
|
+
["observability", news.observability],
|
|
2545
|
+
["placement", news.placement],
|
|
2546
|
+
["limits", news.limits],
|
|
2547
|
+
["workersDev", news.workersDev],
|
|
2548
|
+
["vite", news.vite],
|
|
2549
|
+
] as const
|
|
2550
|
+
).flatMap(([key, value]) => (value !== undefined ? [key] : []));
|
|
2551
|
+
if (forbidden.length > 0) {
|
|
2552
|
+
return yield* Effect.fail(
|
|
2553
|
+
new WorkerVersionConfigError({
|
|
2554
|
+
message:
|
|
2555
|
+
`version.parent uploads a version of '${parentName}' — script-level settings belong to the parent Worker and cannot be set here: ${forbidden.join(", ")}. ` +
|
|
2556
|
+
`Remove ${forbidden.length === 1 ? "this prop" : "these props"} or configure ${forbidden.length === 1 ? "it" : "them"} on the parent.`,
|
|
2557
|
+
}),
|
|
2558
|
+
);
|
|
2559
|
+
}
|
|
2560
|
+
yield* validateTraffic(news.version?.traffic);
|
|
2561
|
+
const cronBindings = getCronBindings(bindings);
|
|
2562
|
+
if (cronBindings.length > 0) {
|
|
2563
|
+
return yield* Effect.fail(
|
|
2564
|
+
new WorkerVersionConfigError({
|
|
2565
|
+
message: `Cron Triggers are script-level settings and cannot be registered from a version of '${parentName}'. Configure crons on the parent Worker.`,
|
|
2566
|
+
}),
|
|
2567
|
+
);
|
|
2568
|
+
}
|
|
2569
|
+
// Any locally-hosted DO class (a durable_object_namespace binding
|
|
2570
|
+
// without a foreign scriptName) or DO/Workflow export would require
|
|
2571
|
+
// migrations, which the versions API can't carry — and which would
|
|
2572
|
+
// mutate the parent's namespaces.
|
|
2573
|
+
const hostedClasses = getDurableObjectBindings(bindings, parentName);
|
|
2574
|
+
const exportedClasses = Object.keys(news.exports ?? {});
|
|
2575
|
+
if (hostedClasses.length > 0 || exportedClasses.length > 0) {
|
|
2576
|
+
return yield* Effect.fail(
|
|
2577
|
+
new WorkerVersionConfigError({
|
|
2578
|
+
message: `A version of '${parentName}' cannot host Durable Object or Workflow classes (${[
|
|
2579
|
+
...new Set([
|
|
2580
|
+
...hostedClasses.map((c) => c.className),
|
|
2581
|
+
...exportedClasses,
|
|
2582
|
+
]),
|
|
2583
|
+
].join(
|
|
2584
|
+
", ",
|
|
2585
|
+
)}): class migrations apply to the parent script. Host the classes on the parent Worker and reference them cross-script instead.`,
|
|
2586
|
+
}),
|
|
2587
|
+
);
|
|
2588
|
+
}
|
|
2589
|
+
});
|
|
2590
|
+
|
|
2591
|
+
/**
|
|
2592
|
+
* Reconcile a *version worker*: upload this Worker's code + bindings
|
|
2593
|
+
* as an immutable version of the parent's script (no script of its
|
|
2594
|
+
* own), then optionally shift `version.traffic`% of the parent's
|
|
2595
|
+
* traffic to it. Idempotent in the reconciler sense: every run
|
|
2596
|
+
* converges cloud state to "the parent's script has a version with
|
|
2597
|
+
* exactly this content, receiving exactly this traffic" — re-running
|
|
2598
|
+
* with changed content simply uploads the next immutable version.
|
|
2599
|
+
*/
|
|
2600
|
+
const putWorkerVersion = Effect.fn(function* (
|
|
2601
|
+
id: string,
|
|
2602
|
+
news: WorkerProps,
|
|
2603
|
+
bindings: ResourceBinding<Worker["Binding"]>[],
|
|
2604
|
+
session: ScopedPlanStatusSession,
|
|
2605
|
+
output: Worker["Attributes"] | undefined,
|
|
2606
|
+
) {
|
|
2607
|
+
const { accountId } = yield* yield* CloudflareEnvironment;
|
|
2608
|
+
const version = news.version!;
|
|
2609
|
+
const parentName = resolveVersionParentName(version);
|
|
2610
|
+
if (parentName === undefined) {
|
|
2611
|
+
return yield* Effect.fail(
|
|
2612
|
+
new WorkerVersionConfigError({
|
|
2613
|
+
message: `version.parent did not resolve to a Worker script name. Pass a Worker (e.g. \`yield* Cloudflare.Worker.ref(id, { stage })\`) or a literal script name.`,
|
|
2614
|
+
}),
|
|
2615
|
+
);
|
|
2616
|
+
}
|
|
2617
|
+
yield* validateVersionWorkerProps(news, bindings, parentName);
|
|
2618
|
+
const traffic = version.traffic ?? 0;
|
|
2619
|
+
// Resolve the alias and preview context BEFORE the upload: the
|
|
2620
|
+
// aliased preview URL (`<alias>-<script>.<subdomain>.workers.dev`)
|
|
2621
|
+
// is deterministic ahead of time — unlike the versioned URL, whose
|
|
2622
|
+
// prefix comes from the server-assigned version id — which is what
|
|
2623
|
+
// lets a `Worker.URL` (self_url) binding be baked into the
|
|
2624
|
+
// version's own bindings.
|
|
2625
|
+
const alias = yield* resolveVersionAlias(id, news, parentName);
|
|
2626
|
+
const { previewsEnabled, accountSubdomain } =
|
|
2627
|
+
yield* resolveVersionPreviewContext(accountId, parentName);
|
|
2628
|
+
const aliasedUrl =
|
|
2629
|
+
alias !== undefined && previewsEnabled
|
|
2630
|
+
? `https://${alias}-${parentName}.${accountSubdomain}.workers.dev`
|
|
2631
|
+
: undefined;
|
|
2632
|
+
const selfUrl = hasSelfUrlBinding(bindings) ? aliasedUrl : undefined;
|
|
2633
|
+
if (hasSelfUrlBinding(bindings) && selfUrl === undefined) {
|
|
2634
|
+
return yield* Effect.fail(
|
|
2635
|
+
new WorkerVersionConfigError({
|
|
2636
|
+
message: previewsEnabled
|
|
2637
|
+
? `A version of '${parentName}' binds its own URL (Worker.URL), but no preview alias fits: '<alias>-${parentName}' must stay within a 63-character DNS label. Set a short version.alias or shorten the parent's name.`
|
|
2638
|
+
: `A version of '${parentName}' binds its own URL (Worker.URL), but the parent's workers.dev previews are disabled — a version's URL is its aliased preview URL. Enable the parent's workers.dev subdomain (url: true, the default).`,
|
|
2639
|
+
}),
|
|
2640
|
+
);
|
|
2641
|
+
}
|
|
2642
|
+
yield* Effect.logInfo(
|
|
2643
|
+
`Cloudflare Worker version: preparing bundle for ${parentName} (from ${id})`,
|
|
2644
|
+
);
|
|
2645
|
+
const { bundle, hash: preparedHash } = yield* prepareAssetsAndBundle(
|
|
2646
|
+
id,
|
|
2647
|
+
news,
|
|
2648
|
+
{ skipAssetsRead: true },
|
|
2649
|
+
);
|
|
2650
|
+
const metadataHash = yield* resolveWorkerMetadataHash({
|
|
2651
|
+
props: news,
|
|
2652
|
+
bindings,
|
|
2653
|
+
accountId,
|
|
2654
|
+
stack: { name: stack.name, stage: stack.stage },
|
|
2655
|
+
selfUrl,
|
|
2656
|
+
});
|
|
2657
|
+
const hash = {
|
|
2658
|
+
...preparedHash,
|
|
2659
|
+
metadata: metadataHash,
|
|
2660
|
+
} satisfies Worker["Attributes"]["hash"];
|
|
2661
|
+
// Lower the `Worker.URL` sentinel into the aliased preview URL —
|
|
2662
|
+
// same lowering `putWorker` performs, with the alias standing in
|
|
2663
|
+
// for the script's own URL.
|
|
2664
|
+
const metadataBindings = bindings.flatMap((b) =>
|
|
2665
|
+
(b.data.bindings ?? []).map((item) =>
|
|
2666
|
+
item.type === "self_url"
|
|
2667
|
+
? { type: "plain_text" as const, name: item.name, text: selfUrl! }
|
|
2668
|
+
: item,
|
|
2669
|
+
),
|
|
2670
|
+
);
|
|
2671
|
+
appendAlchemyAndEnvBindings(
|
|
2672
|
+
metadataBindings,
|
|
2673
|
+
news,
|
|
2674
|
+
accountId,
|
|
2675
|
+
parentName,
|
|
2676
|
+
);
|
|
2677
|
+
const compatibility = getCompatibility(news);
|
|
2678
|
+
yield* session.note(`Uploading version of ${parentName} ...`);
|
|
2679
|
+
const created = yield* workers
|
|
2680
|
+
.createScriptVersion({
|
|
2681
|
+
accountId,
|
|
2682
|
+
scriptName: parentName,
|
|
2683
|
+
metadata: {
|
|
2684
|
+
mainModule: bundle.main!,
|
|
2685
|
+
bindings:
|
|
2686
|
+
metadataBindings as unknown as workers.CreateScriptVersionRequest["metadata"]["bindings"],
|
|
2687
|
+
compatibilityDate: compatibility.date,
|
|
2688
|
+
compatibilityFlags: compatibility.flags,
|
|
2689
|
+
cache: news.cache ?? getCacheBinding(bindings),
|
|
2690
|
+
annotations:
|
|
2691
|
+
alias !== undefined ||
|
|
2692
|
+
version.message !== undefined ||
|
|
2693
|
+
version.tag !== undefined
|
|
2694
|
+
? {
|
|
2695
|
+
workersAlias: alias,
|
|
2696
|
+
workersMessage: version.message,
|
|
2697
|
+
workersTag: version.tag,
|
|
2698
|
+
}
|
|
2699
|
+
: undefined,
|
|
2700
|
+
},
|
|
2701
|
+
files: bundle.files,
|
|
2702
|
+
})
|
|
2703
|
+
.pipe(
|
|
2704
|
+
Effect.retry({
|
|
2705
|
+
while: isBindingTargetNotFound,
|
|
2706
|
+
schedule: bindingTargetNotFoundRetrySchedule(),
|
|
2707
|
+
}),
|
|
2708
|
+
Effect.catchTag("WorkerNotFound", () =>
|
|
2709
|
+
Effect.fail(
|
|
2710
|
+
new WorkerVersionConfigError({
|
|
2711
|
+
message: `version.parent script '${parentName}' does not exist. Deploy the parent Worker first (or check the referenced stage/stack).`,
|
|
2712
|
+
}),
|
|
2713
|
+
),
|
|
2714
|
+
),
|
|
2715
|
+
);
|
|
2716
|
+
const versionId = created.id ?? undefined;
|
|
2717
|
+
if (versionId === undefined) {
|
|
2718
|
+
return yield* Effect.fail(
|
|
2719
|
+
new WorkerVersionConfigError({
|
|
2720
|
+
message: `Cloudflare did not return a version id for the uploaded version of '${parentName}'.`,
|
|
2721
|
+
}),
|
|
2722
|
+
);
|
|
2723
|
+
}
|
|
2724
|
+
let deploymentId: string | undefined;
|
|
2725
|
+
if (traffic > 0) {
|
|
2726
|
+
yield* session.note(
|
|
2727
|
+
`Deploying version at ${traffic}% of ${parentName}'s traffic ...`,
|
|
2728
|
+
);
|
|
2729
|
+
deploymentId = yield* deployVersionTraffic({
|
|
2730
|
+
accountId,
|
|
2731
|
+
scriptName: parentName,
|
|
2732
|
+
versionId,
|
|
2733
|
+
traffic,
|
|
2734
|
+
message: version.message,
|
|
2735
|
+
});
|
|
2736
|
+
}
|
|
2737
|
+
// Version-affinity transform rules (`version.affinity`): a canary
|
|
2738
|
+
// rides the *parent's* zone traffic, so the rules land on the
|
|
2739
|
+
// parent's zones — observed custom-domain attachments plus, when
|
|
2740
|
+
// `parent` was passed as a Worker resource, its recorded routes.
|
|
2741
|
+
const previousAffinityZoneIds = output?.affinityZoneIds ?? [];
|
|
2742
|
+
let affinityZoneIds: string[] | undefined;
|
|
2743
|
+
if (version.affinity !== undefined || previousAffinityZoneIds.length) {
|
|
2744
|
+
const resolvedAffinity =
|
|
2745
|
+
version.affinity !== undefined
|
|
2746
|
+
? yield* resolveVersionAffinity(version.affinity)
|
|
2747
|
+
: undefined;
|
|
2748
|
+
let hostsByZone: Map<string, AffinityZoneHost[]> | undefined;
|
|
2749
|
+
if (resolvedAffinity !== undefined) {
|
|
2750
|
+
hostsByZone = new Map();
|
|
2751
|
+
const addHost = (zoneId: string, host: AffinityZoneHost) => {
|
|
2752
|
+
const hosts = hostsByZone!.get(zoneId) ?? [];
|
|
2753
|
+
hosts.push(host);
|
|
2754
|
+
hostsByZone!.set(zoneId, hosts);
|
|
2755
|
+
};
|
|
2756
|
+
// A `version.parent` passed as a Worker resource resolves to
|
|
2757
|
+
// the parent's Attributes at reconcile time (see
|
|
2758
|
+
// resolveVersionParentName) — its recorded routes and domain
|
|
2759
|
+
// tell us the zones the parent serves on.
|
|
2760
|
+
const parentAttrs =
|
|
2761
|
+
typeof version.parent === "object" && version.parent !== null
|
|
2762
|
+
? (version.parent as unknown as Partial<Worker["Attributes"]>)
|
|
2763
|
+
: undefined;
|
|
2764
|
+
const redirectHosts = new Set(parentAttrs?.domain?.redirects ?? []);
|
|
2765
|
+
const attached = yield* workers
|
|
2766
|
+
.listDomains({ accountId, service: parentName })
|
|
2767
|
+
.pipe(Effect.map((r) => r.result ?? []));
|
|
2768
|
+
for (const d of attached) {
|
|
2769
|
+
if (!d.hostname || !d.zoneId || redirectHosts.has(d.hostname)) {
|
|
2770
|
+
continue;
|
|
2771
|
+
}
|
|
2772
|
+
addHost(d.zoneId, { host: d.hostname, wildcard: false });
|
|
2773
|
+
}
|
|
2774
|
+
for (const route of parentAttrs?.routes ?? []) {
|
|
2775
|
+
const host = route.pattern.split("/")[0];
|
|
2776
|
+
if (!host || host.includes('"')) continue;
|
|
2777
|
+
addHost(route.zoneId, { host, wildcard: host.includes("*") });
|
|
2778
|
+
}
|
|
2779
|
+
if (hostsByZone.size === 0) {
|
|
2780
|
+
return yield* Effect.fail(
|
|
2781
|
+
new WorkerVersionConfigError({
|
|
2782
|
+
message:
|
|
2783
|
+
`version.affinity pins users via a zone Transform Rule setting the ${AFFINITY_HEADER} header, which only sees zone traffic — the parent '${parentName}' has no custom domains (or recorded zone routes) to place the rule on. ` +
|
|
2784
|
+
`Give the parent a \`domain\` or zone \`routes\`, or pass the parent as a Worker resource so its routes are visible here.`,
|
|
2785
|
+
}),
|
|
2786
|
+
);
|
|
2787
|
+
}
|
|
2788
|
+
yield* session.note("Reconciling version-affinity rules ...");
|
|
2789
|
+
}
|
|
2790
|
+
const placed = yield* reconcileAffinityRules({
|
|
2791
|
+
scriptName: parentName,
|
|
2792
|
+
desired:
|
|
2793
|
+
resolvedAffinity !== undefined && hostsByZone !== undefined
|
|
2794
|
+
? { affinity: resolvedAffinity, hostsByZone }
|
|
2795
|
+
: undefined,
|
|
2796
|
+
previousZoneIds: previousAffinityZoneIds,
|
|
2797
|
+
});
|
|
2798
|
+
affinityZoneIds = placed.length > 0 ? placed : undefined;
|
|
2799
|
+
}
|
|
2800
|
+
// The aliased URL is primary (`url`): it is stable across deploys,
|
|
2801
|
+
// re-pointing at each newly uploaded version. The per-version URL
|
|
2802
|
+
// (prefixed by the version id) rides along in `urls`.
|
|
2803
|
+
const versionedUrl = previewsEnabled
|
|
2804
|
+
? `https://${versionId.split("-")[0]}-${parentName}.${accountSubdomain}.workers.dev`
|
|
2805
|
+
: undefined;
|
|
2806
|
+
const urls = [
|
|
2807
|
+
...(aliasedUrl ? [aliasedUrl] : []),
|
|
2808
|
+
...(versionedUrl ? [versionedUrl] : []),
|
|
2809
|
+
];
|
|
2810
|
+
return {
|
|
2811
|
+
workerId: parentName,
|
|
2812
|
+
workerName: parentName,
|
|
2813
|
+
namespace: undefined,
|
|
2814
|
+
logpush: undefined,
|
|
2815
|
+
url: urls[0],
|
|
2816
|
+
urls,
|
|
2817
|
+
domain: undefined,
|
|
2818
|
+
tags: undefined,
|
|
2819
|
+
durableObjectNamespaces: {},
|
|
2820
|
+
accountId,
|
|
2821
|
+
routes: [],
|
|
2822
|
+
crons: [],
|
|
2823
|
+
versionOf: parentName,
|
|
2824
|
+
versionId,
|
|
2825
|
+
versionAlias: alias,
|
|
2826
|
+
deploymentId,
|
|
2827
|
+
affinityZoneIds,
|
|
2828
|
+
hash,
|
|
2829
|
+
} satisfies Worker["Attributes"];
|
|
2830
|
+
});
|
|
2831
|
+
|
|
1367
2832
|
const putWorker = Effect.fn(function* (
|
|
1368
2833
|
id: string,
|
|
1369
2834
|
news: WorkerProps,
|
|
@@ -1383,6 +2848,26 @@ export const LiveWorkerProvider = () =>
|
|
|
1383
2848
|
// account-level script. The put/settings calls switch endpoints and
|
|
1384
2849
|
// the subdomain / custom-domain / cron reconciliation is skipped.
|
|
1385
2850
|
const dispatchNamespace = resolveNamespaceName(news?.namespace);
|
|
2851
|
+
yield* validateTraffic(news.version?.traffic);
|
|
2852
|
+
if (news.version !== undefined && dispatchNamespace) {
|
|
2853
|
+
return yield* Effect.fail(
|
|
2854
|
+
new WorkerVersionConfigError({
|
|
2855
|
+
message: `Workers for Platforms user workers do not support versions or gradual deployments — remove the version prop from '${name}'.`,
|
|
2856
|
+
}),
|
|
2857
|
+
);
|
|
2858
|
+
}
|
|
2859
|
+
// Resolve the Worker's own URL up front when a `Worker.URL` binding
|
|
2860
|
+
// is present: the value must exist before the bundle is built (Vite
|
|
2861
|
+
// inlines VITE_*-prefixed env into the client bundle) and before the
|
|
2862
|
+
// metadata bindings are assembled (the `self_url` sentinel lowers
|
|
2863
|
+
// into a plain_text binding below).
|
|
2864
|
+
const selfUrl = hasSelfUrlBinding(bindings)
|
|
2865
|
+
? dispatchNamespace
|
|
2866
|
+
? yield* Effect.die(
|
|
2867
|
+
`Worker "${name}" binds its own URL (Worker.URL), but a dispatch-namespace user worker is invoked via dynamic dispatch and has no URL of its own.`,
|
|
2868
|
+
)
|
|
2869
|
+
: yield* resolveSelfUrl(name, news, accountId)
|
|
2870
|
+
: undefined;
|
|
1386
2871
|
yield* Effect.logInfo(
|
|
1387
2872
|
`Cloudflare Worker ${olds ? "update" : "create"}: preparing bundle for ${name}`,
|
|
1388
2873
|
);
|
|
@@ -1398,6 +2883,7 @@ export const LiveWorkerProvider = () =>
|
|
|
1398
2883
|
hash: preparedHash,
|
|
1399
2884
|
} = yield* prepareAssetsAndBundle(id, news, {
|
|
1400
2885
|
skipAssetsRead: prebuiltAssets?.skip,
|
|
2886
|
+
selfUrl,
|
|
1401
2887
|
});
|
|
1402
2888
|
// When the caller supplied a precomputed hash (e.g. via
|
|
1403
2889
|
// `Command.Build`), store *that* hash in output state so the
|
|
@@ -1410,6 +2896,7 @@ export const LiveWorkerProvider = () =>
|
|
|
1410
2896
|
bindings,
|
|
1411
2897
|
accountId,
|
|
1412
2898
|
stack: { name: stack.name, stage: stack.stage },
|
|
2899
|
+
selfUrl,
|
|
1413
2900
|
});
|
|
1414
2901
|
const hash = {
|
|
1415
2902
|
...preparedHash,
|
|
@@ -1421,7 +2908,12 @@ export const LiveWorkerProvider = () =>
|
|
|
1421
2908
|
// `transferred_classes` migration below and must be stripped from the
|
|
1422
2909
|
// wire-shape binding before upload.
|
|
1423
2910
|
const metadataBindings = bindings.flatMap((b) =>
|
|
1424
|
-
(b.data.bindings ?? []).map((item):
|
|
2911
|
+
(b.data.bindings ?? []).map((item): WireWorkerBinding => {
|
|
2912
|
+
// Lower the `Worker.URL` sentinel into the resolved URL —
|
|
2913
|
+
// Cloudflare has no native binding for it.
|
|
2914
|
+
if (item.type === "self_url") {
|
|
2915
|
+
return { type: "plain_text", name: item.name, text: selfUrl! };
|
|
2916
|
+
}
|
|
1425
2917
|
if (
|
|
1426
2918
|
item.type === "durable_object_namespace" &&
|
|
1427
2919
|
item.transferredFrom !== undefined
|
|
@@ -1429,6 +2921,16 @@ export const LiveWorkerProvider = () =>
|
|
|
1429
2921
|
const { transferredFrom: _, ...rest } = item;
|
|
1430
2922
|
return rest;
|
|
1431
2923
|
}
|
|
2924
|
+
// `queueId` (mode discrimination) and `shim` (dev-mode remote
|
|
2925
|
+
// producer) are alchemy-only metadata on queue bindings — strip
|
|
2926
|
+
// them from the wire shape.
|
|
2927
|
+
if (
|
|
2928
|
+
item.type === "queue" &&
|
|
2929
|
+
(item.queueId !== undefined || item.shim !== undefined)
|
|
2930
|
+
) {
|
|
2931
|
+
const { queueId: _, shim: __, ...rest } = item;
|
|
2932
|
+
return rest;
|
|
2933
|
+
}
|
|
1432
2934
|
return item;
|
|
1433
2935
|
}),
|
|
1434
2936
|
);
|
|
@@ -1445,7 +2947,16 @@ export const LiveWorkerProvider = () =>
|
|
|
1445
2947
|
`Cloudflare Worker update: assets unchanged for ${name}, keeping existing`,
|
|
1446
2948
|
);
|
|
1447
2949
|
keepAssets = true;
|
|
1448
|
-
|
|
2950
|
+
// `keepAssets` only preserves the uploaded files — the PUT
|
|
2951
|
+
// replaces the asset config wholesale. The skip path never
|
|
2952
|
+
// walked the directory, so read just `_headers`/`_redirects`
|
|
2953
|
+
// here or a no-op deploy would wipe their rules.
|
|
2954
|
+
metadataAssets = {
|
|
2955
|
+
config: mergeAssetsConfigFiles(
|
|
2956
|
+
prebuiltAssets.config,
|
|
2957
|
+
yield* readAssetsConfigFiles(prebuiltAssets.directory),
|
|
2958
|
+
),
|
|
2959
|
+
};
|
|
1449
2960
|
metadataBindings.push({
|
|
1450
2961
|
type: "assets",
|
|
1451
2962
|
name: "ASSETS",
|
|
@@ -1484,58 +2995,7 @@ export const LiveWorkerProvider = () =>
|
|
|
1484
2995
|
name: "ASSETS",
|
|
1485
2996
|
});
|
|
1486
2997
|
}
|
|
1487
|
-
metadataBindings
|
|
1488
|
-
{
|
|
1489
|
-
type: "plain_text",
|
|
1490
|
-
name: "ALCHEMY_PHASE",
|
|
1491
|
-
text: "runtime",
|
|
1492
|
-
},
|
|
1493
|
-
{
|
|
1494
|
-
type: "plain_text",
|
|
1495
|
-
name: "ALCHEMY_STACK_NAME",
|
|
1496
|
-
text: stack.name,
|
|
1497
|
-
},
|
|
1498
|
-
{
|
|
1499
|
-
type: "plain_text",
|
|
1500
|
-
name: "ALCHEMY_STAGE",
|
|
1501
|
-
text: stack.stage,
|
|
1502
|
-
},
|
|
1503
|
-
{
|
|
1504
|
-
type: "plain_text",
|
|
1505
|
-
name: "ALCHEMY_CLOUDFLARE_ACCOUNT_ID",
|
|
1506
|
-
text: accountId,
|
|
1507
|
-
},
|
|
1508
|
-
);
|
|
1509
|
-
// Add environment variables as metadata bindings
|
|
1510
|
-
if (news.env) {
|
|
1511
|
-
for (const [key, value] of Object.entries(news.env)) {
|
|
1512
|
-
if (value === undefined) continue;
|
|
1513
|
-
if (metadataBindings.some((b) => b.name === key)) continue;
|
|
1514
|
-
if (Redacted.isRedacted(value)) {
|
|
1515
|
-
const unredacted = Redacted.value(value);
|
|
1516
|
-
metadataBindings.push({
|
|
1517
|
-
type: "secret_text",
|
|
1518
|
-
name: key,
|
|
1519
|
-
text:
|
|
1520
|
-
typeof unredacted === "string"
|
|
1521
|
-
? unredacted
|
|
1522
|
-
: JSON.stringify(unredacted),
|
|
1523
|
-
});
|
|
1524
|
-
} else if (typeof value === "string") {
|
|
1525
|
-
metadataBindings.push({
|
|
1526
|
-
type: "plain_text",
|
|
1527
|
-
name: key,
|
|
1528
|
-
text: value,
|
|
1529
|
-
});
|
|
1530
|
-
} else {
|
|
1531
|
-
metadataBindings.push({
|
|
1532
|
-
type: "json",
|
|
1533
|
-
name: key,
|
|
1534
|
-
json: value,
|
|
1535
|
-
});
|
|
1536
|
-
}
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
2998
|
+
appendAlchemyAndEnvBindings(metadataBindings, news, accountId, name);
|
|
1539
2999
|
yield* Effect.logInfo(
|
|
1540
3000
|
`Cloudflare Worker ${olds ? "update" : "create"}: uploading script for ${name}`,
|
|
1541
3001
|
);
|
|
@@ -1882,47 +3342,155 @@ export const LiveWorkerProvider = () =>
|
|
|
1882
3342
|
tailConsumers: undefined,
|
|
1883
3343
|
usageModel: undefined,
|
|
1884
3344
|
};
|
|
1885
|
-
const
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
3345
|
+
const rolloutTraffic = getSelfRolloutTraffic(news);
|
|
3346
|
+
let versionId: string | undefined;
|
|
3347
|
+
let deploymentId: string | undefined;
|
|
3348
|
+
let worker: { id?: string | null; logpush?: boolean | null };
|
|
3349
|
+
// A gradual rollout (`version.traffic` < 100) deploys through the
|
|
3350
|
+
// versions API instead of the full-cutover script PUT. That's only
|
|
3351
|
+
// possible when the script already has a live deployment to split
|
|
3352
|
+
// traffic against (otherwise the first deploy takes 100%), and only
|
|
3353
|
+
// for changes a version can carry — code, bindings, compatibility
|
|
3354
|
+
// settings, cache. Script-level settings in the upload metadata
|
|
3355
|
+
// (tags, observability, limits, placement, logpush) keep their live
|
|
3356
|
+
// values until the next full deploy, matching
|
|
3357
|
+
// `wrangler versions upload` semantics.
|
|
3358
|
+
if (
|
|
3359
|
+
rolloutTraffic !== undefined &&
|
|
3360
|
+
existingSettings !== undefined &&
|
|
3361
|
+
output?.hash !== undefined &&
|
|
3362
|
+
!dispatchNamespace
|
|
3363
|
+
) {
|
|
3364
|
+
if (metadataAssets !== undefined) {
|
|
3365
|
+
return yield* Effect.fail(
|
|
3366
|
+
new WorkerVersionConfigError({
|
|
3367
|
+
message: `Worker '${name}' has static assets, which the versions API cannot carry — gradual rollouts (version.traffic) are not supported for Workers with assets.`,
|
|
3368
|
+
}),
|
|
1902
3369
|
);
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
3370
|
+
}
|
|
3371
|
+
const migratedClasses = [
|
|
3372
|
+
...migrations.newClasses,
|
|
3373
|
+
...migrations.newSqliteClasses,
|
|
3374
|
+
...migrations.deletedClasses,
|
|
3375
|
+
...migrations.renamedClasses.map((r) => r.to),
|
|
3376
|
+
...migrations.transferredClasses.map((t) => t.to),
|
|
3377
|
+
];
|
|
3378
|
+
if (migratedClasses.length > 0) {
|
|
3379
|
+
return yield* Effect.fail(
|
|
3380
|
+
new WorkerVersionConfigError({
|
|
3381
|
+
message: `This deploy of '${name}' changes Durable Object classes (${migratedClasses.join(", ")}), which requires a migration — migrations cannot ride a gradual rollout. Deploy at 100% (remove version.traffic) first, then resume gradual rollouts.`,
|
|
3382
|
+
}),
|
|
3383
|
+
);
|
|
3384
|
+
}
|
|
3385
|
+
yield* session.note(
|
|
3386
|
+
`Uploading version of ${name} (${bundleSize}) ...`,
|
|
3387
|
+
);
|
|
3388
|
+
const created = yield* workers
|
|
3389
|
+
.createScriptVersion({
|
|
3390
|
+
accountId,
|
|
3391
|
+
scriptName: name,
|
|
3392
|
+
metadata: {
|
|
3393
|
+
mainModule: metadata.mainModule!,
|
|
3394
|
+
bindings:
|
|
3395
|
+
metadata.bindings as unknown as workers.CreateScriptVersionRequest["metadata"]["bindings"],
|
|
3396
|
+
compatibilityDate: metadata.compatibilityDate,
|
|
3397
|
+
compatibilityFlags: metadata.compatibilityFlags,
|
|
3398
|
+
cache: metadata.cache,
|
|
3399
|
+
annotations:
|
|
3400
|
+
news.version?.alias !== undefined ||
|
|
3401
|
+
news.version?.message !== undefined ||
|
|
3402
|
+
news.version?.tag !== undefined
|
|
3403
|
+
? {
|
|
3404
|
+
workersAlias: news.version?.alias,
|
|
3405
|
+
workersMessage: news.version?.message,
|
|
3406
|
+
workersTag: news.version?.tag,
|
|
3407
|
+
}
|
|
3408
|
+
: undefined,
|
|
3409
|
+
},
|
|
3410
|
+
files: bundle.files,
|
|
3411
|
+
})
|
|
3412
|
+
.pipe(
|
|
3413
|
+
Effect.retry({
|
|
3414
|
+
while: isBindingTargetNotFound,
|
|
3415
|
+
schedule: bindingTargetNotFoundRetrySchedule(),
|
|
3416
|
+
}),
|
|
3417
|
+
);
|
|
3418
|
+
versionId = created.id ?? undefined;
|
|
3419
|
+
if (versionId === undefined) {
|
|
3420
|
+
return yield* Effect.fail(
|
|
3421
|
+
new WorkerVersionConfigError({
|
|
3422
|
+
message: `Cloudflare did not return a version id for the uploaded version of '${name}'.`,
|
|
3423
|
+
}),
|
|
3424
|
+
);
|
|
3425
|
+
}
|
|
3426
|
+
if (rolloutTraffic > 0) {
|
|
3427
|
+
yield* session.note(
|
|
3428
|
+
`Deploying version at ${rolloutTraffic}% of traffic ...`,
|
|
3429
|
+
);
|
|
3430
|
+
deploymentId = yield* deployVersionTraffic({
|
|
3431
|
+
accountId,
|
|
3432
|
+
scriptName: name,
|
|
3433
|
+
versionId,
|
|
3434
|
+
traffic: rolloutTraffic,
|
|
3435
|
+
message: news.version?.message,
|
|
3436
|
+
});
|
|
3437
|
+
}
|
|
3438
|
+
worker = { id: name, logpush: existingSettings.logpush };
|
|
3439
|
+
} else {
|
|
3440
|
+
if (rolloutTraffic !== undefined) {
|
|
3441
|
+
// First deploy of this script (or a pre-create placeholder is
|
|
3442
|
+
// the only thing live) — there is no previous version worth
|
|
3443
|
+
// splitting traffic with, so this deploy takes 100%.
|
|
3444
|
+
yield* Effect.logInfo(
|
|
3445
|
+
`Cloudflare Worker ${name}: no previous live version to split traffic with; deploying at 100%`,
|
|
3446
|
+
);
|
|
3447
|
+
}
|
|
3448
|
+
worker = yield* putWorkerScriptWithMigrationRecovery();
|
|
3449
|
+
}
|
|
3450
|
+
|
|
3451
|
+
function putWorkerScriptWithMigrationRecovery() {
|
|
3452
|
+
return putWorkerScript({
|
|
3453
|
+
accountId,
|
|
3454
|
+
scriptName: name,
|
|
3455
|
+
dispatchNamespace,
|
|
3456
|
+
metadata,
|
|
3457
|
+
files: bundle.files,
|
|
3458
|
+
}).pipe(
|
|
3459
|
+
Effect.catch((err) => {
|
|
3460
|
+
// When adopting a Worker managed by Wrangler (or after a previous
|
|
3461
|
+
// deploy with mismatched migrations), the old_tag precondition
|
|
3462
|
+
// fails. The only way to discover the actual tag is through the
|
|
3463
|
+
// error message — getScriptSettings is meant to return it but
|
|
3464
|
+
// doesn't at runtime.
|
|
3465
|
+
const msg = String(
|
|
3466
|
+
typeof err === "object" && err !== null && "message" in err
|
|
3467
|
+
? err.message
|
|
3468
|
+
: err,
|
|
3469
|
+
);
|
|
3470
|
+
const expectedTag = msg.match(
|
|
3471
|
+
/when expected tag is ['"]?([^'"]+)['"]?/,
|
|
3472
|
+
)?.[1];
|
|
3473
|
+
if (expectedTag) {
|
|
3474
|
+
return putWorkerScript({
|
|
3475
|
+
accountId,
|
|
3476
|
+
scriptName: name,
|
|
3477
|
+
dispatchNamespace,
|
|
3478
|
+
metadata: {
|
|
3479
|
+
...metadata,
|
|
3480
|
+
migrations: {
|
|
3481
|
+
...migrations,
|
|
3482
|
+
oldTag: expectedTag,
|
|
3483
|
+
newTag: bumpMigrationTagVersion(expectedTag),
|
|
3484
|
+
},
|
|
1917
3485
|
},
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
}
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
3486
|
+
files: bundle.files,
|
|
3487
|
+
});
|
|
3488
|
+
}
|
|
3489
|
+
// @effect-diagnostics-next-line anyUnknownInErrorContext:off
|
|
3490
|
+
return Effect.fail(err as any);
|
|
3491
|
+
}),
|
|
3492
|
+
);
|
|
3493
|
+
}
|
|
1926
3494
|
const { settings, durableObjectNamespaces } =
|
|
1927
3495
|
yield* getWorkerSettingsWithDurableObjects(
|
|
1928
3496
|
name,
|
|
@@ -1943,20 +3511,21 @@ export const LiveWorkerProvider = () =>
|
|
|
1943
3511
|
tags: settings.tags ?? metadata.tags,
|
|
1944
3512
|
durableObjectNamespaces,
|
|
1945
3513
|
accountId,
|
|
1946
|
-
|
|
3514
|
+
urls: [],
|
|
3515
|
+
domain: undefined,
|
|
1947
3516
|
routes: [],
|
|
1948
3517
|
crons: [],
|
|
1949
3518
|
hash,
|
|
1950
3519
|
} satisfies Worker["Attributes"];
|
|
1951
3520
|
}
|
|
1952
|
-
// Reconcile workers.dev
|
|
1953
|
-
// We can't diff `news.
|
|
1954
|
-
// default to `undefined` (meaning "enable") — that
|
|
1955
|
-
// would skip the API call on every deploy where the user
|
|
1956
|
-
//
|
|
3521
|
+
// Reconcile the workers.dev settings against observed cloud state.
|
|
3522
|
+
// We can't diff `news.workersDev` against `olds.workersDev` here
|
|
3523
|
+
// because both default to `undefined` (meaning "enable") — that
|
|
3524
|
+
// comparison would skip the API call on every deploy where the user
|
|
3525
|
+
// never set the prop, leaving the subdomain in whatever state
|
|
1957
3526
|
// Cloudflare currently has it (disabled by default, or whatever
|
|
1958
3527
|
// a previous failed/external action left it as).
|
|
1959
|
-
const
|
|
3528
|
+
const workersDev = resolveWorkersDev(news.workersDev);
|
|
1960
3529
|
const observedSubdomain = yield* workers
|
|
1961
3530
|
.getScriptSubdomain({
|
|
1962
3531
|
accountId,
|
|
@@ -1969,11 +3538,11 @@ export const LiveWorkerProvider = () =>
|
|
|
1969
3538
|
})),
|
|
1970
3539
|
);
|
|
1971
3540
|
if (
|
|
1972
|
-
|
|
1973
|
-
|
|
3541
|
+
workersDev.enabled !== observedSubdomain.enabled ||
|
|
3542
|
+
workersDev.previewsEnabled !== observedSubdomain.previewsEnabled
|
|
1974
3543
|
) {
|
|
1975
3544
|
yield* session.note(
|
|
1976
|
-
`${
|
|
3545
|
+
`${workersDev.enabled || workersDev.previewsEnabled ? "Enabling" : "Disabling"} workers.dev subdomain...`,
|
|
1977
3546
|
);
|
|
1978
3547
|
// Cloudflare's script registry is eventually consistent — for the
|
|
1979
3548
|
// first few hundred ms after `putScript` returns, POST /subdomain
|
|
@@ -1983,7 +3552,7 @@ export const LiveWorkerProvider = () =>
|
|
|
1983
3552
|
// Retry the subdomain toggle on those transient tags with a short
|
|
1984
3553
|
// exponential backoff; same pattern we use elsewhere in this
|
|
1985
3554
|
// provider for DO-namespace propagation and for `putScript` itself.
|
|
1986
|
-
yield* setWorkerSubdomain(name,
|
|
3555
|
+
yield* setWorkerSubdomain(name, workersDev).pipe(
|
|
1987
3556
|
Effect.retry({
|
|
1988
3557
|
while: (error) =>
|
|
1989
3558
|
error._tag === "WorkerNotFound" ||
|
|
@@ -1996,22 +3565,99 @@ export const LiveWorkerProvider = () =>
|
|
|
1996
3565
|
}),
|
|
1997
3566
|
);
|
|
1998
3567
|
}
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
3568
|
+
// Custom domains are managed only when `domain` is declared on props
|
|
3569
|
+
// (#942): an omitted `domain` leaves live attachments alone — and
|
|
3570
|
+
// spares a plain workers.dev Worker the listDomains call on every
|
|
3571
|
+
// deploy (#926) — while `domain: null` explicitly detaches
|
|
3572
|
+
// everything. When managed, the canonical name + aliases + redirect
|
|
3573
|
+
// hostnames are all attached (DNS + edge certificate); redirect
|
|
3574
|
+
// hostnames additionally get a redirect rule, which runs before the
|
|
3575
|
+
// Worker so those requests never invoke it. Unmanaged domains
|
|
3576
|
+
// persisted in state carry forward so read keeps observing them.
|
|
3577
|
+
const manageCustomDomains = news.domain !== undefined;
|
|
3578
|
+
const domainConfig = yield* resolveWorkerDomain(news.domain);
|
|
3579
|
+
const previousDomain = stateWorkerDomain(output);
|
|
3580
|
+
let effectiveDomain = manageCustomDomains
|
|
3581
|
+
? domainConfig
|
|
3582
|
+
: previousDomain;
|
|
3583
|
+
if (!manageCustomDomains && previousDomain !== undefined) {
|
|
3584
|
+
// Unmanaged, but state remembers domains: observe which of them
|
|
3585
|
+
// are actually still attached and carry only those forward —
|
|
3586
|
+
// stale state entries (e.g. legacy records whose attach never
|
|
3587
|
+
// happened, or hostnames detached out-of-band) must not
|
|
3588
|
+
// resurface in `urls`. Plain workers.dev Workers (no persisted
|
|
3589
|
+
// domains) never pay this listDomains call (#926).
|
|
3590
|
+
const live = new Set(
|
|
3591
|
+
yield* workers.listDomains({ accountId, service: name }).pipe(
|
|
3592
|
+
Effect.map((r) =>
|
|
3593
|
+
(r.result ?? []).flatMap((d) =>
|
|
3594
|
+
d.hostname ? [d.hostname] : [],
|
|
3595
|
+
),
|
|
3596
|
+
),
|
|
3597
|
+
Effect.catch(() => Effect.succeed([] as string[])),
|
|
3598
|
+
),
|
|
3599
|
+
);
|
|
3600
|
+
const serving = [
|
|
3601
|
+
...(live.has(previousDomain.name) ? [previousDomain.name] : []),
|
|
3602
|
+
...previousDomain.aliases.filter((h) => live.has(h)),
|
|
3603
|
+
];
|
|
3604
|
+
effectiveDomain =
|
|
3605
|
+
serving.length > 0
|
|
3606
|
+
? {
|
|
3607
|
+
name: serving[0],
|
|
3608
|
+
aliases: serving.slice(1),
|
|
3609
|
+
redirects: previousDomain.redirects.filter((h) =>
|
|
3610
|
+
live.has(h),
|
|
3611
|
+
),
|
|
3612
|
+
}
|
|
3613
|
+
: undefined;
|
|
3614
|
+
}
|
|
3615
|
+
if (manageCustomDomains) {
|
|
3616
|
+
const desiredHostnames = domainConfig
|
|
3617
|
+
? [
|
|
3618
|
+
domainConfig.name,
|
|
3619
|
+
...domainConfig.aliases,
|
|
3620
|
+
...domainConfig.redirects,
|
|
3621
|
+
]
|
|
3622
|
+
: [];
|
|
2002
3623
|
yield* session.note(
|
|
2003
|
-
`Reconciling custom domains (${
|
|
3624
|
+
`Reconciling custom domains (${desiredHostnames.length}) ...`,
|
|
2004
3625
|
);
|
|
3626
|
+
// Capture hostname → zone for *currently attached* domains before
|
|
3627
|
+
// reconcile detaches removed ones — a removed redirect hostname's
|
|
3628
|
+
// zone is otherwise unresolvable and its rule couldn't be cleaned.
|
|
3629
|
+
const liveBeforeReconcile = yield* workers
|
|
3630
|
+
.listDomains({ accountId, service: name })
|
|
3631
|
+
.pipe(
|
|
3632
|
+
Effect.map((r) =>
|
|
3633
|
+
(r.result ?? []).flatMap((d) =>
|
|
3634
|
+
d.hostname && d.zoneId
|
|
3635
|
+
? [[d.hostname, d.zoneId] as const]
|
|
3636
|
+
: [],
|
|
3637
|
+
),
|
|
3638
|
+
),
|
|
3639
|
+
Effect.catch(() => Effect.succeed([])),
|
|
3640
|
+
);
|
|
3641
|
+
const reconciled = yield* reconcileDomains(name, desiredHostnames);
|
|
3642
|
+
const zoneIdByHostname = new Map([
|
|
3643
|
+
...liveBeforeReconcile,
|
|
3644
|
+
...reconciled.map((d) => [d.hostname, d.zoneId] as const),
|
|
3645
|
+
]);
|
|
3646
|
+
yield* reconcileRedirectRules({
|
|
3647
|
+
scriptName: name,
|
|
3648
|
+
domain: domainConfig,
|
|
3649
|
+
zoneIdByHostname,
|
|
3650
|
+
previousRedirects: previousDomain?.redirects ?? [],
|
|
3651
|
+
});
|
|
3652
|
+
effectiveDomain = domainConfig;
|
|
2005
3653
|
}
|
|
2006
|
-
const
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
...(workersDevUrl ? [workersDevUrl] : []),
|
|
2014
|
-
];
|
|
3654
|
+
const urls = yield* computeWorkerUrls({
|
|
3655
|
+
scriptName: name,
|
|
3656
|
+
workersDev,
|
|
3657
|
+
domain: effectiveDomain,
|
|
3658
|
+
uploadedVersionId: versionId,
|
|
3659
|
+
uploadedVersionAlias: news.version?.alias,
|
|
3660
|
+
});
|
|
2015
3661
|
const desiredRoutes = yield* normalizeRoutes(news.routes);
|
|
2016
3662
|
const previousRoutes = output?.routes ?? [];
|
|
2017
3663
|
if (desiredRoutes.length > 0 || previousRoutes.length > 0) {
|
|
@@ -2024,24 +3670,96 @@ export const LiveWorkerProvider = () =>
|
|
|
2024
3670
|
desiredRoutes,
|
|
2025
3671
|
previousRoutes,
|
|
2026
3672
|
);
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
3673
|
+
// Version-affinity transform rules (`version.affinity`): pin each
|
|
3674
|
+
// user to one version during gradual rollouts by filling the
|
|
3675
|
+
// version-key header from a stable request property, on every zone
|
|
3676
|
+
// this Worker serves on. Runs after the domain/route reconciles so
|
|
3677
|
+
// freshly attached surfaces resolve their zones, and also when only
|
|
3678
|
+
// *previous* state holds rules, so removing affinity converges.
|
|
3679
|
+
const affinity =
|
|
3680
|
+
news.version?.parent == null ? news.version?.affinity : undefined;
|
|
3681
|
+
const previousAffinityZoneIds = output?.affinityZoneIds ?? [];
|
|
3682
|
+
let affinityZoneIds: string[] | undefined;
|
|
3683
|
+
if (affinity !== undefined || previousAffinityZoneIds.length > 0) {
|
|
3684
|
+
const resolvedAffinity =
|
|
3685
|
+
affinity !== undefined
|
|
3686
|
+
? yield* resolveVersionAffinity(affinity)
|
|
3687
|
+
: undefined;
|
|
3688
|
+
let hostsByZone: Map<string, AffinityZoneHost[]> | undefined;
|
|
3689
|
+
if (resolvedAffinity !== undefined) {
|
|
3690
|
+
hostsByZone = new Map();
|
|
3691
|
+
const addHost = (zoneId: string, host: AffinityZoneHost) => {
|
|
3692
|
+
const hosts = hostsByZone!.get(zoneId) ?? [];
|
|
3693
|
+
hosts.push(host);
|
|
3694
|
+
hostsByZone!.set(zoneId, hosts);
|
|
3695
|
+
};
|
|
3696
|
+
// Serving hostnames from *observed* domain attachments (covers
|
|
3697
|
+
// managed, unmanaged, and adopted domains alike); redirect
|
|
3698
|
+
// hostnames 301 before the Worker and never need a key.
|
|
3699
|
+
const redirectHosts = new Set(effectiveDomain?.redirects ?? []);
|
|
3700
|
+
const attached = yield* workers
|
|
3701
|
+
.listDomains({ accountId, service: name })
|
|
3702
|
+
.pipe(Effect.map((r) => r.result ?? []));
|
|
3703
|
+
for (const d of attached) {
|
|
3704
|
+
if (!d.hostname || !d.zoneId || redirectHosts.has(d.hostname)) {
|
|
3705
|
+
continue;
|
|
3706
|
+
}
|
|
3707
|
+
addHost(d.zoneId, { host: d.hostname, wildcard: false });
|
|
3708
|
+
}
|
|
3709
|
+
for (const route of routes) {
|
|
3710
|
+
const host = route.pattern.split("/")[0];
|
|
3711
|
+
if (!host || host.includes('"')) continue;
|
|
3712
|
+
addHost(route.zoneId, { host, wildcard: host.includes("*") });
|
|
3713
|
+
}
|
|
3714
|
+
if (hostsByZone.size === 0) {
|
|
3715
|
+
return yield* Effect.fail(
|
|
3716
|
+
new WorkerVersionConfigError({
|
|
3717
|
+
message:
|
|
3718
|
+
`version.affinity pins users via a zone Transform Rule setting the ${AFFINITY_HEADER} header, which only sees zone traffic — give '${name}' a custom domain (\`domain\`) or zone \`routes\`. ` +
|
|
3719
|
+
`On the bare workers.dev URL, clients must send the header themselves.`,
|
|
3720
|
+
}),
|
|
3721
|
+
);
|
|
3722
|
+
}
|
|
3723
|
+
yield* session.note("Reconciling version-affinity rules ...");
|
|
3724
|
+
}
|
|
3725
|
+
const placed = yield* reconcileAffinityRules({
|
|
3726
|
+
scriptName: name,
|
|
3727
|
+
desired:
|
|
3728
|
+
resolvedAffinity !== undefined && hostsByZone !== undefined
|
|
3729
|
+
? { affinity: resolvedAffinity, hostsByZone }
|
|
3730
|
+
: undefined,
|
|
3731
|
+
previousZoneIds: previousAffinityZoneIds,
|
|
3732
|
+
});
|
|
3733
|
+
affinityZoneIds = placed.length > 0 ? placed : undefined;
|
|
3734
|
+
}
|
|
3735
|
+
const desiredCrons = normalizeCrons([
|
|
3736
|
+
...getCronBindings(bindings),
|
|
3737
|
+
...(news.crons ?? []),
|
|
3738
|
+
]);
|
|
3739
|
+
const previousCrons = output?.crons ?? [];
|
|
3740
|
+
// Same gating as read: skip getScriptSchedule when neither props nor
|
|
3741
|
+
// prior state indicate cron management (#926).
|
|
3742
|
+
const crons =
|
|
3743
|
+
desiredCrons.length > 0 || previousCrons.length > 0
|
|
3744
|
+
? yield* reconcileCrons(name, desiredCrons, previousCrons, session)
|
|
3745
|
+
: [];
|
|
2033
3746
|
return {
|
|
2034
3747
|
workerId: worker.id ?? name,
|
|
2035
3748
|
workerName: name,
|
|
2036
3749
|
namespace: undefined,
|
|
2037
3750
|
logpush: worker.logpush ?? undefined,
|
|
2038
|
-
url:
|
|
3751
|
+
url: urls[0],
|
|
3752
|
+
urls,
|
|
3753
|
+
domain: effectiveDomain,
|
|
2039
3754
|
tags: settings.tags ?? metadata.tags,
|
|
2040
3755
|
durableObjectNamespaces,
|
|
2041
3756
|
accountId,
|
|
2042
|
-
domains,
|
|
2043
3757
|
routes,
|
|
2044
3758
|
crons,
|
|
3759
|
+
versionOf: undefined,
|
|
3760
|
+
versionId,
|
|
3761
|
+
deploymentId,
|
|
3762
|
+
affinityZoneIds,
|
|
2045
3763
|
hash,
|
|
2046
3764
|
} satisfies Worker["Attributes"];
|
|
2047
3765
|
});
|
|
@@ -2060,11 +3778,34 @@ export const LiveWorkerProvider = () =>
|
|
|
2060
3778
|
// unresolved: the hash can't be computed deterministically here, and
|
|
2061
3779
|
// the eventual apply stores it once bindings resolve.
|
|
2062
3780
|
if (bindings) {
|
|
3781
|
+
// For a version worker, `Worker.URL` resolves to the *aliased
|
|
3782
|
+
// preview URL* (see `putWorkerVersion`) — recompute the same
|
|
3783
|
+
// value here so the metadata hash matches what the apply stored
|
|
3784
|
+
// and unchanged deploys stay noops.
|
|
3785
|
+
const versionParent =
|
|
3786
|
+
props.version?.parent != null
|
|
3787
|
+
? (resolveVersionParentName(props.version) ?? output.versionOf)
|
|
3788
|
+
: undefined;
|
|
3789
|
+
const selfUrl = hasSelfUrlBinding(bindings)
|
|
3790
|
+
? versionParent !== undefined
|
|
3791
|
+
? yield* Effect.gen(function* () {
|
|
3792
|
+
const alias = yield* resolveVersionAlias(
|
|
3793
|
+
id,
|
|
3794
|
+
props,
|
|
3795
|
+
versionParent,
|
|
3796
|
+
);
|
|
3797
|
+
return alias !== undefined
|
|
3798
|
+
? `https://${alias}-${versionParent}.${yield* getAccountSubdomain(accountId)}.workers.dev`
|
|
3799
|
+
: undefined;
|
|
3800
|
+
})
|
|
3801
|
+
: yield* resolveSelfUrl(output.workerName, props, accountId)
|
|
3802
|
+
: undefined;
|
|
2063
3803
|
const metadataHash = yield* resolveWorkerMetadataHash({
|
|
2064
3804
|
props,
|
|
2065
3805
|
bindings,
|
|
2066
3806
|
accountId,
|
|
2067
3807
|
stack: { name: stack.name, stage: stack.stage },
|
|
3808
|
+
selfUrl,
|
|
2068
3809
|
});
|
|
2069
3810
|
if (metadataHash !== output.hash?.metadata) {
|
|
2070
3811
|
return true;
|
|
@@ -2094,6 +3835,25 @@ export const LiveWorkerProvider = () =>
|
|
|
2094
3835
|
);
|
|
2095
3836
|
return hash !== output.hash?.input;
|
|
2096
3837
|
}
|
|
3838
|
+
// Assets-only Worker — there is no bundle to hash. A stored bundle
|
|
3839
|
+
// hash means the Worker previously had a script and is being
|
|
3840
|
+
// converted to assets-only, which must deploy.
|
|
3841
|
+
if (props.main === undefined) {
|
|
3842
|
+
if (output.hash?.bundle !== undefined) {
|
|
3843
|
+
return true;
|
|
3844
|
+
}
|
|
3845
|
+
const assetsHash =
|
|
3846
|
+
props.assets && Predicate.hasProperty(props.assets, "hash")
|
|
3847
|
+
? props.assets.hash
|
|
3848
|
+
: undefined;
|
|
3849
|
+
if (assetsHash === undefined) {
|
|
3850
|
+
// Same conservative rule as the directory-shaped `assets` below:
|
|
3851
|
+
// don't read the directory during diff; `putWorker` reads once
|
|
3852
|
+
// and keeps the existing manifest if nothing actually changed.
|
|
3853
|
+
return true;
|
|
3854
|
+
}
|
|
3855
|
+
return assetsHash !== output.hash?.assets;
|
|
3856
|
+
}
|
|
2097
3857
|
const bundleHash = yield* prepareBundle(id, props).pipe(
|
|
2098
3858
|
Effect.map((b) => b.hash),
|
|
2099
3859
|
);
|
|
@@ -2171,7 +3931,8 @@ export const LiveWorkerProvider = () =>
|
|
|
2171
3931
|
url: undefined,
|
|
2172
3932
|
tags: script.tags ?? undefined,
|
|
2173
3933
|
durableObjectNamespaces: {},
|
|
2174
|
-
|
|
3934
|
+
urls: [],
|
|
3935
|
+
domain: undefined,
|
|
2175
3936
|
routes: [],
|
|
2176
3937
|
crons: [],
|
|
2177
3938
|
},
|
|
@@ -2213,6 +3974,27 @@ export const LiveWorkerProvider = () =>
|
|
|
2213
3974
|
if (newNamespace !== oldNamespace) {
|
|
2214
3975
|
return { action: "replace" };
|
|
2215
3976
|
}
|
|
3977
|
+
// A version worker (version.parent) and a script-owning Worker are
|
|
3978
|
+
// distinct cloud resources, and a version belongs to exactly one
|
|
3979
|
+
// parent script — switching mode or parent is a replacement.
|
|
3980
|
+
const newIsVersion = news.version?.parent != null;
|
|
3981
|
+
const oldIsVersion =
|
|
3982
|
+
output?.versionOf !== undefined || olds?.version?.parent != null;
|
|
3983
|
+
if (newIsVersion !== oldIsVersion) {
|
|
3984
|
+
return { action: "replace" };
|
|
3985
|
+
}
|
|
3986
|
+
if (newIsVersion) {
|
|
3987
|
+
const newParent = resolveVersionParentName(news.version);
|
|
3988
|
+
const oldParent =
|
|
3989
|
+
output?.versionOf ?? resolveVersionParentName(olds?.version);
|
|
3990
|
+
if (
|
|
3991
|
+
newParent !== undefined &&
|
|
3992
|
+
oldParent !== undefined &&
|
|
3993
|
+
newParent !== oldParent
|
|
3994
|
+
) {
|
|
3995
|
+
return { action: "replace" };
|
|
3996
|
+
}
|
|
3997
|
+
}
|
|
2216
3998
|
const oldWorkerName =
|
|
2217
3999
|
output?.workerName ?? (yield* createWorkerName(id, olds?.name));
|
|
2218
4000
|
// Auto-generated names are engine-owned: the deployed name stays
|
|
@@ -2227,15 +4009,23 @@ export const LiveWorkerProvider = () =>
|
|
|
2227
4009
|
if (!output) {
|
|
2228
4010
|
return;
|
|
2229
4011
|
}
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
const
|
|
2234
|
-
|
|
2235
|
-
|
|
4012
|
+
// Compare the full domain config — name, aliases (ordered: they
|
|
4013
|
+
// drive `urls` order), and redirects — against persisted state
|
|
4014
|
+
// (with legacy `domains`-list state migrated on the fly).
|
|
4015
|
+
const newDomainConfig = yield* resolveWorkerDomain(news.domain);
|
|
4016
|
+
const oldDomainConfig = stateWorkerDomain(output);
|
|
4017
|
+
const domainKey = (d: ResolvedWorkerDomain | undefined) =>
|
|
4018
|
+
d === undefined
|
|
4019
|
+
? ""
|
|
4020
|
+
: JSON.stringify([d.name, d.aliases, [...d.redirects].sort()]);
|
|
4021
|
+
// An omitted `domain` unmanages the surface (#942): reconcile
|
|
4022
|
+
// carries previously-observed custom domains forward in state, so
|
|
4023
|
+
// comparing the empty desired config against those would report a
|
|
4024
|
+
// dirty plan on every deploy, forever. Only a declared `domain`
|
|
4025
|
+
// (including `null`, the explicit detach-all) participates.
|
|
2236
4026
|
const domainsChanged =
|
|
2237
|
-
|
|
2238
|
-
|
|
4027
|
+
news.domain !== undefined &&
|
|
4028
|
+
domainKey(newDomainConfig) !== domainKey(oldDomainConfig);
|
|
2239
4029
|
const newCrons = normalizeCrons([
|
|
2240
4030
|
...(Array.isArray(newBindings)
|
|
2241
4031
|
? getCronBindings(
|
|
@@ -2255,25 +4045,30 @@ export const LiveWorkerProvider = () =>
|
|
|
2255
4045
|
const routesChanged =
|
|
2256
4046
|
newRouteKeys.length !== oldRouteKeys.length ||
|
|
2257
4047
|
newRouteKeys.some((key, index) => key !== oldRouteKeys[index]);
|
|
2258
|
-
// `url` is `
|
|
2259
|
-
//
|
|
2260
|
-
//
|
|
2261
|
-
//
|
|
2262
|
-
//
|
|
2263
|
-
//
|
|
2264
|
-
//
|
|
2265
|
-
//
|
|
2266
|
-
//
|
|
2267
|
-
//
|
|
2268
|
-
//
|
|
2269
|
-
//
|
|
2270
|
-
const
|
|
2271
|
-
const
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
4048
|
+
// `url` is `urls[0]`: the canonical custom domain when one is
|
|
4049
|
+
// configured (or carried forward from state when the surface is
|
|
4050
|
+
// unmanaged, #942/#975), else the stable workers.dev URL. It's
|
|
4051
|
+
// stable across this update exactly when the recomputed value
|
|
4052
|
+
// matches what's deployed. Carry it forward as a stable only
|
|
4053
|
+
// then, so downstream resources that reference `worker.url`
|
|
4054
|
+
// (e.g. a GitHub Webhook delivery URL built via
|
|
4055
|
+
// `Output.interpolate`) resolve it to a concrete value during
|
|
4056
|
+
// planning instead of an unresolved Output — otherwise every
|
|
4057
|
+
// worker update spuriously re-updates them. Previews-only mode
|
|
4058
|
+
// and version workers derive `url` from version preview URLs —
|
|
4059
|
+
// never assumed stable here.
|
|
4060
|
+
const newWorkersDev = resolveWorkersDev(news.workersDev);
|
|
4061
|
+
const effectiveDomainConfig =
|
|
4062
|
+
news.domain !== undefined ? newDomainConfig : oldDomainConfig;
|
|
4063
|
+
const newUrl = newIsVersion
|
|
4064
|
+
? undefined
|
|
4065
|
+
: effectiveDomainConfig !== undefined
|
|
4066
|
+
? `https://${effectiveDomainConfig.name}`
|
|
4067
|
+
: newWorkersDev.enabled
|
|
4068
|
+
? (output.urls ?? []).find(
|
|
4069
|
+
(u) =>
|
|
4070
|
+
isWorkersDevHostname(urlHostname(u)) &&
|
|
4071
|
+
urlHostname(u).startsWith(`${workerName}.`),
|
|
2277
4072
|
)
|
|
2278
4073
|
: undefined;
|
|
2279
4074
|
const urlStable = newUrl !== undefined && newUrl === output.url;
|
|
@@ -2342,6 +4137,32 @@ export const LiveWorkerProvider = () =>
|
|
|
2342
4137
|
precreate: Effect.fn(function* ({ id, news, session, bindings }) {
|
|
2343
4138
|
const { accountId } = yield* yield* CloudflareEnvironment;
|
|
2344
4139
|
const name = yield* createWorkerName(id, news.name);
|
|
4140
|
+
// A version worker uploads to its parent's script during
|
|
4141
|
+
// reconcile; pre-creating a placeholder script under this
|
|
4142
|
+
// resource's own generated name would leave a stray script behind.
|
|
4143
|
+
// Version workers can't host Durable Object / Workflow classes, so
|
|
4144
|
+
// no placeholder is needed for circular bindings either. (`news`
|
|
4145
|
+
// is raw here — `version.parent` may be an unresolved ref, but its
|
|
4146
|
+
// *presence* is statically known.)
|
|
4147
|
+
if (news.version?.parent != null) {
|
|
4148
|
+
yield* Effect.logInfo(
|
|
4149
|
+
`Cloudflare Worker precreate: skipping stub for version worker ${id}`,
|
|
4150
|
+
);
|
|
4151
|
+
return {
|
|
4152
|
+
workerId: name,
|
|
4153
|
+
workerName: name,
|
|
4154
|
+
namespace: undefined,
|
|
4155
|
+
logpush: undefined,
|
|
4156
|
+
url: undefined,
|
|
4157
|
+
tags: undefined,
|
|
4158
|
+
durableObjectNamespaces: {},
|
|
4159
|
+
accountId,
|
|
4160
|
+
urls: [],
|
|
4161
|
+
domain: undefined,
|
|
4162
|
+
routes: [],
|
|
4163
|
+
crons: [],
|
|
4164
|
+
} satisfies Worker["Attributes"];
|
|
4165
|
+
}
|
|
2345
4166
|
// A Workers for Platforms user worker can't be pre-created: precreate
|
|
2346
4167
|
// runs on raw, *unresolved* props (so resources in a dependency cycle
|
|
2347
4168
|
// can signal early), meaning a `namespace` that references the
|
|
@@ -2364,7 +4185,8 @@ export const LiveWorkerProvider = () =>
|
|
|
2364
4185
|
tags: undefined,
|
|
2365
4186
|
durableObjectNamespaces: {},
|
|
2366
4187
|
accountId,
|
|
2367
|
-
|
|
4188
|
+
urls: [],
|
|
4189
|
+
domain: undefined,
|
|
2368
4190
|
routes: [],
|
|
2369
4191
|
crons: [],
|
|
2370
4192
|
} satisfies Worker["Attributes"];
|
|
@@ -2459,6 +4281,7 @@ export const LiveWorkerProvider = () =>
|
|
|
2459
4281
|
);
|
|
2460
4282
|
} else {
|
|
2461
4283
|
yield* session.note("Pre-creating worker...");
|
|
4284
|
+
const compatibility = getCompatibility(news);
|
|
2462
4285
|
const mainModule = "main.js";
|
|
2463
4286
|
const placeholderScript = `${doClasses.length > 0 ? 'import { DurableObject } from "cloudflare:workers";\n\n' : ""}export default { fetch() { return new Response("Alchemy worker is being deployed...") } };\n${doClasses
|
|
2464
4287
|
.map(
|
|
@@ -2480,7 +4303,11 @@ export const LiveWorkerProvider = () =>
|
|
|
2480
4303
|
className,
|
|
2481
4304
|
}))
|
|
2482
4305
|
: undefined,
|
|
2483
|
-
|
|
4306
|
+
// Spreading `getCompatibility` here would set `date`/`flags`,
|
|
4307
|
+
// which are not metadata keys — the placeholder would upload
|
|
4308
|
+
// with no compatibility date or flags at all.
|
|
4309
|
+
compatibilityDate: compatibility.date,
|
|
4310
|
+
compatibilityFlags: compatibility.flags,
|
|
2484
4311
|
containers,
|
|
2485
4312
|
migrations:
|
|
2486
4313
|
doClasses.length > 0
|
|
@@ -2554,7 +4381,8 @@ export const LiveWorkerProvider = () =>
|
|
|
2554
4381
|
tags: existingSettings?.tags ?? tags,
|
|
2555
4382
|
durableObjectNamespaces,
|
|
2556
4383
|
accountId,
|
|
2557
|
-
|
|
4384
|
+
urls: [],
|
|
4385
|
+
domain: undefined,
|
|
2558
4386
|
routes: [],
|
|
2559
4387
|
crons: [],
|
|
2560
4388
|
} satisfies Worker["Attributes"];
|
|
@@ -2562,6 +4390,34 @@ export const LiveWorkerProvider = () =>
|
|
|
2562
4390
|
read: Effect.fn(
|
|
2563
4391
|
function* ({ id, output, olds }) {
|
|
2564
4392
|
const { accountId } = yield* yield* CloudflareEnvironment;
|
|
4393
|
+
// Version workers don't own a script — their `workerName` is the
|
|
4394
|
+
// *parent's* script, so the normal read below would hydrate (and
|
|
4395
|
+
// potentially brand as adoptable) a resource we don't own. The
|
|
4396
|
+
// version itself is immutable; all we verify is that it still
|
|
4397
|
+
// exists on the parent.
|
|
4398
|
+
if (
|
|
4399
|
+
output?.versionOf !== undefined ||
|
|
4400
|
+
olds?.version?.parent != null
|
|
4401
|
+
) {
|
|
4402
|
+
if (output?.versionOf === undefined || !output.versionId) {
|
|
4403
|
+
// No recorded version (e.g. state was written before the
|
|
4404
|
+
// upload completed) — nothing to observe; reconcile will
|
|
4405
|
+
// upload a fresh version.
|
|
4406
|
+
return undefined;
|
|
4407
|
+
}
|
|
4408
|
+
return yield* workers
|
|
4409
|
+
.getScriptVersion({
|
|
4410
|
+
accountId,
|
|
4411
|
+
scriptName: output.versionOf,
|
|
4412
|
+
versionId: output.versionId,
|
|
4413
|
+
})
|
|
4414
|
+
.pipe(
|
|
4415
|
+
Effect.map(() => output),
|
|
4416
|
+
Effect.catchTag(["WorkerNotFound", "VersionNotFound"], () =>
|
|
4417
|
+
Effect.succeed(undefined),
|
|
4418
|
+
),
|
|
4419
|
+
);
|
|
4420
|
+
}
|
|
2565
4421
|
const workerName =
|
|
2566
4422
|
output?.workerName ?? (yield* createWorkerName(id, olds?.name));
|
|
2567
4423
|
const dispatchNamespace =
|
|
@@ -2591,7 +4447,8 @@ export const LiveWorkerProvider = () =>
|
|
|
2591
4447
|
url: undefined,
|
|
2592
4448
|
tags: settings.tags ?? undefined,
|
|
2593
4449
|
durableObjectNamespaces: getDurableObjects(settings.bindings),
|
|
2594
|
-
|
|
4450
|
+
urls: [],
|
|
4451
|
+
domain: undefined,
|
|
2595
4452
|
routes: [],
|
|
2596
4453
|
crons: [],
|
|
2597
4454
|
} satisfies Worker["Attributes"];
|
|
@@ -2608,6 +4465,18 @@ export const LiveWorkerProvider = () =>
|
|
|
2608
4465
|
// `WorkerNotFound` if the script doesn't exist, which the
|
|
2609
4466
|
// surrounding `Effect.catchTag` turns into `undefined` — that's
|
|
2610
4467
|
// all the existence check we need.
|
|
4468
|
+
//
|
|
4469
|
+
// Domain / route / cron observation is gated on whether Alchemy
|
|
4470
|
+
// manages that surface for this Worker (#926). A plain workers.dev
|
|
4471
|
+
// Worker otherwise pays for listDomains + zone route listings +
|
|
4472
|
+
// getScriptSchedule on every plan/deploy read. Route observation
|
|
4473
|
+
// is additionally scoped to the zones state already associates
|
|
4474
|
+
// with this Worker — see readWorkerRoutes. Empty-array props
|
|
4475
|
+
// (`domain: []`, `routes: []`, `crons: []`) still observe so we
|
|
4476
|
+
// can detect drift and converge deletions.
|
|
4477
|
+
const observeDomains = shouldObserveWorkerDomains(olds, output);
|
|
4478
|
+
const observeRoutes = shouldObserveWorkerRoutes(olds, output);
|
|
4479
|
+
const observeCrons = shouldObserveWorkerCrons(olds, output);
|
|
2611
4480
|
const [subdomain, settings, domainsList, routesList] =
|
|
2612
4481
|
yield* Effect.all(
|
|
2613
4482
|
[
|
|
@@ -2619,41 +4488,73 @@ export const LiveWorkerProvider = () =>
|
|
|
2619
4488
|
accountId,
|
|
2620
4489
|
scriptName: workerName,
|
|
2621
4490
|
}),
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
4491
|
+
observeDomains
|
|
4492
|
+
? workers
|
|
4493
|
+
.listDomains({
|
|
4494
|
+
accountId,
|
|
4495
|
+
service: workerName,
|
|
4496
|
+
})
|
|
4497
|
+
.pipe(Effect.map((r) => r.result ?? []))
|
|
4498
|
+
: Effect.succeed(
|
|
4499
|
+
[] as workers.ListDomainsResponse["result"],
|
|
4500
|
+
),
|
|
4501
|
+
observeRoutes
|
|
4502
|
+
? readWorkerRoutes(workerName, output?.routes)
|
|
4503
|
+
: Effect.succeed([] as Worker["Attributes"]["routes"]),
|
|
2629
4504
|
],
|
|
2630
|
-
|
|
4505
|
+
// Bound concurrency so a single Worker read doesn't stampede
|
|
4506
|
+
// the account alongside every other Worker's lifecycle calls.
|
|
4507
|
+
{ concurrency: 1 },
|
|
2631
4508
|
);
|
|
2632
|
-
//
|
|
2633
|
-
//
|
|
2634
|
-
//
|
|
2635
|
-
//
|
|
2636
|
-
//
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
4509
|
+
// Classify the observed hostnames using the declared config
|
|
4510
|
+
// (`olds.domain`): the canonical name and each alias/redirect
|
|
4511
|
+
// keep their declared role while still attached; drift (attached
|
|
4512
|
+
// hostnames we don't know about) is appended to the aliases. The
|
|
4513
|
+
// Cloudflare API returns domains in non-deterministic order, so
|
|
4514
|
+
// the declared order is what keeps `url`/`urls` from flipping
|
|
4515
|
+
// between reads.
|
|
4516
|
+
const declared = yield* resolveWorkerDomain(olds?.domain).pipe(
|
|
4517
|
+
Effect.orElseSucceed(() => undefined),
|
|
4518
|
+
);
|
|
4519
|
+
const observed = new Set(
|
|
4520
|
+
domainsList.flatMap((d) => (d.hostname ? [d.hostname] : [])),
|
|
4521
|
+
);
|
|
4522
|
+
const keptName =
|
|
4523
|
+
declared !== undefined && observed.has(declared.name)
|
|
4524
|
+
? declared.name
|
|
4525
|
+
: undefined;
|
|
4526
|
+
const keptAliases =
|
|
4527
|
+
declared?.aliases.filter((h) => observed.has(h)) ?? [];
|
|
4528
|
+
const keptRedirects =
|
|
4529
|
+
declared?.redirects.filter((h) => observed.has(h)) ?? [];
|
|
4530
|
+
const classified = new Set([
|
|
4531
|
+
...(keptName ? [keptName] : []),
|
|
4532
|
+
...keptAliases,
|
|
4533
|
+
...keptRedirects,
|
|
4534
|
+
]);
|
|
4535
|
+
const drift = [...observed].filter((h) => !classified.has(h));
|
|
4536
|
+
const serving = [
|
|
4537
|
+
...(keptName ? [keptName] : []),
|
|
4538
|
+
...keptAliases,
|
|
4539
|
+
...drift,
|
|
2648
4540
|
];
|
|
2649
|
-
const
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
4541
|
+
const observedDomain =
|
|
4542
|
+
serving.length > 0
|
|
4543
|
+
? {
|
|
4544
|
+
name: serving[0],
|
|
4545
|
+
aliases: serving.slice(1),
|
|
4546
|
+
redirects: keptRedirects,
|
|
4547
|
+
}
|
|
4548
|
+
: undefined;
|
|
4549
|
+
const urls = [
|
|
4550
|
+
...serving.map((h) => `https://${h}`),
|
|
4551
|
+
...(subdomain.enabled
|
|
4552
|
+
? [
|
|
4553
|
+
`https://${workerName}.${yield* getAccountSubdomain(accountId)}.workers.dev`,
|
|
4554
|
+
]
|
|
4555
|
+
: []),
|
|
2655
4556
|
];
|
|
2656
|
-
const crons = yield* getWorkerCrons(workerName);
|
|
4557
|
+
const crons = observeCrons ? yield* getWorkerCrons(workerName) : [];
|
|
2657
4558
|
yield* Effect.logInfo(
|
|
2658
4559
|
`Cloudflare Worker read: found ${workerName}`,
|
|
2659
4560
|
);
|
|
@@ -2663,12 +4564,17 @@ export const LiveWorkerProvider = () =>
|
|
|
2663
4564
|
workerName,
|
|
2664
4565
|
namespace: undefined,
|
|
2665
4566
|
logpush: settings.logpush ?? undefined,
|
|
2666
|
-
url:
|
|
4567
|
+
url: urls[0],
|
|
4568
|
+
urls,
|
|
4569
|
+
domain: observedDomain,
|
|
2667
4570
|
tags: settings.tags ?? undefined,
|
|
2668
4571
|
durableObjectNamespaces: getDurableObjects(settings.bindings),
|
|
2669
|
-
domains,
|
|
2670
4572
|
routes: routesList,
|
|
2671
4573
|
crons,
|
|
4574
|
+
// Rule placement is provider-managed state, not observed here
|
|
4575
|
+
// (a getPhas call per known zone on every read); carry the
|
|
4576
|
+
// cleanup list forward like any other stable cache.
|
|
4577
|
+
affinityZoneIds: output?.affinityZoneIds,
|
|
2672
4578
|
} satisfies Worker["Attributes"];
|
|
2673
4579
|
|
|
2674
4580
|
// Centralized ownership decision: the engine routes `read`'s
|
|
@@ -2709,6 +4615,12 @@ export const LiveWorkerProvider = () =>
|
|
|
2709
4615
|
output,
|
|
2710
4616
|
session,
|
|
2711
4617
|
}) {
|
|
4618
|
+
// A version worker uploads an immutable version to its parent's
|
|
4619
|
+
// script instead of owning a script of its own — none of the
|
|
4620
|
+
// script-level observation below applies.
|
|
4621
|
+
if (news.version?.parent != null) {
|
|
4622
|
+
return yield* putWorkerVersion(id, news, bindings, session, output);
|
|
4623
|
+
}
|
|
2712
4624
|
const { accountId } = yield* yield* CloudflareEnvironment;
|
|
2713
4625
|
const name =
|
|
2714
4626
|
output?.workerName ?? (yield* createWorkerName(id, news.name));
|
|
@@ -2777,6 +4689,70 @@ export const LiveWorkerProvider = () =>
|
|
|
2777
4689
|
);
|
|
2778
4690
|
}),
|
|
2779
4691
|
delete: Effect.fn(function* ({ output }) {
|
|
4692
|
+
// Version workers own a version, not the script — `workerName` is
|
|
4693
|
+
// the *parent's* script and must never be deleted here. Versions
|
|
4694
|
+
// can't be deleted through the API (they age out of Cloudflare's
|
|
4695
|
+
// retention window); all we clean up is live traffic: if the
|
|
4696
|
+
// current deployment routes traffic to our version, restore 100%
|
|
4697
|
+
// to the other version in the split.
|
|
4698
|
+
if (output.versionOf !== undefined) {
|
|
4699
|
+
// Remove our affinity rules from the parent's zones — the
|
|
4700
|
+
// canary owned them, and with the canary gone there is no
|
|
4701
|
+
// split left to pin. Best-effort: a zone we can no longer
|
|
4702
|
+
// touch must not fail the destroy.
|
|
4703
|
+
if (output.affinityZoneIds?.length) {
|
|
4704
|
+
yield* reconcileAffinityRules({
|
|
4705
|
+
scriptName: output.versionOf,
|
|
4706
|
+
desired: undefined,
|
|
4707
|
+
previousZoneIds: output.affinityZoneIds,
|
|
4708
|
+
}).pipe(Effect.catch(() => Effect.void));
|
|
4709
|
+
}
|
|
4710
|
+
if (!output.versionId) return;
|
|
4711
|
+
yield* Effect.logInfo(
|
|
4712
|
+
`Cloudflare Worker delete: releasing version ${output.versionId} of ${output.versionOf}`,
|
|
4713
|
+
);
|
|
4714
|
+
const { deployments } = yield* workers
|
|
4715
|
+
.listScriptDeployments({
|
|
4716
|
+
accountId: output.accountId,
|
|
4717
|
+
scriptName: output.versionOf,
|
|
4718
|
+
})
|
|
4719
|
+
.pipe(
|
|
4720
|
+
Effect.catchTag("WorkerNotFound", () =>
|
|
4721
|
+
Effect.succeed({
|
|
4722
|
+
deployments:
|
|
4723
|
+
[] as workers.ListScriptDeploymentsResponse["deployments"],
|
|
4724
|
+
}),
|
|
4725
|
+
),
|
|
4726
|
+
);
|
|
4727
|
+
const latest = deployments[0];
|
|
4728
|
+
if (
|
|
4729
|
+
!latest?.versions.some((v) => v.versionId === output.versionId)
|
|
4730
|
+
) {
|
|
4731
|
+
// Not part of the live deployment — the version just ages out.
|
|
4732
|
+
return;
|
|
4733
|
+
}
|
|
4734
|
+
const stable = latest.versions
|
|
4735
|
+
.filter((v) => v.versionId !== output.versionId)
|
|
4736
|
+
.sort((a, b) => b.percentage - a.percentage)[0];
|
|
4737
|
+
if (!stable) {
|
|
4738
|
+
// Our version holds 100% of traffic; there is no other version
|
|
4739
|
+
// to restore. Deleting the resource must not take the parent's
|
|
4740
|
+
// script down — leave the deployment in place.
|
|
4741
|
+
yield* Effect.logWarning(
|
|
4742
|
+
`Cloudflare Worker delete: version ${output.versionId} serves 100% of '${output.versionOf}' traffic; leaving the deployment as-is. Re-deploy the parent to move off it.`,
|
|
4743
|
+
);
|
|
4744
|
+
return;
|
|
4745
|
+
}
|
|
4746
|
+
yield* workers
|
|
4747
|
+
.createScriptDeployment({
|
|
4748
|
+
accountId: output.accountId,
|
|
4749
|
+
scriptName: output.versionOf,
|
|
4750
|
+
strategy: "percentage",
|
|
4751
|
+
versions: [{ versionId: stable.versionId, percentage: 100 }],
|
|
4752
|
+
})
|
|
4753
|
+
.pipe(Effect.catchTag("WorkerNotFound", () => Effect.void));
|
|
4754
|
+
return;
|
|
4755
|
+
}
|
|
2780
4756
|
yield* Effect.logInfo(
|
|
2781
4757
|
`Cloudflare Worker delete: deleting ${output.workerName}`,
|
|
2782
4758
|
);
|
|
@@ -2811,6 +4787,36 @@ export const LiveWorkerProvider = () =>
|
|
|
2811
4787
|
Effect.map((r) => r.result ?? []),
|
|
2812
4788
|
Effect.catch(() => Effect.succeed([])),
|
|
2813
4789
|
);
|
|
4790
|
+
// Remove our redirect rules from each affected zone's dynamic
|
|
4791
|
+
// redirect entrypoint *before* detaching the domains — the live
|
|
4792
|
+
// domain list is what resolves each redirect hostname's zone.
|
|
4793
|
+
// Reconciling with an empty domain config removes exactly the
|
|
4794
|
+
// rules tagged `alchemy:worker:<script>:redirect:*`, leaving
|
|
4795
|
+
// everything else in the shared entrypoint untouched.
|
|
4796
|
+
// Remove our version-affinity rules from every zone state says
|
|
4797
|
+
// holds them. Best-effort, like the redirect rules below.
|
|
4798
|
+
if (output.affinityZoneIds?.length) {
|
|
4799
|
+
yield* reconcileAffinityRules({
|
|
4800
|
+
scriptName: output.workerName,
|
|
4801
|
+
desired: undefined,
|
|
4802
|
+
previousZoneIds: output.affinityZoneIds,
|
|
4803
|
+
}).pipe(Effect.catch(() => Effect.void));
|
|
4804
|
+
}
|
|
4805
|
+
const redirects = stateWorkerDomain(output)?.redirects ?? [];
|
|
4806
|
+
if (redirects.length > 0) {
|
|
4807
|
+
yield* reconcileRedirectRules({
|
|
4808
|
+
scriptName: output.workerName,
|
|
4809
|
+
domain: undefined,
|
|
4810
|
+
zoneIdByHostname: new Map(
|
|
4811
|
+
liveDomains.flatMap((d) =>
|
|
4812
|
+
d.hostname && d.zoneId
|
|
4813
|
+
? [[d.hostname, d.zoneId] as const]
|
|
4814
|
+
: [],
|
|
4815
|
+
),
|
|
4816
|
+
),
|
|
4817
|
+
previousRedirects: redirects,
|
|
4818
|
+
}).pipe(Effect.catch(() => Effect.void));
|
|
4819
|
+
}
|
|
2814
4820
|
if (liveDomains.length) {
|
|
2815
4821
|
yield* Effect.all(
|
|
2816
4822
|
liveDomains.flatMap((d) =>
|