diff --git a/.vscode/settings.json b/.vscode/settings.json index 1fb40712b..615333a2f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,6 @@ { "diffEditor.renderSideBySide": false, - "js/ts.experimental.useTsgo": true, + "typescript.experimental.useTsgo": true, "typescript.enablePromptUseWorkspaceTsdk": true, "typescript.preferences.importModuleSpecifier": "relative", "typescript.preferences.importModuleSpecifierEnding": "js", diff --git a/README.md b/README.md index fd6fb1116..c90abcca7 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ export default Cloudflare.Worker( "api", { main: import.meta.filename }, Effect.gen(function* () { - const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); + const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); return { fetch: Effect.gen(function* () { const request = yield* HttpServerRequest; diff --git a/bun.lock b/bun.lock index ff86a17c6..002c6be03 100644 --- a/bun.lock +++ b/bun.lock @@ -476,6 +476,16 @@ "vite": "catalog:", }, }, + "examples/cloudflare-agent": { + "name": "cloudflare-agent", + "version": "0.0.0", + "dependencies": { + "@effect/platform-bun": "catalog:", + "@effect/platform-node": "catalog:", + "alchemy": "workspace:*", + "effect": "catalog:", + }, + }, "examples/cloudflare-dev": { "name": "cloudflare-dev", "version": "0.0.0", @@ -2514,6 +2524,8 @@ "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], + "cloudflare-agent": ["cloudflare-agent@workspace:examples/cloudflare-agent"], + "cloudflare-dev": ["cloudflare-dev@workspace:examples/cloudflare-dev"], "cloudflare-email": ["cloudflare-email@workspace:examples/cloudflare-email"], @@ -4468,8 +4480,6 @@ "@tanstack/router-core/cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="], - "@tanstack/router-generator/prettier": ["prettier@3.8.4", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q=="], - "@tanstack/router-generator/zod": ["zod@4.4.3", "", {}, "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ=="], "@tanstack/router-plugin/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="], @@ -4786,8 +4796,6 @@ "yaml-language-server/ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], - "yaml-language-server/prettier": ["prettier@3.8.4", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q=="], - "yaml-language-server/request-light": ["request-light@0.5.8", "", {}, "sha512-3Zjgh+8b5fhRJBQZoy+zbVKpAQGLyka0MPgW3zruTF4dFFJ8Fqcfu9YsAvi/rvdcaTeWG3MkbZv4WKxAn/84Lg=="], "yaml-language-server/yaml": ["yaml@2.7.1", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ=="], diff --git a/bunfig.toml b/bunfig.toml index 9cbddfe70..51b35c901 100644 --- a/bunfig.toml +++ b/bunfig.toml @@ -49,4 +49,11 @@ minimumReleaseAgeExcludes = [ "@typescript/typescript-openbsd-arm64", "@typescript/typescript-openbsd-x64", "@typescript/typescript-sunos-x64", + "@typescript/native-preview-linux-arm64", + "@typescript/native-preview-linux-x64", + "@typescript/native-preview-linux-arm", + "@typescript/native-preview-win32-x64", + "@typescript/native-preview-win32-arm64", + "@typescript/native-preview-darwin-x64", + "@typescript/native-preview-darwin-arm64", ] diff --git a/cf-temp-failures-2.txt b/cf-temp-failures-2.txt new file mode 100644 index 000000000..fa1f0c406 --- /dev/null +++ b/cf-temp-failures-2.txt @@ -0,0 +1,522 @@ +# Failed Cloudflare tests (temporary account run) +# total failed: 190 across 70 files + +test/Cloudflare/Access/Application.test.ts + - !!! create and delete a self_hosted application gated by a reusable policy + Forbidden: Requires permission "com.cloudflare.api.account.zone.create" to create zones for the selected account + +test/Cloudflare/Access/Group.test.ts + - !!! create, update rules, and delete group + Unauthorized: Authentication error + - !!! rename updates the group in place + CloudflareHttpError: null + +test/Cloudflare/Access/IdentityProvider.test.ts + - !!! changing the type replaces the IdP + TooManyRequests: Please wait and consider throttling your request speed + - !!! create, verify, and destroy an OIDC IdP + TooManyRequests: Please wait and consider throttling your request speed + - !!! list enumerates the deployed IdP + TooManyRequests: Please wait and consider throttling your request speed + - !!! update name and config in place (same id) + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Access/KeyConfiguration.test.ts + - !!! list returns the account key configuration singleton + TooManyRequests: Please wait and consider throttling your request speed + - !!! pins the rotation interval, updates in place, and restores on destroy + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Access/McpPortal.test.ts + - !!! create, update in place, and destroy an MCP portal + CloudflareHttpError: null + - !!! list enumerates the deployed MCP portal + CloudflareHttpError: null + +test/Cloudflare/Access/Organization.test.ts + - !!! Organization > list returns the account Access organization + CloudflareHttpError: null + +test/Cloudflare/Access/Policy.test.ts + - !!! adopts an out-of-band reusable policy + Unauthorized: Authentication error + - !!! create and delete basic allow policy + Unauthorized: Authentication error + - !!! list enumerates the deployed reusable policy + CloudflareHttpError: null + - !!! update mutates includes without replacing + CloudflareHttpError: null + +test/Cloudflare/Access/ServiceToken.test.ts + - !!! create, update duration, and delete service token + CloudflareHttpError: null + - !!! incrementing clientSecretVersion rotates the secret + CloudflareHttpError: null + - !!! list enumerates the deployed service token + CloudflareHttpError: null + +test/Cloudflare/Access/Tag.test.ts + - !!! create, verify out-of-band, and destroy tag + TooManyRequests: Please wait and consider throttling your request speed + - !!! list enumerates the deployed access tag + TooManyRequests: Please wait and consider throttling your request speed + - !!! rename replaces the tag + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Account/Account.test.ts + - !!! createAccount is entitlement-gated: typed AccountCreationForbidden + AssertionError: expected 'InternalServerError' to deeply equal 'AccountCreationForbidden' + - !!! list enumerates accessible accounts (read-only) + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Account/Member.test.ts + - !!! create member, update roles in place, delete + TooManyRequests: Please wait and consider throttling your request speed + - !!! list enumerates the account members + TooManyRequests: Please wait and consider throttling your request speed + - !!! recreates after out-of-band delete + TooManyRequests: Please wait and consider throttling your request speed + - !!! replaces the member when the email changes + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Acm/CustomTrustStore.test.ts + - !!! surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Addressing/Prefix.test.ts + - !!! list enumerates account prefixes (read-only) + Unauthorized: Authentication error + - !!! lists the services catalog and prefixes (read-only) + Unauthorized: Authentication error + +test/Cloudflare/Addressing/ServiceBinding.test.ts + - !!! list enumerates service bindings across prefixes (read-only) + CloudflareHttpError: null + +test/Cloudflare/AiGateway/ProviderConfig.test.ts + - !!! create, noop, replace, delete a BYOK provider config + Unauthorized: Authentication error + - !!! list enumerates the deployed provider config + Unauthorized: Authentication error + +test/Cloudflare/AiSearch/AiSearch.test.ts + - !!! construct auto-creates a managed token and wires it into the instance + Unauthorized: Authentication error + - !!! web-crawler source skips token minting + Forbidden: Authentication error + +test/Cloudflare/AiSearch/Namespace.test.ts + - !!! adopts the reserved default namespace without deleting it on teardown + Forbidden: Authentication error + - !!! changing the name triggers a replacement + Forbidden: Authentication error + - !!! create, update description in place, and delete a namespace + Forbidden: Authentication error + - !!! list enumerates the deployed namespace + Forbidden: Authentication error + - !!! recreates after out-of-band delete + Forbidden: Authentication error + +test/Cloudflare/AiSearch/Token.test.ts + - !!! an AI Search instance syncs with a stack-minted service token + Unauthorized: Authentication error + - !!! create, rename in place, and delete a service token + Unauthorized: Unauthorized to access requested resource + - !!! list enumerates the deployed service token + Unauthorized: Unauthorized to access requested resource + - !!! recreates after out-of-band delete + Unauthorized: Unauthorized to access requested resource + +test/Cloudflare/Alerting/NotificationPolicy.test.ts + - create, update, delete notification policy + CloudflareHttpError: null + - list enumerates the deployed notification policy + CloudflareHttpError: null + - replaces policy when alertType changes + CloudflareHttpError: null + +test/Cloudflare/Alerting/Silence.test.ts + - create, update window in place, delete silence + CloudflareHttpError: null + - list enumerates the deployed silence + CloudflareHttpError: null + - replaces silence when the policy changes + CloudflareHttpError: null + +test/Cloudflare/Alerting/Webhook.test.ts + - create, update, delete webhook destination + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - list enumerates the deployed webhook destination + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + +test/Cloudflare/Calls/App.test.ts + - create and delete an app with default name + TooManyRequests: Please wait and consider throttling your request speed + - list enumerates the deployed app + TooManyRequests: Please wait and consider throttling your request speed + - recreates after out-of-band delete + TooManyRequests: Please wait and consider throttling your request speed + - update name in place (same appId, secret preserved) + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Calls/TurnKey.test.ts + - !!! create and delete a TURN key with default name + Forbidden: Authentication error + - !!! list enumerates the deployed TURN key + Forbidden: Authentication error + - !!! recreates after out-of-band delete + Forbidden: Authentication error + - !!! update name in place (same keyId, key preserved) + Forbidden: Authentication error + +test/Cloudflare/CloudforceOne/ScanConfig.test.ts + - !!! unentitled accounts surface the typed Unauthorized error + AssertionError: expected 'Authentication error' to contain 'cfone.port_scan' + +test/Cloudflare/Connectivity/DirectoryService.test.ts + - http service with explicit ports and default name + TooManyRequests: Please wait and consider throttling your request speed + - list enumerates the deployed directory service + TooManyRequests: Please wait and consider throttling your request speed + - recreates after out-of-band delete + TooManyRequests: Please wait and consider throttling your request speed + - tcp service lifecycle: create, update, host switch + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Container/ContainerApplication.test.ts + - ContainerApplication > list enumerates container applications + Provider not found for undefined + +test/Cloudflare/D1/D1Binding.test.ts + - D1Connection.bind exercises the full client surface + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + +test/Cloudflare/D1/Database.test.ts + - clones a database by databaseId + Forbidden: System limit reached: databases per account (1) + - clones a database by name lookup + Forbidden: System limit reached: databases per account (1) + - clones a database by passing the source resource directly + Forbidden: System limit reached: databases per account (1) + - imports SQL files via importFiles + Forbidden: System limit reached: databases per account (1) + +test/Cloudflare/DdosProtection/AllowlistEntry.test.ts + - !!! list returns a well-typed empty array without Magic Transit + CloudflareHttpError: null + - !!! surfaces the typed AdvancedTcpProtectionNotEntitled error without Magic Transit + AssertionError: expected 'CloudflareHttpError' to deeply equal 'AdvancedTcpProtectionNotEntitled' + +test/Cloudflare/Devices/CustomProfile.test.ts + - !!! create, update in place, and delete a custom device profile + TooManyRequests: Please wait and consider throttling your request speed + - !!! list enumerates the deployed custom device profile + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Devices/DefaultProfile.test.ts + - !!! DefaultProfile > list returns the singleton default device profile + Unauthorized: Authentication error + +test/Cloudflare/Devices/DexTest.test.ts + - !!! list returns a well-typed empty array on unentitled accounts + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Devices/PostureIntegration.test.ts + - !!! creates without a real provider surface the typed InvalidPostureIntegrationConfig error + CloudflareHttpError: null + +test/Cloudflare/Diagnostics/EndpointHealthcheck.test.ts + - !!! creates an endpoint healthcheck, updates in place, and destroys + Forbidden: Authentication error + - !!! list enumerates the deployed endpoint healthcheck + Forbidden: Authentication error + - !!! rejects public IPs with the typed InvalidHealthcheckEndpoint error + AssertionError: expected 'CloudflareHttpError' to deeply equal 'InvalidHealthcheckEndpoint' + +test/Cloudflare/Dlp/Entry.test.ts + - !!! list returns the account's custom DLP entries + Unauthorized: Authentication error + +test/Cloudflare/Dns/ZoneTransferPeer.test.ts + - !!! create with connection settings, update in place, and delete a peer + Unauthorized: Authentication error + - !!! list enumerates the deployed peer + Unauthorized: Authentication error + - !!! wires a TSIG reference through to the peer + Unauthorized: Authentication error + +test/Cloudflare/Dns/ZoneTransferTsig.test.ts + - !!! create, rotate the secret in place, and delete a TSIG + Unauthorized: Authentication error + - !!! list enumerates the deployed TSIG + Unauthorized: Authentication error + +test/Cloudflare/DnsFirewall/DnsFirewall.test.ts + - !!! list returns the DNS firewall cluster Attributes array + TooManyRequests: Please wait and consider throttling your request speed + - !!! surfaces the typed DnsFirewallNotEntitled error on unentitled accounts + AssertionError: expected 'TooManyRequests' to deeply equal 'DnsFirewallNotEntitled' + +test/Cloudflare/Email/EmailAddress.test.ts + - !!! list enumerates the deployed email address + CloudflareHttpError: null + +test/Cloudflare/Gateway/Certificate.test.ts + - !!! create an activated certificate, deactivate in place, destroy + CloudflareHttpError: null + - !!! list enumerates the deployed gateway certificate + CloudflareHttpError: null + +test/Cloudflare/Gateway/Configuration.test.ts + - !!! list returns the account Gateway configuration singleton + Forbidden: Authentication error + - !!! manage activityLog and protocolDetection, then restore on destroy + CloudflareHttpError: null + +test/Cloudflare/Hyperdrive/Hyperdrive.test.ts + - list enumerates the deployed hyperdrive + BadRequest: This account has reached its limit for how many Hyperdrives it can have (2) + +test/Cloudflare/Intel/IndicatorFeed.test.ts + - list returns a well-typed array of indicator feeds + CloudflareHttpError: null + +test/Cloudflare/KeylessCertificate/KeylessCertificate.test.ts + - !!! surfaces the typed KeylessSslNotAvailable error on non-Enterprise zones + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/LoadBalancer/Monitor.test.ts + - !!! list returns an array of monitor attributes + CloudflareHttpError: null + - !!! surfaces the typed MonitorIntervalOutOfRange error without the LB subscription + AssertionError: expected 'CloudflareHttpError' to deeply equal 'MonitorIntervalOutOfRange' + +test/Cloudflare/LoadBalancer/MonitorGroup.test.ts + - !!! list returns an array of monitor groups + Unauthorized: Authentication error + -!!! surfaces the typed MonitorGroupsNotEnabled error on non-Enterprise accounts + AssertionError: expected 'CloudflareHttpError' to deeply equal 'MonitorGroupsNotEnabled' + +test/Cloudflare/LoadBalancer/Pool.test.ts + - !!! surfaces the typed PoolAccessFailed error without the LB subscription + AssertionError: expected 'TooManyRequests' to deeply equal 'PoolAccessFailed' + +test/Cloudflare/Pages/Deployment.test.ts + - create, replace on branch change, and destroy a deployment + CloudflareHttpError: null + +test/Cloudflare/Pages/Domain.test.ts + - attach and detach a custom domain + CloudflareHttpError: null + - changing the domain name triggers replacement + Error: Test timed out in 120000ms. + +test/Cloudflare/Pages/Project.test.ts + - changing the name triggers replacement + CloudflareHttpError: null + - create and delete a project with generated name + Forbidden: Authentication error + - list enumerates the deployed project + CloudflareHttpError: null + - update mutable props in place (same project id) + CloudflareHttpError: null + +test/Cloudflare/Queue/QueueConsumer.test.ts + - adopts existing consumer after local state loss + CloudflareHttpError: null + - create, update settings, replace script, delete + CloudflareHttpError: null + - fails clearly when queue has consumer for different script + AssertionError: expected '{"_id":"Exit","_tag":"Failure","cause…' to contain 'fails-clearly-when-queue-has-consumer…' + - list enumerates the deployed consumer + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - promotes a dev consumer to a live consumer on deploy + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - recreates consumer after out-of-band delete + CloudflareHttpError: null + +test/Cloudflare/R2/Bucket.test.ts + - create and delete bucket with default props + TooManyRequests: Please wait and consider throttling your request speed + - create, update, delete bucket + TooManyRequests: Please wait and consider throttling your request speed + - destroying a bucket empties its objects first + TooManyRequests: Please wait and consider throttling your request speed + - existing bucket (matching name) is silently adopted without --adopt + TooManyRequests: Please wait and consider throttling your request speed + - lifecycle rules are added, updated, and removed + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/R2/BucketEventNotification.test.ts + - changing the queue triggers a replacement + TooManyRequests: Please wait and consider throttling your request speed + - create, update rules in place, and delete + TooManyRequests: Please wait and consider throttling your request speed + - list enumerates deployed bucket event notifications + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/R2/BucketSippy.test.ts + - !!! list enumerates buckets that have Sippy enabled + Unauthorized: Authentication error + - !!! reads the disabled baseline and surfaces typed errors without external creds + Unauthorized: Authentication error + +test/Cloudflare/R2/CustomDomain.test.ts + - !!! creates, updates, and deletes a bucket custom domain + Unauthorized: Authentication error + - !!! creates, updates, and deletes a bucket with multiple custom domains + Unauthorized: Authentication error + +test/Cloudflare/R2/R2Bucket.test.ts + - !!! list enumerates the deployed R2 bucket + Unauthorized: Authentication error + +test/Cloudflare/R2DataCatalog/R2DataCatalog.test.ts + - enable, sync maintenance config, register credential, destroy + TooManyRequests: Please wait and consider throttling your request speed + - list enumerates the deployed catalog + TooManyRequests: Please wait and consider throttling your request speed + - re-enables after out-of-band disable + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Rum/Site.test.ts + - !!! create and delete a host (gray-clouded) site + CloudflareHttpError: null + - !!! list enumerates the deployed RUM site + Forbidden: Authentication error + - !!! recreates after out-of-band delete + Forbidden: Authentication error + - !!! update mutable props in place (same siteTag) + Forbidden: Authentication error + - !!! zone (orange-clouded) site with autoInstall, replaced on identity flip + Error: Please wait and consider throttling your request speed + +test/Cloudflare/SecretsStore/Secret.test.ts + - list enumerates the deployed secret across stores + CloudflareHttpError: null + +test/Cloudflare/StateStore/State.test.ts + - State > DELETE /resources/:fqn (deleteState) removes a single resource + CloudflareHttpError: null + - State > DELETE /stacks/:stack (no stage) removes the stack from listStacks + CloudflareHttpError: null + - State > DELETE /stacks/:stack?stage=... clears one stage but leaves the stack registered + CloudflareHttpError: null + - State > DELETE /state/stacks/:stack wipes the test namespace (cleanup) + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - State > GET /output (getStackOutput) reads back the persisted output + CloudflareHttpError: null + - State > GET /output returns undefined for an un-deployed stage + CloudflareHttpError: null + - State > GET /replaced-resources (getReplacedResources) returns status===replaced rows + CloudflareHttpError: null + - State > GET /resources (listResources) returns the FQNs in the stage + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - State > GET /resources/:fqn (getState) reads back the persisted value + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - State > GET /resources/:fqn returns undefined for a missing fqn + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - State > GET /stacks (listStacks) includes the registered test stack + CloudflareHttpError: null + - State > GET /stacks/:stack/stages (listStages) includes the test stage + CloudflareHttpError: null + - State > GET+PUT interleaved 30x5 — engine traffic pattern + CloudflareHttpError: null + - State > getVersion returns the current STATE_STORE_VERSION + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - State > PUT /output (setStackOutput) persists a stack output + CloudflareHttpError: null + - State > PUT /resources/:fqn (setState) persists a resource + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - State > setState 100x concurrent — surfaces racy transient failures + CloudflareHttpError: null + - State > setState 100x sequential — surfaces transient failures + CloudflareHttpError: null + +test/Cloudflare/Tags/AccountResourceTags.test.ts + - adoption — existing tags error without adopt, take over with adopt(true) + CloudflareHttpError: null + - changing resourceId triggers replacement + Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time. + - create, update, and clear tags on a KV namespace + Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time. + - list enumerates account-wide tagged resources + Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time. + +test/Cloudflare/Tunnel/WarpConnector.test.ts + - !!! create, rename in place, and destroy a WARP Connector tunnel + Forbidden: Authentication error + - !!! list enumerates the deployed WARP Connector tunnel + Forbidden: Authentication error + +test/Cloudflare/Turnstile/Widget.test.ts + - create and delete a widget with default name + CloudflareHttpError: null + - list enumerates the deployed widget + CloudflareHttpError: null + - recreates after out-of-band delete + CloudflareHttpError: null + - update mutable props in place (same sitekey) + CloudflareHttpError: null + +test/Cloudflare/VpcService/VpcService.test.ts + - !!! create vpc service with ipv4 host + Unauthorized: Authentication error + - !!! create, update, delete vpc service + Unauthorized: Authentication error + - !!! list enumerates the deployed vpc service + Unauthorized: Authentication error + +test/Cloudflare/VpcService/VpcServiceRef.test.ts + - !!! reference vpc service by name and by id + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Website/Vite.test.ts + - Vite: `env` props are inlined as `import.meta.env.*` into the bundle + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - Vite: class form deploys and serves the built assets + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - Vite: editing a source file republishes the assets in a single deploy + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - Vite: relocating rootDir (and deleting the old one) is a no-op when sources are identical + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + +test/Cloudflare/Workers/AccountSetting.test.ts + - AccountSetting > flips green compute, updates in place, and restores the baseline on destroy + Forbidden: Authentication error + +test/Cloudflare/Workers/ObservabilityDestination.test.ts + - !!! create and delete a destination with default name + Forbidden: Authentication error + - list enumerates the deployed destination + CloudflareHttpError: null + - replacement on name change (new slug, old destination removed) + CloudflareHttpError: null + - update url, headers, and enabled in place (same slug) + CloudflareHttpError: null + +test/Cloudflare/Workers/PrebuiltWorker.test.ts + - Cloudflare.Worker with bundle: false > uploads a prebuilt module graph byte-for-byte + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + +test/Cloudflare/Workers/Worker.test.ts + - Cloudflare.Worker > adopt(true) takes over a foreign-tagged worker + TooManyRequests: Please wait and consider throttling your request speed + - Cloudflare.Worker > create, update, delete internal worker + TooManyRequests: Please wait and consider throttling your request speed + - Cloudflare.Worker > create, update, delete worker + Unauthorized: Authentication error + - Cloudflare.Worker > domains reflects the workers.dev subdomain and tracks url + TooManyRequests: Please wait and consider throttling your request speed + - Cloudflare.Worker > downstream referencing worker.url is not re-updated when the worker changes + TooManyRequests: Please wait and consider throttling your request speed + - Cloudflare.Worker > list enumerates the deployed worker + TooManyRequests: Please wait and consider throttling your request speed + - Cloudflare.Worker > redeploy re-enables previewsEnabled when externally disabled + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - Cloudflare.Worker > toggling url between deploys flips the workers.dev subdomain + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - Cloudflare.Worker > url: false disables the workers.dev subdomain on first deploy + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - Cloudflare.Worker > worker.durableObjectNamespaces stability across DO and worker changes + TooManyRequests: Please wait and consider throttling your request speed diff --git a/cf-temp-failures-3.txt b/cf-temp-failures-3.txt new file mode 100644 index 000000000..796adf5dd --- /dev/null +++ b/cf-temp-failures-3.txt @@ -0,0 +1,240 @@ +# Failed Cloudflare tests (temporary account run) +# total failed: 94 across 25 files + +test/Cloudflare/Alerting/NotificationPolicy.test.ts + - !!! create, update, delete notification policy + Unauthorized: Authentication error + - !!! list enumerates the deployed notification policy + Unauthorized: Authentication error + - !!! replaces policy when alertType changes + Unauthorized: Authentication error + +test/Cloudflare/Alerting/Silence.test.ts + - !!! create, update window in place, delete silence + Unauthorized: Authentication error + - !!! list enumerates the deployed silence + Unauthorized: Authentication error + - !!! replaces silence when the policy changes + Unauthorized: Authentication error + +test/Cloudflare/Alerting/Webhook.test.ts + - create, update, delete webhook destination + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - create, update, delete webhook destination + Unauthorized: Authentication error + - list enumerates the deployed webhook destination + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - list enumerates the deployed webhook destination + Unauthorized: Authentication error + +test/Cloudflare/Calls/App.test.ts + - !!! create and delete an app with default name + Forbidden: Authentication error + - !!! list enumerates the deployed app + Forbidden: Authentication error + - !!! recreates after out-of-band delete + Forbidden: Authentication error + - !!! update name in place (same appId, secret preserved) + Forbidden: Authentication error + +test/Cloudflare/Connectivity/DirectoryService.test.ts + - !!! http service with explicit ports and default name + Unauthorized: Authentication error + - !!! list enumerates the deployed directory service + Unauthorized: Authentication error + - !!! recreates after out-of-band delete + Unauthorized: Authentication error + - !!! tcp service lifecycle: create, update, host switch + Unauthorized: Authentication error + +test/Cloudflare/Container/ContainerApplication.test.ts + - ContainerApplication > list enumerates container applications + Provider not found for undefined + +test/Cloudflare/D1/Database.test.ts + - applies migrations from migrationsDir + Forbidden: System limit reached: databases per account (1) + - !!! clones a database by databaseId + Forbidden: System limit reached: databases per account (1) + - !!! clones a database by name lookup + Forbidden: System limit reached: databases per account (1) + - !!! clones a database by passing the source resource directly + Forbidden: System limit reached: databases per account (1) + - existing database (matching name) is silently adopted without --adopt + Forbidden: System limit reached: databases per account (1) + - imports SQL files via importFiles + Forbidden: System limit reached: databases per account (1) + +test/Cloudflare/Pages/Deployment.test.ts + - create, replace on branch change, and destroy a deployment + Error: Test timed out in 120000ms. + +test/Cloudflare/Pages/Domain.test.ts + - attach and detach a custom domain + Error: Test timed out in 120000ms. + - changing the domain name triggers replacement + Error: Test timed out in 120000ms. + +test/Cloudflare/Pages/Project.test.ts + - changing the name triggers replacement + Error: Test timed out in 120000ms. + - create and delete a project with generated name + Forbidden: Authentication error + - list enumerates the deployed project + Error: Test timed out in 120000ms. + - update mutable props in place (same project id) + Error: Test timed out in 120000ms. + +test/Cloudflare/Queue/QueueConsumer.test.ts + - create, update settings, replace script, delete + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - fails clearly when queue has consumer for different script + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - list enumerates the deployed consumer + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - list enumerates the deployed consumer + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - promotes a dev consumer to a live consumer on deploy + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - recreates consumer after out-of-band delete + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + +test/Cloudflare/Queue/RoundTrip.test.ts + - send → subscribe handler → DO state → polled by test client + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + +test/Cloudflare/R2/Bucket.test.ts + - !!! create and delete bucket with default props + Unauthorized: Authentication error + - !!! create, update, delete bucket + Unauthorized: Authentication error + - !!! destroying a bucket empties its objects first + Unauthorized: Authentication error + - !!! existing bucket (matching name) is silently adopted without --adopt + Unauthorized: Authentication error + - !!! lifecycle rules are added, updated, and removed + Unauthorized: Authentication error + +test/Cloudflare/R2/BucketEventNotification.test.ts + - !!! changing the queue triggers a replacement + Unauthorized: Authentication error + - !!! create, update rules in place, and delete + Unauthorized: Authentication error + - !!! list enumerates deployed bucket event notifications + Unauthorized: Authentication error + +test/Cloudflare/R2DataCatalog/R2DataCatalog.test.ts + - !!! enable, sync maintenance config, register credential, destroy + Unauthorized: Authentication error + - !!! list enumerates the deployed catalog + Unauthorized: Authentication error + - !!! re-enables after out-of-band disable + Unauthorized: Authentication error + +test/Cloudflare/SecretsStore/Secret.test.ts + - !!! list enumerates the deployed secret across stores + Unauthorized: Authentication error + +test/Cloudflare/StateStore/State.test.ts + - !!! State > DELETE /resources/:fqn (deleteState) removes a single resource + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - !!! State > DELETE /stacks/:stack (no stage) removes the stack from listStacks + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - !!! State > DELETE /stacks/:stack?stage=... clears one stage but leaves the stack registered + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - !!! State > DELETE /state/stacks/:stack wipes the test namespace (cleanup) + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - !!! State > GET /output (getStackOutput) reads back the persisted output + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - !!! State > GET /output returns undefined for an un-deployed stage + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - !!! State > GET /replaced-resources (getReplacedResources) returns status===replaced rows + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - !!! State > GET /resources (listResources) returns the FQNs in the stage + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - !!! State > GET /resources/:fqn (getState) reads back the persisted value + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - !!! State > GET /resources/:fqn returns undefined for a missing fqn + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - !!! State > GET /stacks (listStacks) includes the registered test stack + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - !!! State > GET /stacks/:stack/stages (listStages) includes the test stage + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - !!! State > GET+PUT interleaved 30x5 — engine traffic pattern + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - !!! State > getVersion returns the current STATE_STORE_VERSION + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - !!! State > PUT /output (setStackOutput) persists a stack output + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - !!! State > PUT /resources/:fqn (setState) persists a resource + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - !!! State > setState 100x concurrent — surfaces racy transient failures + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - !!! State > setState 100x sequential — surfaces transient failures + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + +test/Cloudflare/Tags/AccountResourceTags.test.ts + - adoption — existing tags error without adopt, take over with adopt(true) + Error: Test timed out in 120000ms. + - changing resourceId triggers replacement + Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time. + - create, update, and clear tags on a KV namespace + Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time. + - list enumerates account-wide tagged resources + Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time. + +test/Cloudflare/Turnstile/Widget.test.ts + - !!! create and delete a widget with default name + Unauthorized: Authentication error + - !!! list enumerates the deployed widget + Unauthorized: Authentication error + - !!! recreates after out-of-band delete + Unauthorized: Authentication error + - !!! update mutable props in place (same sitekey) + Unauthorized: Authentication error + +test/Cloudflare/Website/StaticSite.test.ts + - StaticSite: a bundle-only change keeps the asset manifest (hash.assets stable) + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - StaticSite: class form deploys and serves the built assets + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - StaticSite: editing a source file republishes the assets in a single deploy + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - StaticSite: relocating the project (and deleting the old one) preserves hash.assets + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + +test/Cloudflare/Website/Vite.test.ts + - Vite: class form deploys and serves the built assets + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + +test/Cloudflare/Workers/AccountSetting.test.ts + - AccountSetting > flips green compute, updates in place, and restores the baseline on destroy + Forbidden: Authentication error + +test/Cloudflare/Workers/ObservabilityDestination.test.ts + - list enumerates the deployed destination + Error: Test timed out in 300000ms. + - replacement on name change (new slug, old destination removed) + Error: Test timed out in 300000ms. + - update url, headers, and enabled in place (same slug) + Error: Test timed out in 300000ms. + +test/Cloudflare/Workers/PrebuiltWorker.test.ts + - Cloudflare.Worker with bundle: false > uploads a prebuilt module graph byte-for-byte + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + +test/Cloudflare/Workers/Worker.test.ts + - Cloudflare.Worker > create, update, delete worker + Unauthorized: Authentication error + - Cloudflare.Worker > create, update, delete worker + Unauthorized: Authentication error + - Cloudflare.Worker > downstream referencing worker.url is not re-updated when the worker changes + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - Cloudflare.Worker > downstream referencing worker.url is not re-updated when the worker changes + Unauthorized: Authentication error + - Cloudflare.Worker > list enumerates the deployed worker + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - Cloudflare.Worker > worker.durableObjectNamespaces stability across DO and worker changes + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - Cloudflare.Worker > worker.durableObjectNamespaces stability across DO and worker changes + Unauthorized: Authentication error diff --git a/cf-temp-failures-4.txt b/cf-temp-failures-4.txt new file mode 100644 index 000000000..d30a5c673 --- /dev/null +++ b/cf-temp-failures-4.txt @@ -0,0 +1,54 @@ +# Failed Cloudflare tests (temporary account run) +# total failed: 18 across 8 files + +test/Cloudflare/Container/ContainerApplication.test.ts + - ContainerApplication > list enumerates container applications + Provider not found for undefined + +test/Cloudflare/D1/Database.test.ts + - existing database (matching name) is silently adopted without --adopt + Forbidden: System limit reached: databases per account (1) + +test/Cloudflare/Pages/Deployment.test.ts + - create, replace on branch change, and destroy a deployment + Error: Test timed out in 120000ms. + +test/Cloudflare/Pages/Domain.test.ts + - attach and detach a custom domain + Error: Test timed out in 120000ms. + - changing the domain name triggers replacement + Error: Test timed out in 120000ms. + +test/Cloudflare/Pages/Project.test.ts + - changing the name triggers replacement + Error: Test timed out in 120000ms. + - create and delete a project with generated name + Forbidden: Authentication error + - list enumerates the deployed project + Error: Test timed out in 120000ms. + - update mutable props in place (same project id) + Error: Test timed out in 120000ms. + +test/Cloudflare/Tags/AccountResourceTags.test.ts + - adoption — existing tags error without adopt, take over with adopt(true) + Error: Test timed out in 120000ms. + - changing resourceId triggers replacement + Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time. + - create, update, and clear tags on a KV namespace + Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time. + - list enumerates account-wide tagged resources + Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time. + +test/Cloudflare/Website/Vite.test.ts + - Vite: `env` props are inlined as `import.meta.env.*` into the bundle + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - Vite: class form deploys and serves the built assets + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - Vite: editing a source file republishes the assets in a single deploy + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - Vite: relocating rootDir (and deleting the old one) is a no-op when sources are identical + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + +test/Cloudflare/Workers/AccountSetting.test.ts + - !!! AccountSetting > flips green compute, updates in place, and restores the baseline on destroy + Forbidden: Authentication error diff --git a/cf-temp-failures-5.txt b/cf-temp-failures-5.txt new file mode 100644 index 000000000..5795cc696 --- /dev/null +++ b/cf-temp-failures-5.txt @@ -0,0 +1,54 @@ +# Failed Cloudflare tests (temporary account run) +# total failed: 18 across 8 files + +test/Cloudflare/Container/ContainerApplication.test.ts + - ContainerApplication > list enumerates container applications + Provider not found for undefined + +test/Cloudflare/D1/Database.test.ts + - applies migrations from migrationsDir + Forbidden: System limit reached: databases per account (1) + - applies migrations using a custom migrationsTable + Forbidden: System limit reached: databases per account (1) + - create, update, delete database + Forbidden: System limit reached: databases per account (1) + - migrates legacy 2-column migration table to wrangler schema + Forbidden: System limit reached: databases per account (1) + +test/Cloudflare/Hyperdrive/Hyperdrive.test.ts + - create, update, delete hyperdrive + BadRequest: This account has reached its limit for how many Hyperdrives it can have (2) + +test/Cloudflare/Pages/Deployment.test.ts + - create, replace on branch change, and destroy a deployment + Error: Test timed out in 120000ms. + +test/Cloudflare/Pages/Domain.test.ts + - attach and detach a custom domain + Error: Test timed out in 120000ms. + - changing the domain name triggers replacement + Error: Test timed out in 120000ms. + +test/Cloudflare/Pages/Project.test.ts + - changing the name triggers replacement + Error: Test timed out in 120000ms. + - create and delete a project with generated name + Forbidden: Authentication error + - list enumerates the deployed project + Error: Test timed out in 120000ms. + - update mutable props in place (same project id) + Error: Test timed out in 120000ms. + +test/Cloudflare/Tags/AccountResourceTags.test.ts + - adoption — existing tags error without adopt, take over with adopt(true) + Error: Test timed out in 120000ms. + - changing resourceId triggers replacement + Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time. + - create, update, and clear tags on a KV namespace + Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time. + - list enumerates account-wide tagged resources + Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time. + +test/Cloudflare/Workers/AccountSetting.test.ts + - AccountSetting > flips green compute, updates in place, and restores the baseline on destroy + Forbidden: Authentication error diff --git a/cf-temp-failures.txt b/cf-temp-failures.txt new file mode 100644 index 000000000..6f01d26af --- /dev/null +++ b/cf-temp-failures.txt @@ -0,0 +1,1568 @@ +# Failed Cloudflare tests (temporary account run) +# total failed: 551 across 232 files + +test/Cloudflare/Access/Application.test.ts + - create and delete a self_hosted application gated by a reusable policy + Error: Test timed out in 120000ms. + - !!! list enumerates the deployed access application + Forbidden: Requires permission "com.cloudflare.api.account.zone.create" to create zones for the selected account + - !!! update policies in place keeps the applicationId stable + Forbidden: Requires permission "com.cloudflare.api.account.zone.create" to create zones for the selected account + +test/Cloudflare/Access/Bookmark.test.ts + - !!! legacy bookmark creation surfaces the typed AccessBookmarkNotFound error + AssertionError: expected 'Unauthorized' to deeply equal 'AccessBookmarkNotFound' + - !!! list enumerates access bookmarks at the account scope + Unauthorized: Authentication error + +test/Cloudflare/Access/Certificate.test.ts + - !!! list enumerates account access certificates + Unauthorized: Authentication error + - !!! unentitled accounts surface the typed AccessCertificateQuotaExceeded error + AssertionError: expected 'Unauthorized' to deeply equal 'AccessCertificateQuotaExceeded' + +test/Cloudflare/Access/CustomPage.test.ts + - !!! list enumerates access custom pages at the account scope + Unauthorized: Authentication error + - !!! unentitled accounts surface the typed AccessCustomPagesNotEntitled error + AssertionError: expected 'Unauthorized' to deeply equal 'AccessCustomPagesNotEntitled' + +test/Cloudflare/Access/Group.test.ts + - create, update rules, and delete group + Error: Test timed out in 120000ms. + - !!! group can be referenced from an access policy + CloudflareHttpError: null + - !!! list enumerates the deployed access group + CloudflareHttpError: null + - rename updates the group in place + Error: Test timed out in 120000ms. + +test/Cloudflare/Access/IdentityProvider.test.ts + - changing the type replaces the IdP + TooManyRequests: Please wait and consider throttling your request speed + - create, verify, and destroy an OIDC IdP + TooManyRequests: Please wait and consider throttling your request speed + - list enumerates the deployed IdP + TooManyRequests: Please wait and consider throttling your request speed + - update name and config in place (same id) + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Access/InfrastructureTarget.test.ts + - !!! list enumerates the deployed infrastructure target + Forbidden: Authentication error + +test/Cloudflare/Access/KeyConfiguration.test.ts + - list returns the account key configuration singleton + TooManyRequests: Please wait and consider throttling your request speed + - pins the rotation interval, updates in place, and restores on destroy + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Access/McpPortal.test.ts + - create, update in place, and destroy an MCP portal + CloudflareHttpError: null + - list enumerates the deployed MCP portal + CloudflareHttpError: null + +test/Cloudflare/Access/Organization.test.ts + - Organization > list returns the account Access organization + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Access/Policy.test.ts + - adopts an out-of-band reusable policy + Unauthorized: Authentication error + - create and delete basic allow policy + CloudflareHttpError: null + - list enumerates the deployed reusable policy + CloudflareHttpError: null + - update mutates includes without replacing + CloudflareHttpError: null + +test/Cloudflare/Access/ServiceToken.test.ts + - create, update duration, and delete service token + CloudflareHttpError: null + - incrementing clientSecretVersion rotates the secret + CloudflareHttpError: null + - list enumerates the deployed service token + CloudflareHttpError: null + +test/Cloudflare/Access/Tag.test.ts + - create, verify out-of-band, and destroy tag + TooManyRequests: Please wait and consider throttling your request speed + - list enumerates the deployed access tag + TooManyRequests: Please wait and consider throttling your request speed + - rename replaces the tag + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Account/Account.test.ts + - createAccount is entitlement-gated: typed AccountCreationForbidden + AssertionError: expected 'InternalServerError' to deeply equal 'AccountCreationForbidden' + - list enumerates accessible accounts (read-only) + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Account/Member.test.ts + - create member, update roles in place, delete + TooManyRequests: Please wait and consider throttling your request speed + - list enumerates the account members + TooManyRequests: Please wait and consider throttling your request speed + - recreates after out-of-band delete + TooManyRequests: Please wait and consider throttling your request speed + - replaces the member when the email changes + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Acm/CustomTrustStore.test.ts + - surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Acm/TotalTls.test.ts + - !!! TotalTls > converges enabled:false as a no-op on a zone without the ACM entitlement + Error: zone "alchemy-test-2.us" not found in account + - !!! TotalTls > list enumerates the setting across all zones + Error: zone "alchemy-test-2.us" not found in account + - !!! TotalTls > surfaces the typed AdvancedCertificateManagerRequired error when enabling without the entitlement + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Addressing/AddressMap.test.ts + - !!! address map lifecycle (typed FeatureNotEnabled on unentitled accounts) + AssertionError: expected 'CloudflareHttpError' to deeply equal 'FeatureNotEnabled' + - !!! list enumerates the deployed address map + AssertionError: expected 'CloudflareHttpError' to deeply equal 'FeatureNotEnabled' + +test/Cloudflare/Addressing/BgpPrefix.test.ts + - !!! list enumerates BGP prefixes across BYOIP prefixes + Unauthorized: Authentication error + +test/Cloudflare/Addressing/Prefix.test.ts + - list enumerates account prefixes (read-only) + TooManyRequests: Please wait and consider throttling your request speed + - lists the services catalog and prefixes (read-only) + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Addressing/PrefixDelegation.test.ts + - !!! list enumerates prefix delegations (read-only) + Unauthorized: Authentication error + +test/Cloudflare/Addressing/ServiceBinding.test.ts + - list enumerates service bindings across prefixes (read-only) + CloudflareHttpError: null + +test/Cloudflare/AiGateway/AiGatewaySpendingLimit.test.ts + - !!! AiGatewaySpendingLimit > create, read-back, and delete the account spending limit + Unauthorized: Authentication error + - !!! AiGatewaySpendingLimit > list enumerates the deployed account spending limit + TooManyRequests: Please wait and consider throttling your request speed + - !!! AiGatewaySpendingLimit > update the spending limit in place + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/AiGateway/Dataset.test.ts + - !!! AiGatewayDataset > create, update, delete a dataset on a gateway + Unauthorized: Authentication error + - !!! AiGatewayDataset > list enumerates datasets across gateways + TooManyRequests: Please wait and consider throttling your request speed + - !!! AiGatewayDataset > recreates a dataset after out-of-band delete + TooManyRequests: Please wait and consider throttling your request speed + - !!! AiGatewayDataset > replaces dataset when the gateway changes + CloudflareHttpError: null + +test/Cloudflare/AiGateway/DynamicRouting.test.ts + - !!! create, update elements (new deployed version), rename, delete + Unauthorized: Authentication error + - !!! list enumerates routes across all gateways + Unauthorized: Authentication error + - !!! recreates a route after out-of-band delete + Unauthorized: Authentication error + - !!! replaces route when the gateway changes + Unauthorized: Authentication error + +test/Cloudflare/AiGateway/Evaluation.test.ts + - !!! create, noop, replace, delete an evaluation + Unauthorized: Authentication error + - !!! list enumerates the deployed evaluation + Unauthorized: Authentication error + +test/Cloudflare/AiGateway/ProviderConfig.test.ts + - create, noop, replace, delete a BYOK provider config + CloudflareHttpError: null + - list enumerates the deployed provider config + CloudflareHttpError: null + +test/Cloudflare/AiSearch/AiSearch.test.ts + - construct auto-creates a managed token and wires it into the instance + TooManyRequests: Please wait and consider throttling your request speed + - web-crawler source skips token minting + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/AiSearch/Instance.test.ts + - !!! changing the embedding model triggers a replacement + Unauthorized: Authentication error + - !!! create, update mutable props, and delete an r2-backed instance + Unauthorized: Authentication error + - !!! creates a web-crawler instance (no service token) + Forbidden: Authentication error + - !!! creates an instance in a custom namespace and moving namespaces replaces + CloudflareHttpError: null + - !!! list enumerates the deployed instance + Unauthorized: Authentication error + - !!! recreates after out-of-band delete + Unauthorized: Authentication error + +test/Cloudflare/AiSearch/Namespace.test.ts + - adopts the reserved default namespace without deleting it on teardown + TooManyRequests: Please wait and consider throttling your request speed + - changing the name triggers a replacement + TooManyRequests: Please wait and consider throttling your request speed + - create, update description in place, and delete a namespace + TooManyRequests: Please wait and consider throttling your request speed + - list enumerates the deployed namespace + TooManyRequests: Please wait and consider throttling your request speed + - recreates after out-of-band delete + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/AiSearch/Token.test.ts + - an AI Search instance syncs with a stack-minted service token + TooManyRequests: Please wait and consider throttling your request speed + - create, rename in place, and delete a service token + TooManyRequests: Please wait and consider throttling your request speed + - list enumerates the deployed service token + TooManyRequests: Please wait and consider throttling your request speed + - recreates after out-of-band delete + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/AiSecurity/CustomTopics.test.ts + - !!! surfaces the typed AiSecurityNotEntitled error on unentitled accounts + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/AiSecurity/Settings.test.ts + - !!! surfaces the typed AiSecurityNotEntitled error on unentitled accounts + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Alerting/NotificationPolicy.test.ts + - create, update, delete notification policy + TooManyRequests: Please wait and consider throttling your request speed + - list enumerates the deployed notification policy + TooManyRequests: Please wait and consider throttling your request speed + - replaces policy when alertType changes + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Alerting/Silence.test.ts + - create, update window in place, delete silence + TooManyRequests: Please wait and consider throttling your request speed + - list enumerates the deployed silence + TooManyRequests: Please wait and consider throttling your request speed + - replaces silence when the policy changes + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Alerting/Webhook.test.ts + - create, update, delete webhook destination + CloudflareHttpError: null + - list enumerates the deployed webhook destination + CloudflareHttpError: null + +test/Cloudflare/ApiShield/Configuration.test.ts + - !!! surfaces the typed NotEntitled error on unentitled zones + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/ApiShield/Label.test.ts + - !!! create, update description in place, destroy a label + Error: zone "alchemy-test-2.us" not found in account + - !!! generated name respects Cloudflare's 24-character limit + Error: zone "alchemy-test-2.us" not found in account + - !!! list enumerates the deployed label + Error: zone "alchemy-test-2.us" not found in account + - !!! renaming a label triggers replacement + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/ApiShield/Operation.test.ts + - !!! changing the method triggers replacement + Error: zone "alchemy-test-2.us" not found in account + - !!! create, no-op redeploy, destroy an API Shield operation + Error: zone "alchemy-test-2.us" not found in account + - !!! list enumerates the deployed API Shield operation + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/ApiShield/UserSchema.test.ts + - !!! changing the schema source triggers replacement + Error: zone "alchemy-test-2.us" not found in account + - !!! create, enable validation in place, destroy a user schema + Error: zone "alchemy-test-2.us" not found in account + - !!! list enumerates the deployed user schema + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/ApiToken/AccountApiToken.test.ts + - !!! list enumerates the deployed account token + Unauthorized: Unauthorized to access requested resource + +test/Cloudflare/Argo/SmartRouting.test.ts + - !!! SmartRouting > surfaces the typed NotAuthorized error on zones without the Argo add-on + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Argo/TieredCaching.test.ts + - !!! TieredCaching > enables Tiered Caching and restores the original value on destroy + Error: zone "alchemy-test-2.us" not found in account + - !!! TieredCaching > list enumerates the setting across all zones + Error: zone "alchemy-test-2.us" not found in account + - !!! TieredCaching > updates enabled in place and keeps the captured initial value + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/BotManagement/BotManagement.test.ts + - !!! BotManagement > deploy with no settings set adopts the singleton without writing + Error: zone "alchemy-test-2.us" not found in account + - !!! BotManagement > list enumerates bot management across all zones + Error: zone "alchemy-test-2.us" not found in account + - !!! BotManagement > manages SBFM settings on the zone singleton and restores them on destroy + Error: zone "alchemy-test-2.us" not found in account + - !!! BotManagement > toggles a boolean SBFM field and restores it on destroy + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Cache/CacheReserve.test.ts + - !!! CacheReserve > surfaces the typed SettingUnavailableForPlan error on unentitled zones + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Cache/OriginCloudRegion.test.ts + - !!! creates, updates in place, replaces on ip change, and deletes the mapping + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Cache/RegionalTieredCache.test.ts + - !!! RegionalTieredCache > surfaces the typed SettingUnavailableForPlan error on non-Enterprise zones + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Cache/SmartTieredCache.test.ts + - !!! SmartTieredCache > enables Smart Tiered Cache and restores the original value on destroy + Error: zone "alchemy-test-2.us" not found in account + - !!! SmartTieredCache > list enumerates the setting across all zones + Error: zone "alchemy-test-2.us" not found in account + - !!! SmartTieredCache > updates the setting in place and keeps the captured initial value + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Cache/Variants.test.ts + - !!! Variants > creates, updates in place, and deletes the variants setting + Error: zone "alchemy-test-2.us" not found in account + - !!! Variants > deploy is idempotent and converges out-of-band drift + Error: zone "alchemy-test-2.us" not found in account + - !!! Variants > list enumerates the configured variants settings + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Calls/App.test.ts + - create and delete an app with default name + TooManyRequests: Please wait and consider throttling your request speed + - list enumerates the deployed app + TooManyRequests: Please wait and consider throttling your request speed + - recreates after out-of-band delete + TooManyRequests: Please wait and consider throttling your request speed + - update name in place (same appId, secret preserved) + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Calls/TurnKey.test.ts + - create and delete a TURN key with default name + TooManyRequests: Please wait and consider throttling your request speed + - list enumerates the deployed TURN key + TooManyRequests: Please wait and consider throttling your request speed + - recreates after out-of-band delete + TooManyRequests: Please wait and consider throttling your request speed + - update name in place (same keyId, key preserved) + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/CertificateAuthorities/HostnameAssociation.test.ts + - !!! HostnameAssociation > associates hostnames with an uploaded CA and destroys before the cert + Error: zone "alchemy-test-2.us" not found in account + - !!! HostnameAssociation > changing the certificate key replaces the association + Error: zone "alchemy-test-2.us" not found in account + - !!! HostnameAssociation > pins Managed CA hostnames, updates in place, and clears on destroy + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/ClientCertificate/ClientCertificate.test.ts + - !!! changing validityDays replaces — new id issued, old certificate revoked + Error: zone "alchemy-test-2.us" not found in account + - !!! create signs the CSR, destroy revokes the certificate + Error: zone "alchemy-test-2.us" not found in account + - !!! list enumerates client certificates across zones + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/CloudConnector/Rules.test.ts + - !!! Rules > cloud connector rules — create, update in place, destroy clears the list + Error: zone "alchemy-test-2.us" not found in account + - !!! Rules > list enumerates rule lists across all zones + Error: zone "alchemy-test-2.us" not found in account + - !!! Rules > no-op redeploy leaves the rule list untouched and ids stable + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/CloudforceOne/ScanConfig.test.ts + - list returns an array of scan configs + CloudflareHttpError: null + - unentitled accounts surface the typed Unauthorized error + CloudflareHttpError: null + +test/Cloudflare/Connectivity/DirectoryService.test.ts + - http service with explicit ports and default name + TooManyRequests: Please wait and consider throttling your request speed + - list enumerates the deployed directory service + TooManyRequests: Please wait and consider throttling your request speed + - recreates after out-of-band delete + TooManyRequests: Please wait and consider throttling your request speed + - tcp service lifecycle: create, update, host switch + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Container/ContainerApplication.test.ts + - ContainerApplication > list enumerates container applications + Provider not found for undefined + +test/Cloudflare/ContentScanning/ContentScanning.test.ts + - !!! ContentScanning > list enumerates the status across all zones + Error: zone "alchemy-test-2.us" not found in account + - !!! ContentScanning > pins Content Scanning off on an unentitled zone and destroys cleanly + Error: zone "alchemy-test-2.us" not found in account + - !!! ContentScanning > surfaces the typed ContentScanningNotEntitled error on unentitled zones + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/ContentScanning/Expression.test.ts + - !!! Expression > surfaces the typed ContentScanningNotEnabled error when scanning is disabled + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/CustomCertificate/CustomCertificate.test.ts + - !!! surfaces the typed PlanLevelNotAllowed error on unentitled zones + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/D1/D1Database.test.ts + - list enumerates the deployed database + Forbidden: System limit reached: databases per account (1) + +test/Cloudflare/DdosProtection/AllowlistEntry.test.ts + - list returns a well-typed empty array without Magic Transit + TooManyRequests: Please wait and consider throttling your request speed + - surfaces the typed AdvancedTcpProtectionNotEntitled error without Magic Transit + AssertionError: expected 'TooManyRequests' to deeply equal 'AdvancedTcpProtectionNotEntitled' + +test/Cloudflare/DdosProtection/TcpFlowProtectionRule.test.ts + - list returns a well-typed empty array without Magic Transit + CloudflareHttpError: null + +test/Cloudflare/Devices/CustomProfile.test.ts + - create, update in place, and delete a custom device profile + TooManyRequests: Please wait and consider throttling your request speed + - list enumerates the deployed custom device profile + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Devices/DefaultProfile.test.ts + - DefaultProfile > list returns the singleton default device profile + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Devices/DexTest.test.ts + - !!! unentitled accounts surface the typed Forbidden entitlement error + AssertionError: expected 'Authentication error' to contain 'dex.api.entitlements.missing' + +test/Cloudflare/Devices/ManagedNetwork.test.ts + - !!! create, update in place, and delete a managed network + Forbidden: Authentication error + - !!! list enumerates the deployed managed network + Forbidden: Authentication error + +test/Cloudflare/Devices/PostureIntegration.test.ts + - creates without a real provider surface the typed InvalidPostureIntegrationConfig error + Error: Test timed out in 90000ms. + +test/Cloudflare/Devices/PostureRule.test.ts + - !!! changing the rule type triggers a replacement + TooManyRequests: Please wait and consider throttling your request speed + - !!! create, update in place, and delete a posture rule + TooManyRequests: Please wait and consider throttling your request speed + - !!! list enumerates the deployed posture rule + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Devices/Settings.test.ts + - !!! list returns the account's device settings singleton + Forbidden: Authentication error + - !!! patches disableForTime and restores the original value on destroy + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Diagnostics/EndpointHealthcheck.test.ts + - creates an endpoint healthcheck, updates in place, and destroys + CloudflareHttpError: null + - list enumerates the deployed endpoint healthcheck + CloudflareHttpError: null + - rejects public IPs with the typed InvalidHealthcheckEndpoint error + AssertionError: expected 'CloudflareHttpError' to deeply equal 'InvalidHealthcheckEndpoint' + +test/Cloudflare/Dlp/Dlp.test.ts + - !!! unentitled accounts surface the typed Forbidden error + AssertionError: expected 'CloudflareHttpError' to deeply equal 'Forbidden' + +test/Cloudflare/Dlp/Entry.test.ts + - list returns the account's custom DLP entries + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Dlp/Profile.test.ts + - !!! list enumerates custom DLP profiles (read-only) + Unauthorized: Authentication error + +test/Cloudflare/Dns/AccountSettings.test.ts + - !!! AccountSettings > list returns the account's DNS settings singleton + Unauthorized: Authentication error + - !!! AccountSettings > pins a zone default and restores the pre-management value on destroy + CloudflareHttpError: null + - !!! AccountSettings > updates in place, unions managedKeys, restores all managed fields + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Dns/DnsRecord.test.ts + - !!! adoption — existing record errors without adopt, takes over with adopt(true) + Error: zone "alchemy-test-2.us" not found in account + - !!! changing the record type triggers replacement + Error: zone "alchemy-test-2.us" not found in account + - !!! create and delete an A record with default props + Error: zone "alchemy-test-2.us" not found in account + - !!! list enumerates the deployed DNS record + Error: zone "alchemy-test-2.us" not found in account + - !!! updating mutable fields patches in place + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Dns/Dnssec.test.ts + - !!! Dnssec > enables DNSSEC, captures the disabled baseline, destroy deactivates + Error: zone "alchemy-test-2.us" not found in account + - !!! Dnssec > list enumerates active DNSSEC across zones + Error: zone "alchemy-test-2.us" not found in account + - !!! Dnssec > updates status in place — same zone singleton, no replacement + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Dns/View.test.ts + - !!! list returns a well-typed array of internal DNS views + TooManyRequests: Please wait and consider throttling your request speed + - !!! surfaces the typed InternalDnsNotAvailable error on unentitled accounts + Error: Please wait and consider throttling your request speed + +test/Cloudflare/Dns/ZoneSettings.test.ts + - !!! ZoneSettings > list enumerates DNS settings across all zones + Error: zone "alchemy-test-2.us" not found in account + - !!! ZoneSettings > pins flattenAllCnames and restores the pre-management value on destroy + Error: zone "alchemy-test-2.us" not found in account + - !!! ZoneSettings > updates in place, unions managedKeys, restores all managed fields + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Dns/ZoneTransferAcl.test.ts + - !!! create, update in place, and delete a zone transfer ACL + CloudflareHttpError: null + - !!! generates a deterministic name when none is provided + TooManyRequests: Please wait and consider throttling your request speed + - !!! list enumerates the deployed zone transfer ACLs + CloudflareHttpError: null + +test/Cloudflare/Dns/ZoneTransferIncoming.test.ts + - !!! incoming config does not persist on non-secondary zones (typed not-found) + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Dns/ZoneTransferOutgoing.test.ts + - !!! list enumerates the outgoing transfer config per zone + Error: zone "alchemy-test-2.us" not found in account + - !!! surfaces the typed OutgoingZoneTransfersNotAllowed error on unentitled zones + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Dns/ZoneTransferPeer.test.ts + - create with connection settings, update in place, and delete a peer + TooManyRequests: Please wait and consider throttling your request speed + - list enumerates the deployed peer + CloudflareHttpError: null + - wires a TSIG reference through to the peer + CloudflareHttpError: null + +test/Cloudflare/Dns/ZoneTransferTsig.test.ts + - create, rotate the secret in place, and delete a TSIG + TooManyRequests: Please wait and consider throttling your request speed + - list enumerates the deployed TSIG + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/DnsFirewall/DnsFirewall.test.ts + - list returns the DNS firewall cluster Attributes array + TooManyRequests: Please wait and consider throttling your request speed + - surfaces the typed DnsFirewallNotEntitled error on unentitled accounts + AssertionError: expected 'TooManyRequests' to deeply equal 'DnsFirewallNotEntitled' + +test/Cloudflare/Email/EmailAddress.test.ts + - list enumerates the deployed email address + Error: Test timed out in 120000ms. + +test/Cloudflare/Email/EmailCatchAll.test.ts + - !!! EmailCatchAll > configures the catch-all rule and restores the baseline on destroy + Error: zone "alchemy-test-2.us" not found in account + - !!! EmailCatchAll > list enumerates the catch-all rule across all zones + Error: zone "alchemy-test-2.us" not found in account + - !!! EmailCatchAll > updates the catch-all rule in place and keeps the captured baseline + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Email/EmailRouting.test.ts + - !!! EmailRouting > list enumerates email routing across all zones + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Email/EmailRule.test.ts + - !!! EmailRule > list enumerates the deployed email rule across all zones + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Email/SendingSubdomain.test.ts + - !!! adoption — existing subdomain errors without adopt, takes over with adopt(true) + Error: zone "alchemy-test-2.us" not found in account + - !!! changing the name triggers replacement + Error: zone "alchemy-test-2.us" not found in account + - !!! create and destroy a sending subdomain + Error: zone "alchemy-test-2.us" not found in account + - !!! list enumerates the deployed sending subdomain + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/EmailSecurity/AllowPolicy.test.ts + - !!! list enumerates allow policies (read-only) + TooManyRequests: Please wait and consider throttling your request speed + - !!! surfaces the typed EmailSecurityNotEntitled error on unentitled accounts + AssertionError: expected 'TooManyRequests' to deeply equal 'EmailSecurityNotEntitled' + +test/Cloudflare/EmailSecurity/Domain.test.ts + - !!! list enumerates Email Security domains (or [] when unentitled) + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/EmailSecurity/ImpersonationRegistryEntry.test.ts + - !!! list returns the account impersonation registry + Forbidden: Authentication error + +test/Cloudflare/EmailSecurity/TrustedDomain.test.ts + - !!! list enumerates trusted domains + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Firewall/AccessRule.test.ts + - !!! account-scoped rule (no zoneId) create, update, delete + CloudflareHttpError: null + - !!! changing the configuration triggers replacement + Error: zone "alchemy-test-2.us" not found in account + - !!! create and delete a zone-scoped ip rule + Error: zone "alchemy-test-2.us" not found in account + - !!! list enumerates the deployed zone-scoped rule + Error: zone "alchemy-test-2.us" not found in account + - !!! update mode and notes in place (same ruleId) + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Firewall/Lockdown.test.ts + - !!! create, update urls/configurations/description/paused in place, destroy + Error: zone "alchemy-test-2.us" not found in account + - !!! list enumerates the deployed lockdown rule + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Firewall/UaRule.test.ts + - !!! create, update mode/paused/description/userAgent in place, destroy + Error: zone "alchemy-test-2.us" not found in account + - !!! list enumerates UA rules across all zones + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Flagship/App.test.ts + - !!! create, update, delete a Flagship app + TooManyRequests: Please wait and consider throttling your request speed + - !!! list enumerates the deployed Flagship app + TooManyRequests: Please wait and consider throttling your request speed + - !!! recreates an app after out-of-band delete + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Flagship/Flag.test.ts + - !!! create, update, delete a flag in an app + Unauthorized: Authentication error + - !!! list enumerates the deployed flag + Unauthorized: Authentication error + - !!! recreates a flag after out-of-band delete + Unauthorized: Authentication error + - !!! replaces the flag when the key changes + Unauthorized: Authentication error + +test/Cloudflare/Fraud/FraudDetectionSettings.test.ts + - !!! FraudDetectionSettings > adopts the zone singleton without writing and restores nothing on destroy + Error: zone "alchemy-test-2.us" not found in account + - !!! FraudDetectionSettings > list enumerates the settings across all zones + Error: zone "alchemy-test-2.us" not found in account + - !!! FraudDetectionSettings > surfaces the typed FraudDetectionNotEntitled error on unentitled zones + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Gateway/Certificate.test.ts + - create an activated certificate, deactivate in place, destroy + CloudflareHttpError: null + - list enumerates the deployed gateway certificate + CloudflareHttpError: null + +test/Cloudflare/Gateway/Configuration.test.ts + - list returns the account Gateway configuration singleton + TooManyRequests: Please wait and consider throttling your request speed + - manage activityLog and protocolDetection, then restore on destroy + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Gateway/List.test.ts + - !!! changing the type replaces the list + Unauthorized: Authentication error + - !!! create, verify, and destroy a DOMAIN list + Unauthorized: Authentication error + - !!! update items, description, and name in place + Unauthorized: Authentication error + +test/Cloudflare/Gateway/Location.test.ts + - !!! create, verify, and destroy a location + TooManyRequests: Please wait and consider throttling your request speed + - !!! list enumerates deployed gateway locations + TooManyRequests: Please wait and consider throttling your request speed + - !!! recreates after out-of-band delete + TooManyRequests: Please wait and consider throttling your request speed + - !!! update name and ecsSupport in place (same id) + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Gateway/Logging.test.ts + - !!! list returns the account's Gateway logging singleton + TooManyRequests: Please wait and consider throttling your request speed + - !!! manage logging settings, update in place, restore on destroy + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Gateway/ProxyEndpoint.test.ts + - !!! create, update, and destroy an identity-kind proxy endpoint + TooManyRequests: Please wait and consider throttling your request speed + - !!! ip-kind endpoints surface the typed entitlement error + TooManyRequests: Please wait and consider throttling your request speed + - !!! list enumerates the deployed proxy endpoint + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Gateway/Rule.test.ts + - !!! list enumerates the deployed Gateway rule + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/GoogleTagGateway/GoogleTagGateway.test.ts + - !!! GoogleTagGateway > configures the gateway, updates in place, and restores the baseline on destroy + Error: zone "alchemy-test-2.us" not found in account + - !!! GoogleTagGateway > no-op redeploy skips the PUT and destroy is idempotent + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Healthcheck/Healthcheck.test.ts + - !!! adoption — existing check errors without adopt, takes over with adopt(true) + Error: zone "alchemy-test-2.us" not found in account + - !!! changing type HTTP→TCP updates in place; delete is idempotent + Error: zone "alchemy-test-2.us" not found in account + - !!! create and delete an HTTP health check with default name + Error: zone "alchemy-test-2.us" not found in account + - !!! list enumerates the deployed health check across zones + Error: zone "alchemy-test-2.us" not found in account + - !!! update mutable props in place (same id), then no-op redeploy + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/HostnameTlsSetting/HostnameTlsSetting.test.ts + - !!! lists overrides and surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Iam/ResourceGroup.test.ts + - !!! create, verify out-of-band, update name+scope in place, destroy + Error: Please wait and consider throttling your request speed + - !!! list enumerates the deployed resource group + Unauthorized: Authentication error + +test/Cloudflare/Iam/UserGroup.test.ts + - !!! create with a policy, verify out-of-band, update policies in place, destroy + TooManyRequests: Please wait and consider throttling your request speed + - !!! list enumerates the deployed user group + CloudflareHttpError: null + +test/Cloudflare/Iam/UserGroupMembership.test.ts + - !!! create, verify out-of-band, replace when the user group changes, destroy + Error: no accepted account members found + - !!! list enumerates memberships across all user groups in the account + Error: no accepted account members found + +test/Cloudflare/Images/SigningKey.test.ts + - !!! list enumerates the account's signing keys + TooManyRequests: Please wait and consider throttling your request speed + - !!! surfaces the typed ImagesAccessNotEnabled error on unentitled accounts + AssertionError: expected 'TooManyRequests' to deeply equal 'ImagesAccessNotEnabled' + +test/Cloudflare/Images/Variant.test.ts + - !!! create, update in place, and delete a variant + Unauthorized: Authentication error + - !!! replace a variant when the name changes + Unauthorized: Authentication error + +test/Cloudflare/Intel/IndicatorFeed.test.ts + - !!! unentitled accounts surface the typed IndicatorFeedsNotEntitled error + AssertionError: expected 'Forbidden' to deeply equal 'IndicatorFeedsNotEntitled' + +test/Cloudflare/KeylessCertificate/KeylessCertificate.test.ts + - surfaces the typed KeylessSslNotAvailable error on non-Enterprise zones + Error: Please wait and consider throttling your request speed + +test/Cloudflare/LeakedCredentialCheck/Detection.test.ts + - !!! !!! LeakedCredentialDetection > list enumerates custom detections across all zones + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/LeakedCredentialCheck/LeakedCredentialCheck.test.ts + - !!! LeakedCredentialCheck > enables leaked credential checks and restores the baseline on destroy + Error: zone "alchemy-test-2.us" not found in account + - !!! LeakedCredentialCheck > list enumerates the check across all zones + Error: zone "alchemy-test-2.us" not found in account + - !!! LeakedCredentialCheck > surfaces the typed DetectionQuotaExceeded error on unentitled zones + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/LoadBalancer/LoadBalancer.test.ts + - !!! surfaces the typed LoadBalancingNotEnabledForZone error without the LB subscription + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/LoadBalancer/Monitor.test.ts + - list returns an array of monitor attributes + TooManyRequests: Please wait and consider throttling your request speed + - surfaces the typed MonitorIntervalOutOfRange error without the LB subscription + AssertionError: expected 'TooManyRequests' to deeply equal 'MonitorIntervalOutOfRange' + +test/Cloudflare/LoadBalancer/MonitorGroup.test.ts + - list returns an array of monitor groups + Unauthorized: Authentication error + - surfaces the typed MonitorGroupsNotEnabled error on non-Enterprise accounts + AssertionError: expected 'CloudflareHttpError' to deeply equal 'MonitorGroupsNotEnabled' + +test/Cloudflare/LoadBalancer/Pool.test.ts + - surfaces the typed PoolAccessFailed error without the LB subscription + AssertionError: expected 'TooManyRequests' to deeply equal 'PoolAccessFailed' + +test/Cloudflare/LoadBalancer/_diag.test.ts + - !!! diag list + Unauthorized: Authentication error + +test/Cloudflare/Logpush/Job.test.ts + - !!! create, update, and delete an account-scoped job pushing to R2 + UnknownCloudflareError: Invalid API Token + - !!! list enumerates the deployed Logpush job + UnknownCloudflareError: Invalid API Token + +test/Cloudflare/LogsControl/CmbConfig.test.ts + - !!! list tolerates the typed LogsControlNotAuthorized error on unentitled accounts + Forbidden: Authentication error + - !!! surfaces the typed LogsControlNotAuthorized error on unentitled accounts + AssertionError: expected 'Forbidden' to deeply equal 'LogsControlNotAuthorized' + +test/Cloudflare/LogsControl/RetentionFlag.test.ts + - !!! surfaces the typed LogsControlNotAuthorized error on unentitled zones + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/MagicCloudNetworking/CatalogSync.test.ts + - !!! list enumerates account catalog syncs + CloudflareHttpError: null + - !!! unentitled accounts surface the typed FeatureNotEnabled error + CloudflareHttpError: null + +test/Cloudflare/MagicCloudNetworking/CloudIntegration.test.ts + - !!! list returns a well-typed array of integrations + CloudflareHttpError: null + - !!! unentitled accounts surface the typed FeatureNotEnabled error + CloudflareHttpError: null + +test/Cloudflare/MagicCloudNetworking/OnRamp.test.ts + - !!! list returns on-ramps or a typed [] when unentitled + TooManyRequests: Please wait and consider throttling your request speed + - !!! unentitled accounts surface the typed FeatureNotEnabled error + CloudflareHttpError: null + +test/Cloudflare/MagicNetworkMonitoring/Config.test.ts + - !!! MagicNetworkMonitoring.Config list > list enumerates the account MNM config + CloudflareHttpError: null + +test/Cloudflare/MagicNetworkMonitoring/MagicNetworkMonitoring.test.ts + - !!! MagicNetworkMonitoring > creates a threshold rule, updates it in place, and replaces on type change + TooManyRequests: Please wait and consider throttling your request speed + - !!! MagicNetworkMonitoring > creates, updates in place, and deletes the account config + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/MagicNetworkMonitoring/Rule.test.ts + - !!! MagicNetworkMonitoring.Rule > list enumerates the deployed rule + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/MagicTransit/App.test.ts + - !!! list enumerates account apps (well-typed [] when unentitled) + TooManyRequests: Please wait and consider throttling your request speed + - !!! unentitled accounts surface the typed MagicWanUnauthorized error + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/MagicTransit/IpsecTunnel.test.ts + - !!! unentitled accounts surface the typed MagicTransitNotOnboarded error + AssertionError: expected [ 'MagicTransitNotOnboarded', …(1) ] to include 'CloudflareHttpError' + +test/Cloudflare/MagicTransit/Site.test.ts + - !!! list enumerates account Magic WAN sites + Forbidden: Authentication error + - !!! unentitled accounts surface the typed MagicWanUnauthorized error + AssertionError: expected [ 'MagicWanUnauthorized', 'Forbidden' ] to include 'CloudflareHttpError' + +test/Cloudflare/MagicTransit/SiteAcl.test.ts + - !!! unentitled accounts surface the typed Magic WAN error for ACL listing + AssertionError: expected [ 'MagicWanUnauthorized', 'Forbidden' ] to include 'TooManyRequests' + +test/Cloudflare/MagicTransit/SiteLan.test.ts + - !!! list returns a well-typed array (empty on unentitled accounts) + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/MagicTransit/StaticRoute.test.ts + - !!! list returns a well-typed array of routes + Forbidden: Authentication error + - !!! unentitled accounts surface the typed MagicTransitNotOnboarded error + AssertionError: expected [ 'MagicTransitNotOnboarded', …(1) ] to include 'TooManyRequests' + +test/Cloudflare/ManagedTransforms/ManagedTransforms.test.ts + - !!! ManagedTransforms > deploy with no transforms named adopts the singleton without writing + Error: zone "alchemy-test-2.us" not found in account + - !!! ManagedTransforms > destroy restores a transform that was enabled before management + Error: zone "alchemy-test-2.us" not found in account + - !!! ManagedTransforms > manages a named transform, updates in place, and restores it on destroy + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/MtlsCertificate/MtlsCertificate.test.ts + - !!! MtlsCertificate > create and delete a CA certificate + Unauthorized: Authentication error + - !!! MtlsCertificate > create and delete a leaf certificate with private key + TooManyRequests: Please wait and consider throttling your request speed + - !!! MtlsCertificate > list enumerates the deployed mTLS certificate + TooManyRequests: Please wait and consider throttling your request speed + - !!! MtlsCertificate > replaces the certificate when the PEM changes + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/NetworkInterconnects/Settings.test.ts + - !!! list enumerates the CNI settings singleton + TooManyRequests: Please wait and consider throttling your request speed + - !!! pins the default ASN, updates in place, and restores the original on destroy + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Organization/Organization.test.ts + - !!! list either enumerates organizations or tolerates the unentitled account + Unauthorized: Authentication failed (status: 400) + - !!! unentitled accounts surface the typed Forbidden error + Unauthorized: Authentication failed (status: 400) + +test/Cloudflare/OriginCaCertificate/OriginCaCertificate.test.ts + - !!! issue, verify, and revoke a certificate + HostnameNotAuthorized: Failed to validate requested hostname origin.alchemy-test-2.us: This zone is either not part of your account, or you do not have access to it. Please contact support if using a multi-user organization. + - !!! list enumerates issued certificates + HostnameNotAuthorized: Failed to validate requested hostname origin.alchemy-test-2.us: This zone is either not part of your account, or you do not have access to it. Please contact support if using a multi-user organization. + - !!! replacement on hostnames change + HostnameNotAuthorized: Failed to validate requested hostname origin.alchemy-test-2.us: This zone is either not part of your account, or you do not have access to it. Please contact support if using a multi-user organization. + - !!! replacement on requestedValidity change + HostnameNotAuthorized: Failed to validate requested hostname origin.alchemy-test-2.us: This zone is either not part of your account, or you do not have access to it. Please contact support if using a multi-user organization. + +test/Cloudflare/OriginPostQuantumEncryption/OriginPostQuantumEncryption.test.ts + - !!! OriginPostQuantumEncryption > list enumerates the setting across all zones + Error: zone "alchemy-test-2.us" not found in account + - !!! OriginPostQuantumEncryption > no-op redeploy converges without changing the setting + Error: zone "alchemy-test-2.us" not found in account + - !!! OriginPostQuantumEncryption > pins the setting and restores the original value on destroy + Error: zone "alchemy-test-2.us" not found in account + - !!! OriginPostQuantumEncryption > updates the value in place and keeps the captured initial value + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/OriginTlsClientAuth/Certificate.test.ts + - !!! OriginTlsClientAuthCertificate > list enumerates the deployed zone client certificate + Error: zone "alchemy-test-2.us" not found in account + - !!! OriginTlsClientAuthCertificate > replaces the certificate when the PEM changes + Error: zone "alchemy-test-2.us" not found in account + - !!! OriginTlsClientAuthCertificate > uploads and deletes a zone client certificate + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/OriginTlsClientAuth/HostnameAssociation.test.ts + - !!! HostnameAssociation > associates a hostname, updates cert and enablement in place, voids on destroy + Error: zone "alchemy-test-2.us" not found in account + - !!! HostnameAssociation > replaces the association when the hostname changes + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/OriginTlsClientAuth/HostnameCertificate.test.ts + - !!! list enumerates the deployed hostname certificate + Error: zone "alchemy-test-2.us" not found in account + - !!! replaces the hostname certificate when the PEM changes + Error: zone "alchemy-test-2.us" not found in account + - !!! uploads and deletes a hostname client certificate + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/OriginTlsClientAuth/Setting.test.ts + - !!! Setting > enables AOP and restores the original value on destroy + Error: zone "alchemy-test-2.us" not found in account + - !!! Setting > list enumerates the setting across all zones + Error: zone "alchemy-test-2.us" not found in account + - !!! Setting > updates the enabled flag in place + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/PageRule/PageRule.test.ts + - !!! adoption — existing rule errors without adopt, takes over with adopt(true) + Error: zone "alchemy-test-2.us" not found in account + - !!! create, verify out-of-band, and destroy a page rule + Error: zone "alchemy-test-2.us" not found in account + - !!! list enumerates the deployed page rule + Error: zone "alchemy-test-2.us" not found in account + - !!! updating actions, status, priority and target syncs in place + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/PageShield/Policy.test.ts + - !!! surfaces the typed PolicyQuotaExceeded error on unentitled zones + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/PageShield/Settings.test.ts + - !!! enables Page Shield, updates in place, and restores the baseline on destroy + Error: zone "alchemy-test-2.us" not found in account + - !!! list enumerates Page Shield settings across all zones + Error: zone "alchemy-test-2.us" not found in account + - !!! surfaces the typed NotEntitled error for plan-gated flags + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Pages/Deployment.test.ts + - create, replace on branch change, and destroy a deployment + Error: Test timed out in 120000ms. + +test/Cloudflare/Pages/Domain.test.ts + - attach and detach a custom domain + Error: Test timed out in 120000ms. + - changing the domain name triggers replacement + Error: Test timed out in 120000ms. + +test/Cloudflare/Pages/Project.test.ts + - changing the name triggers replacement + CloudflareHttpError: null + - create and delete a project with generated name + Forbidden: Authentication error + - list enumerates the deployed project + CloudflareHttpError: null + - update mutable props in place (same project id) + CloudflareHttpError: null + +test/Cloudflare/Pipelines/LegacyPipeline.test.ts + - !!! legacy pipeline: create, in-place update, replace on name change + UnknownCloudflareError: Invalid API Token + +test/Cloudflare/Pipelines/Pipeline.test.ts + - !!! list enumerates the deployed pipeline + UnknownCloudflareError: Invalid API Token + +test/Cloudflare/Pipelines/Pipelines.test.ts + - !!! end-to-end: stream → sql pipeline → r2 sink, replacement on sink change + TooManyRequests: Please wait and consider throttling your request speed + - !!! stream: create with defaults, patch http in place, replace on schema change + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Pipelines/Sink.test.ts + - !!! list enumerates the deployed sink + UnknownCloudflareError: Invalid API Token + +test/Cloudflare/Pipelines/Stream.test.ts + - !!! list enumerates the deployed pipeline stream + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Queue/QueueConsumer.test.ts + - create, update settings, replace script, delete + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - fails clearly when queue has consumer for different script + AssertionError: expected '{"_id":"Exit","_tag":"Failure","cause…' to contain 'fails-clearly-when-queue-has-consumer…' + - list enumerates the deployed consumer + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + +test/Cloudflare/Queue/RoundTrip.test.ts + - send → subscribe handler → DO state → polled by test client + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Queue/Subscription.test.ts + - !!! Subscription > create r2 event subscription into a queue and destroy it + Unauthorized: Authentication error + +test/Cloudflare/R2/Bucket.test.ts + - create and delete bucket with default props + TooManyRequests: Please wait and consider throttling your request speed + - create, update, delete bucket + TooManyRequests: Please wait and consider throttling your request speed + - destroying a bucket empties its objects first + TooManyRequests: Please wait and consider throttling your request speed + - existing bucket (matching name) is silently adopted without --adopt + TooManyRequests: Please wait and consider throttling your request speed + - lifecycle rules are added, updated, and removed + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/R2/BucketEventNotification.test.ts + - changing the queue triggers a replacement + TooManyRequests: Please wait and consider throttling your request speed + - create, update rules in place, and delete + TooManyRequests: Please wait and consider throttling your request speed + - list enumerates deployed bucket event notifications + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/R2/BucketSippy.test.ts + - list enumerates buckets that have Sippy enabled + TooManyRequests: Please wait and consider throttling your request speed + - reads the disabled baseline and surfaces typed errors without external creds + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/R2/CustomDomain.test.ts + - creates, updates, and deletes a bucket custom domain + TooManyRequests: Please wait and consider throttling your request speed + - creates, updates, and deletes a bucket with multiple custom domains + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/R2/R2Bucket.test.ts + - list enumerates the deployed R2 bucket + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/R2DataCatalog/R2DataCatalog.test.ts + - enable, sync maintenance config, register credential, destroy + TooManyRequests: Please wait and consider throttling your request speed + - list enumerates the deployed catalog + TooManyRequests: Please wait and consider throttling your request speed + - re-enables after out-of-band disable + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/RealtimeKit/App.test.ts + - create (or adopt), verify out-of-band, destroy forgets but keeps the app + TooManyRequests: Please wait and consider throttling your request speed + - list enumerates the deployed RealtimeKit app + TooManyRequests: Please wait and consider throttling your request speed + - unentitled accounts surface the typed Forbidden error + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/RegionalHostname/RegionalHostname.test.ts + - !!! regional hostname lifecycle (typed error on unentitled zones) + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Registrar/Domain.test.ts + - !!! Domain > adopts a registered domain, no-op syncs, and never releases it on destroy + TooManyRequests: Please wait and consider throttling your request speed + - !!! Domain > list enumerates registrar domains on the account + TooManyRequests: Please wait and consider throttling your request speed + - !!! Domain > updates settings in place and restores the baseline on destroy + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/ResourceSharing/Share.test.ts + - !!! list enumerates the account's sent shares (typed Attributes), and includes a deployed share when entitled + Forbidden: Authentication error + - !!! reads succeed and write-blocked accounts surface the typed Forbidden error + Forbidden: Authentication error + +test/Cloudflare/ResourceSharing/ShareResource.test.ts + - !!! list enumerates share resources across the account's sent shares (typed Attributes), and includes a deployed entry when entitled + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Rules/List.test.ts + - !!! adopts an existing list with the same name + TooManyRequests: Please wait and consider throttling your request speed + - !!! changing kind replaces the list + Forbidden: Authentication error + - !!! create and delete an ip list with default name + Forbidden: Authentication error + - !!! recreates after out-of-band delete + Forbidden: Authentication error + - !!! update description and items in place (same listId) + Forbidden: Authentication error + +test/Cloudflare/Ruleset/AccountEntrypoint.test.ts + - !!! account entrypoint lifecycle (typed PhaseNotEntitled on unentitled accounts) + AssertionError: expected 'TooManyRequests' to deeply equal 'PhaseNotEntitled' + - !!! list enumerates the account phase entrypoints + AssertionError: expected 'TooManyRequests' to deeply equal 'PhaseNotEntitled' + +test/Cloudflare/Ruleset/CustomRuleset.test.ts + - !!! custom ruleset lifecycle (typed PhaseNotEntitled on unentitled accounts) + AssertionError: expected 'TooManyRequests' to deeply equal 'PhaseNotEntitled' + - !!! list enumerates the deployed custom ruleset + AssertionError: expected 'TooManyRequests' to deeply equal 'PhaseNotEntitled' + +test/Cloudflare/Ruleset/Ruleset.test.ts + - !!! Ruleset > creates and tears down a ruleset whose zone is provisioned in the same deploy + Forbidden: Requires permission "com.cloudflare.api.account.zone.create" to create zones for the selected account + - !!! Ruleset > creates, updates, and deletes a zone phase entrypoint ruleset + Forbidden: Requires permission "com.cloudflare.api.account.zone.create" to create zones for the selected account + +test/Cloudflare/Rum/Rule.test.ts + - !!! RumRule > create, update in place, and delete a rule + Error: zone "alchemy-test-3.us" not found in account + - !!! RumRule > list enumerates rules across all rulesets + Error: zone "alchemy-test-3.us" not found in account + - !!! RumRule > recreates after out-of-band delete + Error: zone "alchemy-test-3.us" not found in account + +test/Cloudflare/Rum/Site.test.ts + - create and delete a host (gray-clouded) site + TooManyRequests: Please wait and consider throttling your request speed + - list enumerates the deployed RUM site + TooManyRequests: Please wait and consider throttling your request speed + - recreates after out-of-band delete + TooManyRequests: Please wait and consider throttling your request speed + - update mutable props in place (same siteTag) + TooManyRequests: Please wait and consider throttling your request speed + - zone (orange-clouded) site with autoInstall, replaced on identity flip + Error: Please wait and consider throttling your request speed + +test/Cloudflare/SchemaValidation/OperationSetting.test.ts + - !!! list enumerates the deployed per-operation override + Error: zone "alchemy-test-2.us" not found in account + - !!! sets a per-operation override, updates it in place, and clears it on destroy + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/SchemaValidation/Schema.test.ts + - !!! list enumerates the deployed schema across all zones + Error: zone "alchemy-test-2.us" not found in account + - !!! uploads a schema, enables in place, replaces on disable and source change, destroys + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/SchemaValidation/Settings.test.ts + - !!! list enumerates the schema validation settings across all zones + Error: zone "alchemy-test-2.us" not found in account + - !!! pins zone settings, updates in place, and restores the baseline on destroy + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/SecretsStore/Secret.test.ts + - list enumerates the deployed secret across stores + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/SecretsStore/SecretsStore.test.ts + - !!! list enumerates the deployed secrets store + Unauthorized: Authentication error + +test/Cloudflare/SecurityTxt/SecurityTxt.test.ts + - !!! SecurityTxt > creates a security.txt, verifies out-of-band, and deletes on destroy + Error: zone "alchemy-test-2.us" not found in account + - !!! SecurityTxt > disables the file without deleting it, then destroy removes it + Error: zone "alchemy-test-2.us" not found in account + - !!! SecurityTxt > list enumerates configured security.txt files + Error: zone "alchemy-test-2.us" not found in account + - !!! SecurityTxt > updates mutable fields in place (full-replace PUT) + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Snippets/Snippet.test.ts + - !!! create with generated name, update code in place, destroy + Error: zone "alchemy-test-2.us" not found in account + - !!! renaming an explicit snippet triggers replacement + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Snippets/SnippetRules.test.ts + - !!! snippet rules — create, update, list, and destroy in dependency order + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Spectrum/Application.test.ts + - !!! surfaces the typed SpectrumProtocolNotAvailable error on unentitled zones + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Speed/TestSchedule.test.ts + - !!! adoption — existing schedule errors without adopt, takes over with adopt(true) + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Ssl/CertificatePack.test.ts + - !!! surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones + Error: zone "alchemy-test-2.us" not found + +test/Cloudflare/Ssl/UniversalSsl.test.ts + - !!! UniversalSsl > destroy restores a disabled baseline when managing from a disabled zone + Error: zone "alchemy-test-2.us" not found in account + - !!! UniversalSsl > disables Universal SSL and restores the original value on destroy + Error: zone "alchemy-test-2.us" not found in account + - !!! UniversalSsl > list enumerates the setting across all zones + Error: zone "alchemy-test-2.us" not found in account + - !!! UniversalSsl > updates enabled in place and keeps the captured initial value + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/StateStore/State.test.ts + - State > DELETE /resources/:fqn (deleteState) removes a single resource + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - State > DELETE /stacks/:stack (no stage) removes the stack from listStacks + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - State > DELETE /stacks/:stack?stage=... clears one stage but leaves the stack registered + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - State > DELETE /state/stacks/:stack wipes the test namespace (cleanup) + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - State > GET /output (getStackOutput) reads back the persisted output + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - State > GET /output returns undefined for an un-deployed stage + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - State > GET /replaced-resources (getReplacedResources) returns status===replaced rows + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - State > GET /resources (listResources) returns the FQNs in the stage + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - State > GET /resources/:fqn (getState) reads back the persisted value + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - State > GET /resources/:fqn returns undefined for a missing fqn + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - State > GET /stacks (listStacks) includes the registered test stack + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - State > GET /stacks/:stack/stages (listStages) includes the test stage + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - State > GET+PUT interleaved 30x5 — engine traffic pattern + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - State > getVersion returns the current STATE_STORE_VERSION + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - State > PUT /output (setStackOutput) persists a stack output + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - State > PUT /resources/:fqn (setState) persists a resource + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - State > setState 100x concurrent — surfaces racy transient failures + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + - State > setState 100x sequential — surfaces transient failures + AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first. + +test/Cloudflare/Stream/LiveInput.test.ts + - !!! create, update in place, and delete a live input + Unauthorized: Authentication error + - !!! recreates after out-of-band delete + Unauthorized: Authentication error + +test/Cloudflare/Stream/LiveInputOutput.test.ts + - !!! create, toggle enabled in place, replace destination, and delete + Unauthorized: Authentication error + - !!! recreates after out-of-band delete + Unauthorized: Authentication error + +test/Cloudflare/Stream/SigningKey.test.ts + - !!! create and delete a signing key + TooManyRequests: Please wait and consider throttling your request speed + - !!! list enumerates the deployed signing key + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Stream/Watermark.test.ts + - !!! create and delete a watermark with default name + CloudflareHttpError: null + - !!! list enumerates the deployed watermark + CloudflareHttpError: null + - !!! prop change replaces the watermark (create-only resource) + CloudflareHttpError: null + +test/Cloudflare/Stream/Webhook.test.ts + - !!! configure, update, and delete the account webhook + TooManyRequests: Please wait and consider throttling your request speed + - !!! list enumerates the account Stream webhook + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Tags/AccountResourceTags.test.ts + - adoption — existing tags error without adopt, take over with adopt(true) + TooManyRequests: Please wait and consider throttling your request speed + - changing resourceId triggers replacement + Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time. + - create, update, and clear tags on a KV namespace + Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time. + - list enumerates account-wide tagged resources + Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time. + +test/Cloudflare/Tags/ZoneResourceTags.test.ts + - !!! create, update, and clear tags on a DNS record + Error: zone "alchemy-test-2.us" not found in account + - !!! list enumerates tagged zone-scoped resources + Error: zone "alchemy-test-2.us" not found in account + - !!! tag the zone itself and clear on destroy + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/TokenValidation/TokenValidation.test.ts + - !!! surfaces the typed TokenValidationNotEntitled error on unentitled accounts + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Tunnel/Configuration.test.ts + - !!! Tunnel Configuration > list enumerates configurations across all tunnels + Unauthorized: Authentication error + +test/Cloudflare/Tunnel/HostnameRoute.test.ts + - !!! create, update comment in place, and destroy a hostname route + Unauthorized: Authentication error + - !!! list enumerates the deployed hostname route + Unauthorized: Authentication error + +test/Cloudflare/Tunnel/Route.test.ts + - !!! adopt: takes over a pre-existing route + Unauthorized: Authentication error + - !!! create and delete route with default props + Unauthorized: Authentication error + - !!! list enumerates the deployed route + Unauthorized: Authentication error + - !!! updating the comment patches in place + Unauthorized: Authentication error + +test/Cloudflare/Tunnel/VirtualNetwork.test.ts + - !!! create, verify, and destroy a virtual network + Unauthorized: Authentication error + - !!! list enumerates the deployed virtual network + Unauthorized: Authentication error + - !!! recreates after out-of-band delete + Unauthorized: Authentication error + - !!! update name and comment in place (same id) + Unauthorized: Authentication error + +test/Cloudflare/Tunnel/WarpConnector.test.ts + - create, rename in place, and destroy a WARP Connector tunnel + TooManyRequests: Please wait and consider throttling your request speed + - list enumerates the deployed WARP Connector tunnel + CloudflareHttpError: null + +test/Cloudflare/Turnstile/Widget.test.ts + - create and delete a widget with default name + TooManyRequests: Please wait and consider throttling your request speed + - list enumerates the deployed widget + TooManyRequests: Please wait and consider throttling your request speed + - recreates after out-of-band delete + TooManyRequests: Please wait and consider throttling your request speed + - update mutable props in place (same sitekey) + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/UrlNormalization/UrlNormalization.test.ts + - !!! UrlNormalization > applies Cloudflare defaults when scope and type are omitted + Error: zone "alchemy-test-2.us" not found in account + - !!! UrlNormalization > configures URL normalization and resets to defaults on destroy + Error: zone "alchemy-test-2.us" not found in account + - !!! UrlNormalization > list enumerates URL normalization across all zones + Error: zone "alchemy-test-2.us" not found in account + - !!! UrlNormalization > updates scope and type in place + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Vectorize/VectorizeIndex.test.ts + - !!! create and delete index with explicit dimensions + Unauthorized: Authentication error + - !!! create index from a preset + Unauthorized: Authentication error + - !!! list enumerates the deployed index + Unauthorized: Authentication error + - !!! replaces index when dimensions change + Unauthorized: Authentication error + +test/Cloudflare/Vectorize/VectorizeIndexBinding.test.ts + - !!! VectorizeIndex.bind exercises the client surface + Unauthorized: Authentication error + +test/Cloudflare/Vectorize/VectorizeMetadataIndex.test.ts + - !!! Cloudflare.VectorizeMetadataIndex > create and delete a metadata index + CloudflareHttpError: null + - !!! Cloudflare.VectorizeMetadataIndex > destroy is idempotent when the parent index was deleted out-of-band + CloudflareHttpError: null + - !!! Cloudflare.VectorizeMetadataIndex > list enumerates the deployed metadata index + CloudflareHttpError: null + - !!! Cloudflare.VectorizeMetadataIndex > multiple metadata indexes on the same parent coexist + CloudflareHttpError: null + - !!! Cloudflare.VectorizeMetadataIndex > replacing the parent index also replaces the metadata index + CloudflareHttpError: null + +test/Cloudflare/VpcService/VpcService.test.ts + - create vpc service with ipv4 host + TooManyRequests: Please wait and consider throttling your request speed + - create, update, delete vpc service + CloudflareHttpError: null + - list enumerates the deployed vpc service + CloudflareHttpError: null + +test/Cloudflare/VpcService/VpcServiceRef.test.ts + - reference vpc service by name and by id + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/VulnerabilityScanner/Credential.test.ts + - !!! list enumerates the deployed credential + Unauthorized: Authentication error + +test/Cloudflare/VulnerabilityScanner/CredentialSet.test.ts + - !!! create, rename, and destroy a credential set + Unauthorized: Authentication error + - !!! credential lifecycle — create, mutate in place, rotate value, destroy + Unauthorized: Authentication error + - !!! list enumerates the deployed credential set + Unauthorized: Authentication error + - !!! moving a credential to a different set triggers replacement + Unauthorized: Authentication error + +test/Cloudflare/VulnerabilityScanner/TargetEnvironment.test.ts + - !!! create, verify, and destroy a target environment + Error: zone "alchemy-test-2.us" not found in account + - !!! list enumerates the deployed target environment + Error: zone "alchemy-test-2.us" not found in account + - !!! updating name and description patches in place; clearing description + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/WaitingRoom/Settings.test.ts + - !!! Settings > list enumerates the settings across all zones + Error: zone "alchemy-test-2.us" not found in account + - !!! Settings > pins the settings to the default baseline without touching the API + Error: zone "alchemy-test-2.us" not found in account + - !!! Settings > surfaces the typed ZoneNotEntitled error when enabling on unentitled zones + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/WaitingRoom/WaitingRoom.test.ts + - !!! surfaces the typed ZoneNotEntitled error on unentitled zones + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Web3/Web3Hostname.test.ts + - !!! surfaces the typed Web3HostnameNotEntitled error on unentitled accounts + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Website/StaticSite.test.ts + - StaticSite: a bundle-only change keeps the asset manifest (hash.assets stable) + CloudflareHttpError: null + - StaticSite: class form deploys and serves the built assets + TooManyRequests: Please wait and consider throttling your request speed + - StaticSite: editing a source file republishes the assets in a single deploy + TooManyRequests: Please wait and consider throttling your request speed + - StaticSite: relocating the project (and deleting the old one) preserves hash.assets + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Website/Vite.test.ts + - Vite: relocating rootDir (and deleting the old one) is a no-op when sources are identical + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + +test/Cloudflare/Workers/AccountSetting.test.ts + - AccountSetting > flips green compute, updates in place, and restores the baseline on destroy + CloudflareHttpError: null + - AccountSetting > list returns the account settings singleton + TooManyRequests: Please wait and consider throttling your request speed + - AccountSetting > no-op deploy when desired settings already match the live account + CloudflareHttpError: null + +test/Cloudflare/Workers/ObservabilityDestination.test.ts + - create and delete a destination with default name + Forbidden: Authentication error + - list enumerates the deployed destination + TooManyRequests: Please wait and consider throttling your request speed + - replacement on name change (new slug, old destination removed) + TooManyRequests: Please wait and consider throttling your request speed + - update url, headers, and enabled in place (same slug) + TooManyRequests: Please wait and consider throttling your request speed + +test/Cloudflare/Workers/PrebuiltWorker.test.ts + - Cloudflare.Worker with bundle: false > uploads a prebuilt module graph byte-for-byte + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + +test/Cloudflare/Workers/Route.test.ts + - !!! adoption — existing route errors without adopt, takes over with adopt(true) + Error: zone "alchemy-test-2.us" not found in account + - !!! create and delete an opt-out route (no script) + Error: zone "alchemy-test-2.us" not found in account + - !!! list enumerates the deployed route across all zones + Error: zone "alchemy-test-2.us" not found in account + - !!! route to a Worker, then update pattern and script in place + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Workers/Worker.test.ts + - Cloudflare.Worker > create, update, delete worker + Unauthorized: Authentication error + - Cloudflare.Worker > downstream referencing worker.url is not re-updated when the worker changes + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + - Cloudflare.Worker > worker.durableObjectNamespaces stability across DO and worker changes + Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details. + +test/Cloudflare/WorkersForPlatforms/DispatchNamespace.test.ts + - !!! create, replace, and destroy a dispatch namespace + Forbidden: You do not have access to dispatch namespaces. You can purchase it within the Cloudflare dashboard here: https://dash.cloudflare.com?to=/:account/workers-for-platforms - if you are an Enterprise customer please contact your account team. + - !!! list enumerates the deployed dispatch namespace + Forbidden: You do not have access to dispatch namespaces. You can purchase it within the Cloudflare dashboard here: https://dash.cloudflare.com?to=/:account/workers-for-platforms - if you are an Enterprise customer please contact your account team. + - !!! upload, update in place, and destroy a namespace script + Forbidden: You do not have access to dispatch namespaces. You can purchase it within the Cloudflare dashboard here: https://dash.cloudflare.com?to=/:account/workers-for-platforms - if you are an Enterprise customer please contact your account team. + +test/Cloudflare/Zaraz/ZarazConfig.test.ts + - !!! ZarazConfig > list enumerates Zaraz config across all zones + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Zone/CustomNameservers.test.ts + - !!! pins the custom nameserver toggle and leaves the zone at its baseline on destroy + Error: zone "alchemy-test-2.us" not found in account + - !!! surfaces the typed CustomNameserverSetNotFound error when enabling without an account nameserver set + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Zone/Hold.test.ts + - !!! list enumerates the hold state across all zones + Error: zone "alchemy-test-2.us" not found in account + - !!! surfaces the typed ZoneHoldsRequireEnterprise error on non-Enterprise zones + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Zone/Setting.test.ts + - !!! changing settingId replaces — old setting restored, new setting pinned + Error: zone "alchemy-test-2.us" not found in account + - !!! list enumerates the deployed (zone, setting) pair + Error: zone "alchemy-test-2.us" not found in account + - !!! pins a toggle setting and restores the original value on destroy + Error: zone "alchemy-test-2.us" not found in account + - !!! updates a numeric setting in place and keeps the captured initial value + Error: zone "alchemy-test-2.us" not found in account + +test/Cloudflare/Zone/Zone.test.ts + - !!! adoption — existing zone errors without adopt, takes over with adopt(true) + Forbidden: Requires permission "com.cloudflare.api.account.zone.create" to create zones for the selected account + - !!! create zone retains by default — destroy() opts in to deletion + Forbidden: Requires permission "com.cloudflare.api.account.zone.create" to create zones for the selected account + - !!! create zone retains by default — survives stack.destroy() + Forbidden: Requires permission "com.cloudflare.api.account.zone.create" to create zones for the selected account + - !!! list enumerates every zone in the account + AssertionError: expected undefined to be defined diff --git a/cf-temp-results-2.json b/cf-temp-results-2.json new file mode 100644 index 000000000..986c057e4 --- /dev/null +++ b/cf-temp-results-2.json @@ -0,0 +1 @@ +{"numTotalTestSuites":366,"numPassedTestSuites":259,"numFailedTestSuites":107,"numPendingTestSuites":0,"numTotalTests":969,"numPassedTests":156,"numFailedTests":190,"numPendingTests":623,"numTodoTests":0,"snapshot":{"added":0,"failure":false,"filesAdded":0,"filesRemoved":0,"filesRemovedList":[],"filesUnmatched":0,"filesUpdated":0,"matched":0,"total":0,"unchecked":0,"uncheckedKeysByFile":[],"unmatched":0,"updated":0,"didUpdate":false},"startTime":1782381326629,"success":false,"testResults":[{"assertionResults":[{"ancestorTitles":[],"fullName":"building the Cloudflare provider layers should not fail for unknown profile","status":"passed","title":"building the Cloudflare provider layers should not fail for unknown profile","duration":104.24589999999807,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381756628,"endTime":1782381756732.2458,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Providers.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a self_hosted application gated by a reusable policy","status":"failed","title":"create and delete a self_hosted application gated by a reusable policy","duration":85690.1829,"failureMessages":["Forbidden: Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create and delete a warp device-enrollment application","status":"passed","title":"create and delete a warp device-enrollment application","duration":1898.2896000000037,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed access application","status":"skipped","title":"list enumerates the deployed access application","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update policies in place keeps the applicationId stable","status":"skipped","title":"update policies in place keeps the applicationId stable","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381443584,"endTime":1782381529274.1829,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Application.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"legacy bookmark creation surfaces the typed AccessBookmarkNotFound error","status":"skipped","title":"legacy bookmark creation surfaces the typed AccessBookmarkNotFound error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy bookmark","status":"skipped","title":"create, update in place, and destroy bookmark","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates access bookmarks at the account scope","status":"skipped","title":"list enumerates access bookmarks at the account scope","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Bookmark.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed AccessCertificateQuotaExceeded error","status":"skipped","title":"unentitled accounts surface the typed AccessCertificateQuotaExceeded error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update hostnames, replace on PEM change, destroy","status":"skipped","title":"create, update hostnames, replace on PEM change, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account access certificates","status":"skipped","title":"list enumerates account access certificates","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Certificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed AccessCustomPagesNotEntitled error","status":"skipped","title":"unentitled accounts surface the typed AccessCustomPagesNotEntitled error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update html in place, replace on type change, destroy","status":"skipped","title":"create, update html in place, replace on type change, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates access custom pages at the account scope","status":"skipped","title":"list enumerates access custom pages at the account scope","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/CustomPage.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update rules, and delete group","status":"failed","title":"create, update rules, and delete group","duration":81173.9794,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"rename updates the group in place","status":"failed","title":"rename updates the group in place","duration":81029.9009,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed access group","status":"skipped","title":"list enumerates the deployed access group","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"group can be referenced from an access policy","status":"skipped","title":"group can be referenced from an access policy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381443677,"endTime":1782381524850.9795,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Group.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy an OIDC IdP","status":"failed","title":"create, verify, and destroy an OIDC IdP","duration":26768.624900000003,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name and config in place (same id)","status":"failed","title":"update name and config in place (same id)","duration":27346.3288,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed IdP","status":"failed","title":"list enumerates the deployed IdP","duration":26788.4259,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the type replaces the IdP","status":"failed","title":"changing the type replaces the IdP","duration":26812.9476,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782381553622,"endTime":1782381580971.3289,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/IdentityProvider.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed infrastructure target","status":"skipped","title":"list enumerates the deployed infrastructure target","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/InfrastructureTarget.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins the rotation interval, updates in place, and restores on destroy","status":"failed","title":"pins the rotation interval, updates in place, and restores on destroy","duration":26128.2395,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account key configuration singleton","status":"failed","title":"list returns the account key configuration singleton","duration":26092.239500000003,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782381553602,"endTime":1782381579730.2395,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/KeyConfiguration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and destroy an MCP portal","status":"failed","title":"create, update in place, and destroy an MCP portal","duration":40858.6742,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed MCP portal","status":"failed","title":"list enumerates the deployed MCP portal","duration":39962.4214,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782381487753,"endTime":1782381528611.6743,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/McpPortal.test.ts"},{"assertionResults":[{"ancestorTitles":["Organization"],"fullName":"Organization adopts the existing Access organization","status":"skipped","title":"adopts the existing Access organization","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Organization"],"fullName":"Organization toggles allow_authenticate_via_warp and restores","status":"skipped","title":"toggles allow_authenticate_via_warp and restores","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Organization"],"fullName":"Organization list returns the account Access organization","status":"failed","title":"list returns the account Access organization","duration":10439.8376,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782381516070,"endTime":1782381526509.8376,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Organization.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete basic allow policy","status":"failed","title":"create and delete basic allow policy","duration":80854.9918,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutates includes without replacing","status":"failed","title":"update mutates includes without replacing","duration":81068.7301,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopts an out-of-band reusable policy","status":"failed","title":"adopts an out-of-band reusable policy","duration":29123.53210000001,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed reusable policy","status":"failed","title":"list enumerates the deployed reusable policy","duration":81021.04140000002,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782381443604,"endTime":1782381524675.73,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Policy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update duration, and delete service token","status":"failed","title":"create, update duration, and delete service token","duration":60015.1986,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed service token","status":"failed","title":"list enumerates the deployed service token","duration":60411.421299999995,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"incrementing clientSecretVersion rotates the secret","status":"failed","title":"incrementing clientSecretVersion rotates the secret","duration":59958.10089999999,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782381466602,"endTime":1782381527015.4214,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/ServiceToken.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, and destroy tag","status":"failed","title":"create, verify out-of-band, and destroy tag","duration":25457.164,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"rename replaces the tag","status":"failed","title":"rename replaces the tag","duration":25495.154199999997,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed access tag","status":"failed","title":"list enumerates the deployed access tag","duration":26594.8997,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782381551755,"endTime":1782381578351.8997,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Tag.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete ai gateway with default props","status":"skipped","title":"create and delete ai gateway with default props","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete ai gateway","status":"skipped","title":"create, update, delete ai gateway","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed ai gateway","status":"skipped","title":"list enumerates the deployed ai gateway","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"existing ai gateway (matching id) is silently adopted without --adopt","status":"skipped","title":"existing ai gateway (matching id) is silently adopted without --adopt","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"deployed worker can call AiGateway binding (effect-native getUrl)","status":"skipped","title":"deployed worker can call AiGateway binding (effect-native getUrl)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/AiGateway.test.ts"},{"assertionResults":[{"ancestorTitles":["AiGatewaySpendingLimit"],"fullName":"AiGatewaySpendingLimit create, read-back, and delete the account spending limit","status":"skipped","title":"create, read-back, and delete the account spending limit","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewaySpendingLimit"],"fullName":"AiGatewaySpendingLimit update the spending limit in place","status":"skipped","title":"update the spending limit in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewaySpendingLimit"],"fullName":"AiGatewaySpendingLimit list enumerates the deployed account spending limit","status":"skipped","title":"list enumerates the deployed account spending limit","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/AiGatewaySpendingLimit.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"first turn against a fresh thread persists user + assistant messages","status":"skipped","title":"first turn against a fresh thread persists user + assistant messages","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"persisted chat survives across DO invocations","status":"skipped","title":"persisted chat survives across DO invocations","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"distinct thread ids map to isolated histories","status":"skipped","title":"distinct thread ids map to isolated histories","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/ChatPersistence.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"send round-trips text and turns through the RpcWorker → DO","status":"skipped","title":"send round-trips text and turns through the RpcWorker → DO","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"streamMessage yields effect/ai Response parts that decode to real classes","status":"skipped","title":"streamMessage yields effect/ai Response parts that decode to real classes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"streamed turn is persisted: a follow-up send recalls it","status":"skipped","title":"streamed turn is persisted: a follow-up send recalls it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/ChatPersistenceRpc.test.ts"},{"assertionResults":[{"ancestorTitles":["AiGatewayDataset"],"fullName":"AiGatewayDataset create, update, delete a dataset on a gateway","status":"skipped","title":"create, update, delete a dataset on a gateway","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewayDataset"],"fullName":"AiGatewayDataset replaces dataset when the gateway changes","status":"skipped","title":"replaces dataset when the gateway changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewayDataset"],"fullName":"AiGatewayDataset recreates a dataset after out-of-band delete","status":"skipped","title":"recreates a dataset after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewayDataset"],"fullName":"AiGatewayDataset list enumerates datasets across gateways","status":"skipped","title":"list enumerates datasets across gateways","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/Dataset.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update elements (new deployed version), rename, delete","status":"skipped","title":"create, update elements (new deployed version), rename, delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces route when the gateway changes","status":"skipped","title":"replaces route when the gateway changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates a route after out-of-band delete","status":"skipped","title":"recreates a route after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates routes across all gateways","status":"skipped","title":"list enumerates routes across all gateways","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/DynamicRouting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, noop, replace, delete an evaluation","status":"skipped","title":"create, noop, replace, delete an evaluation","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed evaluation","status":"skipped","title":"list enumerates the deployed evaluation","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/Evaluation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed worker generates text via AiGateway-backed LanguageModel","status":"skipped","title":"deployed worker generates text via AiGateway-backed LanguageModel","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"deployed worker streams text via AiGateway-backed LanguageModel","status":"skipped","title":"deployed worker streams text via AiGateway-backed LanguageModel","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"streamed parts respect ordering: text-start → text-delta+ → text-end → finish","status":"skipped","title":"streamed parts respect ordering: text-start → text-delta+ → text-end → finish","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"DEBUG: dump raw Workers AI SSE stream","status":"skipped","title":"DEBUG: dump raw Workers AI SSE stream","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"stream finish part reports the real token counts and a `stop` reason","status":"skipped","title":"stream finish part reports the real token counts and a `stop` reason","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"stream emits multiple text-delta chunks for a long-form response","status":"skipped","title":"stream emits multiple text-delta chunks for a long-form response","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"persisted chat survives across DO invocations","status":"skipped","title":"persisted chat survives across DO invocations","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"deployed worker invokes a tool via AiGateway-backed LanguageModel","status":"skipped","title":"deployed worker invokes a tool via AiGateway-backed LanguageModel","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"streams tool-call parts via AiGateway-backed LanguageModel","status":"skipped","title":"streams tool-call parts via AiGateway-backed LanguageModel","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"concatenated tool-params-delta payloads parse back into the requested arguments","status":"skipped","title":"concatenated tool-params-delta payloads parse back into the requested arguments","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"streams Effect-native parts and prints them live","status":"skipped","title":"streams Effect-native parts and prints them live","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/LanguageModel.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, noop, replace, delete a BYOK provider config","status":"failed","title":"create, noop, replace, delete a BYOK provider config","duration":29529.373699999996,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed provider config","status":"failed","title":"list enumerates the deployed provider config","duration":29453.1412,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782381670530,"endTime":1782381700059.3738,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/ProviderConfig.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","status":"failed","title":"surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","duration":2891.1911,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Acm/CustomTrustStore.test.ts:43:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates trust store certificates across zones","status":"passed","title":"list enumerates trust store certificates across zones","duration":3178.3938000000016,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes the deployed trust store certificate on an entitled zone","status":"skipped","title":"list includes the deployed trust store certificate on an entitled zone","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"uploads a root CA, replaces it on certificate change, and deletes it","status":"skipped","title":"uploads a root CA, replaces it on certificate change, and deletes it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381721886,"endTime":1782381725066.3938,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Acm/CustomTrustStore.test.ts"},{"assertionResults":[{"ancestorTitles":["TotalTls"],"fullName":"TotalTls converges enabled:false as a no-op on a zone without the ACM entitlement","status":"skipped","title":"converges enabled:false as a no-op on a zone without the ACM entitlement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TotalTls"],"fullName":"TotalTls surfaces the typed AdvancedCertificateManagerRequired error when enabling without the entitlement","status":"skipped","title":"surfaces the typed AdvancedCertificateManagerRequired error when enabling without the entitlement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TotalTls"],"fullName":"TotalTls enables Total TLS, updates the CA in place, and restores the baseline on destroy","status":"skipped","title":"enables Total TLS, updates the CA in place, and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TotalTls"],"fullName":"TotalTls list enumerates the setting across all zones","status":"skipped","title":"list enumerates the setting across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Acm/TotalTls.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"read path: getAccount observes the testing account","status":"passed","title":"read path: getAccount observes the testing account","duration":5783.490099999995,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"inaccessible account surfaces a typed tag","status":"passed","title":"inaccessible account surfaces a typed tag","duration":4125.801399999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates accessible accounts (read-only)","status":"failed","title":"list enumerates accessible accounts (read-only)","duration":104876.92299999998,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"createAccount is entitlement-gated: typed AccountCreationForbidden","status":"failed","title":"createAccount is entitlement-gated: typed AccountCreationForbidden","duration":31034.307499999995,"failureMessages":["AssertionError: expected 'InternalServerError' to deeply equal 'AccountCreationForbidden'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Account/Account.test.ts:97:26)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create subaccount, update name and settings in place, delete","status":"skipped","title":"create subaccount, update name and settings in place, delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381443631,"endTime":1782381548510.923,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Account/Account.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the account members","status":"failed","title":"list enumerates the account members","duration":25773.452500000003,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create member, update roles in place, delete","status":"failed","title":"create member, update roles in place, delete","duration":25662.377299999996,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the member when the email changes","status":"failed","title":"replaces the member when the email changes","duration":25627.099500000004,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":25736.337399999997,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782381548515,"endTime":1782381574288.4524,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Account/Member.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"construct auto-creates a managed token and wires it into the instance","status":"failed","title":"construct auto-creates a managed token and wires it into the instance","duration":39378.13979999999,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"web-crawler source skips token minting","status":"failed","title":"web-crawler source skips token minting","duration":80174.5246,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]}],"startTime":1782381444181,"endTime":1782381524359.5247,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/AiSearch.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"worker deploys with ai_search + ai_search_namespace bindings injected","status":"skipped","title":"worker deploys with ai_search + ai_search_namespace bindings injected","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker resolves ai_search + ai_search_namespace via Effect clients","status":"skipped","title":"effect worker resolves ai_search + ai_search_namespace via Effect clients","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/Bindings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update mutable props, and delete an r2-backed instance","status":"skipped","title":"create, update mutable props, and delete an r2-backed instance","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the embedding model triggers a replacement","status":"skipped","title":"changing the embedding model triggers a replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed instance","status":"skipped","title":"list enumerates the deployed instance","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a web-crawler instance (no service token)","status":"skipped","title":"creates a web-crawler instance (no service token)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an instance in a custom namespace and moving namespaces replaces","status":"skipped","title":"creates an instance in a custom namespace and moving namespaces replaces","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/Instance.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update description in place, and delete a namespace","status":"failed","title":"create, update description in place, and delete a namespace","duration":26560.984,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the name triggers a replacement","status":"failed","title":"changing the name triggers a replacement","duration":28111.534900000002,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed namespace","status":"failed","title":"list enumerates the deployed namespace","duration":28134.7745,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopts the reserved default namespace without deleting it on teardown","status":"failed","title":"adopts the reserved default namespace without deleting it on teardown","duration":28090.570200000002,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":27928.4418,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]}],"startTime":1782381569786,"endTime":1782381597923.7744,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/Namespace.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, rename in place, and delete a service token","status":"failed","title":"create, rename in place, and delete a service token","duration":26779.219399999998,"failureMessages":["Unauthorized: Unauthorized to access requested resource\n at Object.9109 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:91:22)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":26802.807399999998,"failureMessages":["Unauthorized: Unauthorized to access requested resource\n at Object.9109 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:91:22)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed service token","status":"failed","title":"list enumerates the deployed service token","duration":26814.3721,"failureMessages":["Unauthorized: Unauthorized to access requested resource\n at Object.9109 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:91:22)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"an AI Search instance syncs with a stack-minted service token","status":"failed","title":"an AI Search instance syncs with a stack-minted service token","duration":26947.0363,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782381486477,"endTime":1782381513436.0364,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/Token.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AiSecurityNotEntitled error on unentitled accounts","status":"skipped","title":"surfaces the typed AiSecurityNotEntitled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the custom topics across all zones","status":"passed","title":"list enumerates the custom topics across all zones","duration":3943.0382000000027,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"sets topics, updates the list in place, and restores on destroy","status":"skipped","title":"sets topics, updates the list in place, and restores on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381672050,"endTime":1782381675993.038,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSecurity/CustomTopics.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AiSecurityNotEntitled error on unentitled accounts","status":"skipped","title":"surfaces the typed AiSecurityNotEntitled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the setting across all entitled zones","status":"passed","title":"list enumerates the setting across all entitled zones","duration":7654.580499999996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"pins enabled, updates in place, and restores the original on destroy","status":"skipped","title":"pins enabled, updates in place, and restores the original on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381641921,"endTime":1782381649575.5806,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSecurity/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"address map lifecycle (typed FeatureNotEnabled on unentitled accounts)","status":"skipped","title":"address map lifecycle (typed FeatureNotEnabled on unentitled accounts)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed address map","status":"skipped","title":"list enumerates the deployed address map","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/AddressMap.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates BGP prefixes across BYOIP prefixes","status":"skipped","title":"list enumerates BGP prefixes across BYOIP prefixes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/BgpPrefix.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"lists the services catalog and prefixes (read-only)","status":"failed","title":"lists the services catalog and prefixes (read-only)","duration":27211.047599999998,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account prefixes (read-only)","status":"failed","title":"list enumerates account prefixes (read-only)","duration":27241.002600000003,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"prefix: create, patch description in place, destroy","status":"skipped","title":"prefix: create, patch description in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"bgp prefix: adopt-or-create by cidr, toggle advertisement, withdraw on destroy","status":"skipped","title":"bgp prefix: adopt-or-create by cidr, toggle advertisement, withdraw on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"prefix delegation: create and destroy (replace-only resource)","status":"skipped","title":"prefix delegation: create and destroy (replace-only resource)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"service binding: create against the CDN service and destroy","status":"skipped","title":"service binding: create against the CDN service and destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381571335,"endTime":1782381598578.0027,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/Prefix.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates prefix delegations (read-only)","status":"skipped","title":"list enumerates prefix delegations (read-only)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/PrefixDelegation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates service bindings across prefixes (read-only)","status":"failed","title":"list enumerates service bindings across prefixes (read-only)","duration":11196.5316,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782381590707,"endTime":1782381601903.5315,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/ServiceBinding.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed worker can write data points through the Analytics Engine binding","status":"skipped","title":"deployed worker can write data points through the Analytics Engine binding","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AnalyticsEngine/AnalyticsEngineDataset.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed NotEntitled error on unentitled zones","status":"skipped","title":"surfaces the typed NotEntitled error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the configuration across all zones","status":"passed","title":"list enumerates the configuration across all zones","duration":3797.0926,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"sets session identifiers and restores the original value on destroy","status":"skipped","title":"sets session identifiers and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381718782,"endTime":1782381722579.0925,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/Configuration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update description in place, destroy a label","status":"skipped","title":"create, update description in place, destroy a label","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"renaming a label triggers replacement","status":"skipped","title":"renaming a label triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"generated name respects Cloudflare's 24-character limit","status":"skipped","title":"generated name respects Cloudflare's 24-character limit","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed label","status":"skipped","title":"list enumerates the deployed label","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/Label.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, no-op redeploy, destroy an API Shield operation","status":"skipped","title":"create, no-op redeploy, destroy an API Shield operation","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the method triggers replacement","status":"skipped","title":"changing the method triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed API Shield operation","status":"skipped","title":"list enumerates the deployed API Shield operation","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/Operation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, enable validation in place, destroy a user schema","status":"skipped","title":"create, enable validation in place, destroy a user schema","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the schema source triggers replacement","status":"skipped","title":"changing the schema source triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed user schema","status":"skipped","title":"list enumerates the deployed user schema","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/UserSchema.test.ts"},{"assertionResults":[{"ancestorTitles":["SmartRouting"],"fullName":"SmartRouting surfaces the typed NotAuthorized error on zones without the Argo add-on","status":"skipped","title":"surfaces the typed NotAuthorized error on zones without the Argo add-on","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SmartRouting"],"fullName":"SmartRouting enables Smart Routing and restores the original value on destroy","status":"skipped","title":"enables Smart Routing and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SmartRouting"],"fullName":"SmartRouting list enumerates Argo-entitled zones","status":"passed","title":"list enumerates Argo-entitled zones","duration":2746.4159999999974,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381620900,"endTime":1782381623646.416,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Argo/SmartRouting.test.ts"},{"assertionResults":[{"ancestorTitles":["TieredCaching"],"fullName":"TieredCaching enables Tiered Caching and restores the original value on destroy","status":"skipped","title":"enables Tiered Caching and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TieredCaching"],"fullName":"TieredCaching updates enabled in place and keeps the captured initial value","status":"skipped","title":"updates enabled in place and keeps the captured initial value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TieredCaching"],"fullName":"TieredCaching list enumerates the setting across all zones","status":"skipped","title":"list enumerates the setting across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Argo/TieredCaching.test.ts"},{"assertionResults":[{"ancestorTitles":["BotManagement"],"fullName":"BotManagement manages SBFM settings on the zone singleton and restores them on destroy","status":"skipped","title":"manages SBFM settings on the zone singleton and restores them on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["BotManagement"],"fullName":"BotManagement deploy with no settings set adopts the singleton without writing","status":"skipped","title":"deploy with no settings set adopts the singleton without writing","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["BotManagement"],"fullName":"BotManagement toggles a boolean SBFM field and restores it on destroy","status":"skipped","title":"toggles a boolean SBFM field and restores it on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["BotManagement"],"fullName":"BotManagement list enumerates bot management across all zones","status":"skipped","title":"list enumerates bot management across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/BotManagement/BotManagement.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed account token","status":"skipped","title":"list enumerates the deployed account token","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiToken/AccountApiToken.test.ts"},{"assertionResults":[{"ancestorTitles":["UserApiToken"],"fullName":"UserApiToken create and delete user token with default props","status":"skipped","title":"create and delete user token with default props","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UserApiToken"],"fullName":"UserApiToken create, update, delete user token","status":"skipped","title":"create, update, delete user token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UserApiToken list"],"fullName":"UserApiToken list list enumerates user tokens","status":"skipped","title":"list enumerates user tokens","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UserApiToken list probe"],"fullName":"UserApiToken list probe list rejects with typed Unauthorized under scoped token","status":"passed","title":"list rejects with typed Unauthorized under scoped token","duration":414.90799999999945,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381746962,"endTime":1782381747376.908,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiToken/UserApiToken.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete notification policy","status":"failed","title":"create, update, delete notification policy","duration":32056.406399999996,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces policy when alertType changes","status":"failed","title":"replaces policy when alertType changes","duration":32069.6742,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed notification policy","status":"failed","title":"list enumerates the deployed notification policy","duration":32038.713600000003,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782381570168,"endTime":1782381602243.6743,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Alerting/NotificationPolicy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update window in place, delete silence","status":"failed","title":"create, update window in place, delete silence","duration":13470.961899999998,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces silence when the policy changes","status":"failed","title":"replaces silence when the policy changes","duration":32252.259899999997,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed silence","status":"failed","title":"list enumerates the deployed silence","duration":15223.4421,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782381586536,"endTime":1782381618790.26,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Alerting/Silence.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete webhook destination","status":"failed","title":"create, update, delete webhook destination","duration":5745.9035,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed webhook destination","status":"failed","title":"list enumerates the deployed webhook destination","duration":6151.615399999995,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782381660367,"endTime":1782381666520.6155,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Alerting/Webhook.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"async worker renders a page title through Browser Rendering","status":"skipped","title":"async worker renders a page title through Browser Rendering","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker exercises content via quickAction wrapper","status":"skipped","title":"effect worker exercises content via quickAction wrapper","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker converts a page to markdown","status":"skipped","title":"effect worker converts a page to markdown","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker extracts links","status":"skipped","title":"effect worker extracts links","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker scrapes elements by selector","status":"skipped","title":"effect worker scrapes elements by selector","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker takes a page snapshot","status":"skipped","title":"effect worker takes a page snapshot","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker streams a screenshot","status":"skipped","title":"effect worker streams a screenshot","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker streams a PDF","status":"skipped","title":"effect worker streams a PDF","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker extracts JSON with AI","status":"skipped","title":"effect worker extracts JSON with AI","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker calls the generic quickAction","status":"skipped","title":"effect worker calls the generic quickAction","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker exposes the raw BrowserRun binding","status":"skipped","title":"effect worker exposes the raw BrowserRun binding","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Browser/Browser.test.ts"},{"assertionResults":[{"ancestorTitles":["CacheReserve"],"fullName":"CacheReserve surfaces the typed SettingUnavailableForPlan error on unentitled zones","status":"skipped","title":"surfaces the typed SettingUnavailableForPlan error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["CacheReserve"],"fullName":"CacheReserve enables Cache Reserve and restores the original value on destroy","status":"skipped","title":"enables Cache Reserve and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["CacheReserve"],"fullName":"CacheReserve list enumerates Cache Reserve across all zones","status":"passed","title":"list enumerates Cache Reserve across all zones","duration":2120.1309,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["CacheReserve"],"fullName":"CacheReserve list includes the entitled zone's Cache Reserve setting","status":"skipped","title":"list includes the entitled zone's Cache Reserve setting","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381708207,"endTime":1782381710327.1309,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/CacheReserve.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates, updates in place, replaces on ip change, and deletes the mapping","status":"skipped","title":"creates, updates in place, replaces on ip change, and deletes the mapping","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/OriginCloudRegion.test.ts"},{"assertionResults":[{"ancestorTitles":["RegionalTieredCache"],"fullName":"RegionalTieredCache surfaces the typed SettingUnavailableForPlan error on non-Enterprise zones","status":"skipped","title":"surfaces the typed SettingUnavailableForPlan error on non-Enterprise zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RegionalTieredCache"],"fullName":"RegionalTieredCache enables Regional Tiered Cache and restores the original value on destroy","status":"skipped","title":"enables Regional Tiered Cache and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RegionalTieredCache"],"fullName":"RegionalTieredCache list enumerates the setting across entitled zones","status":"passed","title":"list enumerates the setting across entitled zones","duration":1965.6004999999932,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381675701,"endTime":1782381677666.6006,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/RegionalTieredCache.test.ts"},{"assertionResults":[{"ancestorTitles":["SmartTieredCache"],"fullName":"SmartTieredCache enables Smart Tiered Cache and restores the original value on destroy","status":"skipped","title":"enables Smart Tiered Cache and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SmartTieredCache"],"fullName":"SmartTieredCache updates the setting in place and keeps the captured initial value","status":"skipped","title":"updates the setting in place and keeps the captured initial value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SmartTieredCache"],"fullName":"SmartTieredCache list enumerates the setting across all zones","status":"skipped","title":"list enumerates the setting across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/SmartTieredCache.test.ts"},{"assertionResults":[{"ancestorTitles":["Variants"],"fullName":"Variants creates, updates in place, and deletes the variants setting","status":"skipped","title":"creates, updates in place, and deletes the variants setting","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Variants"],"fullName":"Variants deploy is idempotent and converges out-of-band drift","status":"skipped","title":"deploy is idempotent and converges out-of-band drift","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Variants"],"fullName":"Variants list enumerates the configured variants settings","status":"skipped","title":"list enumerates the configured variants settings","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/Variants.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an app with default name","status":"failed","title":"create and delete an app with default name","duration":25576.381400000002,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name in place (same appId, secret preserved)","status":"failed","title":"update name in place (same appId, secret preserved)","duration":25359.2514,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed app","status":"failed","title":"list enumerates the deployed app","duration":25547.2034,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":25411.2671,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782381559910,"endTime":1782381585486.3813,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Calls/App.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a TURN key with default name","status":"failed","title":"create and delete a TURN key with default name","duration":25491.9785,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name in place (same keyId, key preserved)","status":"failed","title":"update name in place (same keyId, key preserved)","duration":25280.411699999997,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":25469.987999999998,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed TURN key","status":"failed","title":"list enumerates the deployed TURN key","duration":25315.420899999997,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]}],"startTime":1782381578407,"endTime":1782381603898.9785,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Calls/TurnKey.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create signs the CSR, destroy revokes the certificate","status":"skipped","title":"create signs the CSR, destroy revokes the certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing validityDays replaces — new id issued, old certificate revoked","status":"skipped","title":"changing validityDays replaces — new id issued, old certificate revoked","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates client certificates across zones","status":"skipped","title":"list enumerates client certificates across zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ClientCertificate/ClientCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":["Rules"],"fullName":"Rules cloud connector rules — create, update in place, destroy clears the list","status":"skipped","title":"cloud connector rules — create, update in place, destroy clears the list","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Rules"],"fullName":"Rules no-op redeploy leaves the rule list untouched and ids stable","status":"skipped","title":"no-op redeploy leaves the rule list untouched and ids stable","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Rules"],"fullName":"Rules list enumerates rule lists across all zones","status":"skipped","title":"list enumerates rule lists across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CloudConnector/Rules.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Unauthorized error","status":"failed","title":"unentitled accounts surface the typed Unauthorized error","duration":53326.4834,"failureMessages":["AssertionError: expected 'Authentication error' to contain 'cfone.port_scan'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CloudforceOne/ScanConfig.test.ts:58:33)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create a scan config, update ports in place, delete","status":"skipped","title":"create a scan config, update ports in place, delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns an array of scan configs","status":"passed","title":"list returns an array of scan configs","duration":27951.7484,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed scan config","status":"skipped","title":"list enumerates the deployed scan config","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381711734,"endTime":1782381765060.4834,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CloudforceOne/ScanConfig.test.ts"},{"assertionResults":[{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation pins Managed CA hostnames, updates in place, and clears on destroy","status":"skipped","title":"pins Managed CA hostnames, updates in place, and clears on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation associates hostnames with an uploaded CA and destroys before the cert","status":"skipped","title":"associates hostnames with an uploaded CA and destroys before the cert","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation changing the certificate key replaces the association","status":"skipped","title":"changing the certificate key replaces the association","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CertificateAuthorities/HostnameAssociation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"tcp service lifecycle: create, update, host switch","status":"failed","title":"tcp service lifecycle: create, update, host switch","duration":26247.4737,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"http service with explicit ports and default name","status":"failed","title":"http service with explicit ports and default name","duration":27472.121099999997,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed directory service","status":"failed","title":"list enumerates the deployed directory service","duration":26200.5198,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":26276.5571,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782381549834,"endTime":1782381577308.121,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Connectivity/DirectoryService.test.ts"},{"assertionResults":[{"ancestorTitles":["effectful container (main)"],"fullName":"effectful container (main) deploys and serves over its TCP port","status":"skipped","title":"deploys and serves over its TCP port","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["external container (context/dockerfile)"],"fullName":"external container (context/dockerfile) builds the user Dockerfile and serves it over its TCP port","status":"skipped","title":"builds the user Dockerfile and serves it over its TCP port","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["remote container (image)"],"fullName":"remote container (image) pulls and re-pushes the remote image and serves it over its TCP port","status":"skipped","title":"pulls and re-pushes the remote image and serves it over its TCP port","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Container/Container.test.ts"},{"assertionResults":[{"ancestorTitles":["ContainerApplication"],"fullName":"ContainerApplication list enumerates container applications","status":"failed","title":"list enumerates container applications","duration":923.2692999999999,"failureMessages":["Provider not found for undefined"],"meta":{},"tags":[]}],"startTime":1782381724192,"endTime":1782381725115.2693,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Container/ContainerApplication.test.ts"},{"assertionResults":[{"ancestorTitles":["ContentScanning"],"fullName":"ContentScanning surfaces the typed ContentScanningNotEntitled error on unentitled zones","status":"skipped","title":"surfaces the typed ContentScanningNotEntitled error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ContentScanning"],"fullName":"ContentScanning pins Content Scanning off on an unentitled zone and destroys cleanly","status":"skipped","title":"pins Content Scanning off on an unentitled zone and destroys cleanly","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ContentScanning"],"fullName":"ContentScanning list enumerates the status across all zones","status":"skipped","title":"list enumerates the status across all zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ContentScanning"],"fullName":"ContentScanning enables Content Scanning and restores the original status on destroy","status":"skipped","title":"enables Content Scanning and restores the original status on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ContentScanning/ContentScanning.test.ts"},{"assertionResults":[{"ancestorTitles":["Expression"],"fullName":"Expression surfaces the typed ContentScanningNotEnabled error when scanning is disabled","status":"skipped","title":"surfaces the typed ContentScanningNotEnabled error when scanning is disabled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Expression"],"fullName":"Expression creates a custom expression, replaces on payload change, destroys cleanly","status":"skipped","title":"creates a custom expression, replaces on payload change, destroys cleanly","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Expression"],"fullName":"Expression list returns a well-typed array across all zones","status":"passed","title":"list returns a well-typed array across all zones","duration":2612.8943,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Expression"],"fullName":"Expression list enumerates the deployed expression","status":"skipped","title":"list enumerates the deployed expression","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381696682,"endTime":1782381699294.8943,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ContentScanning/Expression.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a custom hostname with default ssl","status":"skipped","title":"create and delete a custom hostname with default ssl","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating ssl method patches in place","status":"skipped","title":"updating ssl method patches in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of custom hostnames","status":"passed","title":"list returns a well-typed array of custom hostnames","duration":605.2272000000012,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed custom hostname","status":"skipped","title":"list enumerates the deployed custom hostname","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the hostname triggers replacement","status":"skipped","title":"changing the hostname triggers replacement","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381746198,"endTime":1782381746803.2273,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CustomHostname/CustomHostname.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates fallback origins across all zones","status":"passed","title":"list enumerates fallback origins across all zones","duration":633.0308000000005,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list surfaces a deployed fallback origin","status":"skipped","title":"list surfaces a deployed fallback origin","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"set, update and delete the zone fallback origin","status":"skipped","title":"set, update and delete the zone fallback origin","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381745769,"endTime":1782381746402.0308,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CustomHostname/FallbackOrigin.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"D1Connection.bind exercises the full client surface","status":"failed","title":"D1Connection.bind exercises the full client surface","duration":4045.1349999999984,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782381733406,"endTime":1782381737451.135,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/D1/D1Binding.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed database","status":"passed","title":"list enumerates the deployed database","duration":6927.208999999999,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381692941,"endTime":1782381699868.209,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/D1/D1Database.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete database with default props","status":"passed","title":"create and delete database with default props","duration":4824.449499999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete database","status":"passed","title":"create, update, delete database","duration":6267.156499999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"applies migrations from migrationsDir","status":"passed","title":"applies migrations from migrationsDir","duration":7023.456399999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"applies migrations using a custom migrationsTable","status":"passed","title":"applies migrations using a custom migrationsTable","duration":5562.919600000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"migrates legacy 2-column migration table to wrangler schema","status":"passed","title":"migrates legacy 2-column migration table to wrangler schema","duration":6990.1170999999995,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"imports SQL files via importFiles","status":"failed","title":"imports SQL files via importFiles","duration":1794.8136000000013,"failureMessages":["Forbidden: System limit reached: databases per account (1)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"clones a database by databaseId","status":"failed","title":"clones a database by databaseId","duration":1168.2457000000031,"failureMessages":["Forbidden: System limit reached: databases per account (1)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"clones a database by name lookup","status":"failed","title":"clones a database by name lookup","duration":3758.0070000000014,"failureMessages":["Forbidden: System limit reached: databases per account (1)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"clones a database by passing the source resource directly","status":"failed","title":"clones a database by passing the source resource directly","duration":3683.969000000001,"failureMessages":["Forbidden: System limit reached: databases per account (1)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"existing database (matching name) is silently adopted without --adopt","status":"passed","title":"existing database (matching name) is silently adopted without --adopt","duration":2996.7175000000025,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381734299,"endTime":1782381744602.969,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/D1/Database.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed PlanLevelNotAllowed error on unentitled zones","status":"skipped","title":"surfaces the typed PlanLevelNotAllowed error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates custom certificates across zones","status":"passed","title":"list enumerates custom certificates across zones","duration":2478.500100000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"uploads, rotates in place, replaces on type change, and deletes","status":"skipped","title":"uploads, rotates in place, replaces on type change, and deletes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381697598,"endTime":1782381700076.5,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CustomCertificate/CustomCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AdvancedTcpProtectionNotEntitled error without Magic Transit","status":"failed","title":"surfaces the typed AdvancedTcpProtectionNotEntitled error without Magic Transit","duration":10375.362899999998,"failureMessages":["AssertionError: expected 'CloudflareHttpError' to deeply equal 'AdvancedTcpProtectionNotEntitled'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/AllowlistEntry.test.ts:60:26)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an allowlist entry, updates it in place, replaces on prefix change, and destroys","status":"skipped","title":"creates an allowlist entry, updates it in place, replaces on prefix change, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed empty array without Magic Transit","status":"failed","title":"list returns a well-typed empty array without Magic Transit","duration":10613.6206,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed allowlist entry","status":"skipped","title":"list enumerates the deployed allowlist entry","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381516778,"endTime":1782381527395.6206,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/AllowlistEntry.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates a SYN protection filter, updates it in place, and destroys","status":"skipped","title":"creates a SYN protection filter, updates it in place, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of SYN protection filters","status":"passed","title":"list returns a well-typed array of SYN protection filters","duration":27269.652199999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed SYN protection filter","status":"skipped","title":"list enumerates the deployed SYN protection filter","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381732307,"endTime":1782381759576.652,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/SynProtectionFilter.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates a global SYN protection rule, updates it in place, and destroys","status":"skipped","title":"creates a global SYN protection rule, updates it in place, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of SYN protection rules","status":"passed","title":"list returns a well-typed array of SYN protection rules","duration":26901.723800000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed SYN protection rule","status":"skipped","title":"list enumerates the deployed SYN protection rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381729265,"endTime":1782381756166.7239,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/SynProtectionRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates a TCP flow protection filter, updates it in place, and destroys","status":"skipped","title":"creates a TCP flow protection filter, updates it in place, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account's TCP flow protection filters","status":"passed","title":"list returns the account's TCP flow protection filters","duration":26603.149500000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed TCP flow protection filter","status":"skipped","title":"list enumerates the deployed TCP flow protection filter","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381733623,"endTime":1782381760226.1494,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/TcpFlowProtectionFilter.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates a global TCP flow protection rule, updates it in place, and destroys","status":"skipped","title":"creates a global TCP flow protection rule, updates it in place, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed empty array without Magic Transit","status":"passed","title":"list returns a well-typed empty array without Magic Transit","duration":29049.2446,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed TCP flow protection rule","status":"skipped","title":"list enumerates the deployed TCP flow protection rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381715722,"endTime":1782381744771.2446,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/TcpFlowProtectionRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed CustomNameserversNotEnabled error on unentitled accounts","status":"passed","title":"surfaces the typed CustomNameserversNotEnabled error on unentitled accounts","duration":921.7337000000007,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account custom nameservers","status":"passed","title":"list enumerates account custom nameservers","duration":1021.5291000000034,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed custom nameserver","status":"skipped","title":"list includes a deployed custom nameserver","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, replace on nsSet change, and destroy a custom nameserver","status":"skipped","title":"create, replace on nsSet change, and destroy a custom nameserver","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381741237,"endTime":1782381742260.529,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CustomNameserver/CustomNameserver.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a custom device profile","status":"failed","title":"create, update in place, and delete a custom device profile","duration":26346.984500000002,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed custom device profile","status":"failed","title":"list enumerates the deployed custom device profile","duration":26316.8853,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782381552640,"endTime":1782381578986.9846,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/CustomProfile.test.ts"},{"assertionResults":[{"ancestorTitles":["DefaultProfile"],"fullName":"DefaultProfile reads the existing default device profile without mutating it","status":"skipped","title":"reads the existing default device profile without mutating it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DefaultProfile"],"fullName":"DefaultProfile toggles captivePortal and restores the original value","status":"skipped","title":"toggles captivePortal and restores the original value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DefaultProfile"],"fullName":"DefaultProfile list returns the singleton default device profile","status":"failed","title":"list returns the singleton default device profile","duration":26977.815900000005,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782381582836,"endTime":1782381609813.816,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/DefaultProfile.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden entitlement error","status":"skipped","title":"unentitled accounts surface the typed Forbidden entitlement error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a DEX test","status":"skipped","title":"create, update in place, and destroy a DEX test","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed DEX test","status":"skipped","title":"list enumerates the deployed DEX test","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed empty array on unentitled accounts","status":"failed","title":"list returns a well-typed empty array on unentitled accounts","duration":26098.1873,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782381538540,"endTime":1782381564638.1873,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/DexTest.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a managed network","status":"skipped","title":"create, update in place, and delete a managed network","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed managed network","status":"skipped","title":"list enumerates the deployed managed network","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/ManagedNetwork.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates without a real provider surface the typed InvalidPostureIntegrationConfig error","status":"failed","title":"creates without a real provider surface the typed InvalidPostureIntegrationConfig error","duration":81495.55639999999,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a posture integration","status":"skipped","title":"create, update in place, and destroy a posture integration","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates posture integrations in the account","status":"passed","title":"list enumerates posture integrations in the account","duration":30632.355200000005,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed posture integration","status":"skipped","title":"list includes a deployed posture integration","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381443681,"endTime":1782381525176.5564,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/PostureIntegration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a posture rule","status":"skipped","title":"create, update in place, and delete a posture rule","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the rule type triggers a replacement","status":"skipped","title":"changing the rule type triggers a replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed posture rule","status":"skipped","title":"list enumerates the deployed posture rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/PostureRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"patches disableForTime and restores the original value on destroy","status":"skipped","title":"patches disableForTime and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account's device settings singleton","status":"skipped","title":"list returns the account's device settings singleton","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden error","status":"skipped","title":"unentitled accounts surface the typed Forbidden error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, and destroy a DLP profile with a standalone entry","status":"skipped","title":"create, update, and destroy a DLP profile with a standalone entry","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dlp/Dlp.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns the account's custom DLP entries","status":"failed","title":"list returns the account's custom DLP entries","duration":26667.161799999998,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed DLP entry","status":"skipped","title":"list enumerates the deployed DLP entry","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381571400,"endTime":1782381598067.1619,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dlp/Entry.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates custom DLP profiles (read-only)","status":"skipped","title":"list enumerates custom DLP profiles (read-only)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed custom DLP profile","status":"skipped","title":"list includes a deployed custom DLP profile","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dlp/Profile.test.ts"},{"assertionResults":[{"ancestorTitles":["AccountSettings"],"fullName":"AccountSettings list returns the account's DNS settings singleton","status":"skipped","title":"list returns the account's DNS settings singleton","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AccountSettings"],"fullName":"AccountSettings pins a zone default and restores the pre-management value on destroy","status":"skipped","title":"pins a zone default and restores the pre-management value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AccountSettings"],"fullName":"AccountSettings updates in place, unions managedKeys, restores all managed fields","status":"skipped","title":"updates in place, unions managedKeys, restores all managed fields","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/AccountSettings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed worker drives the full DNS record CRUD surface via DnsReadWrite","status":"skipped","title":"deployed worker drives the full DNS record CRUD surface via DnsReadWrite","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/Dns.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an A record with default props","status":"skipped","title":"create and delete an A record with default props","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating mutable fields patches in place","status":"skipped","title":"updating mutable fields patches in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the record type triggers replacement","status":"skipped","title":"changing the record type triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing record errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing record errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed DNS record","status":"skipped","title":"list enumerates the deployed DNS record","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/DnsRecord.test.ts"},{"assertionResults":[{"ancestorTitles":["Dnssec"],"fullName":"Dnssec enables DNSSEC, captures the disabled baseline, destroy deactivates","status":"skipped","title":"enables DNSSEC, captures the disabled baseline, destroy deactivates","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Dnssec"],"fullName":"Dnssec updates status in place — same zone singleton, no replacement","status":"skipped","title":"updates status in place — same zone singleton, no replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Dnssec"],"fullName":"Dnssec list enumerates active DNSSEC across zones","status":"skipped","title":"list enumerates active DNSSEC across zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/Dnssec.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed InternalDnsNotAvailable error on unentitled accounts","status":"skipped","title":"surfaces the typed InternalDnsNotAvailable error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of internal DNS views","status":"skipped","title":"list returns a well-typed array of internal DNS views","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates a deployed internal DNS view","status":"skipped","title":"list enumerates a deployed internal DNS view","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update zones in place, and delete an internal DNS view","status":"skipped","title":"create, update zones in place, and delete an internal DNS view","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/View.test.ts"},{"assertionResults":[{"ancestorTitles":["ZoneSettings"],"fullName":"ZoneSettings pins flattenAllCnames and restores the pre-management value on destroy","status":"skipped","title":"pins flattenAllCnames and restores the pre-management value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ZoneSettings"],"fullName":"ZoneSettings updates in place, unions managedKeys, restores all managed fields","status":"skipped","title":"updates in place, unions managedKeys, restores all managed fields","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ZoneSettings"],"fullName":"ZoneSettings list enumerates DNS settings across all zones","status":"skipped","title":"list enumerates DNS settings across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneSettings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a zone transfer ACL","status":"skipped","title":"create, update in place, and delete a zone transfer ACL","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"generates a deterministic name when none is provided","status":"skipped","title":"generates a deterministic name when none is provided","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed zone transfer ACLs","status":"skipped","title":"list enumerates the deployed zone transfer ACLs","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferAcl.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates incoming configs across all zones","status":"passed","title":"list enumerates incoming configs across all zones","duration":2494.832699999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"incoming config does not persist on non-secondary zones (typed not-found)","status":"skipped","title":"incoming config does not persist on non-secondary zones (typed not-found)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update autoRefreshSeconds in place, and delete the incoming config","status":"skipped","title":"create, update autoRefreshSeconds in place, and delete the incoming config","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381664049,"endTime":1782381666543.8328,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferIncoming.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed OutgoingZoneTransfersNotAllowed error on unentitled zones","status":"skipped","title":"surfaces the typed OutgoingZoneTransfersNotAllowed error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, sync peers and enabled state, and delete the outgoing config","status":"skipped","title":"create, sync peers and enabled state, and delete the outgoing config","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the outgoing transfer config per zone","status":"skipped","title":"list enumerates the outgoing transfer config per zone","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferOutgoing.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create with connection settings, update in place, and delete a peer","status":"failed","title":"create with connection settings, update in place, and delete a peer","duration":31065.120800000004,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"wires a TSIG reference through to the peer","status":"failed","title":"wires a TSIG reference through to the peer","duration":28735.862900000007,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed peer","status":"failed","title":"list enumerates the deployed peer","duration":31286.371000000014,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782381443677,"endTime":1782381474965.371,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferPeer.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, rotate the secret in place, and delete a TSIG","status":"failed","title":"create, rotate the secret in place, and delete a TSIG","duration":32572.35239999999,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed TSIG","status":"failed","title":"list enumerates the deployed TSIG","duration":32531.806099999987,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782381443851,"endTime":1782381476423.3523,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferTsig.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed DnsFirewallNotEntitled error on unentitled accounts","status":"failed","title":"surfaces the typed DnsFirewallNotEntitled error on unentitled accounts","duration":26559.4062,"failureMessages":["AssertionError: expected 'TooManyRequests' to deeply equal 'DnsFirewallNotEntitled'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DnsFirewall/DnsFirewall.test.ts:69:26)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, verify out-of-band, destroy, and wait until gone","status":"skipped","title":"create, verify out-of-band, destroy, and wait until gone","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable settings in place, replace on rename","status":"skipped","title":"update mutable settings in place, replace on rename","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the DNS firewall cluster Attributes array","status":"failed","title":"list returns the DNS firewall cluster Attributes array","duration":26488.343300000004,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed DNS firewall cluster","status":"skipped","title":"list enumerates the deployed DNS firewall cluster","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381552945,"endTime":1782381579504.4062,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DnsFirewall/DnsFirewall.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"rejects public IPs with the typed InvalidHealthcheckEndpoint error","status":"failed","title":"rejects public IPs with the typed InvalidHealthcheckEndpoint error","duration":11052.331699999999,"failureMessages":["AssertionError: expected 'CloudflareHttpError' to deeply equal 'InvalidHealthcheckEndpoint'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Diagnostics/EndpointHealthcheck.test.ts:70:26)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an endpoint healthcheck, updates in place, and destroys","status":"failed","title":"creates an endpoint healthcheck, updates in place, and destroys","duration":29260.705899999997,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed endpoint healthcheck","status":"failed","title":"list enumerates the deployed endpoint healthcheck","duration":29500.3136,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]}],"startTime":1782381589958,"endTime":1782381619461.3137,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Diagnostics/EndpointHealthcheck.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed email address","status":"failed","title":"list enumerates the deployed email address","duration":83588.4217,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782381443725,"endTime":1782381527313.4216,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailAddress.test.ts"},{"assertionResults":[{"ancestorTitles":["EmailCatchAll"],"fullName":"EmailCatchAll configures the catch-all rule and restores the baseline on destroy","status":"skipped","title":"configures the catch-all rule and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["EmailCatchAll"],"fullName":"EmailCatchAll updates the catch-all rule in place and keeps the captured baseline","status":"skipped","title":"updates the catch-all rule in place and keeps the captured baseline","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["EmailCatchAll"],"fullName":"EmailCatchAll list enumerates the catch-all rule across all zones","status":"skipped","title":"list enumerates the catch-all rule across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailCatchAll.test.ts"},{"assertionResults":[{"ancestorTitles":["EmailRouting"],"fullName":"EmailRouting list enumerates email routing across all zones","status":"skipped","title":"list enumerates email routing across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailRouting.test.ts"},{"assertionResults":[{"ancestorTitles":["EmailRule"],"fullName":"EmailRule list enumerates the deployed email rule across all zones","status":"skipped","title":"list enumerates the deployed email rule across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"sends an email through the Worker send_email binding","status":"skipped","title":"sends an email through the Worker send_email binding","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/SendEmail.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and destroy a sending subdomain","status":"skipped","title":"create and destroy a sending subdomain","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the name triggers replacement","status":"skipped","title":"changing the name triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing subdomain errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing subdomain errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed sending subdomain","status":"skipped","title":"list enumerates the deployed sending subdomain","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/SendingSubdomain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a zone-scoped ip rule","status":"skipped","title":"create and delete a zone-scoped ip rule","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mode and notes in place (same ruleId)","status":"skipped","title":"update mode and notes in place (same ruleId)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the configuration triggers replacement","status":"skipped","title":"changing the configuration triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed zone-scoped rule","status":"skipped","title":"list enumerates the deployed zone-scoped rule","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"account-scoped rule (no zoneId) create, update, delete","status":"skipped","title":"account-scoped rule (no zoneId) create, update, delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Firewall/AccessRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update urls/configurations/description/paused in place, destroy","status":"skipped","title":"create, update urls/configurations/description/paused in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed lockdown rule","status":"skipped","title":"list enumerates the deployed lockdown rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Firewall/Lockdown.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update mode/paused/description/userAgent in place, destroy","status":"skipped","title":"create, update mode/paused/description/userAgent in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates UA rules across all zones","status":"skipped","title":"list enumerates UA rules across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Firewall/UaRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed EmailSecurityNotEntitled error on unentitled accounts","status":"skipped","title":"surfaces the typed EmailSecurityNotEntitled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates allow policies (read-only)","status":"skipped","title":"list enumerates allow policies (read-only)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes the deployed allow policy","status":"skipped","title":"list includes the deployed allow policy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, destroy","status":"skipped","title":"create, update in place, destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/AllowPolicy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, destroy","status":"skipped","title":"create, update in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed block sender","status":"passed","title":"list enumerates the deployed block sender","duration":26797.872399999997,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381731635,"endTime":1782381758432.8723,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/BlockSender.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"adopts an onboarded domain, patches settings in place, offboards on destroy","status":"skipped","title":"adopts an onboarded domain, patches settings in place, offboards on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates Email Security domains (or [] when unentitled)","status":"skipped","title":"list enumerates Email Security domains (or [] when unentitled)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/Domain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, destroy","status":"skipped","title":"create, update in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account impersonation registry","status":"skipped","title":"list returns the account impersonation registry","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed impersonation registry entry","status":"skipped","title":"list enumerates the deployed impersonation registry entry","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/ImpersonationRegistryEntry.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, destroy","status":"skipped","title":"create, update in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates trusted domains","status":"skipped","title":"list enumerates trusted domains","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/TrustedDomain.test.ts"},{"assertionResults":[{"ancestorTitles":["FraudDetectionSettings"],"fullName":"FraudDetectionSettings adopts the zone singleton without writing and restores nothing on destroy","status":"skipped","title":"adopts the zone singleton without writing and restores nothing on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["FraudDetectionSettings"],"fullName":"FraudDetectionSettings surfaces the typed FraudDetectionNotEntitled error on unentitled zones","status":"skipped","title":"surfaces the typed FraudDetectionNotEntitled error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["FraudDetectionSettings"],"fullName":"FraudDetectionSettings manages fraud detection settings and restores them on destroy","status":"skipped","title":"manages fraud detection settings and restores them on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["FraudDetectionSettings"],"fullName":"FraudDetectionSettings list enumerates the settings across all zones","status":"skipped","title":"list enumerates the settings across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Fraud/FraudDetectionSettings.test.ts"},{"assertionResults":[{"ancestorTitles":["GoogleTagGateway"],"fullName":"GoogleTagGateway configures the gateway, updates in place, and restores the baseline on destroy","status":"skipped","title":"configures the gateway, updates in place, and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["GoogleTagGateway"],"fullName":"GoogleTagGateway no-op redeploy skips the PUT and destroy is idempotent","status":"skipped","title":"no-op redeploy skips the PUT and destroy is idempotent","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["GoogleTagGateway"],"fullName":"GoogleTagGateway list enumerates configured zones","status":"skipped","title":"list enumerates configured zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/GoogleTagGateway/GoogleTagGateway.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create an activated certificate, deactivate in place, destroy","status":"failed","title":"create an activated certificate, deactivate in place, destroy","duration":10860.212599999999,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the validity period replaces the certificate","status":"skipped","title":"changing the validity period replaces the certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed gateway certificate","status":"failed","title":"list enumerates the deployed gateway certificate","duration":10904.7393,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782381590992,"endTime":1782381601898.7393,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Certificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"manage activityLog and protocolDetection, then restore on destroy","status":"failed","title":"manage activityLog and protocolDetection, then restore on destroy","duration":32441.315599999998,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account Gateway configuration singleton","status":"failed","title":"list returns the account Gateway configuration singleton","duration":27218.620000000003,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]}],"startTime":1782381568177,"endTime":1782381600618.3157,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Configuration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy a DOMAIN list","status":"skipped","title":"create, verify, and destroy a DOMAIN list","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update items, description, and name in place","status":"skipped","title":"update items, description, and name in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the type replaces the list","status":"skipped","title":"changing the type replaces the list","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/List.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy a location","status":"skipped","title":"create, verify, and destroy a location","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name and ecsSupport in place (same id)","status":"skipped","title":"update name and ecsSupport in place (same id)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates deployed gateway locations","status":"skipped","title":"list enumerates deployed gateway locations","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Location.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"manage logging settings, update in place, restore on destroy","status":"skipped","title":"manage logging settings, update in place, restore on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account's Gateway logging singleton","status":"skipped","title":"list returns the account's Gateway logging singleton","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Logging.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, and destroy an identity-kind proxy endpoint","status":"skipped","title":"create, update, and destroy an identity-kind proxy endpoint","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"ip-kind endpoints surface the typed entitlement error","status":"skipped","title":"ip-kind endpoints surface the typed entitlement error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed proxy endpoint","status":"skipped","title":"list enumerates the deployed proxy endpoint","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/ProxyEndpoint.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed Gateway rule","status":"skipped","title":"list enumerates the deployed Gateway rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Rule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete a Flagship app","status":"skipped","title":"create, update, delete a Flagship app","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates an app after out-of-band delete","status":"skipped","title":"recreates an app after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed Flagship app","status":"skipped","title":"list enumerates the deployed Flagship app","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Flagship/App.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete a flag in an app","status":"skipped","title":"create, update, delete a flag in an app","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the flag when the key changes","status":"skipped","title":"replaces the flag when the key changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates a flag after out-of-band delete","status":"skipped","title":"recreates a flag after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed flag","status":"skipped","title":"list enumerates the deployed flag","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Flagship/Flag.test.ts"},{"assertionResults":[],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"FlagshipApp is not a function","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Flagship/Flagship.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an HTTP health check with default name","status":"skipped","title":"create and delete an HTTP health check with default name","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable props in place (same id), then no-op redeploy","status":"skipped","title":"update mutable props in place (same id), then no-op redeploy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing type HTTP→TCP updates in place; delete is idempotent","status":"skipped","title":"changing type HTTP→TCP updates in place; delete is idempotent","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing check errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing check errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed health check across zones","status":"skipped","title":"list enumerates the deployed health check across zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Healthcheck/Healthcheck.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"lists overrides and surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","status":"skipped","title":"lists overrides and surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates per-hostname TLS overrides","status":"passed","title":"list enumerates per-hostname TLS overrides","duration":2342.8387000000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a min_tls_version override","status":"skipped","title":"create, update in place, and destroy a min_tls_version override","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381706714,"endTime":1782381709056.8386,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/HostnameTlsSetting/HostnameTlsSetting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete hyperdrive with default props","status":"passed","title":"create and delete hyperdrive with default props","duration":8538.864499999996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete hyperdrive","status":"passed","title":"create, update, delete hyperdrive","duration":9765.059599999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed hyperdrive","status":"failed","title":"list enumerates the deployed hyperdrive","duration":8308.2529,"failureMessages":["BadRequest: This account has reached its limit for how many Hyperdrives it can have (2)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782381738806,"endTime":1782381748574.0596,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Hyperdrive/Hyperdrive.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"async worker reads image info via env Images binding","status":"skipped","title":"async worker reads image info via env Images binding","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker reads image info via yield* Images","status":"skipped","title":"effect worker reads image info via yield* Images","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Images/Images.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the account's signing keys","status":"skipped","title":"list enumerates the account's signing keys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"surfaces the typed ImagesAccessNotEnabled error on unentitled accounts","status":"skipped","title":"surfaces the typed ImagesAccessNotEnabled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create a signing key, no-op redeploy keeps the value, delete","status":"skipped","title":"create a signing key, no-op redeploy keeps the value, delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Images/SigningKey.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a variant","status":"skipped","title":"create, update in place, and delete a variant","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replace a variant when the name changes","status":"skipped","title":"replace a variant when the name changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed variant","status":"skipped","title":"list enumerates the deployed variant","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Images/Variant.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed IndicatorFeedsNotEntitled error","status":"skipped","title":"unentitled accounts surface the typed IndicatorFeedsNotEntitled error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of indicator feeds","status":"failed","title":"list returns a well-typed array of indicator feeds","duration":5415.215099999998,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed indicator feed","status":"skipped","title":"list enumerates the deployed indicator feed","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create (or adopt), verify out-of-band, update in place, destroy","status":"skipped","title":"create (or adopt), verify out-of-band, update in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"uploads a STIX2 snapshot and grants/revokes a consumer permission","status":"skipped","title":"uploads a STIX2 snapshot and grants/revokes a consumer permission","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381518836,"endTime":1782381524251.215,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Intel/IndicatorFeed.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns [] for the non-listable IndicatorFeedPermission","status":"passed","title":"list returns [] for the non-listable IndicatorFeedPermission","duration":128.66470000000118,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381747628,"endTime":1782381747756.6648,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Intel/IndicatorFeedPermission.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed KeylessSslNotAvailable error on non-Enterprise zones","status":"failed","title":"surfaces the typed KeylessSslNotAvailable error on non-Enterprise zones","duration":920.3670999999995,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/KeylessCertificate/KeylessCertificate.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates keyless certificates across zones","status":"passed","title":"list enumerates keyless certificates across zones","duration":1003.0041000000019,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed keyless certificate","status":"skipped","title":"list includes a deployed keyless certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates, updates in place, replaces on certificate change, and destroys","status":"skipped","title":"creates, updates in place, replaces on certificate change, and destroys","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381595303,"endTime":1782381596308.0042,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/KeylessCertificate/KeylessCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, update name+scope in place, destroy","status":"skipped","title":"create, verify out-of-band, update name+scope in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed resource group","status":"skipped","title":"list enumerates the deployed resource group","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Iam/ResourceGroup.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create with a policy, verify out-of-band, update policies in place, destroy","status":"skipped","title":"create with a policy, verify out-of-band, update policies in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed user group","status":"skipped","title":"list enumerates the deployed user group","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Iam/UserGroup.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, replace when the user group changes, destroy","status":"skipped","title":"create, verify out-of-band, replace when the user group changes, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates memberships across all user groups in the account","status":"skipped","title":"list enumerates memberships across all user groups in the account","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Iam/UserGroupMembership.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete namespace with default props","status":"passed","title":"create and delete namespace with default props","duration":3620.6023999999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete namespace","status":"passed","title":"create, update, delete namespace","duration":4030.316999999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed namespace","status":"passed","title":"list enumerates the deployed namespace","duration":3776.6242999999995,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"existing namespace (matching title) is silently adopted without --adopt","status":"passed","title":"existing namespace (matching title) is silently adopted without --adopt","duration":3713.6015000000007,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381738039,"endTime":1782381742071.317,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/KV/Namespace.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed LoadBalancingNotEnabledForZone error without the LB subscription","status":"skipped","title":"surfaces the typed LoadBalancingNotEnabledForZone error without the LB subscription","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a load balancer","status":"skipped","title":"create, update in place, and destroy a load balancer","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of load balancers","status":"passed","title":"list returns a well-typed array of load balancers","duration":1835.0662000000011,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed load balancer","status":"skipped","title":"list enumerates the deployed load balancer","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381716664,"endTime":1782381718499.0662,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/LoadBalancer.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed MonitorIntervalOutOfRange error without the LB subscription","status":"failed","title":"surfaces the typed MonitorIntervalOutOfRange error without the LB subscription","duration":24998.6938,"failureMessages":["AssertionError: expected 'CloudflareHttpError' to deeply equal 'MonitorIntervalOutOfRange'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/Monitor.test.ts:84:26)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a monitor","status":"skipped","title":"create, update in place, and destroy a monitor","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns an array of monitor attributes","status":"failed","title":"list returns an array of monitor attributes","duration":24926.4576,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed monitor","status":"skipped","title":"list enumerates the deployed monitor","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381575391,"endTime":1782381600389.6938,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/Monitor.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed MonitorGroupsNotEnabled error on non-Enterprise accounts","status":"failed","title":"surfaces the typed MonitorGroupsNotEnabled error on non-Enterprise accounts","duration":82869.57680000001,"failureMessages":["AssertionError: expected 'CloudflareHttpError' to deeply equal 'MonitorGroupsNotEnabled'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/MonitorGroup.test.ts:92:26)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns an array of monitor groups","status":"failed","title":"list returns an array of monitor groups","duration":31157.511599999998,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed monitor group","status":"skipped","title":"list enumerates the deployed monitor group","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a monitor group","status":"skipped","title":"create, update in place, and destroy a monitor group","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381443611,"endTime":1782381526480.577,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/MonitorGroup.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed PoolAccessFailed error without the LB subscription","status":"failed","title":"surfaces the typed PoolAccessFailed error without the LB subscription","duration":24697.4854,"failureMessages":["AssertionError: expected 'TooManyRequests' to deeply equal 'PoolAccessFailed'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/Pool.test.ts:81:26)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a health-checked pool","status":"skipped","title":"create, update in place, and destroy a health-checked pool","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed pool","status":"skipped","title":"list enumerates the deployed pool","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381556533,"endTime":1782381581230.4854,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/Pool.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"diag list","status":"skipped","title":"diag list","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/_diag.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed LogsControlNotAuthorized error on unentitled accounts","status":"skipped","title":"surfaces the typed LogsControlNotAuthorized error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, verify out-of-band, update in place, destroy, and wait until gone","status":"skipped","title":"create, verify out-of-band, update in place, destroy, and wait until gone","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list tolerates the typed LogsControlNotAuthorized error on unentitled accounts","status":"skipped","title":"list tolerates the typed LogsControlNotAuthorized error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the account CMB config singleton","status":"skipped","title":"list enumerates the account CMB config singleton","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LogsControl/CmbConfig.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed LogsControlNotAuthorized error on unentitled zones","status":"skipped","title":"surfaces the typed LogsControlNotAuthorized error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"pins the retention flag, updates in place, and restores on destroy","status":"skipped","title":"pins the retention flag, updates in place, and restores on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the retention flag across all zones","status":"passed","title":"list enumerates the retention flag across all zones","duration":2683.1317000000017,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list contains the entitled test zone's retention flag","status":"skipped","title":"list contains the entitled test zone's retention flag","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381721807,"endTime":1782381724490.1316,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LogsControl/RetentionFlag.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed FeatureNotEnabled error","status":"skipped","title":"unentitled accounts surface the typed FeatureNotEnabled error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a sync, updates mutable props in place, and destroys it","status":"skipped","title":"creates a sync, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account catalog syncs","status":"skipped","title":"list enumerates account catalog syncs","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the sync when destinationType changes","status":"skipped","title":"replaces the sync when destinationType changes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicCloudNetworking/CatalogSync.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed FeatureNotEnabled error","status":"skipped","title":"unentitled accounts surface the typed FeatureNotEnabled error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of integrations","status":"skipped","title":"list returns a well-typed array of integrations","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed integration","status":"skipped","title":"list enumerates the deployed integration","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"registers an AWS integration, updates it in place, and destroys it","status":"skipped","title":"registers an AWS integration, updates it in place, and destroys it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the integration when cloudType changes","status":"skipped","title":"replaces the integration when cloudType changes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicCloudNetworking/CloudIntegration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed FeatureNotEnabled error","status":"skipped","title":"unentitled accounts surface the typed FeatureNotEnabled error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an on-ramp, updates mutable props in place, and destroys it","status":"skipped","title":"creates an on-ramp, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns on-ramps or a typed [] when unentitled","status":"skipped","title":"list returns on-ramps or a typed [] when unentitled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed on-ramp","status":"skipped","title":"list enumerates the deployed on-ramp","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicCloudNetworking/OnRamp.test.ts"},{"assertionResults":[{"ancestorTitles":["LeakedCredentialDetection"],"fullName":"LeakedCredentialDetection list enumerates custom detections across all zones","status":"skipped","title":"list enumerates custom detections across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LeakedCredentialCheck/Detection.test.ts"},{"assertionResults":[{"ancestorTitles":["LeakedCredentialCheck"],"fullName":"LeakedCredentialCheck enables leaked credential checks and restores the baseline on destroy","status":"skipped","title":"enables leaked credential checks and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["LeakedCredentialCheck"],"fullName":"LeakedCredentialCheck surfaces the typed DetectionQuotaExceeded error on unentitled zones","status":"skipped","title":"surfaces the typed DetectionQuotaExceeded error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["LeakedCredentialCheck"],"fullName":"LeakedCredentialCheck list enumerates the check across all zones","status":"skipped","title":"list enumerates the check across all zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["LeakedCredentialCheck"],"fullName":"LeakedCredentialCheck creates, updates, and destroys a custom detection","status":"skipped","title":"creates, updates, and destroys a custom detection","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LeakedCredentialCheck/LeakedCredentialCheck.test.ts"},{"assertionResults":[{"ancestorTitles":["MagicNetworkMonitoring.Config list"],"fullName":"MagicNetworkMonitoring.Config list list enumerates the account MNM config","status":"skipped","title":"list enumerates the account MNM config","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/Config.test.ts"},{"assertionResults":[{"ancestorTitles":["MagicNetworkMonitoring"],"fullName":"MagicNetworkMonitoring creates, updates in place, and deletes the account config","status":"skipped","title":"creates, updates in place, and deletes the account config","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["MagicNetworkMonitoring"],"fullName":"MagicNetworkMonitoring creates a threshold rule, updates it in place, and replaces on type change","status":"skipped","title":"creates a threshold rule, updates it in place, and replaces on type change","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/MagicNetworkMonitoring.test.ts"},{"assertionResults":[{"ancestorTitles":["MagicNetworkMonitoring.Rule"],"fullName":"MagicNetworkMonitoring.Rule list enumerates the deployed rule","status":"skipped","title":"list enumerates the deployed rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/Rule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicWanUnauthorized error","status":"skipped","title":"unentitled accounts surface the typed MagicWanUnauthorized error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a custom app, updates mutable props in place, and destroys it","status":"skipped","title":"creates a custom app, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account apps (well-typed [] when unentitled)","status":"skipped","title":"list enumerates account apps (well-typed [] when unentitled)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed custom app","status":"skipped","title":"list includes a deployed custom app","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/App.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicTransitNotOnboarded error","status":"passed","title":"unentitled accounts surface the typed MagicTransitNotOnboarded error","duration":77202.7647,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed GRE tunnels","status":"passed","title":"list enumerates the deployed GRE tunnels","duration":27614.514,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a GRE tunnel, updates mutable props in place, and destroys it","status":"skipped","title":"creates a GRE tunnel, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381724601,"endTime":1782381801803.7646,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/GreTunnel.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicTransitNotOnboarded error","status":"skipped","title":"unentitled accounts surface the typed MagicTransitNotOnboarded error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account IPsec tunnels (read-only [] when unentitled)","status":"passed","title":"list enumerates account IPsec tunnels (read-only [] when unentitled)","duration":26584.6429,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an IPsec tunnel, updates mutable props in place, and destroys it","status":"skipped","title":"creates an IPsec tunnel, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381466272,"endTime":1782381492856.6428,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/IpsecTunnel.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicWanUnauthorized error","status":"skipped","title":"unentitled accounts surface the typed MagicWanUnauthorized error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account Magic WAN sites","status":"skipped","title":"list enumerates account Magic WAN sites","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a site with WAN, LAN, and ACL, updates in place, and destroys","status":"skipped","title":"creates a site with WAN, LAN, and ACL, updates in place, and destroys","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/Site.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed site ACLs","status":"passed","title":"list enumerates the deployed site ACLs","duration":33150.04509999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Magic WAN error for ACL listing","status":"skipped","title":"unentitled accounts surface the typed Magic WAN error for ACL listing","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381443611,"endTime":1782381476761.0452,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/SiteAcl.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a well-typed array (empty on unentitled accounts)","status":"skipped","title":"list returns a well-typed array (empty on unentitled accounts)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed Magic WAN site LANs","status":"skipped","title":"list enumerates the deployed Magic WAN site LANs","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/SiteLan.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a well-typed array of site WANs","status":"passed","title":"list returns a well-typed array of site WANs","duration":26140.6678,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed site WAN","status":"skipped","title":"list enumerates the deployed site WAN","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381732771,"endTime":1782381758911.6677,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/SiteWan.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicTransitNotOnboarded error","status":"skipped","title":"unentitled accounts surface the typed MagicTransitNotOnboarded error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of routes","status":"skipped","title":"list returns a well-typed array of routes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed static route","status":"skipped","title":"list enumerates the deployed static route","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"routes a prefix over a GRE tunnel, updates in place, and destroys","status":"skipped","title":"routes a prefix over a GRE tunnel, updates in place, and destroys","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/StaticRoute.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, and delete an account-scoped job pushing to R2","status":"skipped","title":"create, update, and delete an account-scoped job pushing to R2","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed Logpush job","status":"skipped","title":"list enumerates the deployed Logpush job","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the dataset triggers a replacement","status":"skipped","title":"changing the dataset triggers a replacement","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Logpush/Job.test.ts"},{"assertionResults":[{"ancestorTitles":["MtlsCertificate"],"fullName":"MtlsCertificate create and delete a CA certificate","status":"skipped","title":"create and delete a CA certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["MtlsCertificate"],"fullName":"MtlsCertificate create and delete a leaf certificate with private key","status":"skipped","title":"create and delete a leaf certificate with private key","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["MtlsCertificate"],"fullName":"MtlsCertificate replaces the certificate when the PEM changes","status":"skipped","title":"replaces the certificate when the PEM changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["MtlsCertificate"],"fullName":"MtlsCertificate list enumerates the deployed mTLS certificate","status":"skipped","title":"list enumerates the deployed mTLS certificate","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MtlsCertificate/MtlsCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins the default ASN, updates in place, and restores the original on destroy","status":"skipped","title":"pins the default ASN, updates in place, and restores the original on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the CNI settings singleton","status":"skipped","title":"list enumerates the CNI settings singleton","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/NetworkInterconnects/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"issue, verify, and revoke a certificate","status":"skipped","title":"issue, verify, and revoke a certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates issued certificates","status":"skipped","title":"list enumerates issued certificates","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replacement on requestedValidity change","status":"skipped","title":"replacement on requestedValidity change","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replacement on hostnames change","status":"skipped","title":"replacement on hostnames change","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginCaCertificate/OriginCaCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden error","status":"skipped","title":"unentitled accounts surface the typed Forbidden error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list either enumerates organizations or tolerates the unentitled account","status":"skipped","title":"list either enumerates organizations or tolerates the unentitled account","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed organization","status":"skipped","title":"list enumerates the deployed organization","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, verify out-of-band, update in place, destroy","status":"skipped","title":"create, verify out-of-band, update in place, destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Organization/Organization.test.ts"},{"assertionResults":[{"ancestorTitles":["OriginPostQuantumEncryption"],"fullName":"OriginPostQuantumEncryption pins the setting and restores the original value on destroy","status":"skipped","title":"pins the setting and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["OriginPostQuantumEncryption"],"fullName":"OriginPostQuantumEncryption updates the value in place and keeps the captured initial value","status":"skipped","title":"updates the value in place and keeps the captured initial value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["OriginPostQuantumEncryption"],"fullName":"OriginPostQuantumEncryption no-op redeploy converges without changing the setting","status":"skipped","title":"no-op redeploy converges without changing the setting","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["OriginPostQuantumEncryption"],"fullName":"OriginPostQuantumEncryption list enumerates the setting across all zones","status":"skipped","title":"list enumerates the setting across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginPostQuantumEncryption/OriginPostQuantumEncryption.test.ts"},{"assertionResults":[{"ancestorTitles":["OriginTlsClientAuthCertificate"],"fullName":"OriginTlsClientAuthCertificate uploads and deletes a zone client certificate","status":"skipped","title":"uploads and deletes a zone client certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["OriginTlsClientAuthCertificate"],"fullName":"OriginTlsClientAuthCertificate replaces the certificate when the PEM changes","status":"skipped","title":"replaces the certificate when the PEM changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["OriginTlsClientAuthCertificate"],"fullName":"OriginTlsClientAuthCertificate list enumerates the deployed zone client certificate","status":"skipped","title":"list enumerates the deployed zone client certificate","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/Certificate.test.ts"},{"assertionResults":[{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation list returns [] for the non-listable association","status":"passed","title":"list returns [] for the non-listable association","duration":883.2446000000054,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation associates a hostname, updates cert and enablement in place, voids on destroy","status":"skipped","title":"associates a hostname, updates cert and enablement in place, voids on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation replaces the association when the hostname changes","status":"skipped","title":"replaces the association when the hostname changes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381649161,"endTime":1782381650044.2446,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/HostnameAssociation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"uploads and deletes a hostname client certificate","status":"skipped","title":"uploads and deletes a hostname client certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the hostname certificate when the PEM changes","status":"skipped","title":"replaces the hostname certificate when the PEM changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed hostname certificate","status":"skipped","title":"list enumerates the deployed hostname certificate","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/HostnameCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":["Setting"],"fullName":"Setting enables AOP and restores the original value on destroy","status":"skipped","title":"enables AOP and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Setting"],"fullName":"Setting updates the enabled flag in place","status":"skipped","title":"updates the enabled flag in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Setting"],"fullName":"Setting list enumerates the setting across all zones","status":"skipped","title":"list enumerates the setting across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/Setting.test.ts"},{"assertionResults":[{"ancestorTitles":["ManagedTransforms"],"fullName":"ManagedTransforms manages a named transform, updates in place, and restores it on destroy","status":"skipped","title":"manages a named transform, updates in place, and restores it on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ManagedTransforms"],"fullName":"ManagedTransforms destroy restores a transform that was enabled before management","status":"skipped","title":"destroy restores a transform that was enabled before management","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ManagedTransforms"],"fullName":"ManagedTransforms deploy with no transforms named adopts the singleton without writing","status":"skipped","title":"deploy with no transforms named adopts the singleton without writing","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ManagedTransforms"],"fullName":"ManagedTransforms list enumerates managed transforms across all zones","status":"skipped","title":"list enumerates managed transforms across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ManagedTransforms/ManagedTransforms.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, and destroy a page rule","status":"skipped","title":"create, verify out-of-band, and destroy a page rule","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating actions, status, priority and target syncs in place","status":"skipped","title":"updating actions, status, priority and target syncs in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing rule errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing rule errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed page rule","status":"skipped","title":"list enumerates the deployed page rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/PageRule/PageRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, replace on branch change, and destroy a deployment","status":"failed","title":"create, replace on branch change, and destroy a deployment","duration":83280.09820000001,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates deployments across Pages projects","status":"skipped","title":"list enumerates deployments across Pages projects","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381443825,"endTime":1782381527105.0981,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Deployment.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"attach and detach a custom domain","status":"failed","title":"attach and detach a custom domain","duration":80881.25419999998,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the domain name triggers replacement","status":"failed","title":"changing the domain name triggers replacement","duration":120065.42730000001,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Domain.test.ts:166:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates domains across all Pages projects","status":"skipped","title":"list enumerates domains across all Pages projects","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381443826,"endTime":1782381563893.4272,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Domain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a project with generated name","status":"failed","title":"create and delete a project with generated name","duration":26045.288399999998,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable props in place (same project id)","status":"failed","title":"update mutable props in place (same project id)","duration":61509.04759999999,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed project","status":"failed","title":"list enumerates the deployed project","duration":61475.20359999999,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the name triggers replacement","status":"failed","title":"changing the name triggers replacement","duration":61611.096,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782381466239,"endTime":1782381527852.096,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Project.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed PolicyQuotaExceeded error on unentitled zones","status":"skipped","title":"surfaces the typed PolicyQuotaExceeded error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates Page Shield policies across all zones","status":"passed","title":"list enumerates Page Shield policies across all zones","duration":3765.809000000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates, updates in place, and deletes a CSP policy","status":"skipped","title":"creates, updates in place, and deletes a CSP policy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381662817,"endTime":1782381666582.809,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/PageShield/Policy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"enables Page Shield, updates in place, and restores the baseline on destroy","status":"skipped","title":"enables Page Shield, updates in place, and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"surfaces the typed NotEntitled error for plan-gated flags","status":"skipped","title":"surfaces the typed NotEntitled error for plan-gated flags","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates Page Shield settings across all zones","status":"skipped","title":"list enumerates Page Shield settings across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/PageShield/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete bucket with default props","status":"failed","title":"create and delete bucket with default props","duration":26565.5768,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete bucket","status":"failed","title":"create, update, delete bucket","duration":26540.0547,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"existing bucket (matching name) is silently adopted without --adopt","status":"failed","title":"existing bucket (matching name) is silently adopted without --adopt","duration":26490.7376,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"destroying a bucket empties its objects first","status":"failed","title":"destroying a bucket empties its objects first","duration":26635.726500000004,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"lifecycle rules are added, updated, and removed","status":"failed","title":"lifecycle rules are added, updated, and removed","duration":26456.831799999996,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782381512414,"endTime":1782381539055.7266,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/Bucket.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update rules in place, and delete","status":"failed","title":"create, update rules in place, and delete","duration":25289.7243,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the queue triggers a replacement","status":"failed","title":"changing the queue triggers a replacement","duration":25237.575600000004,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates deployed bucket event notifications","status":"failed","title":"list enumerates deployed bucket event notifications","duration":25184.630099999995,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782381523352,"endTime":1782381548641.7244,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/BucketEventNotification.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"reads the disabled baseline and surfaces typed errors without external creds","status":"failed","title":"reads the disabled baseline and surfaces typed errors without external creds","duration":24821.502500000002,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates buckets that have Sippy enabled","status":"failed","title":"list enumerates buckets that have Sippy enabled","duration":24854.438100000003,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a bucket with Sippy enabled","status":"skipped","title":"list includes a bucket with Sippy enabled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"enable, no-op redeploy, disable on destroy","status":"skipped","title":"enable, no-op redeploy, disable on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381575356,"endTime":1782381600212.438,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/BucketSippy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates, updates, and deletes a bucket custom domain","status":"failed","title":"creates, updates, and deletes a bucket custom domain","duration":27309.2826,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates, updates, and deletes a bucket with multiple custom domains","status":"failed","title":"creates, updates, and deletes a bucket with multiple custom domains","duration":27449.588999999996,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782381586206,"endTime":1782381613657.589,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/CustomDomain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed R2 bucket","status":"failed","title":"list enumerates the deployed R2 bucket","duration":26266.144800000002,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782381572250,"endTime":1782381598516.1448,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/R2Bucket.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"promotes a dev queue to a live queue on deploy","status":"passed","title":"promotes a dev queue to a live queue on deploy","duration":4105.521400000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed queue","status":"passed","title":"list enumerates the deployed queue","duration":3905.2520000000004,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"suppresses deletion of a dev-only queue","status":"passed","title":"suppresses deletion of a dev-only queue","duration":1809.7677999999978,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381735314,"endTime":1782381739419.5215,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/Queue.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update settings, replace script, delete","status":"failed","title":"create, update settings, replace script, delete","duration":9230.8362,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates consumer after out-of-band delete","status":"failed","title":"recreates consumer after out-of-band delete","duration":9563.273999999998,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopts existing consumer after local state loss","status":"failed","title":"adopts existing consumer after local state loss","duration":9499.608299999998,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"fails clearly when queue has consumer for different script","status":"failed","title":"fails clearly when queue has consumer for different script","duration":9162.196699999999,"failureMessages":["AssertionError: expected '{\"_id\":\"Exit\",\"_tag\":\"Failure\",\"cause…' to contain 'fails-clearly-when-queue-has-consumer…'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/QueueConsumer.test.ts:375:23)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at Array. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1972:15)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:425:25)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"suppresses deletion of a dev-only consumer","status":"passed","title":"suppresses deletion of a dev-only consumer","duration":1394.4997999999996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"promotes a dev consumer to a live consumer on deploy","status":"failed","title":"promotes a dev consumer to a live consumer on deploy","duration":5395.2556,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed consumer","status":"failed","title":"list enumerates the deployed consumer","duration":2340.731599999999,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782381590100,"endTime":1782381599665.274,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/QueueConsumer.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"send → subscribe handler → DO state → polled by test client","status":"passed","title":"send → subscribe handler → DO state → polled by test client","duration":59225.817200000005,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381444236,"endTime":1782381503461.8171,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/RoundTrip.test.ts"},{"assertionResults":[{"ancestorTitles":["Subscription"],"fullName":"Subscription create r2 event subscription into a queue and destroy it","status":"skipped","title":"create r2 event subscription into a queue and destroy it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Subscription"],"fullName":"Subscription update mutable props in place (same subscriptionId)","status":"passed","title":"update mutable props in place (same subscriptionId)","duration":4935.798599999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Subscription"],"fullName":"Subscription replaces the subscription when the source changes","status":"passed","title":"replaces the subscription when the source changes","duration":3807.186899999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Subscription"],"fullName":"Subscription list enumerates the deployed subscription","status":"passed","title":"list enumerates the deployed subscription","duration":3408.876400000001,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381493644,"endTime":1782381505796.8765,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/Subscription.test.ts"},{"assertionResults":[{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings passes scalar fields through unchanged","status":"passed","title":"passes scalar fields through unchanged","duration":13.639299999999821,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings converts maxWaitTime to whole milliseconds","status":"passed","title":"converts maxWaitTime to whole milliseconds","duration":11.773199999999633,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings rounds sub-millisecond maxWaitTime up","status":"passed","title":"rounds sub-millisecond maxWaitTime up","duration":11.579200000000128,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings converts retryDelay to whole seconds","status":"passed","title":"converts retryDelay to whole seconds","duration":11.47820000000047,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings rounds partial-second retryDelay up","status":"passed","title":"rounds partial-second retryDelay up","duration":11.297999999999774,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings leaves missing time fields undefined","status":"passed","title":"leaves missing time fields undefined","duration":1.087800000000243,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381743478,"endTime":1782381743493.088,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/toConsumerSettings.test.ts"},{"assertionResults":[{"ancestorTitles":["async worker (env binding)"],"fullName":"async worker (env binding) throttles requests past the configured limit","status":"skipped","title":"throttles requests past the configured limit","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["async worker (env binding)"],"fullName":"async worker (env binding) tracks each key independently","status":"skipped","title":"tracks each key independently","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["effect worker (yield* RateLimit)"],"fullName":"effect worker (yield* RateLimit) throttles requests past the configured limit","status":"skipped","title":"throttles requests past the configured limit","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["effect worker (yield* RateLimit)"],"fullName":"effect worker (yield* RateLimit) tracks each key independently","status":"skipped","title":"tracks each key independently","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RateLimit/RateLimit.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"legacy pipeline: create, in-place update, replace on name change","status":"skipped","title":"legacy pipeline: create, in-place update, replace on name change","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed legacy pipeline","status":"skipped","title":"list enumerates the deployed legacy pipeline","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/LegacyPipeline.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed pipeline","status":"skipped","title":"list enumerates the deployed pipeline","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/Pipeline.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"stream: create with defaults, patch http in place, replace on schema change","status":"skipped","title":"stream: create with defaults, patch http in place, replace on schema change","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"end-to-end: stream → sql pipeline → r2 sink, replacement on sink change","status":"skipped","title":"end-to-end: stream → sql pipeline → r2 sink, replacement on sink change","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/Pipelines.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed sink","status":"skipped","title":"list enumerates the deployed sink","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/Sink.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed pipeline stream","status":"skipped","title":"list enumerates the deployed pipeline stream","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/Stream.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"enable, sync maintenance config, register credential, destroy","status":"failed","title":"enable, sync maintenance config, register credential, destroy","duration":25884.1213,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed catalog","status":"failed","title":"list enumerates the deployed catalog","duration":26144.644300000004,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"re-enables after out-of-band disable","status":"failed","title":"re-enables after out-of-band disable","duration":25857.055699999997,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782381518497,"endTime":1782381544643.6443,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2DataCatalog/R2DataCatalog.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"regional hostname lifecycle (typed error on unentitled zones)","status":"skipped","title":"regional hostname lifecycle (typed error on unentitled zones)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates regional hostnames across zones","status":"skipped","title":"list enumerates regional hostnames across zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RegionalHostname/RegionalHostname.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden error","status":"passed","title":"unentitled accounts surface the typed Forbidden error","duration":51820.651600000005,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create (or adopt), verify out-of-band, destroy forgets but keeps the app","status":"passed","title":"create (or adopt), verify out-of-band, destroy forgets but keeps the app","duration":25384.219300000004,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed RealtimeKit app","status":"passed","title":"list enumerates the deployed RealtimeKit app","duration":25937.1463,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381563760,"endTime":1782381615580.6516,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RealtimeKit/App.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create with defaults, verify out-of-band, update in place, destroy","status":"passed","title":"create with defaults, verify out-of-band, update in place, destroy","duration":27330.492899999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed preset","status":"passed","title":"list enumerates the deployed preset","duration":27264.731099999997,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381731776,"endTime":1782381759106.493,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RealtimeKit/Preset.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, update in place, destroy","status":"passed","title":"create, verify out-of-band, update in place, destroy","duration":27298.683499999996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates webhooks across the account's apps","status":"passed","title":"list enumerates webhooks across the account's apps","duration":51723.5966,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381726718,"endTime":1782381778443.5967,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RealtimeKit/Webhook.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden error","status":"passed","title":"unentitled accounts surface the typed Forbidden error","duration":27048.221299999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, toggle active in place, and destroy a risk scoring integration","status":"skipped","title":"create, toggle active in place, and destroy a risk scoring integration","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates risk scoring integrations","status":"passed","title":"list enumerates risk scoring integrations","duration":26391.537199999995,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a freshly deployed integration","status":"skipped","title":"list includes a freshly deployed integration","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381733041,"endTime":1782381760089.2212,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RiskScoring/Integration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"reads succeed and write-blocked accounts surface the typed Forbidden error","status":"skipped","title":"reads succeed and write-blocked accounts surface the typed Forbidden error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the account's sent shares (typed Attributes), and includes a deployed share when entitled","status":"skipped","title":"list enumerates the account's sent shares (typed Attributes), and includes a deployed share when entitled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a share with a gateway policy, renames in place, adds and removes a resource, and destroys","status":"skipped","title":"creates a share with a gateway policy, renames in place, adds and removes a resource, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"manages standalone ShareResource and ShareRecipient on an existing share","status":"skipped","title":"manages standalone ShareResource and ShareRecipient on an existing share","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ResourceSharing/Share.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list() enumerates share recipients across the account's sent shares","status":"passed","title":"list() enumerates share recipients across the account's sent shares","duration":27607.030300000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list() includes a freshly deployed ShareRecipient","status":"skipped","title":"list() includes a freshly deployed ShareRecipient","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381727739,"endTime":1782381755346.0303,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ResourceSharing/ShareRecipient.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates share resources across the account's sent shares (typed Attributes), and includes a deployed entry when entitled","status":"skipped","title":"list enumerates share resources across the account's sent shares (typed Attributes), and includes a deployed entry when entitled","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ResourceSharing/ShareResource.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an ip list with default name","status":"skipped","title":"create and delete an ip list with default name","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update description and items in place (same listId)","status":"skipped","title":"update description and items in place (same listId)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing kind replaces the list","status":"skipped","title":"changing kind replaces the list","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopts an existing list with the same name","status":"skipped","title":"adopts an existing list with the same name","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Rules/List.test.ts"},{"assertionResults":[{"ancestorTitles":["Domain"],"fullName":"Domain adopts a registered domain, no-op syncs, and never releases it on destroy","status":"skipped","title":"adopts a registered domain, no-op syncs, and never releases it on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Domain"],"fullName":"Domain updates settings in place and restores the baseline on destroy","status":"skipped","title":"updates settings in place and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Domain"],"fullName":"Domain list enumerates registrar domains on the account","status":"skipped","title":"list enumerates registrar domains on the account","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Registrar/Domain.test.ts"},{"assertionResults":[{"ancestorTitles":["RumRule"],"fullName":"RumRule create, update in place, and delete a rule","status":"skipped","title":"create, update in place, and delete a rule","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RumRule"],"fullName":"RumRule list enumerates rules across all rulesets","status":"skipped","title":"list enumerates rules across all rulesets","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RumRule"],"fullName":"RumRule recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Rum/Rule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a host (gray-clouded) site","status":"failed","title":"create and delete a host (gray-clouded) site","duration":22541.262499999997,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable props in place (same siteTag)","status":"failed","title":"update mutable props in place (same siteTag)","duration":31775.306399999998,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"zone (orange-clouded) site with autoInstall, replaced on identity flip","status":"failed","title":"zone (orange-clouded) site with autoInstall, replaced on identity flip","duration":1241.8410999999978,"failureMessages":["Error: Please wait and consider throttling your request speed\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/Zone/lookup.ts:88:9)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed RUM site","status":"failed","title":"list enumerates the deployed RUM site","duration":31763.3936,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":31450.448099999998,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]}],"startTime":1782381584511,"endTime":1782381616289.3064,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Rum/Site.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"account entrypoint lifecycle (typed PhaseNotEntitled on unentitled accounts)","status":"skipped","title":"account entrypoint lifecycle (typed PhaseNotEntitled on unentitled accounts)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the account phase entrypoints","status":"skipped","title":"list enumerates the account phase entrypoints","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ruleset/AccountEntrypoint.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"custom ruleset lifecycle (typed PhaseNotEntitled on unentitled accounts)","status":"skipped","title":"custom ruleset lifecycle (typed PhaseNotEntitled on unentitled accounts)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed custom ruleset","status":"skipped","title":"list enumerates the deployed custom ruleset","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ruleset/CustomRuleset.test.ts"},{"assertionResults":[{"ancestorTitles":["Ruleset"],"fullName":"Ruleset creates, updates, and deletes a zone phase entrypoint ruleset","status":"skipped","title":"creates, updates, and deletes a zone phase entrypoint ruleset","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Ruleset"],"fullName":"Ruleset creates and tears down a ruleset whose zone is provisioned in the same deploy","status":"skipped","title":"creates and tears down a ruleset whose zone is provisioned in the same deploy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Ruleset"],"fullName":"Ruleset list enumerates the deployed zone phase entrypoint","status":"skipped","title":"list enumerates the deployed zone phase entrypoint","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ruleset/Ruleset.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"sets a per-operation override, updates it in place, and clears it on destroy","status":"skipped","title":"sets a per-operation override, updates it in place, and clears it on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed per-operation override","status":"skipped","title":"list enumerates the deployed per-operation override","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SchemaValidation/OperationSetting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"uploads a schema, enables in place, replaces on disable and source change, destroys","status":"skipped","title":"uploads a schema, enables in place, replaces on disable and source change, destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed schema across all zones","status":"skipped","title":"list enumerates the deployed schema across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SchemaValidation/Schema.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins zone settings, updates in place, and restores the baseline on destroy","status":"skipped","title":"pins zone settings, updates in place, and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the schema validation settings across all zones","status":"skipped","title":"list enumerates the schema validation settings across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SchemaValidation/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"SecretsStore Secret declared on a Worker's env round-trips to runtime as a SecretsStoreSecret","status":"skipped","title":"SecretsStore Secret declared on a Worker's env round-trips to runtime as a SecretsStoreSecret","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SecretsStore/AsyncSecretBinding.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed secret across stores","status":"failed","title":"list enumerates the deployed secret across stores","duration":9992.9505,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782381518651,"endTime":1782381528643.9504,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SecretsStore/Secret.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"createStore POSTs a single JSON object body (regression: invalid_json_body)","status":"passed","title":"createStore POSTs a single JSON object body (regression: invalid_json_body)","duration":96.78180000000066,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"createStore surfaces MaximumStoresExceeded as a tagged error so callers can catchTag it","status":"passed","title":"createStore surfaces MaximumStoresExceeded as a tagged error so callers can catchTag it","duration":95.16560000000027,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed secrets store","status":"skipped","title":"list enumerates the deployed secrets store","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381548370,"endTime":1782381548467.1655,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SecretsStore/SecretsStore.test.ts"},{"assertionResults":[{"ancestorTitles":["SecurityTxt"],"fullName":"SecurityTxt creates a security.txt, verifies out-of-band, and deletes on destroy","status":"skipped","title":"creates a security.txt, verifies out-of-band, and deletes on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SecurityTxt"],"fullName":"SecurityTxt updates mutable fields in place (full-replace PUT)","status":"skipped","title":"updates mutable fields in place (full-replace PUT)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SecurityTxt"],"fullName":"SecurityTxt disables the file without deleting it, then destroy removes it","status":"skipped","title":"disables the file without deleting it, then destroy removes it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SecurityTxt"],"fullName":"SecurityTxt list enumerates configured security.txt files","status":"skipped","title":"list enumerates configured security.txt files","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SecurityTxt/SecurityTxt.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"Config.redacted with literal default round-trips to runtime as Redacted","status":"skipped","title":"Config.redacted with literal default round-trips to runtime as Redacted","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Config.redacted resolved from env deploys as a secret_text and round-trips","status":"skipped","title":"Config.redacted resolved from env deploys as a secret_text and round-trips","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Config.string round-trips to runtime as a string","status":"skipped","title":"Config.string round-trips to runtime as a string","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Config.number round-trips to runtime preserving the number type","status":"skipped","title":"Config.number round-trips to runtime preserving the number type","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Config.string with object default round-trips to runtime preserving nested shape","status":"skipped","title":"Config.string with object default round-trips to runtime preserving nested shape","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Secret/Secret.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed SpectrumProtocolNotAvailable error on unentitled zones","status":"skipped","title":"surfaces the typed SpectrumProtocolNotAvailable error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a tcp/22 application","status":"skipped","title":"create, update in place, and destroy a tcp/22 application","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing app errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing app errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates Spectrum applications across all zones","status":"passed","title":"list enumerates Spectrum applications across all zones","duration":3367.3952999999965,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list surfaces a deployed Spectrum application (entitled zone)","status":"skipped","title":"list surfaces a deployed Spectrum application (entitled zone)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381724035,"endTime":1782381727402.3953,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Spectrum/Application.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create with generated name, update code in place, destroy","status":"skipped","title":"create with generated name, update code in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"renaming an explicit snippet triggers replacement","status":"skipped","title":"renaming an explicit snippet triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed snippet","status":"skipped","title":"list enumerates the deployed snippet","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Snippets/Snippet.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"snippet rules — create, update, list, and destroy in dependency order","status":"skipped","title":"snippet rules — create, update, list, and destroy in dependency order","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Snippets/SnippetRules.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a scheduled speed test","status":"skipped","title":"create and delete a scheduled speed test","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the frequency converges in place","status":"skipped","title":"changing the frequency converges in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the region triggers replacement","status":"skipped","title":"changing the region triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing schedule errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing schedule errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed schedule across zones","status":"skipped","title":"list enumerates the deployed schedule across zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Speed/TestSchedule.test.ts"},{"assertionResults":[{"ancestorTitles":["State"],"fullName":"State getVersion returns the current STATE_STORE_VERSION","status":"failed","title":"getVersion returns the current STATE_STORE_VERSION","duration":840.4032999999981,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State DELETE /state/stacks/:stack wipes the test namespace (cleanup)","status":"failed","title":"DELETE /state/stacks/:stack wipes the test namespace (cleanup)","duration":361.12590000000273,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State PUT /resources/:fqn (setState) persists a resource","status":"failed","title":"PUT /resources/:fqn (setState) persists a resource","duration":254.168700000002,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /resources/:fqn (getState) reads back the persisted value","status":"failed","title":"GET /resources/:fqn (getState) reads back the persisted value","duration":235.47989999999845,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /resources/:fqn returns undefined for a missing fqn","status":"failed","title":"GET /resources/:fqn returns undefined for a missing fqn","duration":208.64869999999792,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /resources (listResources) returns the FQNs in the stage","status":"failed","title":"GET /resources (listResources) returns the FQNs in the stage","duration":200.0601999999999,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /stacks (listStacks) includes the registered test stack","status":"failed","title":"GET /stacks (listStacks) includes the registered test stack","duration":159.3061000000016,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /stacks/:stack/stages (listStages) includes the test stage","status":"failed","title":"GET /stacks/:stack/stages (listStages) includes the test stage","duration":159.4854000000014,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State PUT /output (setStackOutput) persists a stack output","status":"failed","title":"PUT /output (setStackOutput) persists a stack output","duration":145.4532999999974,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /output (getStackOutput) reads back the persisted output","status":"failed","title":"GET /output (getStackOutput) reads back the persisted output","duration":161.0747999999985,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /output returns undefined for an un-deployed stage","status":"failed","title":"GET /output returns undefined for an un-deployed stage","duration":150.27590000000055,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /replaced-resources (getReplacedResources) returns status===replaced rows","status":"failed","title":"GET /replaced-resources (getReplacedResources) returns status===replaced rows","duration":156.3755999999994,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State DELETE /resources/:fqn (deleteState) removes a single resource","status":"failed","title":"DELETE /resources/:fqn (deleteState) removes a single resource","duration":139.1758000000009,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State DELETE /stacks/:stack?stage=... clears one stage but leaves the stack registered","status":"failed","title":"DELETE /stacks/:stack?stage=... clears one stage but leaves the stack registered","duration":167.87839999999778,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State DELETE /stacks/:stack (no stage) removes the stack from listStacks","status":"failed","title":"DELETE /stacks/:stack (no stage) removes the stack from listStacks","duration":129.32359999999971,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State setState 100x sequential — surfaces transient failures","status":"failed","title":"setState 100x sequential — surfaces transient failures","duration":158.97769999999946,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State setState 100x concurrent — surfaces racy transient failures","status":"failed","title":"setState 100x concurrent — surfaces racy transient failures","duration":136.23049999999785,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET+PUT interleaved 30x5 — engine traffic pattern","status":"failed","title":"GET+PUT interleaved 30x5 — engine traffic pattern","duration":192.6143000000011,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782381596966,"endTime":1782381600923.6143,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/StateStore/State.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, and clear tags on a KV namespace","status":"failed","title":"create, update, and clear tags on a KV namespace","duration":31718.56869999999,"failureMessages":["Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time.\n at Proxy. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Output.ts:494:19)\n at String ()\n at setQueryValue (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:729:23)\n at Module.buildRequestParts (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:785:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:631:30)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing resourceId triggers replacement","status":"failed","title":"changing resourceId triggers replacement","duration":34115.83559999999,"failureMessages":["Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time.\n at Proxy. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Output.ts:494:19)\n at String ()\n at setQueryValue (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:729:23)\n at Module.buildRequestParts (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:785:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:631:30)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing tags error without adopt, take over with adopt(true)","status":"failed","title":"adoption — existing tags error without adopt, take over with adopt(true)","duration":84952.3915,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account-wide tagged resources","status":"failed","title":"list enumerates account-wide tagged resources","duration":30355.90049999999,"failureMessages":["Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time.\n at Proxy. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Output.ts:494:19)\n at String ()\n at setQueryValue (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:729:23)\n at Module.buildRequestParts (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:785:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:631:30)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)"],"meta":{},"tags":[]}],"startTime":1782381443707,"endTime":1782381528661.3916,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tags/AccountResourceTags.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, and clear tags on a DNS record","status":"skipped","title":"create, update, and clear tags on a DNS record","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"tag the zone itself and clear on destroy","status":"skipped","title":"tag the zone itself and clear on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates tagged zone-scoped resources","status":"skipped","title":"list enumerates tagged zone-scoped resources","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tags/ZoneResourceTags.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","status":"skipped","title":"surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates advanced certificate packs across zones","status":"passed","title":"list enumerates advanced certificate packs across zones","duration":3164.4683000000005,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed advanced certificate pack","status":"skipped","title":"list includes a deployed advanced certificate pack","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"orders a pack, updates validation method in place, and deletes it","status":"skipped","title":"orders a pack, updates validation method in place, and deletes it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381689448,"endTime":1782381692612.4683,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ssl/CertificatePack.test.ts"},{"assertionResults":[{"ancestorTitles":["UniversalSsl"],"fullName":"UniversalSsl disables Universal SSL and restores the original value on destroy","status":"skipped","title":"disables Universal SSL and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UniversalSsl"],"fullName":"UniversalSsl updates enabled in place and keeps the captured initial value","status":"skipped","title":"updates enabled in place and keeps the captured initial value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UniversalSsl"],"fullName":"UniversalSsl destroy restores a disabled baseline when managing from a disabled zone","status":"skipped","title":"destroy restores a disabled baseline when managing from a disabled zone","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UniversalSsl"],"fullName":"UniversalSsl list enumerates the setting across all zones","status":"skipped","title":"list enumerates the setting across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ssl/UniversalSsl.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a live input","status":"skipped","title":"create, update in place, and delete a live input","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed live input","status":"skipped","title":"list enumerates the deployed live input","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/LiveInput.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, toggle enabled in place, replace destination, and delete","status":"skipped","title":"create, toggle enabled in place, replace destination, and delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates outputs across all live inputs","status":"skipped","title":"list enumerates outputs across all live inputs","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/LiveInputOutput.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a signing key","status":"skipped","title":"create and delete a signing key","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed signing key","status":"skipped","title":"list enumerates the deployed signing key","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/SigningKey.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a watermark with default name","status":"skipped","title":"create and delete a watermark with default name","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"prop change replaces the watermark (create-only resource)","status":"skipped","title":"prop change replaces the watermark (create-only resource)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed watermark","status":"skipped","title":"list enumerates the deployed watermark","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/Watermark.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"configure, update, and delete the account webhook","status":"skipped","title":"configure, update, and delete the account webhook","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the account Stream webhook","status":"skipped","title":"list enumerates the account Stream webhook","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/Webhook.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a typed array of token validation rules","status":"passed","title":"list returns a typed array of token validation rules","duration":825.6631000000016,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed token validation rule","status":"skipped","title":"list enumerates the deployed token validation rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381746167,"endTime":1782381746992.663,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/TokenValidation/Rule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed TokenValidationNotEntitled error on unentitled accounts","status":"skipped","title":"surfaces the typed TokenValidationNotEntitled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates configurations across all zones","status":"passed","title":"list enumerates configurations across all zones","duration":3477.220799999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"configuration + rule lifecycle: create, update in place, rotate keys, destroy","status":"skipped","title":"configuration + rule lifecycle: create, update in place, rotate keys, destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381696865,"endTime":1782381700342.2207,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/TokenValidation/TokenValidation.test.ts"},{"assertionResults":[{"ancestorTitles":["Tunnel Configuration"],"fullName":"Tunnel Configuration list enumerates configurations across all tunnels","status":"skipped","title":"list enumerates configurations across all tunnels","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/Configuration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update comment in place, and destroy a hostname route","status":"skipped","title":"create, update comment in place, and destroy a hostname route","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed hostname route","status":"skipped","title":"list enumerates the deployed hostname route","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/HostnameRoute.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete route with default props","status":"skipped","title":"create and delete route with default props","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating the comment patches in place","status":"skipped","title":"updating the comment patches in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopt: takes over a pre-existing route","status":"skipped","title":"adopt: takes over a pre-existing route","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed route","status":"skipped","title":"list enumerates the deployed route","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/Route.test.ts"},{"assertionResults":[{"ancestorTitles":["Tunnel runtime bindings"],"fullName":"Tunnel runtime bindings TunnelRead lists tunnels with a read-scoped token","status":"skipped","title":"TunnelRead lists tunnels with a read-scoped token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Tunnel runtime bindings"],"fullName":"Tunnel runtime bindings TunnelWrite creates and deletes a tunnel with a write-scoped token","status":"skipped","title":"TunnelWrite creates and deletes a tunnel with a write-scoped token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Tunnel runtime bindings"],"fullName":"Tunnel runtime bindings TunnelReadWrite drives the full CRUD surface","status":"skipped","title":"TunnelReadWrite drives the full CRUD surface","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Tunnel.list"],"fullName":"Tunnel.list list enumerates the deployed tunnel","status":"skipped","title":"list enumerates the deployed tunnel","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/Tunnel.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy a virtual network","status":"skipped","title":"create, verify, and destroy a virtual network","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name and comment in place (same id)","status":"skipped","title":"update name and comment in place (same id)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed virtual network","status":"skipped","title":"list enumerates the deployed virtual network","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/VirtualNetwork.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, rename in place, and destroy a WARP Connector tunnel","status":"failed","title":"create, rename in place, and destroy a WARP Connector tunnel","duration":26797.2726,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed WARP Connector tunnel","status":"failed","title":"list enumerates the deployed WARP Connector tunnel","duration":26534.859399999998,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]}],"startTime":1782381466899,"endTime":1782381493696.2727,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/WarpConnector.test.ts"},{"assertionResults":[{"ancestorTitles":["UrlNormalization"],"fullName":"UrlNormalization configures URL normalization and resets to defaults on destroy","status":"skipped","title":"configures URL normalization and resets to defaults on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UrlNormalization"],"fullName":"UrlNormalization updates scope and type in place","status":"skipped","title":"updates scope and type in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UrlNormalization"],"fullName":"UrlNormalization applies Cloudflare defaults when scope and type are omitted","status":"skipped","title":"applies Cloudflare defaults when scope and type are omitted","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UrlNormalization"],"fullName":"UrlNormalization list enumerates URL normalization across all zones","status":"skipped","title":"list enumerates URL normalization across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/UrlNormalization/UrlNormalization.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a widget with default name","status":"failed","title":"create and delete a widget with default name","duration":24432.5447,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable props in place (same sitekey)","status":"failed","title":"update mutable props in place (same sitekey)","duration":23795.959400000003,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":23786.449699999997,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed widget","status":"failed","title":"list enumerates the deployed widget","duration":23775.9655,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782381577148,"endTime":1782381601580.5447,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Turnstile/Widget.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete index with explicit dimensions","status":"skipped","title":"create and delete index with explicit dimensions","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create index from a preset","status":"skipped","title":"create index from a preset","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces index when dimensions change","status":"skipped","title":"replaces index when dimensions change","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed index","status":"skipped","title":"list enumerates the deployed index","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Vectorize/VectorizeIndex.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"VectorizeIndex.bind exercises the client surface","status":"skipped","title":"VectorizeIndex.bind exercises the client surface","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Vectorize/VectorizeIndexBinding.test.ts"},{"assertionResults":[{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex create and delete a metadata index","status":"skipped","title":"create and delete a metadata index","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex multiple metadata indexes on the same parent coexist","status":"skipped","title":"multiple metadata indexes on the same parent coexist","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex replacing the parent index also replaces the metadata index","status":"skipped","title":"replacing the parent index also replaces the metadata index","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex list enumerates the deployed metadata index","status":"skipped","title":"list enumerates the deployed metadata index","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex destroy is idempotent when the parent index was deleted out-of-band","status":"skipped","title":"destroy is idempotent when the parent index was deleted out-of-band","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Vectorize/VectorizeMetadataIndex.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed credential","status":"skipped","title":"list enumerates the deployed credential","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VulnerabilityScanner/Credential.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, rename, and destroy a credential set","status":"skipped","title":"create, rename, and destroy a credential set","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed credential set","status":"skipped","title":"list enumerates the deployed credential set","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"credential lifecycle — create, mutate in place, rotate value, destroy","status":"skipped","title":"credential lifecycle — create, mutate in place, rotate value, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"moving a credential to a different set triggers replacement","status":"skipped","title":"moving a credential to a different set triggers replacement","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VulnerabilityScanner/CredentialSet.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy a target environment","status":"skipped","title":"create, verify, and destroy a target environment","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating name and description patches in place; clearing description","status":"skipped","title":"updating name and description patches in place; clearing description","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed target environment","status":"skipped","title":"list enumerates the deployed target environment","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VulnerabilityScanner/TargetEnvironment.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete vpc service","status":"failed","title":"create, update, delete vpc service","duration":56299.1299,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create vpc service with ipv4 host","status":"failed","title":"create vpc service with ipv4 host","duration":56283.162500000006,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create vpc service with dual-stack host","status":"skipped","title":"create vpc service with dual-stack host","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed vpc service","status":"failed","title":"list enumerates the deployed vpc service","duration":56626.5472,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782381443720,"endTime":1782381500349.547,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VpcService/VpcService.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"reference vpc service by name and by id","status":"failed","title":"reference vpc service by name and by id","duration":26326.234399999998,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782381554595,"endTime":1782381580921.2344,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VpcService/VpcServiceRef.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"StaticSite: editing a source file republishes the assets in a single deploy","status":"passed","title":"StaticSite: editing a source file republishes the assets in a single deploy","duration":41534.69219999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"StaticSite: class form deploys and serves the built assets","status":"passed","title":"StaticSite: class form deploys and serves the built assets","duration":21264.269600000014,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"StaticSite: relocating the project (and deleting the old one) preserves hash.assets","status":"passed","title":"StaticSite: relocating the project (and deleting the old one) preserves hash.assets","duration":43291.74210000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"StaticSite: a bundle-only change keeps the asset manifest (hash.assets stable)","status":"passed","title":"StaticSite: a bundle-only change keeps the asset manifest (hash.assets stable)","duration":46525.05440000001,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381444364,"endTime":1782381490894.0544,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Website/StaticSite.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"Vite: editing a source file republishes the assets in a single deploy","status":"failed","title":"Vite: editing a source file republishes the assets in a single deploy","duration":2000.5957000000017,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Vite: class form deploys and serves the built assets","status":"failed","title":"Vite: class form deploys and serves the built assets","duration":2018.427599999999,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Vite: relocating rootDir (and deleting the old one) is a no-op when sources are identical","status":"failed","title":"Vite: relocating rootDir (and deleting the old one) is a no-op when sources are identical","duration":2037.7099999999991,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Vite: `env` props are inlined as `import.meta.env.*` into the bundle","status":"failed","title":"Vite: `env` props are inlined as `import.meta.env.*` into the bundle","duration":2027.3682999999983,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782381495448,"endTime":1782381497487.71,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Website/Vite.test.ts"},{"assertionResults":[{"ancestorTitles":["Settings"],"fullName":"Settings pins the settings to the default baseline without touching the API","status":"skipped","title":"pins the settings to the default baseline without touching the API","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Settings"],"fullName":"Settings surfaces the typed ZoneNotEntitled error when enabling on unentitled zones","status":"skipped","title":"surfaces the typed ZoneNotEntitled error when enabling on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Settings"],"fullName":"Settings enables the crawler bypass and restores the original value on destroy","status":"skipped","title":"enables the crawler bypass and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Settings"],"fullName":"Settings list enumerates the settings across all zones","status":"skipped","title":"list enumerates the settings across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/WaitingRoom/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed ZoneNotEntitled error on unentitled zones","status":"skipped","title":"surfaces the typed ZoneNotEntitled error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a waiting room","status":"skipped","title":"create, update in place, and destroy a waiting room","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates waiting rooms across zones","status":"passed","title":"list enumerates waiting rooms across zones","duration":3260.2144000000008,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381678023,"endTime":1782381681283.2144,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/WaitingRoom/WaitingRoom.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a well-typed array of web3 content lists","status":"passed","title":"list returns a well-typed array of web3 content lists","duration":848.5751999999993,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list surfaces a deployed web3 content list (entitled account)","status":"skipped","title":"list surfaces a deployed web3 content list (entitled account)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381745058,"endTime":1782381745906.5752,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Web3/ContentList.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a well-typed array of web3 hostnames","status":"passed","title":"list returns a well-typed array of web3 hostnames","duration":667.4793999999965,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list surfaces a deployed web3 hostname (entitled account)","status":"skipped","title":"list surfaces a deployed web3 hostname (entitled account)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381745561,"endTime":1782381746228.4795,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Web3/Hostname.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed Web3HostnameNotEntitled error on unentitled accounts","status":"skipped","title":"surfaces the typed Web3HostnameNotEntitled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, replace on target change, destroy","status":"skipped","title":"create, update in place, replace on target change, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"content list — set entries, update declaratively, reset on destroy","status":"skipped","title":"content list — set entries, update declaratively, reset on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Web3/Web3Hostname.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, replace, and destroy a dispatch namespace","status":"skipped","title":"create, replace, and destroy a dispatch namespace","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"upload, update in place, and destroy a namespace script","status":"skipped","title":"upload, update in place, and destroy a namespace script","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed dispatch namespace","status":"skipped","title":"list enumerates the deployed dispatch namespace","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/WorkersForPlatforms/DispatchNamespace.test.ts"},{"assertionResults":[{"ancestorTitles":["AccountSetting"],"fullName":"AccountSetting flips green compute, updates in place, and restores the baseline on destroy","status":"failed","title":"flips green compute, updates in place, and restores the baseline on destroy","duration":30795.94570000001,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":["AccountSetting"],"fullName":"AccountSetting no-op deploy when desired settings already match the live account","status":"passed","title":"no-op deploy when desired settings already match the live account","duration":1681.6261000000231,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AccountSetting"],"fullName":"AccountSetting list returns the account settings singleton","status":"passed","title":"list returns the account settings singleton","duration":245.37280000001192,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381443679,"endTime":1782381476402.3728,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/AccountSetting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed worker fires the scheduled handler on its cron trigger","status":"skipped","title":"deployed worker fires the scheduled handler on its cron trigger","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/CronEventSource.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"Drizzle.postgres query runs inside a Workflow task (ExecutionContext provided per run)","status":"skipped","title":"Drizzle.postgres query runs inside a Workflow task (ExecutionContext provided per run)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/DrizzleWorkflow.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"durable object methods can use binding clients","status":"skipped","title":"durable object methods can use binding clients","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"tick streams sequential values from a durable object (tutorial repro)","status":"skipped","title":"tick streams sequential values from a durable object (tutorial repro)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"async worker durable object binding accepts scriptName","status":"skipped","title":"async worker durable object binding accepts scriptName","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"durable object class migrations across redeploys","status":"skipped","title":"durable object class migrations across redeploys","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/DurableObjectNamespace.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed http-api worker handles createTask + getTask via typed HttpApiClient","status":"skipped","title":"deployed http-api worker handles createTask + getTask via typed HttpApiClient","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"cors middleware adds Access-Control-Allow-Origin header on preflight","status":"skipped","title":"cors middleware adds Access-Control-Allow-Origin header on preflight","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"cors middleware adds Access-Control-Allow-Origin header on actual requests","status":"skipped","title":"cors middleware adds Access-Control-Allow-Origin header on actual requests","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"concurrent createTask survives scope-lifecycle pressure","status":"skipped","title":"concurrent createTask survives scope-lifecycle pressure","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"createTaskDO + getTaskDO round-trip 100x in parallel through the DO HttpApi","status":"skipped","title":"createTaskDO + getTaskDO round-trip 100x in parallel through the DO HttpApi","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/HttpApi.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a destination with default name","status":"failed","title":"create and delete a destination with default name","duration":31546.64079999998,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update url, headers, and enabled in place (same slug)","status":"failed","title":"update url, headers, and enabled in place (same slug)","duration":85209.98530000001,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replacement on name change (new slug, old destination removed)","status":"failed","title":"replacement on name change (new slug, old destination removed)","duration":83242.745,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed destination","status":"failed","title":"list enumerates the deployed destination","duration":82981.8099,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782381443653,"endTime":1782381528864.9854,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/ObservabilityDestination.test.ts"},{"assertionResults":[{"ancestorTitles":["Cloudflare.Worker with bundle: false"],"fullName":"Cloudflare.Worker with bundle: false uploads a prebuilt module graph byte-for-byte","status":"failed","title":"uploads a prebuilt module graph byte-for-byte","duration":8206.2474,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782381624385,"endTime":1782381632591.2473,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/PrebuiltWorker.test.ts"},{"assertionResults":[{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle collects modules matching the default rules, entry first, names POSIX-relative to the entry's directory","status":"passed","title":"collects modules matching the default rules, entry first, names POSIX-relative to the entry's directory","duration":1533.6422000000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle uploads file contents byte-for-byte","status":"passed","title":"uploads file contents byte-for-byte","duration":546.8302999999996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle does not walk outside the entry's directory","status":"passed","title":"does not walk outside the entry's directory","duration":807.0192000000006,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle custom rules replace the defaults","status":"passed","title":"custom rules replace the defaults","duration":835.9560999999994,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle hash is stable across reads and sensitive to module changes","status":"passed","title":"hash is stable across reads and sensitive to module changes","duration":1398.8297000000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle fails with BundleError when the entry does not exist","status":"passed","title":"fails with BundleError when the entry does not exist","duration":228.21460000000116,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381735199,"endTime":1782381736732.642,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/PrebuiltWorkerBundle.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an opt-out route (no script)","status":"skipped","title":"create and delete an opt-out route (no script)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"route to a Worker, then update pattern and script in place","status":"skipped","title":"route to a Worker, then update pattern and script in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing route errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing route errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed route across all zones","status":"skipped","title":"list enumerates the deployed route across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Route.test.ts"},{"assertionResults":[{"ancestorTitles":["RpcDecodeError"],"fullName":"RpcDecodeError message delegates to Error.message when cause is an Error","status":"passed","title":"message delegates to Error.message when cause is an Error","duration":72.53380000000016,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RpcDecodeError"],"fullName":"RpcDecodeError message stringifies non-Error cause","status":"passed","title":"message stringifies non-Error cause","duration":70.4333000000006,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RpcCallError"],"fullName":"RpcCallError message includes method name and Error.message","status":"passed","title":"message includes method name and Error.message","duration":70.22119999999995,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RpcCallError"],"fullName":"RpcCallError message stringifies non-Error cause","status":"passed","title":"message stringifies non-Error cause","duration":70.01470000000063,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcErrorEnvelope"],"fullName":"isRpcErrorEnvelope detects valid envelope","status":"passed","title":"detects valid envelope","duration":69.69619999999941,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcErrorEnvelope"],"fullName":"isRpcErrorEnvelope rejects non-envelope values","status":"passed","title":"rejects non-envelope values","duration":69.8167999999996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcStreamEnvelope"],"fullName":"isRpcStreamEnvelope detects valid bytes envelope","status":"passed","title":"detects valid bytes envelope","duration":69.6864000000005,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcStreamEnvelope"],"fullName":"isRpcStreamEnvelope detects valid jsonl envelope","status":"passed","title":"detects valid jsonl envelope","duration":69.6241,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcStreamEnvelope"],"fullName":"isRpcStreamEnvelope rejects missing or wrong fields","status":"passed","title":"rejects missing or wrong fields","duration":69.49139999999989,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["encodeRpcError"],"fullName":"encodeRpcError preserves tagged error fields","status":"passed","title":"preserves tagged error fields","duration":69.21930000000066,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["encodeRpcError"],"fullName":"encodeRpcError normalizes plain Error to name/message/stack","status":"passed","title":"normalizes plain Error to name/message/stack","duration":69.67450000000008,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["encodeRpcError"],"fullName":"encodeRpcError passes through primitives","status":"passed","title":"passes through primitives","duration":69.56900000000041,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["encodeRpcError"],"fullName":"encodeRpcError passes through plain objects","status":"passed","title":"passes through plain objects","duration":69.49380000000019,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcReadableStream"],"fullName":"fromRpcReadableStream bytes encoding passes raw Uint8Array chunks through","status":"passed","title":"bytes encoding passes raw Uint8Array chunks through","duration":59.43189999999959,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcReadableStream"],"fullName":"fromRpcReadableStream jsonl encoding parses JSON lines","status":"passed","title":"jsonl encoding parses JSON lines","duration":59.60940000000028,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcReadableStream"],"fullName":"fromRpcReadableStream jsonl encoding produces RpcDecodeError on malformed JSON","status":"passed","title":"jsonl encoding produces RpcDecodeError on malformed JSON","duration":59.11790000000019,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcReadableStream"],"fullName":"fromRpcReadableStream jsonl encoding skips empty lines","status":"passed","title":"jsonl encoding skips empty lines","duration":62.10369999999966,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcStreamEnvelope"],"fullName":"fromRpcStreamEnvelope delegates to fromRpcReadableStream","status":"passed","title":"delegates to fromRpcReadableStream","duration":62.069799999999304,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcValue"],"fullName":"decodeRpcValue passes through plain values","status":"passed","title":"passes through plain values","duration":38.94310000000041,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcValue"],"fullName":"decodeRpcValue converts stream envelope to Effect Stream","status":"passed","title":"converts stream envelope to Effect Stream","duration":39.72440000000006,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcValue"],"fullName":"decodeRpcValue converts bare ReadableStream to bytes Effect Stream","status":"passed","title":"converts bare ReadableStream to bytes Effect Stream","duration":39.67650000000049,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult succeeds for plain values","status":"passed","title":"succeeds for plain values","duration":39.32710000000043,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult succeeds for numeric values","status":"passed","title":"succeeds for numeric values","duration":39.31490000000031,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult fails for error envelopes with tagged error","status":"passed","title":"fails for error envelopes with tagged error","duration":39.58359999999993,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult fails with plain Error shape for error envelopes","status":"passed","title":"fails with plain Error shape for error envelopes","duration":39.55969999999979,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult wraps stream envelopes in succeed (stream passthrough)","status":"passed","title":"wraps stream envelopes in succeed (stream passthrough)","duration":40.09659999999985,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream selects jsonl encoding for non-byte data","status":"passed","title":"selects jsonl encoding for non-byte data","duration":31.739800000000287,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream roundtrips a single-element jsonl stream","status":"passed","title":"roundtrips a single-element jsonl stream","duration":45.92950000000019,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream selects bytes encoding for Uint8Array data","status":"passed","title":"selects bytes encoding for Uint8Array data","duration":32.33320000000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream roundtrips a single-element bytes stream","status":"passed","title":"roundtrips a single-element bytes stream","duration":47.87989999999991,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream handles empty stream as jsonl","status":"passed","title":"handles empty stream as jsonl","duration":48.52109999999993,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["makeRpcStub"],"fullName":"makeRpcStub proxies successful calls","status":"passed","title":"proxies successful calls","duration":41.2515999999996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["makeRpcStub"],"fullName":"makeRpcStub wraps rejected promises as RpcCallError","status":"passed","title":"wraps rejected promises as RpcCallError","duration":41.156699999999546,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["makeRpcStub"],"fullName":"makeRpcStub decodes error envelopes into Effect.fail","status":"passed","title":"decodes error envelopes into Effect.fail","duration":41.09809999999925,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["makeRpcStub"],"fullName":"makeRpcStub decodes stream envelopes from successful calls","status":"passed","title":"decodes stream envelopes from successful calls","duration":41.08349999999973,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors toRpcStream encodes a stream that fails immediately","status":"passed","title":"toRpcStream encodes a stream that fails immediately","duration":41.01269999999931,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors toRpcStream encodes a stream that fails after elements","status":"passed","title":"toRpcStream encodes a stream that fails after elements","duration":40.95219999999972,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors fromRpcReadableStream decodes error marker in JSONL","status":"passed","title":"fromRpcReadableStream decodes error marker in JSONL","duration":40.84589999999935,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors fromRpcReadableStream yields elements before error marker","status":"passed","title":"fromRpcReadableStream yields elements before error marker","duration":40.80490000000009,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors makeRpcStub preserves stream errors (not collapsed to RpcCallError)","status":"passed","title":"makeRpcStub preserves stream errors (not collapsed to RpcCallError)","duration":40.778299999999945,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381729101,"endTime":1782381729287.2517,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Rpc.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"RpcDurableObjectNamespace: Increment / Get round-trip via Worker","status":"skipped","title":"RpcDurableObjectNamespace: Increment / Get round-trip via Worker","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcDurableObjectNamespace: separate getByName(id) instances are isolated","status":"skipped","title":"RpcDurableObjectNamespace: separate getByName(id) instances are isolated","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcDurableObjectNamespace: streaming RPC via getByName(id).CountUpTo","status":"skipped","title":"RpcDurableObjectNamespace: streaming RPC via getByName(id).CountUpTo","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker + RpcDurableObjectNamespace: Worker proxies *DO RPCs through the typed namespace","status":"skipped","title":"RpcWorker + RpcDurableObjectNamespace: Worker proxies *DO RPCs through the typed namespace","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcDurableObjectNamespace: 100 concurrent Increment calls do not hang","status":"skipped","title":"RpcDurableObjectNamespace: 100 concurrent Increment calls do not hang","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent unary RPCs do not hang","status":"skipped","title":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent unary RPCs do not hang","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent *DO unary RPCs do not hang","status":"skipped","title":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent *DO unary RPCs do not hang","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent streaming *DO RPCs do not hang","status":"skipped","title":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent streaming *DO RPCs do not hang","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/RpcDurableObjectNamespace.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: unary RPC response","status":"passed","title":"RpcServer.toHttpEffect: unary RPC response","duration":6055.257500000007,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: streaming RPC response","status":"passed","title":"RpcServer.toHttpEffect: streaming RPC response","duration":5591.911500000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: array payload streams response items in order","status":"passed","title":"RpcServer.toHttpEffect: array payload streams response items in order","duration":8318.881099999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: 200 concurrent unary calls do not hang","status":"passed","title":"RpcServer.toHttpEffect: 200 concurrent unary calls do not hang","duration":10835.377999999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: concurrent streaming calls do not hang","status":"passed","title":"RpcServer.toHttpEffect: concurrent streaming calls do not hang","duration":10812.046000000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object unary RPC response","status":"passed","title":"RpcServer.toHttpEffect Durable Object unary RPC response","duration":4606.882499999992,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object streaming RPC response","status":"passed","title":"RpcServer.toHttpEffect Durable Object streaming RPC response","duration":4226.168099999995,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object array payload streams response items in order","status":"passed","title":"RpcServer.toHttpEffect Durable Object array payload streams response items in order","duration":1921.4386999999988,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object concurrent unary calls do not hang","status":"passed","title":"RpcServer.toHttpEffect Durable Object concurrent unary calls do not hang","duration":1454.3139000000083,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object concurrent streaming calls do not hang","status":"passed","title":"RpcServer.toHttpEffect Durable Object concurrent streaming calls do not hang","duration":1022.5430999999953,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381657201,"endTime":1782381668900.314,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/RpcHttp.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"RpcWorker: target worker exposes Greet","status":"skipped","title":"RpcWorker: target worker exposes Greet","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker.bind: caller proxies through service binding to target","status":"skipped","title":"RpcWorker.bind: caller proxies through service binding to target","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker.bind: 100 concurrent ProxyGreet calls do not hang","status":"skipped","title":"RpcWorker.bind: 100 concurrent ProxyGreet calls do not hang","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/RpcWorker.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"adopts the account's existing workers.dev subdomain without mutating it","status":"passed","title":"adopts the account's existing workers.dev subdomain without mutating it","duration":1763.9473999999973,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account's workers.dev subdomain singleton","status":"passed","title":"list returns the account's workers.dev subdomain singleton","duration":1093.9891000000025,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381743524,"endTime":1782381745287.9475,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Subdomain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"D1 counter writes from WorkerA are visible from WorkerB (cross-script DO)","status":"skipped","title":"D1 counter writes from WorkerA are visible from WorkerB (cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"DO storage counter writes from WorkerA are visible from WorkerB (cross-script DO)","status":"skipped","title":"DO storage counter writes from WorkerA are visible from WorkerB (cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"WorkerC hosts its own isolated Counter (writes from A/B are not visible from C)","status":"skipped","title":"WorkerC hosts its own isolated Counter (writes from A/B are not visible from C)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Writes from WorkerB are visible from WorkerA (bidirectional cross-script DO)","status":"skipped","title":"Writes from WorkerB are visible from WorkerA (bidirectional cross-script DO)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/TaggedDO.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"RpcWorker WorkerA exposes the same RPC surface as the underlying DO","status":"skipped","title":"RpcWorker WorkerA exposes the same RPC surface as the underlying DO","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"D1 counter writes via WorkerA's RPC are visible from WorkerB (cross-script DO)","status":"skipped","title":"D1 counter writes via WorkerA's RPC are visible from WorkerB (cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"DO storage counter writes via WorkerA's RPC are visible from WorkerB (cross-script DO)","status":"skipped","title":"DO storage counter writes via WorkerA's RPC are visible from WorkerB (cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Writes from WorkerB are visible from WorkerA's RPC (bidirectional cross-script DO)","status":"skipped","title":"Writes from WorkerB are visible from WorkerA's RPC (bidirectional cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"WorkerC hosts its own isolated Counter (writes from A/B are not visible from C)","status":"skipped","title":"WorkerC hosts its own isolated Counter (writes from A/B are not visible from C)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/TaggedRpcDO.test.ts"},{"assertionResults":[{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker create, update, delete worker","status":"failed","title":"create, update, delete worker","duration":27695.064199999997,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker create, update, delete worker with assets","status":"passed","title":"create, update, delete worker with assets","duration":26021.3378,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker Worker assets: relocating to a fresh path with identical bytes preserves hash and keeps assets","status":"passed","title":"Worker assets: relocating to a fresh path with identical bytes preserves hash and keeps assets","duration":15652.766899999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker Worker assets: editing a file changes the hash and republishes the manifest","status":"passed","title":"Worker assets: editing a file changes the hash and republishes the manifest","duration":15743.596700000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker Worker assets: a bundle-only change keeps the asset manifest (hash.assets stable)","status":"passed","title":"Worker assets: a bundle-only change keeps the asset manifest (hash.assets stable)","duration":15138.379699999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker create, update, delete internal worker","status":"failed","title":"create, update, delete internal worker","duration":82570.1127,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker owned worker (matching alchemy tags) is silently adopted without --adopt","status":"passed","title":"owned worker (matching alchemy tags) is silently adopted without --adopt","duration":20375.1189,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker adopt(true) takes over a foreign-tagged worker","status":"failed","title":"adopt(true) takes over a foreign-tagged worker","duration":82510.5653,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker url defaults to enabling the workers.dev subdomain on first deploy","status":"passed","title":"url defaults to enabling the workers.dev subdomain on first deploy","duration":7330.656300000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker url: false disables the workers.dev subdomain on first deploy","status":"failed","title":"url: false disables the workers.dev subdomain on first deploy","duration":741.6866999999984,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker toggling url between deploys flips the workers.dev subdomain","status":"failed","title":"toggling url between deploys flips the workers.dev subdomain","duration":703.0337,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker redeploy re-enables previewsEnabled when externally disabled","status":"failed","title":"redeploy re-enables previewsEnabled when externally disabled","duration":702.6201000000001,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker domains reflects the workers.dev subdomain and tracks url","status":"failed","title":"domains reflects the workers.dev subdomain and tracks url","duration":68702.4717,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker domains puts custom domains before workers.dev and url is the first","status":"skipped","title":"domains puts custom domains before workers.dev and url is the first","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker list enumerates the deployed worker","status":"failed","title":"list enumerates the deployed worker","duration":29418.717199999992,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker downstream referencing worker.url is not re-updated when the worker changes","status":"failed","title":"downstream referencing worker.url is not re-updated when the worker changes","duration":26850.725300000006,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker worker.durableObjectNamespaces stability across DO and worker changes","status":"failed","title":"worker.durableObjectNamespaces stability across DO and worker changes","duration":24026.4712,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782381493955,"endTime":1782381592500.4717,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Worker.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"target worker's own fetch handler responds","status":"skipped","title":"target worker's own fetch handler responds","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"async caller can call target's RPC method via service binding","status":"skipped","title":"async caller can call target's RPC method via service binding","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect caller can call target's RPC method via bindWorker","status":"skipped","title":"effect caller can call target's RPC method via bindWorker","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/WorkerBinding.test.ts"},{"assertionResults":[{"ancestorTitles":["Cloudflare.Worker env bindings"],"fullName":"Cloudflare.Worker env bindings async worker round-trips every supported binding shape","status":"skipped","title":"async worker round-trips every supported binding shape","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker env bindings"],"fullName":"Cloudflare.Worker env bindings effect worker round-trips env: literals and Redacted via WorkerEnvironment","status":"skipped","title":"effect worker round-trips env: literals and Redacted via WorkerEnvironment","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker env bindings"],"fullName":"Cloudflare.Worker env bindings effect worker resolves the yielded VersionMetadata binding","status":"skipped","title":"effect worker resolves the yielded VersionMetadata binding","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker env bindings"],"fullName":"Cloudflare.Worker env bindings effect worker round-trips Config.xxx bindings captured in Init","status":"skipped","title":"effect worker round-trips Config.xxx bindings captured in Init","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/WorkerEnv.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"async worker loads and proxies to a dynamic worker via env binding","status":"skipped","title":"async worker loads and proxies to a dynamic worker via env binding","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker loads and proxies to a dynamic worker via yield* loader","status":"skipped","title":"effect worker loads and proxies to a dynamic worker via yield* loader","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/WorkerLoader.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed worker can run a workflow to completion","status":"skipped","title":"deployed worker can run a workflow to completion","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed workflow","status":"skipped","title":"list enumerates the deployed workflow","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Workflow.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins the custom nameserver toggle and leaves the zone at its baseline on destroy","status":"skipped","title":"pins the custom nameserver toggle and leaves the zone at its baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"surfaces the typed CustomNameserverSetNotFound error when enabling without an account nameserver set","status":"skipped","title":"surfaces the typed CustomNameserverSetNotFound error when enabling without an account nameserver set","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates zones using account custom nameservers","status":"passed","title":"list enumerates zones using account custom nameservers","duration":4309.247199999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a zone with account custom nameservers enabled","status":"skipped","title":"list includes a zone with account custom nameservers enabled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"enables account custom nameservers and restores the baseline on destroy","status":"skipped","title":"enables account custom nameservers and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381656067,"endTime":1782381660376.2473,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/CustomNameservers.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed ZoneHoldsRequireEnterprise error on non-Enterprise zones","status":"skipped","title":"surfaces the typed ZoneHoldsRequireEnterprise error on non-Enterprise zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the hold state across all zones","status":"skipped","title":"list enumerates the hold state across all zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"places a hold, updates includeSubdomains in place, and removes it on destroy","status":"skipped","title":"places a hold, updates includeSubdomains in place, and removes it on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/Hold.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins a toggle setting and restores the original value on destroy","status":"skipped","title":"pins a toggle setting and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updates a numeric setting in place and keeps the captured initial value","status":"skipped","title":"updates a numeric setting in place and keeps the captured initial value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing settingId replaces — old setting restored, new setting pinned","status":"skipped","title":"changing settingId replaces — old setting restored, new setting pinned","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed (zone, setting) pair","status":"skipped","title":"list enumerates the deployed (zone, setting) pair","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/Setting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create zone retains by default — destroy() opts in to deletion","status":"skipped","title":"create zone retains by default — destroy() opts in to deletion","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create zone retains by default — survives stack.destroy()","status":"skipped","title":"create zone retains by default — survives stack.destroy()","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing zone errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing zone errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates every zone in the account","status":"skipped","title":"list enumerates every zone in the account","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/Zone.test.ts"},{"assertionResults":[{"ancestorTitles":["ZarazConfig"],"fullName":"ZarazConfig updates and retains a zone-level Zaraz config","status":"skipped","title":"updates and retains a zone-level Zaraz config","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ZarazConfig"],"fullName":"ZarazConfig delete true resets Zaraz config to defaults","status":"skipped","title":"delete true resets Zaraz config to defaults","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ZarazConfig"],"fullName":"ZarazConfig updates and retains Zaraz workflow mode","status":"skipped","title":"updates and retains Zaraz workflow mode","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ZarazConfig"],"fullName":"ZarazConfig list enumerates Zaraz config across all zones","status":"skipped","title":"list enumerates Zaraz config across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381326629,"endTime":1782381326629,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zaraz/ZarazConfig.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"Zaraz event contracts are type-only","status":"passed","title":"Zaraz event contracts are type-only","duration":8.167500000000018,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782381740813,"endTime":1782381740821.1675,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zaraz/ZarazEventTypes.test.ts"}]} \ No newline at end of file diff --git a/cf-temp-results-3.json b/cf-temp-results-3.json new file mode 100644 index 000000000..3221c4ceb --- /dev/null +++ b/cf-temp-results-3.json @@ -0,0 +1 @@ +{"testResults":[{"assertionResults":[{"ancestorTitles":[],"fullName":"building the Cloudflare provider layers should not fail for unknown profile","status":"passed","title":"building the Cloudflare provider layers should not fail for unknown profile","duration":461.10959999999614,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382914407,"endTime":1782382914868.1096,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Providers.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","status":"skipped","title":"surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates trust store certificates across zones","status":"passed","title":"list enumerates trust store certificates across zones","duration":1379.8615000000027,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes the deployed trust store certificate on an entitled zone","status":"skipped","title":"list includes the deployed trust store certificate on an entitled zone","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"uploads a root CA, replaces it on certificate change, and deletes it","status":"skipped","title":"uploads a root CA, replaces it on certificate change, and deletes it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382749689,"endTime":1782382751068.8616,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Acm/CustomTrustStore.test.ts"},{"assertionResults":[{"ancestorTitles":["TotalTls"],"fullName":"TotalTls converges enabled:false as a no-op on a zone without the ACM entitlement","status":"skipped","title":"converges enabled:false as a no-op on a zone without the ACM entitlement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TotalTls"],"fullName":"TotalTls surfaces the typed AdvancedCertificateManagerRequired error when enabling without the entitlement","status":"skipped","title":"surfaces the typed AdvancedCertificateManagerRequired error when enabling without the entitlement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TotalTls"],"fullName":"TotalTls enables Total TLS, updates the CA in place, and restores the baseline on destroy","status":"skipped","title":"enables Total TLS, updates the CA in place, and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TotalTls"],"fullName":"TotalTls list enumerates the setting across all zones","status":"skipped","title":"list enumerates the setting across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Acm/TotalTls.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a self_hosted application gated by a reusable policy","status":"skipped","title":"create and delete a self_hosted application gated by a reusable policy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create and delete a warp device-enrollment application","status":"passed","title":"create and delete a warp device-enrollment application","duration":1753.1218999999983,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed access application","status":"skipped","title":"list enumerates the deployed access application","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update policies in place keeps the applicationId stable","status":"skipped","title":"update policies in place keeps the applicationId stable","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382660047,"endTime":1782382661800.1218,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Application.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"legacy bookmark creation surfaces the typed AccessBookmarkNotFound error","status":"skipped","title":"legacy bookmark creation surfaces the typed AccessBookmarkNotFound error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy bookmark","status":"skipped","title":"create, update in place, and destroy bookmark","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates access bookmarks at the account scope","status":"skipped","title":"list enumerates access bookmarks at the account scope","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Bookmark.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed AccessCertificateQuotaExceeded error","status":"skipped","title":"unentitled accounts surface the typed AccessCertificateQuotaExceeded error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update hostnames, replace on PEM change, destroy","status":"skipped","title":"create, update hostnames, replace on PEM change, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account access certificates","status":"skipped","title":"list enumerates account access certificates","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Certificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed AccessCustomPagesNotEntitled error","status":"skipped","title":"unentitled accounts surface the typed AccessCustomPagesNotEntitled error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update html in place, replace on type change, destroy","status":"skipped","title":"create, update html in place, replace on type change, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates access custom pages at the account scope","status":"skipped","title":"list enumerates access custom pages at the account scope","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/CustomPage.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update rules, and delete group","status":"skipped","title":"create, update rules, and delete group","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"rename updates the group in place","status":"skipped","title":"rename updates the group in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed access group","status":"skipped","title":"list enumerates the deployed access group","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"group can be referenced from an access policy","status":"skipped","title":"group can be referenced from an access policy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Group.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy an OIDC IdP","status":"skipped","title":"create, verify, and destroy an OIDC IdP","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name and config in place (same id)","status":"skipped","title":"update name and config in place (same id)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed IdP","status":"skipped","title":"list enumerates the deployed IdP","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the type replaces the IdP","status":"skipped","title":"changing the type replaces the IdP","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/IdentityProvider.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed infrastructure target","status":"skipped","title":"list enumerates the deployed infrastructure target","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/InfrastructureTarget.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins the rotation interval, updates in place, and restores on destroy","status":"skipped","title":"pins the rotation interval, updates in place, and restores on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account key configuration singleton","status":"skipped","title":"list returns the account key configuration singleton","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/KeyConfiguration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and destroy an MCP portal","status":"skipped","title":"create, update in place, and destroy an MCP portal","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed MCP portal","status":"skipped","title":"list enumerates the deployed MCP portal","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/McpPortal.test.ts"},{"assertionResults":[{"ancestorTitles":["Organization"],"fullName":"Organization adopts the existing Access organization","status":"skipped","title":"adopts the existing Access organization","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Organization"],"fullName":"Organization toggles allow_authenticate_via_warp and restores","status":"skipped","title":"toggles allow_authenticate_via_warp and restores","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Organization"],"fullName":"Organization list returns the account Access organization","status":"skipped","title":"list returns the account Access organization","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Organization.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete basic allow policy","status":"skipped","title":"create and delete basic allow policy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutates includes without replacing","status":"skipped","title":"update mutates includes without replacing","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopts an out-of-band reusable policy","status":"skipped","title":"adopts an out-of-band reusable policy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed reusable policy","status":"skipped","title":"list enumerates the deployed reusable policy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Policy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update duration, and delete service token","status":"skipped","title":"create, update duration, and delete service token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed service token","status":"skipped","title":"list enumerates the deployed service token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"incrementing clientSecretVersion rotates the secret","status":"skipped","title":"incrementing clientSecretVersion rotates the secret","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/ServiceToken.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, and destroy tag","status":"skipped","title":"create, verify out-of-band, and destroy tag","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"rename replaces the tag","status":"skipped","title":"rename replaces the tag","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed access tag","status":"skipped","title":"list enumerates the deployed access tag","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Tag.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"read path: getAccount observes the testing account","status":"passed","title":"read path: getAccount observes the testing account","duration":6163.187399999995,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"inaccessible account surfaces a typed tag","status":"passed","title":"inaccessible account surfaces a typed tag","duration":3505.1450999999943,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates accessible accounts (read-only)","status":"skipped","title":"list enumerates accessible accounts (read-only)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"createAccount is entitlement-gated: typed AccountCreationForbidden","status":"skipped","title":"createAccount is entitlement-gated: typed AccountCreationForbidden","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create subaccount, update name and settings in place, delete","status":"skipped","title":"create subaccount, update name and settings in place, delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382660128,"endTime":1782382666291.1875,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Account/Account.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the account members","status":"skipped","title":"list enumerates the account members","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create member, update roles in place, delete","status":"skipped","title":"create member, update roles in place, delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the member when the email changes","status":"skipped","title":"replaces the member when the email changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Account/Member.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"address map lifecycle (typed FeatureNotEnabled on unentitled accounts)","status":"skipped","title":"address map lifecycle (typed FeatureNotEnabled on unentitled accounts)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed address map","status":"skipped","title":"list enumerates the deployed address map","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/AddressMap.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates BGP prefixes across BYOIP prefixes","status":"skipped","title":"list enumerates BGP prefixes across BYOIP prefixes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/BgpPrefix.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"lists the services catalog and prefixes (read-only)","status":"skipped","title":"lists the services catalog and prefixes (read-only)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account prefixes (read-only)","status":"skipped","title":"list enumerates account prefixes (read-only)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"prefix: create, patch description in place, destroy","status":"skipped","title":"prefix: create, patch description in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"bgp prefix: adopt-or-create by cidr, toggle advertisement, withdraw on destroy","status":"skipped","title":"bgp prefix: adopt-or-create by cidr, toggle advertisement, withdraw on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"prefix delegation: create and destroy (replace-only resource)","status":"skipped","title":"prefix delegation: create and destroy (replace-only resource)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"service binding: create against the CDN service and destroy","status":"skipped","title":"service binding: create against the CDN service and destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/Prefix.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates prefix delegations (read-only)","status":"skipped","title":"list enumerates prefix delegations (read-only)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/PrefixDelegation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates service bindings across prefixes (read-only)","status":"skipped","title":"list enumerates service bindings across prefixes (read-only)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/ServiceBinding.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"construct auto-creates a managed token and wires it into the instance","status":"skipped","title":"construct auto-creates a managed token and wires it into the instance","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"web-crawler source skips token minting","status":"skipped","title":"web-crawler source skips token minting","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/AiSearch.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"worker deploys with ai_search + ai_search_namespace bindings injected","status":"skipped","title":"worker deploys with ai_search + ai_search_namespace bindings injected","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker resolves ai_search + ai_search_namespace via Effect clients","status":"skipped","title":"effect worker resolves ai_search + ai_search_namespace via Effect clients","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/Bindings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update mutable props, and delete an r2-backed instance","status":"skipped","title":"create, update mutable props, and delete an r2-backed instance","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the embedding model triggers a replacement","status":"skipped","title":"changing the embedding model triggers a replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed instance","status":"skipped","title":"list enumerates the deployed instance","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a web-crawler instance (no service token)","status":"skipped","title":"creates a web-crawler instance (no service token)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an instance in a custom namespace and moving namespaces replaces","status":"skipped","title":"creates an instance in a custom namespace and moving namespaces replaces","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/Instance.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update description in place, and delete a namespace","status":"skipped","title":"create, update description in place, and delete a namespace","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the name triggers a replacement","status":"skipped","title":"changing the name triggers a replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed namespace","status":"skipped","title":"list enumerates the deployed namespace","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopts the reserved default namespace without deleting it on teardown","status":"skipped","title":"adopts the reserved default namespace without deleting it on teardown","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/Namespace.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, rename in place, and delete a service token","status":"skipped","title":"create, rename in place, and delete a service token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed service token","status":"skipped","title":"list enumerates the deployed service token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"an AI Search instance syncs with a stack-minted service token","status":"skipped","title":"an AI Search instance syncs with a stack-minted service token","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/Token.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete ai gateway with default props","status":"skipped","title":"create and delete ai gateway with default props","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete ai gateway","status":"skipped","title":"create, update, delete ai gateway","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed ai gateway","status":"skipped","title":"list enumerates the deployed ai gateway","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"existing ai gateway (matching id) is silently adopted without --adopt","status":"skipped","title":"existing ai gateway (matching id) is silently adopted without --adopt","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"deployed worker can call AiGateway binding (effect-native getUrl)","status":"skipped","title":"deployed worker can call AiGateway binding (effect-native getUrl)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/AiGateway.test.ts"},{"assertionResults":[{"ancestorTitles":["AiGatewaySpendingLimit"],"fullName":"AiGatewaySpendingLimit create, read-back, and delete the account spending limit","status":"skipped","title":"create, read-back, and delete the account spending limit","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewaySpendingLimit"],"fullName":"AiGatewaySpendingLimit update the spending limit in place","status":"skipped","title":"update the spending limit in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewaySpendingLimit"],"fullName":"AiGatewaySpendingLimit list enumerates the deployed account spending limit","status":"skipped","title":"list enumerates the deployed account spending limit","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/AiGatewaySpendingLimit.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"first turn against a fresh thread persists user + assistant messages","status":"skipped","title":"first turn against a fresh thread persists user + assistant messages","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"persisted chat survives across DO invocations","status":"skipped","title":"persisted chat survives across DO invocations","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"distinct thread ids map to isolated histories","status":"skipped","title":"distinct thread ids map to isolated histories","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/ChatPersistence.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"send round-trips text and turns through the RpcWorker → DO","status":"skipped","title":"send round-trips text and turns through the RpcWorker → DO","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"streamMessage yields effect/ai Response parts that decode to real classes","status":"skipped","title":"streamMessage yields effect/ai Response parts that decode to real classes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"streamed turn is persisted: a follow-up send recalls it","status":"skipped","title":"streamed turn is persisted: a follow-up send recalls it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/ChatPersistenceRpc.test.ts"},{"assertionResults":[{"ancestorTitles":["AiGatewayDataset"],"fullName":"AiGatewayDataset create, update, delete a dataset on a gateway","status":"skipped","title":"create, update, delete a dataset on a gateway","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewayDataset"],"fullName":"AiGatewayDataset replaces dataset when the gateway changes","status":"skipped","title":"replaces dataset when the gateway changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewayDataset"],"fullName":"AiGatewayDataset recreates a dataset after out-of-band delete","status":"skipped","title":"recreates a dataset after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewayDataset"],"fullName":"AiGatewayDataset list enumerates datasets across gateways","status":"skipped","title":"list enumerates datasets across gateways","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/Dataset.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update elements (new deployed version), rename, delete","status":"skipped","title":"create, update elements (new deployed version), rename, delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces route when the gateway changes","status":"skipped","title":"replaces route when the gateway changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates a route after out-of-band delete","status":"skipped","title":"recreates a route after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates routes across all gateways","status":"skipped","title":"list enumerates routes across all gateways","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/DynamicRouting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, noop, replace, delete an evaluation","status":"skipped","title":"create, noop, replace, delete an evaluation","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed evaluation","status":"skipped","title":"list enumerates the deployed evaluation","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/Evaluation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed worker generates text via AiGateway-backed LanguageModel","status":"skipped","title":"deployed worker generates text via AiGateway-backed LanguageModel","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"deployed worker streams text via AiGateway-backed LanguageModel","status":"skipped","title":"deployed worker streams text via AiGateway-backed LanguageModel","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"streamed parts respect ordering: text-start → text-delta+ → text-end → finish","status":"skipped","title":"streamed parts respect ordering: text-start → text-delta+ → text-end → finish","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"DEBUG: dump raw Workers AI SSE stream","status":"skipped","title":"DEBUG: dump raw Workers AI SSE stream","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"stream finish part reports the real token counts and a `stop` reason","status":"skipped","title":"stream finish part reports the real token counts and a `stop` reason","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"stream emits multiple text-delta chunks for a long-form response","status":"skipped","title":"stream emits multiple text-delta chunks for a long-form response","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"persisted chat survives across DO invocations","status":"skipped","title":"persisted chat survives across DO invocations","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"deployed worker invokes a tool via AiGateway-backed LanguageModel","status":"skipped","title":"deployed worker invokes a tool via AiGateway-backed LanguageModel","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"streams tool-call parts via AiGateway-backed LanguageModel","status":"skipped","title":"streams tool-call parts via AiGateway-backed LanguageModel","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"concatenated tool-params-delta payloads parse back into the requested arguments","status":"skipped","title":"concatenated tool-params-delta payloads parse back into the requested arguments","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"streams Effect-native parts and prints them live","status":"skipped","title":"streams Effect-native parts and prints them live","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/LanguageModel.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, noop, replace, delete a BYOK provider config","status":"skipped","title":"create, noop, replace, delete a BYOK provider config","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed provider config","status":"skipped","title":"list enumerates the deployed provider config","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/ProviderConfig.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AiSecurityNotEntitled error on unentitled accounts","status":"skipped","title":"surfaces the typed AiSecurityNotEntitled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the custom topics across all zones","status":"passed","title":"list enumerates the custom topics across all zones","duration":1960.3458999999966,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"sets topics, updates the list in place, and restores on destroy","status":"skipped","title":"sets topics, updates the list in place, and restores on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382812416,"endTime":1782382814376.346,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSecurity/CustomTopics.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AiSecurityNotEntitled error on unentitled accounts","status":"skipped","title":"surfaces the typed AiSecurityNotEntitled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the setting across all entitled zones","status":"passed","title":"list enumerates the setting across all entitled zones","duration":2417.9493,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"pins enabled, updates in place, and restores the original on destroy","status":"skipped","title":"pins enabled, updates in place, and restores the original on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382805148,"endTime":1782382807565.9492,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSecurity/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete notification policy","status":"failed","title":"create, update, delete notification policy","duration":35599.4326,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces policy when alertType changes","status":"failed","title":"replaces policy when alertType changes","duration":35585.8924,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed notification policy","status":"failed","title":"list enumerates the deployed notification policy","duration":36287.797999999995,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782382660260,"endTime":1782382696549.798,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Alerting/NotificationPolicy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update window in place, delete silence","status":"failed","title":"create, update window in place, delete silence","duration":32751.8511,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces silence when the policy changes","status":"failed","title":"replaces silence when the policy changes","duration":33100.8321,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed silence","status":"failed","title":"list enumerates the deployed silence","duration":33075.16039999999,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782382660270,"endTime":1782382693372.832,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Alerting/Silence.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete webhook destination","status":"failed","title":"create, update, delete webhook destination","duration":3542.7716999999975,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed webhook destination","status":"failed","title":"list enumerates the deployed webhook destination","duration":3359.2877999999982,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782382728438,"endTime":1782382731980.7717,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Alerting/Webhook.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed worker can write data points through the Analytics Engine binding","status":"skipped","title":"deployed worker can write data points through the Analytics Engine binding","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AnalyticsEngine/AnalyticsEngineDataset.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed NotEntitled error on unentitled zones","status":"skipped","title":"surfaces the typed NotEntitled error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the configuration across all zones","status":"passed","title":"list enumerates the configuration across all zones","duration":1933.4070000000029,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"sets session identifiers and restores the original value on destroy","status":"skipped","title":"sets session identifiers and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382812974,"endTime":1782382814907.407,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/Configuration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update description in place, destroy a label","status":"skipped","title":"create, update description in place, destroy a label","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"renaming a label triggers replacement","status":"skipped","title":"renaming a label triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"generated name respects Cloudflare's 24-character limit","status":"skipped","title":"generated name respects Cloudflare's 24-character limit","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed label","status":"skipped","title":"list enumerates the deployed label","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/Label.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, no-op redeploy, destroy an API Shield operation","status":"skipped","title":"create, no-op redeploy, destroy an API Shield operation","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the method triggers replacement","status":"skipped","title":"changing the method triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed API Shield operation","status":"skipped","title":"list enumerates the deployed API Shield operation","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/Operation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, enable validation in place, destroy a user schema","status":"skipped","title":"create, enable validation in place, destroy a user schema","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the schema source triggers replacement","status":"skipped","title":"changing the schema source triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed user schema","status":"skipped","title":"list enumerates the deployed user schema","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/UserSchema.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed account token","status":"skipped","title":"list enumerates the deployed account token","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiToken/AccountApiToken.test.ts"},{"assertionResults":[{"ancestorTitles":["UserApiToken"],"fullName":"UserApiToken create and delete user token with default props","status":"skipped","title":"create and delete user token with default props","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UserApiToken"],"fullName":"UserApiToken create, update, delete user token","status":"skipped","title":"create, update, delete user token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UserApiToken list"],"fullName":"UserApiToken list list enumerates user tokens","status":"skipped","title":"list enumerates user tokens","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UserApiToken list probe"],"fullName":"UserApiToken list probe list rejects with typed Unauthorized under scoped token","status":"passed","title":"list rejects with typed Unauthorized under scoped token","duration":3139.4084000000003,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382858664,"endTime":1782382861803.4084,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiToken/UserApiToken.test.ts"},{"assertionResults":[{"ancestorTitles":["SmartRouting"],"fullName":"SmartRouting surfaces the typed NotAuthorized error on zones without the Argo add-on","status":"skipped","title":"surfaces the typed NotAuthorized error on zones without the Argo add-on","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SmartRouting"],"fullName":"SmartRouting enables Smart Routing and restores the original value on destroy","status":"skipped","title":"enables Smart Routing and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SmartRouting"],"fullName":"SmartRouting list enumerates Argo-entitled zones","status":"passed","title":"list enumerates Argo-entitled zones","duration":3757.3151999999973,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382834438,"endTime":1782382838195.3152,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Argo/SmartRouting.test.ts"},{"assertionResults":[{"ancestorTitles":["TieredCaching"],"fullName":"TieredCaching enables Tiered Caching and restores the original value on destroy","status":"skipped","title":"enables Tiered Caching and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TieredCaching"],"fullName":"TieredCaching updates enabled in place and keeps the captured initial value","status":"skipped","title":"updates enabled in place and keeps the captured initial value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TieredCaching"],"fullName":"TieredCaching list enumerates the setting across all zones","status":"skipped","title":"list enumerates the setting across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Argo/TieredCaching.test.ts"},{"assertionResults":[{"ancestorTitles":["BotManagement"],"fullName":"BotManagement manages SBFM settings on the zone singleton and restores them on destroy","status":"skipped","title":"manages SBFM settings on the zone singleton and restores them on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["BotManagement"],"fullName":"BotManagement deploy with no settings set adopts the singleton without writing","status":"skipped","title":"deploy with no settings set adopts the singleton without writing","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["BotManagement"],"fullName":"BotManagement toggles a boolean SBFM field and restores it on destroy","status":"skipped","title":"toggles a boolean SBFM field and restores it on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["BotManagement"],"fullName":"BotManagement list enumerates bot management across all zones","status":"skipped","title":"list enumerates bot management across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/BotManagement/BotManagement.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"async worker renders a page title through Browser Rendering","status":"skipped","title":"async worker renders a page title through Browser Rendering","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker exercises content via quickAction wrapper","status":"skipped","title":"effect worker exercises content via quickAction wrapper","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker converts a page to markdown","status":"skipped","title":"effect worker converts a page to markdown","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker extracts links","status":"skipped","title":"effect worker extracts links","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker scrapes elements by selector","status":"skipped","title":"effect worker scrapes elements by selector","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker takes a page snapshot","status":"skipped","title":"effect worker takes a page snapshot","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker streams a screenshot","status":"skipped","title":"effect worker streams a screenshot","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker streams a PDF","status":"skipped","title":"effect worker streams a PDF","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker extracts JSON with AI","status":"skipped","title":"effect worker extracts JSON with AI","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker calls the generic quickAction","status":"skipped","title":"effect worker calls the generic quickAction","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker exposes the raw BrowserRun binding","status":"skipped","title":"effect worker exposes the raw BrowserRun binding","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Browser/Browser.test.ts"},{"assertionResults":[{"ancestorTitles":["CacheReserve"],"fullName":"CacheReserve surfaces the typed SettingUnavailableForPlan error on unentitled zones","status":"skipped","title":"surfaces the typed SettingUnavailableForPlan error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["CacheReserve"],"fullName":"CacheReserve enables Cache Reserve and restores the original value on destroy","status":"skipped","title":"enables Cache Reserve and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["CacheReserve"],"fullName":"CacheReserve list enumerates Cache Reserve across all zones","status":"passed","title":"list enumerates Cache Reserve across all zones","duration":3548.1165,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["CacheReserve"],"fullName":"CacheReserve list includes the entitled zone's Cache Reserve setting","status":"skipped","title":"list includes the entitled zone's Cache Reserve setting","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382840046,"endTime":1782382843594.1165,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/CacheReserve.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates, updates in place, replaces on ip change, and deletes the mapping","status":"skipped","title":"creates, updates in place, replaces on ip change, and deletes the mapping","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/OriginCloudRegion.test.ts"},{"assertionResults":[{"ancestorTitles":["RegionalTieredCache"],"fullName":"RegionalTieredCache surfaces the typed SettingUnavailableForPlan error on non-Enterprise zones","status":"skipped","title":"surfaces the typed SettingUnavailableForPlan error on non-Enterprise zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RegionalTieredCache"],"fullName":"RegionalTieredCache enables Regional Tiered Cache and restores the original value on destroy","status":"skipped","title":"enables Regional Tiered Cache and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RegionalTieredCache"],"fullName":"RegionalTieredCache list enumerates the setting across entitled zones","status":"passed","title":"list enumerates the setting across entitled zones","duration":4063.7392999999975,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382840980,"endTime":1782382845043.7393,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/RegionalTieredCache.test.ts"},{"assertionResults":[{"ancestorTitles":["SmartTieredCache"],"fullName":"SmartTieredCache enables Smart Tiered Cache and restores the original value on destroy","status":"skipped","title":"enables Smart Tiered Cache and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SmartTieredCache"],"fullName":"SmartTieredCache updates the setting in place and keeps the captured initial value","status":"skipped","title":"updates the setting in place and keeps the captured initial value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SmartTieredCache"],"fullName":"SmartTieredCache list enumerates the setting across all zones","status":"skipped","title":"list enumerates the setting across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/SmartTieredCache.test.ts"},{"assertionResults":[{"ancestorTitles":["Variants"],"fullName":"Variants creates, updates in place, and deletes the variants setting","status":"skipped","title":"creates, updates in place, and deletes the variants setting","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Variants"],"fullName":"Variants deploy is idempotent and converges out-of-band drift","status":"skipped","title":"deploy is idempotent and converges out-of-band drift","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Variants"],"fullName":"Variants list enumerates the configured variants settings","status":"skipped","title":"list enumerates the configured variants settings","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/Variants.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an app with default name","status":"failed","title":"create and delete an app with default name","duration":27548.7355,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name in place (same appId, secret preserved)","status":"failed","title":"update name in place (same appId, secret preserved)","duration":28086.490700000002,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed app","status":"failed","title":"list enumerates the deployed app","duration":28112.064199999997,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":27592.643100000005,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]}],"startTime":1782382683920,"endTime":1782382712034.0642,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Calls/App.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a TURN key with default name","status":"skipped","title":"create and delete a TURN key with default name","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name in place (same keyId, key preserved)","status":"skipped","title":"update name in place (same keyId, key preserved)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed TURN key","status":"skipped","title":"list enumerates the deployed TURN key","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Calls/TurnKey.test.ts"},{"assertionResults":[{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation pins Managed CA hostnames, updates in place, and clears on destroy","status":"skipped","title":"pins Managed CA hostnames, updates in place, and clears on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation associates hostnames with an uploaded CA and destroys before the cert","status":"skipped","title":"associates hostnames with an uploaded CA and destroys before the cert","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation changing the certificate key replaces the association","status":"skipped","title":"changing the certificate key replaces the association","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CertificateAuthorities/HostnameAssociation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create signs the CSR, destroy revokes the certificate","status":"skipped","title":"create signs the CSR, destroy revokes the certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing validityDays replaces — new id issued, old certificate revoked","status":"skipped","title":"changing validityDays replaces — new id issued, old certificate revoked","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates client certificates across zones","status":"skipped","title":"list enumerates client certificates across zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ClientCertificate/ClientCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":["Rules"],"fullName":"Rules cloud connector rules — create, update in place, destroy clears the list","status":"skipped","title":"cloud connector rules — create, update in place, destroy clears the list","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Rules"],"fullName":"Rules no-op redeploy leaves the rule list untouched and ids stable","status":"skipped","title":"no-op redeploy leaves the rule list untouched and ids stable","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Rules"],"fullName":"Rules list enumerates rule lists across all zones","status":"skipped","title":"list enumerates rule lists across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CloudConnector/Rules.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Unauthorized error","status":"skipped","title":"unentitled accounts surface the typed Unauthorized error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create a scan config, update ports in place, delete","status":"skipped","title":"create a scan config, update ports in place, delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns an array of scan configs","status":"passed","title":"list returns an array of scan configs","duration":34045.977,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed scan config","status":"skipped","title":"list enumerates the deployed scan config","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382660047,"endTime":1782382694092.977,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CloudforceOne/ScanConfig.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"tcp service lifecycle: create, update, host switch","status":"failed","title":"tcp service lifecycle: create, update, host switch","duration":56391.76700000001,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"http service with explicit ports and default name","status":"failed","title":"http service with explicit ports and default name","duration":56173.67630000001,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed directory service","status":"failed","title":"list enumerates the deployed directory service","duration":56403.00779999999,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":57481.02929999998,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782382660075,"endTime":1782382717559.0293,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Connectivity/DirectoryService.test.ts"},{"assertionResults":[{"ancestorTitles":["effectful container (main)"],"fullName":"effectful container (main) deploys and serves over its TCP port","status":"skipped","title":"deploys and serves over its TCP port","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["external container (context/dockerfile)"],"fullName":"external container (context/dockerfile) builds the user Dockerfile and serves it over its TCP port","status":"skipped","title":"builds the user Dockerfile and serves it over its TCP port","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["remote container (image)"],"fullName":"remote container (image) pulls and re-pushes the remote image and serves it over its TCP port","status":"skipped","title":"pulls and re-pushes the remote image and serves it over its TCP port","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Container/Container.test.ts"},{"assertionResults":[{"ancestorTitles":["ContainerApplication"],"fullName":"ContainerApplication list enumerates container applications","status":"failed","title":"list enumerates container applications","duration":1044.5722000000023,"failureMessages":["Provider not found for undefined"],"meta":{},"tags":[]}],"startTime":1782382771793,"endTime":1782382772837.5723,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Container/ContainerApplication.test.ts"},{"assertionResults":[{"ancestorTitles":["ContentScanning"],"fullName":"ContentScanning surfaces the typed ContentScanningNotEntitled error on unentitled zones","status":"skipped","title":"surfaces the typed ContentScanningNotEntitled error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ContentScanning"],"fullName":"ContentScanning pins Content Scanning off on an unentitled zone and destroys cleanly","status":"skipped","title":"pins Content Scanning off on an unentitled zone and destroys cleanly","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ContentScanning"],"fullName":"ContentScanning list enumerates the status across all zones","status":"skipped","title":"list enumerates the status across all zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ContentScanning"],"fullName":"ContentScanning enables Content Scanning and restores the original status on destroy","status":"skipped","title":"enables Content Scanning and restores the original status on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ContentScanning/ContentScanning.test.ts"},{"assertionResults":[{"ancestorTitles":["Expression"],"fullName":"Expression surfaces the typed ContentScanningNotEnabled error when scanning is disabled","status":"skipped","title":"surfaces the typed ContentScanningNotEnabled error when scanning is disabled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Expression"],"fullName":"Expression creates a custom expression, replaces on payload change, destroys cleanly","status":"skipped","title":"creates a custom expression, replaces on payload change, destroys cleanly","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Expression"],"fullName":"Expression list returns a well-typed array across all zones","status":"passed","title":"list returns a well-typed array across all zones","duration":2890.8126999999986,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Expression"],"fullName":"Expression list enumerates the deployed expression","status":"skipped","title":"list enumerates the deployed expression","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382834539,"endTime":1782382837429.8127,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ContentScanning/Expression.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed PlanLevelNotAllowed error on unentitled zones","status":"skipped","title":"surfaces the typed PlanLevelNotAllowed error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates custom certificates across zones","status":"passed","title":"list enumerates custom certificates across zones","duration":4339.661700000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"uploads, rotates in place, replaces on type change, and deletes","status":"skipped","title":"uploads, rotates in place, replaces on type change, and deletes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382839770,"endTime":1782382844109.6616,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CustomCertificate/CustomCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a custom hostname with default ssl","status":"skipped","title":"create and delete a custom hostname with default ssl","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating ssl method patches in place","status":"skipped","title":"updating ssl method patches in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of custom hostnames","status":"passed","title":"list returns a well-typed array of custom hostnames","duration":4387.967400000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed custom hostname","status":"skipped","title":"list enumerates the deployed custom hostname","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the hostname triggers replacement","status":"skipped","title":"changing the hostname triggers replacement","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382857975,"endTime":1782382862362.9673,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CustomHostname/CustomHostname.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates fallback origins across all zones","status":"passed","title":"list enumerates fallback origins across all zones","duration":2194.943299999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list surfaces a deployed fallback origin","status":"skipped","title":"list surfaces a deployed fallback origin","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"set, update and delete the zone fallback origin","status":"skipped","title":"set, update and delete the zone fallback origin","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382852508,"endTime":1782382854702.9434,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CustomHostname/FallbackOrigin.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed CustomNameserversNotEnabled error on unentitled accounts","status":"passed","title":"surfaces the typed CustomNameserversNotEnabled error on unentitled accounts","duration":4094.4203000000016,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account custom nameservers","status":"passed","title":"list enumerates account custom nameservers","duration":4296.4247,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed custom nameserver","status":"skipped","title":"list includes a deployed custom nameserver","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, replace on nsSet change, and destroy a custom nameserver","status":"skipped","title":"create, replace on nsSet change, and destroy a custom nameserver","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382841242,"endTime":1782382845540.4248,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CustomNameserver/CustomNameserver.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"D1Connection.bind exercises the full client surface","status":"passed","title":"D1Connection.bind exercises the full client surface","duration":34644.0309,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382741028,"endTime":1782382775672.031,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/D1/D1Binding.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed database","status":"passed","title":"list enumerates the deployed database","duration":3618.082699999999,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382806302,"endTime":1782382809920.0828,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/D1/D1Database.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete database with default props","status":"passed","title":"create and delete database with default props","duration":6216.363599999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete database","status":"passed","title":"create, update, delete database","duration":8190.268799999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"applies migrations from migrationsDir","status":"failed","title":"applies migrations from migrationsDir","duration":5901.2557000000015,"failureMessages":["Forbidden: System limit reached: databases per account (1)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"applies migrations using a custom migrationsTable","status":"passed","title":"applies migrations using a custom migrationsTable","duration":6944.6636,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"migrates legacy 2-column migration table to wrangler schema","status":"passed","title":"migrates legacy 2-column migration table to wrangler schema","duration":8354.731,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"imports SQL files via importFiles","status":"failed","title":"imports SQL files via importFiles","duration":1905.7952000000005,"failureMessages":["Forbidden: System limit reached: databases per account (1)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"clones a database by databaseId","status":"failed","title":"clones a database by databaseId","duration":1181.4063999999998,"failureMessages":["Forbidden: System limit reached: databases per account (1)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"clones a database by name lookup","status":"failed","title":"clones a database by name lookup","duration":1623.7530999999944,"failureMessages":["Forbidden: System limit reached: databases per account (1)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"clones a database by passing the source resource directly","status":"failed","title":"clones a database by passing the source resource directly","duration":1532.975900000005,"failureMessages":["Forbidden: System limit reached: databases per account (1)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"existing database (matching name) is silently adopted without --adopt","status":"failed","title":"existing database (matching name) is silently adopted without --adopt","duration":1159.2151999999987,"failureMessages":["Forbidden: System limit reached: databases per account (1)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782382714891,"endTime":1782382723862.215,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/D1/Database.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AdvancedTcpProtectionNotEntitled error without Magic Transit","status":"skipped","title":"surfaces the typed AdvancedTcpProtectionNotEntitled error without Magic Transit","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an allowlist entry, updates it in place, replaces on prefix change, and destroys","status":"skipped","title":"creates an allowlist entry, updates it in place, replaces on prefix change, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed empty array without Magic Transit","status":"skipped","title":"list returns a well-typed empty array without Magic Transit","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed allowlist entry","status":"skipped","title":"list enumerates the deployed allowlist entry","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/AllowlistEntry.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates a SYN protection filter, updates it in place, and destroys","status":"skipped","title":"creates a SYN protection filter, updates it in place, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of SYN protection filters","status":"passed","title":"list returns a well-typed array of SYN protection filters","duration":29870.8335,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed SYN protection filter","status":"skipped","title":"list enumerates the deployed SYN protection filter","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382789029,"endTime":1782382818899.8335,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/SynProtectionFilter.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates a global SYN protection rule, updates it in place, and destroys","status":"skipped","title":"creates a global SYN protection rule, updates it in place, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of SYN protection rules","status":"passed","title":"list returns a well-typed array of SYN protection rules","duration":29254.9995,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed SYN protection rule","status":"skipped","title":"list enumerates the deployed SYN protection rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382795312,"endTime":1782382824566.9995,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/SynProtectionRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates a TCP flow protection filter, updates it in place, and destroys","status":"skipped","title":"creates a TCP flow protection filter, updates it in place, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account's TCP flow protection filters","status":"passed","title":"list returns the account's TCP flow protection filters","duration":31588.068399999996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed TCP flow protection filter","status":"skipped","title":"list enumerates the deployed TCP flow protection filter","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382801094,"endTime":1782382832682.0684,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/TcpFlowProtectionFilter.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates a global TCP flow protection rule, updates it in place, and destroys","status":"skipped","title":"creates a global TCP flow protection rule, updates it in place, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed empty array without Magic Transit","status":"passed","title":"list returns a well-typed empty array without Magic Transit","duration":30267.355600000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed TCP flow protection rule","status":"skipped","title":"list enumerates the deployed TCP flow protection rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382784117,"endTime":1782382814384.3557,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/TcpFlowProtectionRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a custom device profile","status":"skipped","title":"create, update in place, and delete a custom device profile","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed custom device profile","status":"skipped","title":"list enumerates the deployed custom device profile","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/CustomProfile.test.ts"},{"assertionResults":[{"ancestorTitles":["DefaultProfile"],"fullName":"DefaultProfile reads the existing default device profile without mutating it","status":"skipped","title":"reads the existing default device profile without mutating it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DefaultProfile"],"fullName":"DefaultProfile toggles captivePortal and restores the original value","status":"skipped","title":"toggles captivePortal and restores the original value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DefaultProfile"],"fullName":"DefaultProfile list returns the singleton default device profile","status":"skipped","title":"list returns the singleton default device profile","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/DefaultProfile.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden entitlement error","status":"skipped","title":"unentitled accounts surface the typed Forbidden entitlement error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a DEX test","status":"skipped","title":"create, update in place, and destroy a DEX test","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed DEX test","status":"skipped","title":"list enumerates the deployed DEX test","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed empty array on unentitled accounts","status":"skipped","title":"list returns a well-typed empty array on unentitled accounts","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/DexTest.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a managed network","status":"skipped","title":"create, update in place, and delete a managed network","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed managed network","status":"skipped","title":"list enumerates the deployed managed network","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/ManagedNetwork.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates without a real provider surface the typed InvalidPostureIntegrationConfig error","status":"skipped","title":"creates without a real provider surface the typed InvalidPostureIntegrationConfig error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a posture integration","status":"skipped","title":"create, update in place, and destroy a posture integration","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates posture integrations in the account","status":"passed","title":"list enumerates posture integrations in the account","duration":29915.2632,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed posture integration","status":"skipped","title":"list includes a deployed posture integration","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382660106,"endTime":1782382690021.2632,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/PostureIntegration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a posture rule","status":"skipped","title":"create, update in place, and delete a posture rule","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the rule type triggers a replacement","status":"skipped","title":"changing the rule type triggers a replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed posture rule","status":"skipped","title":"list enumerates the deployed posture rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/PostureRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"patches disableForTime and restores the original value on destroy","status":"skipped","title":"patches disableForTime and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account's device settings singleton","status":"skipped","title":"list returns the account's device settings singleton","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"rejects public IPs with the typed InvalidHealthcheckEndpoint error","status":"skipped","title":"rejects public IPs with the typed InvalidHealthcheckEndpoint error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an endpoint healthcheck, updates in place, and destroys","status":"skipped","title":"creates an endpoint healthcheck, updates in place, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed endpoint healthcheck","status":"skipped","title":"list enumerates the deployed endpoint healthcheck","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Diagnostics/EndpointHealthcheck.test.ts"},{"assertionResults":[{"ancestorTitles":["AccountSettings"],"fullName":"AccountSettings list returns the account's DNS settings singleton","status":"skipped","title":"list returns the account's DNS settings singleton","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AccountSettings"],"fullName":"AccountSettings pins a zone default and restores the pre-management value on destroy","status":"skipped","title":"pins a zone default and restores the pre-management value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AccountSettings"],"fullName":"AccountSettings updates in place, unions managedKeys, restores all managed fields","status":"skipped","title":"updates in place, unions managedKeys, restores all managed fields","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/AccountSettings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed worker drives the full DNS record CRUD surface via DnsReadWrite","status":"skipped","title":"deployed worker drives the full DNS record CRUD surface via DnsReadWrite","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/Dns.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an A record with default props","status":"skipped","title":"create and delete an A record with default props","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating mutable fields patches in place","status":"skipped","title":"updating mutable fields patches in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the record type triggers replacement","status":"skipped","title":"changing the record type triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing record errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing record errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed DNS record","status":"skipped","title":"list enumerates the deployed DNS record","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/DnsRecord.test.ts"},{"assertionResults":[{"ancestorTitles":["Dnssec"],"fullName":"Dnssec enables DNSSEC, captures the disabled baseline, destroy deactivates","status":"skipped","title":"enables DNSSEC, captures the disabled baseline, destroy deactivates","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Dnssec"],"fullName":"Dnssec updates status in place — same zone singleton, no replacement","status":"skipped","title":"updates status in place — same zone singleton, no replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Dnssec"],"fullName":"Dnssec list enumerates active DNSSEC across zones","status":"skipped","title":"list enumerates active DNSSEC across zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/Dnssec.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed InternalDnsNotAvailable error on unentitled accounts","status":"skipped","title":"surfaces the typed InternalDnsNotAvailable error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of internal DNS views","status":"skipped","title":"list returns a well-typed array of internal DNS views","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates a deployed internal DNS view","status":"skipped","title":"list enumerates a deployed internal DNS view","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update zones in place, and delete an internal DNS view","status":"skipped","title":"create, update zones in place, and delete an internal DNS view","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/View.test.ts"},{"assertionResults":[{"ancestorTitles":["ZoneSettings"],"fullName":"ZoneSettings pins flattenAllCnames and restores the pre-management value on destroy","status":"skipped","title":"pins flattenAllCnames and restores the pre-management value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ZoneSettings"],"fullName":"ZoneSettings updates in place, unions managedKeys, restores all managed fields","status":"skipped","title":"updates in place, unions managedKeys, restores all managed fields","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ZoneSettings"],"fullName":"ZoneSettings list enumerates DNS settings across all zones","status":"skipped","title":"list enumerates DNS settings across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneSettings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a zone transfer ACL","status":"skipped","title":"create, update in place, and delete a zone transfer ACL","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"generates a deterministic name when none is provided","status":"skipped","title":"generates a deterministic name when none is provided","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed zone transfer ACLs","status":"skipped","title":"list enumerates the deployed zone transfer ACLs","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferAcl.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates incoming configs across all zones","status":"passed","title":"list enumerates incoming configs across all zones","duration":3892.071,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"incoming config does not persist on non-secondary zones (typed not-found)","status":"skipped","title":"incoming config does not persist on non-secondary zones (typed not-found)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update autoRefreshSeconds in place, and delete the incoming config","status":"skipped","title":"create, update autoRefreshSeconds in place, and delete the incoming config","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382837321,"endTime":1782382841213.071,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferIncoming.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed OutgoingZoneTransfersNotAllowed error on unentitled zones","status":"skipped","title":"surfaces the typed OutgoingZoneTransfersNotAllowed error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, sync peers and enabled state, and delete the outgoing config","status":"skipped","title":"create, sync peers and enabled state, and delete the outgoing config","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the outgoing transfer config per zone","status":"skipped","title":"list enumerates the outgoing transfer config per zone","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferOutgoing.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create with connection settings, update in place, and delete a peer","status":"skipped","title":"create with connection settings, update in place, and delete a peer","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"wires a TSIG reference through to the peer","status":"skipped","title":"wires a TSIG reference through to the peer","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed peer","status":"skipped","title":"list enumerates the deployed peer","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferPeer.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, rotate the secret in place, and delete a TSIG","status":"skipped","title":"create, rotate the secret in place, and delete a TSIG","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed TSIG","status":"skipped","title":"list enumerates the deployed TSIG","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferTsig.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed DnsFirewallNotEntitled error on unentitled accounts","status":"skipped","title":"surfaces the typed DnsFirewallNotEntitled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, verify out-of-band, destroy, and wait until gone","status":"skipped","title":"create, verify out-of-band, destroy, and wait until gone","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable settings in place, replace on rename","status":"skipped","title":"update mutable settings in place, replace on rename","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the DNS firewall cluster Attributes array","status":"skipped","title":"list returns the DNS firewall cluster Attributes array","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed DNS firewall cluster","status":"skipped","title":"list enumerates the deployed DNS firewall cluster","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DnsFirewall/DnsFirewall.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed email address","status":"skipped","title":"list enumerates the deployed email address","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailAddress.test.ts"},{"assertionResults":[{"ancestorTitles":["EmailCatchAll"],"fullName":"EmailCatchAll configures the catch-all rule and restores the baseline on destroy","status":"skipped","title":"configures the catch-all rule and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["EmailCatchAll"],"fullName":"EmailCatchAll updates the catch-all rule in place and keeps the captured baseline","status":"skipped","title":"updates the catch-all rule in place and keeps the captured baseline","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["EmailCatchAll"],"fullName":"EmailCatchAll list enumerates the catch-all rule across all zones","status":"skipped","title":"list enumerates the catch-all rule across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailCatchAll.test.ts"},{"assertionResults":[{"ancestorTitles":["EmailRouting"],"fullName":"EmailRouting list enumerates email routing across all zones","status":"skipped","title":"list enumerates email routing across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailRouting.test.ts"},{"assertionResults":[{"ancestorTitles":["EmailRule"],"fullName":"EmailRule list enumerates the deployed email rule across all zones","status":"skipped","title":"list enumerates the deployed email rule across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"sends an email through the Worker send_email binding","status":"skipped","title":"sends an email through the Worker send_email binding","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/SendEmail.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and destroy a sending subdomain","status":"skipped","title":"create and destroy a sending subdomain","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the name triggers replacement","status":"skipped","title":"changing the name triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing subdomain errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing subdomain errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed sending subdomain","status":"skipped","title":"list enumerates the deployed sending subdomain","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/SendingSubdomain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed EmailSecurityNotEntitled error on unentitled accounts","status":"skipped","title":"surfaces the typed EmailSecurityNotEntitled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates allow policies (read-only)","status":"skipped","title":"list enumerates allow policies (read-only)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes the deployed allow policy","status":"skipped","title":"list includes the deployed allow policy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, destroy","status":"skipped","title":"create, update in place, destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/AllowPolicy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, destroy","status":"skipped","title":"create, update in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed block sender","status":"passed","title":"list enumerates the deployed block sender","duration":30614.420799999996,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382800068,"endTime":1782382830682.421,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/BlockSender.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"adopts an onboarded domain, patches settings in place, offboards on destroy","status":"skipped","title":"adopts an onboarded domain, patches settings in place, offboards on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates Email Security domains (or [] when unentitled)","status":"skipped","title":"list enumerates Email Security domains (or [] when unentitled)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/Domain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, destroy","status":"skipped","title":"create, update in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account impersonation registry","status":"skipped","title":"list returns the account impersonation registry","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed impersonation registry entry","status":"skipped","title":"list enumerates the deployed impersonation registry entry","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/ImpersonationRegistryEntry.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, destroy","status":"skipped","title":"create, update in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates trusted domains","status":"skipped","title":"list enumerates trusted domains","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/TrustedDomain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden error","status":"skipped","title":"unentitled accounts surface the typed Forbidden error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, and destroy a DLP profile with a standalone entry","status":"skipped","title":"create, update, and destroy a DLP profile with a standalone entry","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dlp/Dlp.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns the account's custom DLP entries","status":"skipped","title":"list returns the account's custom DLP entries","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed DLP entry","status":"skipped","title":"list enumerates the deployed DLP entry","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dlp/Entry.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates custom DLP profiles (read-only)","status":"skipped","title":"list enumerates custom DLP profiles (read-only)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed custom DLP profile","status":"skipped","title":"list includes a deployed custom DLP profile","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dlp/Profile.test.ts"},{"assertionResults":[{"ancestorTitles":["FraudDetectionSettings"],"fullName":"FraudDetectionSettings adopts the zone singleton without writing and restores nothing on destroy","status":"skipped","title":"adopts the zone singleton without writing and restores nothing on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["FraudDetectionSettings"],"fullName":"FraudDetectionSettings surfaces the typed FraudDetectionNotEntitled error on unentitled zones","status":"skipped","title":"surfaces the typed FraudDetectionNotEntitled error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["FraudDetectionSettings"],"fullName":"FraudDetectionSettings manages fraud detection settings and restores them on destroy","status":"skipped","title":"manages fraud detection settings and restores them on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["FraudDetectionSettings"],"fullName":"FraudDetectionSettings list enumerates the settings across all zones","status":"skipped","title":"list enumerates the settings across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Fraud/FraudDetectionSettings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete a Flagship app","status":"skipped","title":"create, update, delete a Flagship app","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates an app after out-of-band delete","status":"skipped","title":"recreates an app after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed Flagship app","status":"skipped","title":"list enumerates the deployed Flagship app","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Flagship/App.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete a flag in an app","status":"skipped","title":"create, update, delete a flag in an app","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the flag when the key changes","status":"skipped","title":"replaces the flag when the key changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates a flag after out-of-band delete","status":"skipped","title":"recreates a flag after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed flag","status":"skipped","title":"list enumerates the deployed flag","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Flagship/Flag.test.ts"},{"assertionResults":[],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"FlagshipApp is not a function","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Flagship/Flagship.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create an activated certificate, deactivate in place, destroy","status":"skipped","title":"create an activated certificate, deactivate in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the validity period replaces the certificate","status":"skipped","title":"changing the validity period replaces the certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed gateway certificate","status":"skipped","title":"list enumerates the deployed gateway certificate","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Certificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"manage activityLog and protocolDetection, then restore on destroy","status":"skipped","title":"manage activityLog and protocolDetection, then restore on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account Gateway configuration singleton","status":"skipped","title":"list returns the account Gateway configuration singleton","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Configuration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy a DOMAIN list","status":"skipped","title":"create, verify, and destroy a DOMAIN list","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update items, description, and name in place","status":"skipped","title":"update items, description, and name in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the type replaces the list","status":"skipped","title":"changing the type replaces the list","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/List.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy a location","status":"skipped","title":"create, verify, and destroy a location","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name and ecsSupport in place (same id)","status":"skipped","title":"update name and ecsSupport in place (same id)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates deployed gateway locations","status":"skipped","title":"list enumerates deployed gateway locations","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Location.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"manage logging settings, update in place, restore on destroy","status":"skipped","title":"manage logging settings, update in place, restore on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account's Gateway logging singleton","status":"skipped","title":"list returns the account's Gateway logging singleton","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Logging.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, and destroy an identity-kind proxy endpoint","status":"skipped","title":"create, update, and destroy an identity-kind proxy endpoint","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"ip-kind endpoints surface the typed entitlement error","status":"skipped","title":"ip-kind endpoints surface the typed entitlement error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed proxy endpoint","status":"skipped","title":"list enumerates the deployed proxy endpoint","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/ProxyEndpoint.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed Gateway rule","status":"skipped","title":"list enumerates the deployed Gateway rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Rule.test.ts"},{"assertionResults":[{"ancestorTitles":["GoogleTagGateway"],"fullName":"GoogleTagGateway configures the gateway, updates in place, and restores the baseline on destroy","status":"skipped","title":"configures the gateway, updates in place, and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["GoogleTagGateway"],"fullName":"GoogleTagGateway no-op redeploy skips the PUT and destroy is idempotent","status":"skipped","title":"no-op redeploy skips the PUT and destroy is idempotent","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["GoogleTagGateway"],"fullName":"GoogleTagGateway list enumerates configured zones","status":"skipped","title":"list enumerates configured zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/GoogleTagGateway/GoogleTagGateway.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an HTTP health check with default name","status":"skipped","title":"create and delete an HTTP health check with default name","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable props in place (same id), then no-op redeploy","status":"skipped","title":"update mutable props in place (same id), then no-op redeploy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing type HTTP→TCP updates in place; delete is idempotent","status":"skipped","title":"changing type HTTP→TCP updates in place; delete is idempotent","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing check errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing check errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed health check across zones","status":"skipped","title":"list enumerates the deployed health check across zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Healthcheck/Healthcheck.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, update name+scope in place, destroy","status":"skipped","title":"create, verify out-of-band, update name+scope in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed resource group","status":"skipped","title":"list enumerates the deployed resource group","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Iam/ResourceGroup.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create with a policy, verify out-of-band, update policies in place, destroy","status":"skipped","title":"create with a policy, verify out-of-band, update policies in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed user group","status":"skipped","title":"list enumerates the deployed user group","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Iam/UserGroup.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, replace when the user group changes, destroy","status":"skipped","title":"create, verify out-of-band, replace when the user group changes, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates memberships across all user groups in the account","status":"skipped","title":"list enumerates memberships across all user groups in the account","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Iam/UserGroupMembership.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete hyperdrive with default props","status":"passed","title":"create and delete hyperdrive with default props","duration":10645.371100000004,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete hyperdrive","status":"passed","title":"create, update, delete hyperdrive","duration":11748.114399999991,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed hyperdrive","status":"passed","title":"list enumerates the deployed hyperdrive","duration":12701.097399999999,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382787476,"endTime":1782382800182.0974,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Hyperdrive/Hyperdrive.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"lists overrides and surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","status":"skipped","title":"lists overrides and surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates per-hostname TLS overrides","status":"passed","title":"list enumerates per-hostname TLS overrides","duration":4279.940700000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a min_tls_version override","status":"skipped","title":"create, update in place, and destroy a min_tls_version override","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382841418,"endTime":1782382845697.9407,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/HostnameTlsSetting/HostnameTlsSetting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a zone-scoped ip rule","status":"skipped","title":"create and delete a zone-scoped ip rule","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mode and notes in place (same ruleId)","status":"skipped","title":"update mode and notes in place (same ruleId)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the configuration triggers replacement","status":"skipped","title":"changing the configuration triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed zone-scoped rule","status":"skipped","title":"list enumerates the deployed zone-scoped rule","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"account-scoped rule (no zoneId) create, update, delete","status":"skipped","title":"account-scoped rule (no zoneId) create, update, delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Firewall/AccessRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update urls/configurations/description/paused in place, destroy","status":"skipped","title":"create, update urls/configurations/description/paused in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed lockdown rule","status":"skipped","title":"list enumerates the deployed lockdown rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Firewall/Lockdown.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update mode/paused/description/userAgent in place, destroy","status":"skipped","title":"create, update mode/paused/description/userAgent in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates UA rules across all zones","status":"skipped","title":"list enumerates UA rules across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Firewall/UaRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"async worker reads image info via env Images binding","status":"skipped","title":"async worker reads image info via env Images binding","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker reads image info via yield* Images","status":"skipped","title":"effect worker reads image info via yield* Images","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Images/Images.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the account's signing keys","status":"skipped","title":"list enumerates the account's signing keys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"surfaces the typed ImagesAccessNotEnabled error on unentitled accounts","status":"skipped","title":"surfaces the typed ImagesAccessNotEnabled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create a signing key, no-op redeploy keeps the value, delete","status":"skipped","title":"create a signing key, no-op redeploy keeps the value, delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Images/SigningKey.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a variant","status":"skipped","title":"create, update in place, and delete a variant","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replace a variant when the name changes","status":"skipped","title":"replace a variant when the name changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed variant","status":"skipped","title":"list enumerates the deployed variant","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Images/Variant.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed IndicatorFeedsNotEntitled error","status":"skipped","title":"unentitled accounts surface the typed IndicatorFeedsNotEntitled error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of indicator feeds","status":"passed","title":"list returns a well-typed array of indicator feeds","duration":27638.4614,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed indicator feed","status":"skipped","title":"list enumerates the deployed indicator feed","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create (or adopt), verify out-of-band, update in place, destroy","status":"skipped","title":"create (or adopt), verify out-of-band, update in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"uploads a STIX2 snapshot and grants/revokes a consumer permission","status":"skipped","title":"uploads a STIX2 snapshot and grants/revokes a consumer permission","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382737573,"endTime":1782382765211.4614,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Intel/IndicatorFeed.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns [] for the non-listable IndicatorFeedPermission","status":"passed","title":"list returns [] for the non-listable IndicatorFeedPermission","duration":1641.3367999999973,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382864612,"endTime":1782382866253.337,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Intel/IndicatorFeedPermission.test.ts"},{"assertionResults":[{"ancestorTitles":["LeakedCredentialDetection"],"fullName":"LeakedCredentialDetection list enumerates custom detections across all zones","status":"skipped","title":"list enumerates custom detections across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LeakedCredentialCheck/Detection.test.ts"},{"assertionResults":[{"ancestorTitles":["LeakedCredentialCheck"],"fullName":"LeakedCredentialCheck enables leaked credential checks and restores the baseline on destroy","status":"skipped","title":"enables leaked credential checks and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["LeakedCredentialCheck"],"fullName":"LeakedCredentialCheck surfaces the typed DetectionQuotaExceeded error on unentitled zones","status":"skipped","title":"surfaces the typed DetectionQuotaExceeded error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["LeakedCredentialCheck"],"fullName":"LeakedCredentialCheck list enumerates the check across all zones","status":"skipped","title":"list enumerates the check across all zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["LeakedCredentialCheck"],"fullName":"LeakedCredentialCheck creates, updates, and destroys a custom detection","status":"skipped","title":"creates, updates, and destroys a custom detection","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LeakedCredentialCheck/LeakedCredentialCheck.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed LoadBalancingNotEnabledForZone error without the LB subscription","status":"skipped","title":"surfaces the typed LoadBalancingNotEnabledForZone error without the LB subscription","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a load balancer","status":"skipped","title":"create, update in place, and destroy a load balancer","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of load balancers","status":"passed","title":"list returns a well-typed array of load balancers","duration":3622.2023999999983,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed load balancer","status":"skipped","title":"list enumerates the deployed load balancer","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382841471,"endTime":1782382845093.2024,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/LoadBalancer.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed MonitorIntervalOutOfRange error without the LB subscription","status":"skipped","title":"surfaces the typed MonitorIntervalOutOfRange error without the LB subscription","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a monitor","status":"skipped","title":"create, update in place, and destroy a monitor","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns an array of monitor attributes","status":"skipped","title":"list returns an array of monitor attributes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed monitor","status":"skipped","title":"list enumerates the deployed monitor","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/Monitor.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed MonitorGroupsNotEnabled error on non-Enterprise accounts","status":"skipped","title":"surfaces the typed MonitorGroupsNotEnabled error on non-Enterprise accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns an array of monitor groups","status":"skipped","title":"list returns an array of monitor groups","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed monitor group","status":"skipped","title":"list enumerates the deployed monitor group","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a monitor group","status":"skipped","title":"create, update in place, and destroy a monitor group","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/MonitorGroup.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed PoolAccessFailed error without the LB subscription","status":"skipped","title":"surfaces the typed PoolAccessFailed error without the LB subscription","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a health-checked pool","status":"skipped","title":"create, update in place, and destroy a health-checked pool","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed pool","status":"skipped","title":"list enumerates the deployed pool","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/Pool.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"diag list","status":"skipped","title":"diag list","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/_diag.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed KeylessSslNotAvailable error on non-Enterprise zones","status":"skipped","title":"surfaces the typed KeylessSslNotAvailable error on non-Enterprise zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates keyless certificates across zones","status":"passed","title":"list enumerates keyless certificates across zones","duration":4444.599699999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed keyless certificate","status":"skipped","title":"list includes a deployed keyless certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates, updates in place, replaces on certificate change, and destroys","status":"skipped","title":"creates, updates in place, replaces on certificate change, and destroys","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382769974,"endTime":1782382774418.5996,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/KeylessCertificate/KeylessCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete namespace with default props","status":"passed","title":"create and delete namespace with default props","duration":3871.831900000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete namespace","status":"passed","title":"create, update, delete namespace","duration":4259.742000000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed namespace","status":"passed","title":"list enumerates the deployed namespace","duration":3959.336400000004,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"existing namespace (matching title) is silently adopted without --adopt","status":"passed","title":"existing namespace (matching title) is silently adopted without --adopt","duration":4168.770699999997,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382811620,"endTime":1782382815881.742,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/KV/Namespace.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, and delete an account-scoped job pushing to R2","status":"skipped","title":"create, update, and delete an account-scoped job pushing to R2","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed Logpush job","status":"skipped","title":"list enumerates the deployed Logpush job","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the dataset triggers a replacement","status":"skipped","title":"changing the dataset triggers a replacement","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Logpush/Job.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed LogsControlNotAuthorized error on unentitled accounts","status":"skipped","title":"surfaces the typed LogsControlNotAuthorized error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, verify out-of-band, update in place, destroy, and wait until gone","status":"skipped","title":"create, verify out-of-band, update in place, destroy, and wait until gone","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list tolerates the typed LogsControlNotAuthorized error on unentitled accounts","status":"skipped","title":"list tolerates the typed LogsControlNotAuthorized error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the account CMB config singleton","status":"skipped","title":"list enumerates the account CMB config singleton","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LogsControl/CmbConfig.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed LogsControlNotAuthorized error on unentitled zones","status":"skipped","title":"surfaces the typed LogsControlNotAuthorized error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"pins the retention flag, updates in place, and restores on destroy","status":"skipped","title":"pins the retention flag, updates in place, and restores on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the retention flag across all zones","status":"passed","title":"list enumerates the retention flag across all zones","duration":4556.011400000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list contains the entitled test zone's retention flag","status":"skipped","title":"list contains the entitled test zone's retention flag","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382833578,"endTime":1782382838134.0115,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LogsControl/RetentionFlag.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed FeatureNotEnabled error","status":"skipped","title":"unentitled accounts surface the typed FeatureNotEnabled error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a sync, updates mutable props in place, and destroys it","status":"skipped","title":"creates a sync, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account catalog syncs","status":"skipped","title":"list enumerates account catalog syncs","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the sync when destinationType changes","status":"skipped","title":"replaces the sync when destinationType changes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicCloudNetworking/CatalogSync.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed FeatureNotEnabled error","status":"skipped","title":"unentitled accounts surface the typed FeatureNotEnabled error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of integrations","status":"skipped","title":"list returns a well-typed array of integrations","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed integration","status":"skipped","title":"list enumerates the deployed integration","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"registers an AWS integration, updates it in place, and destroys it","status":"skipped","title":"registers an AWS integration, updates it in place, and destroys it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the integration when cloudType changes","status":"skipped","title":"replaces the integration when cloudType changes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicCloudNetworking/CloudIntegration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed FeatureNotEnabled error","status":"skipped","title":"unentitled accounts surface the typed FeatureNotEnabled error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an on-ramp, updates mutable props in place, and destroys it","status":"skipped","title":"creates an on-ramp, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns on-ramps or a typed [] when unentitled","status":"skipped","title":"list returns on-ramps or a typed [] when unentitled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed on-ramp","status":"skipped","title":"list enumerates the deployed on-ramp","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicCloudNetworking/OnRamp.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicWanUnauthorized error","status":"skipped","title":"unentitled accounts surface the typed MagicWanUnauthorized error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a custom app, updates mutable props in place, and destroys it","status":"skipped","title":"creates a custom app, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account apps (well-typed [] when unentitled)","status":"skipped","title":"list enumerates account apps (well-typed [] when unentitled)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed custom app","status":"skipped","title":"list includes a deployed custom app","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/App.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicTransitNotOnboarded error","status":"passed","title":"unentitled accounts surface the typed MagicTransitNotOnboarded error","duration":80419.0452,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed GRE tunnels","status":"passed","title":"list enumerates the deployed GRE tunnels","duration":28607.890699999996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a GRE tunnel, updates mutable props in place, and destroys it","status":"skipped","title":"creates a GRE tunnel, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382771839,"endTime":1782382852258.0452,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/GreTunnel.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicTransitNotOnboarded error","status":"skipped","title":"unentitled accounts surface the typed MagicTransitNotOnboarded error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account IPsec tunnels (read-only [] when unentitled)","status":"passed","title":"list enumerates account IPsec tunnels (read-only [] when unentitled)","duration":28559.037699999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an IPsec tunnel, updates mutable props in place, and destroys it","status":"skipped","title":"creates an IPsec tunnel, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382805450,"endTime":1782382834009.0376,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/IpsecTunnel.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicWanUnauthorized error","status":"skipped","title":"unentitled accounts surface the typed MagicWanUnauthorized error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account Magic WAN sites","status":"skipped","title":"list enumerates account Magic WAN sites","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a site with WAN, LAN, and ACL, updates in place, and destroys","status":"skipped","title":"creates a site with WAN, LAN, and ACL, updates in place, and destroys","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/Site.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed site ACLs","status":"passed","title":"list enumerates the deployed site ACLs","duration":28548.528599999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Magic WAN error for ACL listing","status":"skipped","title":"unentitled accounts surface the typed Magic WAN error for ACL listing","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382784589,"endTime":1782382813137.5286,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/SiteAcl.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a well-typed array (empty on unentitled accounts)","status":"skipped","title":"list returns a well-typed array (empty on unentitled accounts)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed Magic WAN site LANs","status":"skipped","title":"list enumerates the deployed Magic WAN site LANs","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/SiteLan.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a well-typed array of site WANs","status":"passed","title":"list returns a well-typed array of site WANs","duration":29643.2666,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed site WAN","status":"skipped","title":"list enumerates the deployed site WAN","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382805233,"endTime":1782382834876.2666,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/SiteWan.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicTransitNotOnboarded error","status":"skipped","title":"unentitled accounts surface the typed MagicTransitNotOnboarded error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of routes","status":"skipped","title":"list returns a well-typed array of routes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed static route","status":"skipped","title":"list enumerates the deployed static route","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"routes a prefix over a GRE tunnel, updates in place, and destroys","status":"skipped","title":"routes a prefix over a GRE tunnel, updates in place, and destroys","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/StaticRoute.test.ts"},{"assertionResults":[{"ancestorTitles":["ManagedTransforms"],"fullName":"ManagedTransforms manages a named transform, updates in place, and restores it on destroy","status":"skipped","title":"manages a named transform, updates in place, and restores it on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ManagedTransforms"],"fullName":"ManagedTransforms destroy restores a transform that was enabled before management","status":"skipped","title":"destroy restores a transform that was enabled before management","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ManagedTransforms"],"fullName":"ManagedTransforms deploy with no transforms named adopts the singleton without writing","status":"skipped","title":"deploy with no transforms named adopts the singleton without writing","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ManagedTransforms"],"fullName":"ManagedTransforms list enumerates managed transforms across all zones","status":"skipped","title":"list enumerates managed transforms across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ManagedTransforms/ManagedTransforms.test.ts"},{"assertionResults":[{"ancestorTitles":["MagicNetworkMonitoring.Config list"],"fullName":"MagicNetworkMonitoring.Config list list enumerates the account MNM config","status":"skipped","title":"list enumerates the account MNM config","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/Config.test.ts"},{"assertionResults":[{"ancestorTitles":["MagicNetworkMonitoring"],"fullName":"MagicNetworkMonitoring creates, updates in place, and deletes the account config","status":"skipped","title":"creates, updates in place, and deletes the account config","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["MagicNetworkMonitoring"],"fullName":"MagicNetworkMonitoring creates a threshold rule, updates it in place, and replaces on type change","status":"skipped","title":"creates a threshold rule, updates it in place, and replaces on type change","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/MagicNetworkMonitoring.test.ts"},{"assertionResults":[{"ancestorTitles":["MagicNetworkMonitoring.Rule"],"fullName":"MagicNetworkMonitoring.Rule list enumerates the deployed rule","status":"skipped","title":"list enumerates the deployed rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/Rule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins the default ASN, updates in place, and restores the original on destroy","status":"skipped","title":"pins the default ASN, updates in place, and restores the original on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the CNI settings singleton","status":"skipped","title":"list enumerates the CNI settings singleton","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/NetworkInterconnects/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden error","status":"skipped","title":"unentitled accounts surface the typed Forbidden error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list either enumerates organizations or tolerates the unentitled account","status":"skipped","title":"list either enumerates organizations or tolerates the unentitled account","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed organization","status":"skipped","title":"list enumerates the deployed organization","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, verify out-of-band, update in place, destroy","status":"skipped","title":"create, verify out-of-band, update in place, destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Organization/Organization.test.ts"},{"assertionResults":[{"ancestorTitles":["OriginPostQuantumEncryption"],"fullName":"OriginPostQuantumEncryption pins the setting and restores the original value on destroy","status":"skipped","title":"pins the setting and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["OriginPostQuantumEncryption"],"fullName":"OriginPostQuantumEncryption updates the value in place and keeps the captured initial value","status":"skipped","title":"updates the value in place and keeps the captured initial value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["OriginPostQuantumEncryption"],"fullName":"OriginPostQuantumEncryption no-op redeploy converges without changing the setting","status":"skipped","title":"no-op redeploy converges without changing the setting","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["OriginPostQuantumEncryption"],"fullName":"OriginPostQuantumEncryption list enumerates the setting across all zones","status":"skipped","title":"list enumerates the setting across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginPostQuantumEncryption/OriginPostQuantumEncryption.test.ts"},{"assertionResults":[{"ancestorTitles":["OriginTlsClientAuthCertificate"],"fullName":"OriginTlsClientAuthCertificate uploads and deletes a zone client certificate","status":"skipped","title":"uploads and deletes a zone client certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["OriginTlsClientAuthCertificate"],"fullName":"OriginTlsClientAuthCertificate replaces the certificate when the PEM changes","status":"skipped","title":"replaces the certificate when the PEM changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["OriginTlsClientAuthCertificate"],"fullName":"OriginTlsClientAuthCertificate list enumerates the deployed zone client certificate","status":"skipped","title":"list enumerates the deployed zone client certificate","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/Certificate.test.ts"},{"assertionResults":[{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation list returns [] for the non-listable association","status":"passed","title":"list returns [] for the non-listable association","duration":1339.7252000000008,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation associates a hostname, updates cert and enablement in place, voids on destroy","status":"skipped","title":"associates a hostname, updates cert and enablement in place, voids on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation replaces the association when the hostname changes","status":"skipped","title":"replaces the association when the hostname changes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382845017,"endTime":1782382846356.725,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/HostnameAssociation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"uploads and deletes a hostname client certificate","status":"skipped","title":"uploads and deletes a hostname client certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the hostname certificate when the PEM changes","status":"skipped","title":"replaces the hostname certificate when the PEM changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed hostname certificate","status":"skipped","title":"list enumerates the deployed hostname certificate","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/HostnameCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":["Setting"],"fullName":"Setting enables AOP and restores the original value on destroy","status":"skipped","title":"enables AOP and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Setting"],"fullName":"Setting updates the enabled flag in place","status":"skipped","title":"updates the enabled flag in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Setting"],"fullName":"Setting list enumerates the setting across all zones","status":"skipped","title":"list enumerates the setting across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/Setting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"issue, verify, and revoke a certificate","status":"skipped","title":"issue, verify, and revoke a certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates issued certificates","status":"skipped","title":"list enumerates issued certificates","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replacement on requestedValidity change","status":"skipped","title":"replacement on requestedValidity change","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replacement on hostnames change","status":"skipped","title":"replacement on hostnames change","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginCaCertificate/OriginCaCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, and destroy a page rule","status":"skipped","title":"create, verify out-of-band, and destroy a page rule","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating actions, status, priority and target syncs in place","status":"skipped","title":"updating actions, status, priority and target syncs in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing rule errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing rule errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed page rule","status":"skipped","title":"list enumerates the deployed page rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/PageRule/PageRule.test.ts"},{"assertionResults":[{"ancestorTitles":["MtlsCertificate"],"fullName":"MtlsCertificate create and delete a CA certificate","status":"skipped","title":"create and delete a CA certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["MtlsCertificate"],"fullName":"MtlsCertificate create and delete a leaf certificate with private key","status":"skipped","title":"create and delete a leaf certificate with private key","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["MtlsCertificate"],"fullName":"MtlsCertificate replaces the certificate when the PEM changes","status":"skipped","title":"replaces the certificate when the PEM changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["MtlsCertificate"],"fullName":"MtlsCertificate list enumerates the deployed mTLS certificate","status":"skipped","title":"list enumerates the deployed mTLS certificate","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MtlsCertificate/MtlsCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, replace on branch change, and destroy a deployment","status":"failed","title":"create, replace on branch change, and destroy a deployment","duration":120133.6974,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Deployment.test.ts:92:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates deployments across Pages projects","status":"skipped","title":"list enumerates deployments across Pages projects","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382660071,"endTime":1782382780204.6975,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Deployment.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"attach and detach a custom domain","status":"failed","title":"attach and detach a custom domain","duration":120136.5131,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Domain.test.ts:99:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the domain name triggers replacement","status":"failed","title":"changing the domain name triggers replacement","duration":120183.71159999998,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Domain.test.ts:166:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates domains across all Pages projects","status":"skipped","title":"list enumerates domains across all Pages projects","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382660269,"endTime":1782382780454.7117,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Domain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a project with generated name","status":"failed","title":"create and delete a project with generated name","duration":33151.7055,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable props in place (same project id)","status":"failed","title":"update mutable props in place (same project id)","duration":120161.74089999999,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Project.test.ts:94:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed project","status":"failed","title":"list enumerates the deployed project","duration":120202.12139999999,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Project.test.ts:205:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the name triggers replacement","status":"failed","title":"changing the name triggers replacement","duration":120298.9586,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Project.test.ts:235:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]}],"startTime":1782382660230,"endTime":1782382780531.9585,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Project.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"legacy pipeline: create, in-place update, replace on name change","status":"skipped","title":"legacy pipeline: create, in-place update, replace on name change","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed legacy pipeline","status":"skipped","title":"list enumerates the deployed legacy pipeline","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/LegacyPipeline.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed pipeline","status":"skipped","title":"list enumerates the deployed pipeline","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/Pipeline.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"stream: create with defaults, patch http in place, replace on schema change","status":"skipped","title":"stream: create with defaults, patch http in place, replace on schema change","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"end-to-end: stream → sql pipeline → r2 sink, replacement on sink change","status":"skipped","title":"end-to-end: stream → sql pipeline → r2 sink, replacement on sink change","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/Pipelines.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed sink","status":"skipped","title":"list enumerates the deployed sink","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/Sink.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed pipeline stream","status":"skipped","title":"list enumerates the deployed pipeline stream","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/Stream.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete bucket with default props","status":"failed","title":"create and delete bucket with default props","duration":28668.861999999997,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete bucket","status":"failed","title":"create, update, delete bucket","duration":28232.001600000003,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"existing bucket (matching name) is silently adopted without --adopt","status":"failed","title":"existing bucket (matching name) is silently adopted without --adopt","duration":28439.722100000003,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"destroying a bucket empties its objects first","status":"failed","title":"destroying a bucket empties its objects first","duration":29490.6127,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"lifecycle rules are added, updated, and removed","status":"failed","title":"lifecycle rules are added, updated, and removed","duration":28295.7132,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782382681804,"endTime":1782382711298.6128,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/Bucket.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update rules in place, and delete","status":"failed","title":"create, update rules in place, and delete","duration":27989.5262,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the queue triggers a replacement","status":"failed","title":"changing the queue triggers a replacement","duration":28032.030199999997,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates deployed bucket event notifications","status":"failed","title":"list enumerates deployed bucket event notifications","duration":28056.4922,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782382686068,"endTime":1782382714126.4922,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/BucketEventNotification.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"reads the disabled baseline and surfaces typed errors without external creds","status":"skipped","title":"reads the disabled baseline and surfaces typed errors without external creds","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates buckets that have Sippy enabled","status":"skipped","title":"list enumerates buckets that have Sippy enabled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a bucket with Sippy enabled","status":"skipped","title":"list includes a bucket with Sippy enabled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"enable, no-op redeploy, disable on destroy","status":"skipped","title":"enable, no-op redeploy, disable on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/BucketSippy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates, updates, and deletes a bucket custom domain","status":"skipped","title":"creates, updates, and deletes a bucket custom domain","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates, updates, and deletes a bucket with multiple custom domains","status":"skipped","title":"creates, updates, and deletes a bucket with multiple custom domains","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/CustomDomain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed R2 bucket","status":"skipped","title":"list enumerates the deployed R2 bucket","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/R2Bucket.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"promotes a dev queue to a live queue on deploy","status":"passed","title":"promotes a dev queue to a live queue on deploy","duration":4990.217800000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed queue","status":"passed","title":"list enumerates the deployed queue","duration":5314.154499999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"suppresses deletion of a dev-only queue","status":"passed","title":"suppresses deletion of a dev-only queue","duration":1604.9422000000013,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382808424,"endTime":1782382813740.1545,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/Queue.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update settings, replace script, delete","status":"passed","title":"create, update settings, replace script, delete","duration":21177.857799999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates consumer after out-of-band delete","status":"failed","title":"recreates consumer after out-of-band delete","duration":6699.3411000000015,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopts existing consumer after local state loss","status":"passed","title":"adopts existing consumer after local state loss","duration":11673.910199999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"fails clearly when queue has consumer for different script","status":"failed","title":"fails clearly when queue has consumer for different script","duration":8283.004400000002,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"suppresses deletion of a dev-only consumer","status":"passed","title":"suppresses deletion of a dev-only consumer","duration":2669.449700000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"promotes a dev consumer to a live consumer on deploy","status":"failed","title":"promotes a dev consumer to a live consumer on deploy","duration":4878.221400000002,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed consumer","status":"failed","title":"list enumerates the deployed consumer","duration":2766.5062000000034,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782382714356,"endTime":1782382735533.858,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/QueueConsumer.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"send → subscribe handler → DO state → polled by test client","status":"failed","title":"send → subscribe handler → DO state → polled by test client","duration":4898.894899999999,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782382777545,"endTime":1782382782443.895,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/RoundTrip.test.ts"},{"assertionResults":[{"ancestorTitles":["Subscription"],"fullName":"Subscription create r2 event subscription into a queue and destroy it","status":"skipped","title":"create r2 event subscription into a queue and destroy it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Subscription"],"fullName":"Subscription update mutable props in place (same subscriptionId)","status":"passed","title":"update mutable props in place (same subscriptionId)","duration":7110.789099999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Subscription"],"fullName":"Subscription replaces the subscription when the source changes","status":"passed","title":"replaces the subscription when the source changes","duration":4231.671100000007,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Subscription"],"fullName":"Subscription list enumerates the deployed subscription","status":"passed","title":"list enumerates the deployed subscription","duration":3345.6131000000023,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382806565,"endTime":1782382821254.613,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/Subscription.test.ts"},{"assertionResults":[{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings passes scalar fields through unchanged","status":"passed","title":"passes scalar fields through unchanged","duration":13.954699999998411,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings converts maxWaitTime to whole milliseconds","status":"passed","title":"converts maxWaitTime to whole milliseconds","duration":11.917700000001787,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings rounds sub-millisecond maxWaitTime up","status":"passed","title":"rounds sub-millisecond maxWaitTime up","duration":11.66820000000007,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings converts retryDelay to whole seconds","status":"passed","title":"converts retryDelay to whole seconds","duration":11.576900000000023,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings rounds partial-second retryDelay up","status":"passed","title":"rounds partial-second retryDelay up","duration":11.483799999998155,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings leaves missing time fields undefined","status":"passed","title":"leaves missing time fields undefined","duration":1.4622000000017579,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382849660,"endTime":1782382849676.4622,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/toConsumerSettings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed PolicyQuotaExceeded error on unentitled zones","status":"skipped","title":"surfaces the typed PolicyQuotaExceeded error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates Page Shield policies across all zones","status":"passed","title":"list enumerates Page Shield policies across all zones","duration":2065.4759000000013,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates, updates in place, and deletes a CSP policy","status":"skipped","title":"creates, updates in place, and deletes a CSP policy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382810675,"endTime":1782382812740.4758,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/PageShield/Policy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"enables Page Shield, updates in place, and restores the baseline on destroy","status":"skipped","title":"enables Page Shield, updates in place, and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"surfaces the typed NotEntitled error for plan-gated flags","status":"skipped","title":"surfaces the typed NotEntitled error for plan-gated flags","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates Page Shield settings across all zones","status":"skipped","title":"list enumerates Page Shield settings across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/PageShield/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"enable, sync maintenance config, register credential, destroy","status":"failed","title":"enable, sync maintenance config, register credential, destroy","duration":27428.961099999997,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed catalog","status":"failed","title":"list enumerates the deployed catalog","duration":27400.3852,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"re-enables after out-of-band disable","status":"failed","title":"re-enables after out-of-band disable","duration":27331.7692,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782382685824,"endTime":1782382713252.9612,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2DataCatalog/R2DataCatalog.test.ts"},{"assertionResults":[{"ancestorTitles":["async worker (env binding)"],"fullName":"async worker (env binding) throttles requests past the configured limit","status":"skipped","title":"throttles requests past the configured limit","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["async worker (env binding)"],"fullName":"async worker (env binding) tracks each key independently","status":"skipped","title":"tracks each key independently","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["effect worker (yield* RateLimit)"],"fullName":"effect worker (yield* RateLimit) throttles requests past the configured limit","status":"skipped","title":"throttles requests past the configured limit","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["effect worker (yield* RateLimit)"],"fullName":"effect worker (yield* RateLimit) tracks each key independently","status":"skipped","title":"tracks each key independently","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RateLimit/RateLimit.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden error","status":"passed","title":"unentitled accounts surface the typed Forbidden error","duration":55900.173,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create (or adopt), verify out-of-band, destroy forgets but keeps the app","status":"passed","title":"create (or adopt), verify out-of-band, destroy forgets but keeps the app","duration":31063.218899999996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed RealtimeKit app","status":"passed","title":"list enumerates the deployed RealtimeKit app","duration":31356.5192,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382782001,"endTime":1782382837901.173,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RealtimeKit/App.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create with defaults, verify out-of-band, update in place, destroy","status":"passed","title":"create with defaults, verify out-of-band, update in place, destroy","duration":29755.6592,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed preset","status":"passed","title":"list enumerates the deployed preset","duration":29665.899799999996,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382789221,"endTime":1782382818976.6592,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RealtimeKit/Preset.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, update in place, destroy","status":"passed","title":"create, verify out-of-band, update in place, destroy","duration":30752.274599999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates webhooks across the account's apps","status":"passed","title":"list enumerates webhooks across the account's apps","duration":56094.7337,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382781698,"endTime":1782382837794.7336,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RealtimeKit/Webhook.test.ts"},{"assertionResults":[{"ancestorTitles":["Domain"],"fullName":"Domain adopts a registered domain, no-op syncs, and never releases it on destroy","status":"skipped","title":"adopts a registered domain, no-op syncs, and never releases it on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Domain"],"fullName":"Domain updates settings in place and restores the baseline on destroy","status":"skipped","title":"updates settings in place and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Domain"],"fullName":"Domain list enumerates registrar domains on the account","status":"skipped","title":"list enumerates registrar domains on the account","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Registrar/Domain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"reads succeed and write-blocked accounts surface the typed Forbidden error","status":"skipped","title":"reads succeed and write-blocked accounts surface the typed Forbidden error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the account's sent shares (typed Attributes), and includes a deployed share when entitled","status":"skipped","title":"list enumerates the account's sent shares (typed Attributes), and includes a deployed share when entitled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a share with a gateway policy, renames in place, adds and removes a resource, and destroys","status":"skipped","title":"creates a share with a gateway policy, renames in place, adds and removes a resource, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"manages standalone ShareResource and ShareRecipient on an existing share","status":"skipped","title":"manages standalone ShareResource and ShareRecipient on an existing share","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ResourceSharing/Share.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list() enumerates share recipients across the account's sent shares","status":"passed","title":"list() enumerates share recipients across the account's sent shares","duration":31080.8393,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list() includes a freshly deployed ShareRecipient","status":"skipped","title":"list() includes a freshly deployed ShareRecipient","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382782161,"endTime":1782382813241.8394,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ResourceSharing/ShareRecipient.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates share resources across the account's sent shares (typed Attributes), and includes a deployed entry when entitled","status":"skipped","title":"list enumerates share resources across the account's sent shares (typed Attributes), and includes a deployed entry when entitled","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ResourceSharing/ShareResource.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"regional hostname lifecycle (typed error on unentitled zones)","status":"skipped","title":"regional hostname lifecycle (typed error on unentitled zones)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates regional hostnames across zones","status":"skipped","title":"list enumerates regional hostnames across zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RegionalHostname/RegionalHostname.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden error","status":"passed","title":"unentitled accounts surface the typed Forbidden error","duration":30768.231,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, toggle active in place, and destroy a risk scoring integration","status":"skipped","title":"create, toggle active in place, and destroy a risk scoring integration","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates risk scoring integrations","status":"passed","title":"list enumerates risk scoring integrations","duration":31165.992499999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a freshly deployed integration","status":"skipped","title":"list includes a freshly deployed integration","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382791402,"endTime":1782382822569.9924,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RiskScoring/Integration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"account entrypoint lifecycle (typed PhaseNotEntitled on unentitled accounts)","status":"skipped","title":"account entrypoint lifecycle (typed PhaseNotEntitled on unentitled accounts)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the account phase entrypoints","status":"skipped","title":"list enumerates the account phase entrypoints","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ruleset/AccountEntrypoint.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"custom ruleset lifecycle (typed PhaseNotEntitled on unentitled accounts)","status":"skipped","title":"custom ruleset lifecycle (typed PhaseNotEntitled on unentitled accounts)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed custom ruleset","status":"skipped","title":"list enumerates the deployed custom ruleset","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ruleset/CustomRuleset.test.ts"},{"assertionResults":[{"ancestorTitles":["Ruleset"],"fullName":"Ruleset creates, updates, and deletes a zone phase entrypoint ruleset","status":"skipped","title":"creates, updates, and deletes a zone phase entrypoint ruleset","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Ruleset"],"fullName":"Ruleset creates and tears down a ruleset whose zone is provisioned in the same deploy","status":"skipped","title":"creates and tears down a ruleset whose zone is provisioned in the same deploy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Ruleset"],"fullName":"Ruleset list enumerates the deployed zone phase entrypoint","status":"skipped","title":"list enumerates the deployed zone phase entrypoint","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ruleset/Ruleset.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an ip list with default name","status":"skipped","title":"create and delete an ip list with default name","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update description and items in place (same listId)","status":"skipped","title":"update description and items in place (same listId)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing kind replaces the list","status":"skipped","title":"changing kind replaces the list","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopts an existing list with the same name","status":"skipped","title":"adopts an existing list with the same name","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Rules/List.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"sets a per-operation override, updates it in place, and clears it on destroy","status":"skipped","title":"sets a per-operation override, updates it in place, and clears it on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed per-operation override","status":"skipped","title":"list enumerates the deployed per-operation override","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SchemaValidation/OperationSetting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"uploads a schema, enables in place, replaces on disable and source change, destroys","status":"skipped","title":"uploads a schema, enables in place, replaces on disable and source change, destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed schema across all zones","status":"skipped","title":"list enumerates the deployed schema across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SchemaValidation/Schema.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins zone settings, updates in place, and restores the baseline on destroy","status":"skipped","title":"pins zone settings, updates in place, and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the schema validation settings across all zones","status":"skipped","title":"list enumerates the schema validation settings across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SchemaValidation/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":["RumRule"],"fullName":"RumRule create, update in place, and delete a rule","status":"skipped","title":"create, update in place, and delete a rule","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RumRule"],"fullName":"RumRule list enumerates rules across all rulesets","status":"skipped","title":"list enumerates rules across all rulesets","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RumRule"],"fullName":"RumRule recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Rum/Rule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a host (gray-clouded) site","status":"skipped","title":"create and delete a host (gray-clouded) site","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable props in place (same siteTag)","status":"skipped","title":"update mutable props in place (same siteTag)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"zone (orange-clouded) site with autoInstall, replaced on identity flip","status":"skipped","title":"zone (orange-clouded) site with autoInstall, replaced on identity flip","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed RUM site","status":"skipped","title":"list enumerates the deployed RUM site","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Rum/Site.test.ts"},{"assertionResults":[{"ancestorTitles":["SecurityTxt"],"fullName":"SecurityTxt creates a security.txt, verifies out-of-band, and deletes on destroy","status":"skipped","title":"creates a security.txt, verifies out-of-band, and deletes on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SecurityTxt"],"fullName":"SecurityTxt updates mutable fields in place (full-replace PUT)","status":"skipped","title":"updates mutable fields in place (full-replace PUT)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SecurityTxt"],"fullName":"SecurityTxt disables the file without deleting it, then destroy removes it","status":"skipped","title":"disables the file without deleting it, then destroy removes it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SecurityTxt"],"fullName":"SecurityTxt list enumerates configured security.txt files","status":"skipped","title":"list enumerates configured security.txt files","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SecurityTxt/SecurityTxt.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"SecretsStore Secret declared on a Worker's env round-trips to runtime as a SecretsStoreSecret","status":"skipped","title":"SecretsStore Secret declared on a Worker's env round-trips to runtime as a SecretsStoreSecret","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SecretsStore/AsyncSecretBinding.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed secret across stores","status":"failed","title":"list enumerates the deployed secret across stores","duration":26590.6654,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782382711244,"endTime":1782382737834.6653,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SecretsStore/Secret.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"createStore POSTs a single JSON object body (regression: invalid_json_body)","status":"passed","title":"createStore POSTs a single JSON object body (regression: invalid_json_body)","duration":301.8819000000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"createStore surfaces MaximumStoresExceeded as a tagged error so callers can catchTag it","status":"passed","title":"createStore surfaces MaximumStoresExceeded as a tagged error so callers can catchTag it","duration":299.25820000000385,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed secrets store","status":"skipped","title":"list enumerates the deployed secrets store","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382869432,"endTime":1782382869734.2583,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SecretsStore/SecretsStore.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"Config.redacted with literal default round-trips to runtime as Redacted","status":"skipped","title":"Config.redacted with literal default round-trips to runtime as Redacted","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Config.redacted resolved from env deploys as a secret_text and round-trips","status":"skipped","title":"Config.redacted resolved from env deploys as a secret_text and round-trips","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Config.string round-trips to runtime as a string","status":"skipped","title":"Config.string round-trips to runtime as a string","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Config.number round-trips to runtime preserving the number type","status":"skipped","title":"Config.number round-trips to runtime preserving the number type","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Config.string with object default round-trips to runtime preserving nested shape","status":"skipped","title":"Config.string with object default round-trips to runtime preserving nested shape","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Secret/Secret.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed SpectrumProtocolNotAvailable error on unentitled zones","status":"skipped","title":"surfaces the typed SpectrumProtocolNotAvailable error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a tcp/22 application","status":"skipped","title":"create, update in place, and destroy a tcp/22 application","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing app errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing app errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates Spectrum applications across all zones","status":"passed","title":"list enumerates Spectrum applications across all zones","duration":1738.9078999999983,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list surfaces a deployed Spectrum application (entitled zone)","status":"skipped","title":"list surfaces a deployed Spectrum application (entitled zone)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382829391,"endTime":1782382831129.908,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Spectrum/Application.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a scheduled speed test","status":"skipped","title":"create and delete a scheduled speed test","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the frequency converges in place","status":"skipped","title":"changing the frequency converges in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the region triggers replacement","status":"skipped","title":"changing the region triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing schedule errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing schedule errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed schedule across zones","status":"skipped","title":"list enumerates the deployed schedule across zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Speed/TestSchedule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a live input","status":"skipped","title":"create, update in place, and delete a live input","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed live input","status":"skipped","title":"list enumerates the deployed live input","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/LiveInput.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, toggle enabled in place, replace destination, and delete","status":"skipped","title":"create, toggle enabled in place, replace destination, and delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates outputs across all live inputs","status":"skipped","title":"list enumerates outputs across all live inputs","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/LiveInputOutput.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a signing key","status":"skipped","title":"create and delete a signing key","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed signing key","status":"skipped","title":"list enumerates the deployed signing key","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/SigningKey.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a watermark with default name","status":"skipped","title":"create and delete a watermark with default name","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"prop change replaces the watermark (create-only resource)","status":"skipped","title":"prop change replaces the watermark (create-only resource)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed watermark","status":"skipped","title":"list enumerates the deployed watermark","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/Watermark.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"configure, update, and delete the account webhook","status":"skipped","title":"configure, update, and delete the account webhook","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the account Stream webhook","status":"skipped","title":"list enumerates the account Stream webhook","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/Webhook.test.ts"},{"assertionResults":[{"ancestorTitles":["State"],"fullName":"State getVersion returns the current STATE_STORE_VERSION","status":"failed","title":"getVersion returns the current STATE_STORE_VERSION","duration":5138.530300000002,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State DELETE /state/stacks/:stack wipes the test namespace (cleanup)","status":"failed","title":"DELETE /state/stacks/:stack wipes the test namespace (cleanup)","duration":1493.1189000000013,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State PUT /resources/:fqn (setState) persists a resource","status":"failed","title":"PUT /resources/:fqn (setState) persists a resource","duration":276.3897999999972,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /resources/:fqn (getState) reads back the persisted value","status":"failed","title":"GET /resources/:fqn (getState) reads back the persisted value","duration":575.9563999999955,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /resources/:fqn returns undefined for a missing fqn","status":"failed","title":"GET /resources/:fqn returns undefined for a missing fqn","duration":724.3649000000005,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /resources (listResources) returns the FQNs in the stage","status":"failed","title":"GET /resources (listResources) returns the FQNs in the stage","duration":272.49319999999716,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /stacks (listStacks) includes the registered test stack","status":"failed","title":"GET /stacks (listStacks) includes the registered test stack","duration":207.71719999999914,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /stacks/:stack/stages (listStages) includes the test stage","status":"failed","title":"GET /stacks/:stack/stages (listStages) includes the test stage","duration":199.14549999999872,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State PUT /output (setStackOutput) persists a stack output","status":"failed","title":"PUT /output (setStackOutput) persists a stack output","duration":207.61099999999715,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /output (getStackOutput) reads back the persisted output","status":"failed","title":"GET /output (getStackOutput) reads back the persisted output","duration":207.67459999999846,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /output returns undefined for an un-deployed stage","status":"failed","title":"GET /output returns undefined for an un-deployed stage","duration":211.13430000000517,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /replaced-resources (getReplacedResources) returns status===replaced rows","status":"failed","title":"GET /replaced-resources (getReplacedResources) returns status===replaced rows","duration":508.286500000002,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State DELETE /resources/:fqn (deleteState) removes a single resource","status":"failed","title":"DELETE /resources/:fqn (deleteState) removes a single resource","duration":204.87860000000364,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State DELETE /stacks/:stack?stage=... clears one stage but leaves the stack registered","status":"failed","title":"DELETE /stacks/:stack?stage=... clears one stage but leaves the stack registered","duration":196.2425000000003,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State DELETE /stacks/:stack (no stage) removes the stack from listStacks","status":"failed","title":"DELETE /stacks/:stack (no stage) removes the stack from listStacks","duration":217.06129999999393,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State setState 100x sequential — surfaces transient failures","status":"failed","title":"setState 100x sequential — surfaces transient failures","duration":644.4772999999986,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State setState 100x concurrent — surfaces racy transient failures","status":"failed","title":"setState 100x concurrent — surfaces racy transient failures","duration":436.0178999999989,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET+PUT interleaved 30x5 — engine traffic pattern","status":"failed","title":"GET+PUT interleaved 30x5 — engine traffic pattern","duration":207.9103000000032,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782382741389,"endTime":1782382753319.9104,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/StateStore/State.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","status":"skipped","title":"surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates advanced certificate packs across zones","status":"passed","title":"list enumerates advanced certificate packs across zones","duration":2041.7425000000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed advanced certificate pack","status":"skipped","title":"list includes a deployed advanced certificate pack","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"orders a pack, updates validation method in place, and deletes it","status":"skipped","title":"orders a pack, updates validation method in place, and deletes it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382830767,"endTime":1782382832808.7424,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ssl/CertificatePack.test.ts"},{"assertionResults":[{"ancestorTitles":["UniversalSsl"],"fullName":"UniversalSsl disables Universal SSL and restores the original value on destroy","status":"skipped","title":"disables Universal SSL and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UniversalSsl"],"fullName":"UniversalSsl updates enabled in place and keeps the captured initial value","status":"skipped","title":"updates enabled in place and keeps the captured initial value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UniversalSsl"],"fullName":"UniversalSsl destroy restores a disabled baseline when managing from a disabled zone","status":"skipped","title":"destroy restores a disabled baseline when managing from a disabled zone","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UniversalSsl"],"fullName":"UniversalSsl list enumerates the setting across all zones","status":"skipped","title":"list enumerates the setting across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ssl/UniversalSsl.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create with generated name, update code in place, destroy","status":"skipped","title":"create with generated name, update code in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"renaming an explicit snippet triggers replacement","status":"skipped","title":"renaming an explicit snippet triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed snippet","status":"skipped","title":"list enumerates the deployed snippet","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Snippets/Snippet.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"snippet rules — create, update, list, and destroy in dependency order","status":"skipped","title":"snippet rules — create, update, list, and destroy in dependency order","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Snippets/SnippetRules.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, and clear tags on a KV namespace","status":"failed","title":"create, update, and clear tags on a KV namespace","duration":35022.78199999999,"failureMessages":["Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time.\n at Proxy. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Output.ts:494:19)\n at String ()\n at setQueryValue (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:729:23)\n at Module.buildRequestParts (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:785:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:631:30)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing resourceId triggers replacement","status":"failed","title":"changing resourceId triggers replacement","duration":35046.240000000005,"failureMessages":["Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time.\n at Proxy. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Output.ts:494:19)\n at String ()\n at setQueryValue (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:729:23)\n at Module.buildRequestParts (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:785:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:631:30)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing tags error without adopt, take over with adopt(true)","status":"failed","title":"adoption — existing tags error without adopt, take over with adopt(true)","duration":120130.48240000001,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tags/AccountResourceTags.test.ts:189:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account-wide tagged resources","status":"failed","title":"list enumerates account-wide tagged resources","duration":33349.9323,"failureMessages":["Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time.\n at Proxy. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Output.ts:494:19)\n at String ()\n at setQueryValue (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:729:23)\n at Module.buildRequestParts (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:785:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:631:30)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)"],"meta":{},"tags":[]}],"startTime":1782382660259,"endTime":1782382780392.4824,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tags/AccountResourceTags.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, and clear tags on a DNS record","status":"skipped","title":"create, update, and clear tags on a DNS record","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"tag the zone itself and clear on destroy","status":"skipped","title":"tag the zone itself and clear on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates tagged zone-scoped resources","status":"skipped","title":"list enumerates tagged zone-scoped resources","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tags/ZoneResourceTags.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a typed array of token validation rules","status":"passed","title":"list returns a typed array of token validation rules","duration":4173.725899999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed token validation rule","status":"skipped","title":"list enumerates the deployed token validation rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382847581,"endTime":1782382851754.7258,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/TokenValidation/Rule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed TokenValidationNotEntitled error on unentitled accounts","status":"skipped","title":"surfaces the typed TokenValidationNotEntitled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates configurations across all zones","status":"passed","title":"list enumerates configurations across all zones","duration":1752.0923000000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"configuration + rule lifecycle: create, update in place, rotate keys, destroy","status":"skipped","title":"configuration + rule lifecycle: create, update in place, rotate keys, destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382811523,"endTime":1782382813275.0923,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/TokenValidation/TokenValidation.test.ts"},{"assertionResults":[{"ancestorTitles":["UrlNormalization"],"fullName":"UrlNormalization configures URL normalization and resets to defaults on destroy","status":"skipped","title":"configures URL normalization and resets to defaults on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UrlNormalization"],"fullName":"UrlNormalization updates scope and type in place","status":"skipped","title":"updates scope and type in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UrlNormalization"],"fullName":"UrlNormalization applies Cloudflare defaults when scope and type are omitted","status":"skipped","title":"applies Cloudflare defaults when scope and type are omitted","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UrlNormalization"],"fullName":"UrlNormalization list enumerates URL normalization across all zones","status":"skipped","title":"list enumerates URL normalization across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/UrlNormalization/UrlNormalization.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a widget with default name","status":"failed","title":"create and delete a widget with default name","duration":27812.826299999997,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable props in place (same sitekey)","status":"failed","title":"update mutable props in place (same sitekey)","duration":27161.5079,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":28948.3407,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed widget","status":"failed","title":"list enumerates the deployed widget","duration":28044.6857,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782382710499,"endTime":1782382739450.3408,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Turnstile/Widget.test.ts"},{"assertionResults":[{"ancestorTitles":["Tunnel Configuration"],"fullName":"Tunnel Configuration list enumerates configurations across all tunnels","status":"skipped","title":"list enumerates configurations across all tunnels","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/Configuration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update comment in place, and destroy a hostname route","status":"skipped","title":"create, update comment in place, and destroy a hostname route","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed hostname route","status":"skipped","title":"list enumerates the deployed hostname route","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/HostnameRoute.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete route with default props","status":"skipped","title":"create and delete route with default props","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating the comment patches in place","status":"skipped","title":"updating the comment patches in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopt: takes over a pre-existing route","status":"skipped","title":"adopt: takes over a pre-existing route","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed route","status":"skipped","title":"list enumerates the deployed route","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/Route.test.ts"},{"assertionResults":[{"ancestorTitles":["Tunnel runtime bindings"],"fullName":"Tunnel runtime bindings TunnelRead lists tunnels with a read-scoped token","status":"skipped","title":"TunnelRead lists tunnels with a read-scoped token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Tunnel runtime bindings"],"fullName":"Tunnel runtime bindings TunnelWrite creates and deletes a tunnel with a write-scoped token","status":"skipped","title":"TunnelWrite creates and deletes a tunnel with a write-scoped token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Tunnel runtime bindings"],"fullName":"Tunnel runtime bindings TunnelReadWrite drives the full CRUD surface","status":"skipped","title":"TunnelReadWrite drives the full CRUD surface","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Tunnel.list"],"fullName":"Tunnel.list list enumerates the deployed tunnel","status":"skipped","title":"list enumerates the deployed tunnel","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/Tunnel.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy a virtual network","status":"skipped","title":"create, verify, and destroy a virtual network","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name and comment in place (same id)","status":"skipped","title":"update name and comment in place (same id)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed virtual network","status":"skipped","title":"list enumerates the deployed virtual network","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/VirtualNetwork.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, rename in place, and destroy a WARP Connector tunnel","status":"skipped","title":"create, rename in place, and destroy a WARP Connector tunnel","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed WARP Connector tunnel","status":"skipped","title":"list enumerates the deployed WARP Connector tunnel","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/WarpConnector.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete index with explicit dimensions","status":"skipped","title":"create and delete index with explicit dimensions","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create index from a preset","status":"skipped","title":"create index from a preset","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces index when dimensions change","status":"skipped","title":"replaces index when dimensions change","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed index","status":"skipped","title":"list enumerates the deployed index","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Vectorize/VectorizeIndex.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"VectorizeIndex.bind exercises the client surface","status":"skipped","title":"VectorizeIndex.bind exercises the client surface","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Vectorize/VectorizeIndexBinding.test.ts"},{"assertionResults":[{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex create and delete a metadata index","status":"skipped","title":"create and delete a metadata index","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex multiple metadata indexes on the same parent coexist","status":"skipped","title":"multiple metadata indexes on the same parent coexist","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex replacing the parent index also replaces the metadata index","status":"skipped","title":"replacing the parent index also replaces the metadata index","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex list enumerates the deployed metadata index","status":"skipped","title":"list enumerates the deployed metadata index","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex destroy is idempotent when the parent index was deleted out-of-band","status":"skipped","title":"destroy is idempotent when the parent index was deleted out-of-band","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Vectorize/VectorizeMetadataIndex.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete vpc service","status":"skipped","title":"create, update, delete vpc service","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create vpc service with ipv4 host","status":"skipped","title":"create vpc service with ipv4 host","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create vpc service with dual-stack host","status":"skipped","title":"create vpc service with dual-stack host","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed vpc service","status":"skipped","title":"list enumerates the deployed vpc service","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VpcService/VpcService.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"reference vpc service by name and by id","status":"skipped","title":"reference vpc service by name and by id","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VpcService/VpcServiceRef.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed credential","status":"skipped","title":"list enumerates the deployed credential","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VulnerabilityScanner/Credential.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, rename, and destroy a credential set","status":"skipped","title":"create, rename, and destroy a credential set","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed credential set","status":"skipped","title":"list enumerates the deployed credential set","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"credential lifecycle — create, mutate in place, rotate value, destroy","status":"skipped","title":"credential lifecycle — create, mutate in place, rotate value, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"moving a credential to a different set triggers replacement","status":"skipped","title":"moving a credential to a different set triggers replacement","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VulnerabilityScanner/CredentialSet.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy a target environment","status":"skipped","title":"create, verify, and destroy a target environment","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating name and description patches in place; clearing description","status":"skipped","title":"updating name and description patches in place; clearing description","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed target environment","status":"skipped","title":"list enumerates the deployed target environment","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VulnerabilityScanner/TargetEnvironment.test.ts"},{"assertionResults":[{"ancestorTitles":["Settings"],"fullName":"Settings pins the settings to the default baseline without touching the API","status":"skipped","title":"pins the settings to the default baseline without touching the API","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Settings"],"fullName":"Settings surfaces the typed ZoneNotEntitled error when enabling on unentitled zones","status":"skipped","title":"surfaces the typed ZoneNotEntitled error when enabling on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Settings"],"fullName":"Settings enables the crawler bypass and restores the original value on destroy","status":"skipped","title":"enables the crawler bypass and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Settings"],"fullName":"Settings list enumerates the settings across all zones","status":"skipped","title":"list enumerates the settings across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/WaitingRoom/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed ZoneNotEntitled error on unentitled zones","status":"skipped","title":"surfaces the typed ZoneNotEntitled error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a waiting room","status":"skipped","title":"create, update in place, and destroy a waiting room","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates waiting rooms across zones","status":"passed","title":"list enumerates waiting rooms across zones","duration":2244.9351000000024,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382830062,"endTime":1782382832306.935,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/WaitingRoom/WaitingRoom.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a well-typed array of web3 content lists","status":"passed","title":"list returns a well-typed array of web3 content lists","duration":3370.4586000000018,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list surfaces a deployed web3 content list (entitled account)","status":"skipped","title":"list surfaces a deployed web3 content list (entitled account)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382848270,"endTime":1782382851640.4585,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Web3/ContentList.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a well-typed array of web3 hostnames","status":"passed","title":"list returns a well-typed array of web3 hostnames","duration":3432.4601999999977,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list surfaces a deployed web3 hostname (entitled account)","status":"skipped","title":"list surfaces a deployed web3 hostname (entitled account)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382850835,"endTime":1782382854267.4602,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Web3/Hostname.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed Web3HostnameNotEntitled error on unentitled accounts","status":"skipped","title":"surfaces the typed Web3HostnameNotEntitled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, replace on target change, destroy","status":"skipped","title":"create, update in place, replace on target change, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"content list — set entries, update declaratively, reset on destroy","status":"skipped","title":"content list — set entries, update declaratively, reset on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Web3/Web3Hostname.test.ts"},{"assertionResults":[{"ancestorTitles":["ZarazConfig"],"fullName":"ZarazConfig updates and retains a zone-level Zaraz config","status":"skipped","title":"updates and retains a zone-level Zaraz config","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ZarazConfig"],"fullName":"ZarazConfig delete true resets Zaraz config to defaults","status":"skipped","title":"delete true resets Zaraz config to defaults","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ZarazConfig"],"fullName":"ZarazConfig updates and retains Zaraz workflow mode","status":"skipped","title":"updates and retains Zaraz workflow mode","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ZarazConfig"],"fullName":"ZarazConfig list enumerates Zaraz config across all zones","status":"skipped","title":"list enumerates Zaraz config across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zaraz/ZarazConfig.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"Zaraz event contracts are type-only","status":"passed","title":"Zaraz event contracts are type-only","duration":12.230799999999363,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382838665,"endTime":1782382838677.2307,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zaraz/ZarazEventTypes.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, replace, and destroy a dispatch namespace","status":"skipped","title":"create, replace, and destroy a dispatch namespace","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"upload, update in place, and destroy a namespace script","status":"skipped","title":"upload, update in place, and destroy a namespace script","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed dispatch namespace","status":"skipped","title":"list enumerates the deployed dispatch namespace","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/WorkersForPlatforms/DispatchNamespace.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"StaticSite: editing a source file republishes the assets in a single deploy","status":"failed","title":"StaticSite: editing a source file republishes the assets in a single deploy","duration":17941.9099,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"StaticSite: class form deploys and serves the built assets","status":"failed","title":"StaticSite: class form deploys and serves the built assets","duration":17348.656000000003,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"StaticSite: relocating the project (and deleting the old one) preserves hash.assets","status":"failed","title":"StaticSite: relocating the project (and deleting the old one) preserves hash.assets","duration":17298.960399999996,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"StaticSite: a bundle-only change keeps the asset manifest (hash.assets stable)","status":"failed","title":"StaticSite: a bundle-only change keeps the asset manifest (hash.assets stable)","duration":18664.8512,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782382786715,"endTime":1782382805381.8513,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Website/StaticSite.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"Vite: editing a source file republishes the assets in a single deploy","status":"passed","title":"Vite: editing a source file republishes the assets in a single deploy","duration":47355.6131,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Vite: class form deploys and serves the built assets","status":"failed","title":"Vite: class form deploys and serves the built assets","duration":6277.987599999997,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Vite: relocating rootDir (and deleting the old one) is a no-op when sources are identical","status":"passed","title":"Vite: relocating rootDir (and deleting the old one) is a no-op when sources are identical","duration":46085.5475,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Vite: `env` props are inlined as `import.meta.env.*` into the bundle","status":"passed","title":"Vite: `env` props are inlined as `import.meta.env.*` into the bundle","duration":27285.3872,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382755261,"endTime":1782382802616.613,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Website/Vite.test.ts"},{"assertionResults":[{"ancestorTitles":["AccountSetting"],"fullName":"AccountSetting flips green compute, updates in place, and restores the baseline on destroy","status":"failed","title":"flips green compute, updates in place, and restores the baseline on destroy","duration":32914.87329999999,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":["AccountSetting"],"fullName":"AccountSetting no-op deploy when desired settings already match the live account","status":"passed","title":"no-op deploy when desired settings already match the live account","duration":1003.8041999999987,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AccountSetting"],"fullName":"AccountSetting list returns the account settings singleton","status":"passed","title":"list returns the account settings singleton","duration":275.6771000000008,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382660105,"endTime":1782382694299.677,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/AccountSetting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed worker fires the scheduled handler on its cron trigger","status":"skipped","title":"deployed worker fires the scheduled handler on its cron trigger","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/CronEventSource.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"Drizzle.postgres query runs inside a Workflow task (ExecutionContext provided per run)","status":"skipped","title":"Drizzle.postgres query runs inside a Workflow task (ExecutionContext provided per run)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/DrizzleWorkflow.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"durable object methods can use binding clients","status":"skipped","title":"durable object methods can use binding clients","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"tick streams sequential values from a durable object (tutorial repro)","status":"skipped","title":"tick streams sequential values from a durable object (tutorial repro)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"async worker durable object binding accepts scriptName","status":"skipped","title":"async worker durable object binding accepts scriptName","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"durable object class migrations across redeploys","status":"skipped","title":"durable object class migrations across redeploys","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/DurableObjectNamespace.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed http-api worker handles createTask + getTask via typed HttpApiClient","status":"skipped","title":"deployed http-api worker handles createTask + getTask via typed HttpApiClient","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"cors middleware adds Access-Control-Allow-Origin header on preflight","status":"skipped","title":"cors middleware adds Access-Control-Allow-Origin header on preflight","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"cors middleware adds Access-Control-Allow-Origin header on actual requests","status":"skipped","title":"cors middleware adds Access-Control-Allow-Origin header on actual requests","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"concurrent createTask survives scope-lifecycle pressure","status":"skipped","title":"concurrent createTask survives scope-lifecycle pressure","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"createTaskDO + getTaskDO round-trip 100x in parallel through the DO HttpApi","status":"skipped","title":"createTaskDO + getTaskDO round-trip 100x in parallel through the DO HttpApi","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/HttpApi.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a destination with default name","status":"skipped","title":"create and delete a destination with default name","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update url, headers, and enabled in place (same slug)","status":"failed","title":"update url, headers, and enabled in place (same slug)","duration":300252.9654,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/ObservabilityDestination.test.ts:131:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replacement on name change (new slug, old destination removed)","status":"failed","title":"replacement on name change (new slug, old destination removed)","duration":300386.8048,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/ObservabilityDestination.test.ts:237:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed destination","status":"failed","title":"list enumerates the deployed destination","duration":300414.9423,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/ObservabilityDestination.test.ts:287:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]}],"startTime":1782382660092,"endTime":1782382960508.9424,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/ObservabilityDestination.test.ts"},{"assertionResults":[{"ancestorTitles":["Cloudflare.Worker with bundle: false"],"fullName":"Cloudflare.Worker with bundle: false uploads a prebuilt module graph byte-for-byte","status":"failed","title":"uploads a prebuilt module graph byte-for-byte","duration":3794.405499999997,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782382715981,"endTime":1782382719775.4055,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/PrebuiltWorker.test.ts"},{"assertionResults":[{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle collects modules matching the default rules, entry first, names POSIX-relative to the entry's directory","status":"passed","title":"collects modules matching the default rules, entry first, names POSIX-relative to the entry's directory","duration":2280.368999999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle uploads file contents byte-for-byte","status":"passed","title":"uploads file contents byte-for-byte","duration":1040.1988000000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle does not walk outside the entry's directory","status":"passed","title":"does not walk outside the entry's directory","duration":1020.4724000000006,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle custom rules replace the defaults","status":"passed","title":"custom rules replace the defaults","duration":1034.929,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle hash is stable across reads and sensitive to module changes","status":"passed","title":"hash is stable across reads and sensitive to module changes","duration":1044.6435000000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle fails with BundleError when the entry does not exist","status":"passed","title":"fails with BundleError when the entry does not exist","duration":16.566999999999098,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382830042,"endTime":1782382832322.369,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/PrebuiltWorkerBundle.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an opt-out route (no script)","status":"skipped","title":"create and delete an opt-out route (no script)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"route to a Worker, then update pattern and script in place","status":"skipped","title":"route to a Worker, then update pattern and script in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing route errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing route errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed route across all zones","status":"skipped","title":"list enumerates the deployed route across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Route.test.ts"},{"assertionResults":[{"ancestorTitles":["RpcDecodeError"],"fullName":"RpcDecodeError message delegates to Error.message when cause is an Error","status":"passed","title":"message delegates to Error.message when cause is an Error","duration":288.6835000000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RpcDecodeError"],"fullName":"RpcDecodeError message stringifies non-Error cause","status":"passed","title":"message stringifies non-Error cause","duration":286.9521000000004,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RpcCallError"],"fullName":"RpcCallError message includes method name and Error.message","status":"passed","title":"message includes method name and Error.message","duration":286.79659999999967,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RpcCallError"],"fullName":"RpcCallError message stringifies non-Error cause","status":"passed","title":"message stringifies non-Error cause","duration":286.6246999999994,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcErrorEnvelope"],"fullName":"isRpcErrorEnvelope detects valid envelope","status":"passed","title":"detects valid envelope","duration":286.4919,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcErrorEnvelope"],"fullName":"isRpcErrorEnvelope rejects non-envelope values","status":"passed","title":"rejects non-envelope values","duration":289.0571,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcStreamEnvelope"],"fullName":"isRpcStreamEnvelope detects valid bytes envelope","status":"passed","title":"detects valid bytes envelope","duration":288.97439999999915,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcStreamEnvelope"],"fullName":"isRpcStreamEnvelope detects valid jsonl envelope","status":"passed","title":"detects valid jsonl envelope","duration":288.9940999999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcStreamEnvelope"],"fullName":"isRpcStreamEnvelope rejects missing or wrong fields","status":"passed","title":"rejects missing or wrong fields","duration":288.90900000000056,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["encodeRpcError"],"fullName":"encodeRpcError preserves tagged error fields","status":"passed","title":"preserves tagged error fields","duration":288.8042999999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["encodeRpcError"],"fullName":"encodeRpcError normalizes plain Error to name/message/stack","status":"passed","title":"normalizes plain Error to name/message/stack","duration":289.4885999999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["encodeRpcError"],"fullName":"encodeRpcError passes through primitives","status":"passed","title":"passes through primitives","duration":289.4211000000005,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["encodeRpcError"],"fullName":"encodeRpcError passes through plain objects","status":"passed","title":"passes through plain objects","duration":289.3612000000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcReadableStream"],"fullName":"fromRpcReadableStream bytes encoding passes raw Uint8Array chunks through","status":"passed","title":"bytes encoding passes raw Uint8Array chunks through","duration":168.65480000000025,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcReadableStream"],"fullName":"fromRpcReadableStream jsonl encoding parses JSON lines","status":"passed","title":"jsonl encoding parses JSON lines","duration":168.84959999999955,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcReadableStream"],"fullName":"fromRpcReadableStream jsonl encoding produces RpcDecodeError on malformed JSON","status":"passed","title":"jsonl encoding produces RpcDecodeError on malformed JSON","duration":168.28659999999945,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcReadableStream"],"fullName":"fromRpcReadableStream jsonl encoding skips empty lines","status":"passed","title":"jsonl encoding skips empty lines","duration":171.63850000000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcStreamEnvelope"],"fullName":"fromRpcStreamEnvelope delegates to fromRpcReadableStream","status":"passed","title":"delegates to fromRpcReadableStream","duration":171.59910000000036,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcValue"],"fullName":"decodeRpcValue passes through plain values","status":"passed","title":"passes through plain values","duration":45.456600000000435,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcValue"],"fullName":"decodeRpcValue converts stream envelope to Effect Stream","status":"passed","title":"converts stream envelope to Effect Stream","duration":46.26770000000033,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcValue"],"fullName":"decodeRpcValue converts bare ReadableStream to bytes Effect Stream","status":"passed","title":"converts bare ReadableStream to bytes Effect Stream","duration":46.21230000000014,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult succeeds for plain values","status":"passed","title":"succeeds for plain values","duration":45.886099999999715,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult succeeds for numeric values","status":"passed","title":"succeeds for numeric values","duration":45.868900000000394,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult fails for error envelopes with tagged error","status":"passed","title":"fails for error envelopes with tagged error","duration":46.10999999999967,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult fails with plain Error shape for error envelopes","status":"passed","title":"fails with plain Error shape for error envelopes","duration":46.158400000000256,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult wraps stream envelopes in succeed (stream passthrough)","status":"passed","title":"wraps stream envelopes in succeed (stream passthrough)","duration":46.77800000000025,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream selects jsonl encoding for non-byte data","status":"passed","title":"selects jsonl encoding for non-byte data","duration":36.371399999999994,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream roundtrips a single-element jsonl stream","status":"passed","title":"roundtrips a single-element jsonl stream","duration":54.15569999999934,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream selects bytes encoding for Uint8Array data","status":"passed","title":"selects bytes encoding for Uint8Array data","duration":37.053499999999985,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream roundtrips a single-element bytes stream","status":"passed","title":"roundtrips a single-element bytes stream","duration":56.89730000000054,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream handles empty stream as jsonl","status":"passed","title":"handles empty stream as jsonl","duration":57.76209999999992,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["makeRpcStub"],"fullName":"makeRpcStub proxies successful calls","status":"passed","title":"proxies successful calls","duration":41.342099999999846,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["makeRpcStub"],"fullName":"makeRpcStub wraps rejected promises as RpcCallError","status":"passed","title":"wraps rejected promises as RpcCallError","duration":41.24249999999938,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["makeRpcStub"],"fullName":"makeRpcStub decodes error envelopes into Effect.fail","status":"passed","title":"decodes error envelopes into Effect.fail","duration":41.18549999999959,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["makeRpcStub"],"fullName":"makeRpcStub decodes stream envelopes from successful calls","status":"passed","title":"decodes stream envelopes from successful calls","duration":41.19730000000072,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors toRpcStream encodes a stream that fails immediately","status":"passed","title":"toRpcStream encodes a stream that fails immediately","duration":41.110999999999876,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors toRpcStream encodes a stream that fails after elements","status":"passed","title":"toRpcStream encodes a stream that fails after elements","duration":41.15099999999984,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors fromRpcReadableStream decodes error marker in JSONL","status":"passed","title":"fromRpcReadableStream decodes error marker in JSONL","duration":41.07009999999991,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors fromRpcReadableStream yields elements before error marker","status":"passed","title":"fromRpcReadableStream yields elements before error marker","duration":41.01940000000013,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors makeRpcStub preserves stream errors (not collapsed to RpcCallError)","status":"passed","title":"makeRpcStub preserves stream errors (not collapsed to RpcCallError)","duration":40.849999999999454,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382835765,"endTime":1782382836282.07,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Rpc.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"RpcDurableObjectNamespace: Increment / Get round-trip via Worker","status":"skipped","title":"RpcDurableObjectNamespace: Increment / Get round-trip via Worker","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcDurableObjectNamespace: separate getByName(id) instances are isolated","status":"skipped","title":"RpcDurableObjectNamespace: separate getByName(id) instances are isolated","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcDurableObjectNamespace: streaming RPC via getByName(id).CountUpTo","status":"skipped","title":"RpcDurableObjectNamespace: streaming RPC via getByName(id).CountUpTo","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker + RpcDurableObjectNamespace: Worker proxies *DO RPCs through the typed namespace","status":"skipped","title":"RpcWorker + RpcDurableObjectNamespace: Worker proxies *DO RPCs through the typed namespace","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcDurableObjectNamespace: 100 concurrent Increment calls do not hang","status":"skipped","title":"RpcDurableObjectNamespace: 100 concurrent Increment calls do not hang","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent unary RPCs do not hang","status":"skipped","title":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent unary RPCs do not hang","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent *DO unary RPCs do not hang","status":"skipped","title":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent *DO unary RPCs do not hang","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent streaming *DO RPCs do not hang","status":"skipped","title":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent streaming *DO RPCs do not hang","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/RpcDurableObjectNamespace.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: unary RPC response","status":"passed","title":"RpcServer.toHttpEffect: unary RPC response","duration":2164.2700999999943,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: streaming RPC response","status":"passed","title":"RpcServer.toHttpEffect: streaming RPC response","duration":3494.779500000004,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: array payload streams response items in order","status":"passed","title":"RpcServer.toHttpEffect: array payload streams response items in order","duration":3488.751199999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: 200 concurrent unary calls do not hang","status":"passed","title":"RpcServer.toHttpEffect: 200 concurrent unary calls do not hang","duration":5667.532899999991,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: concurrent streaming calls do not hang","status":"passed","title":"RpcServer.toHttpEffect: concurrent streaming calls do not hang","duration":4408.868899999987,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object unary RPC response","status":"passed","title":"RpcServer.toHttpEffect Durable Object unary RPC response","duration":2831.2137999999977,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object streaming RPC response","status":"passed","title":"RpcServer.toHttpEffect Durable Object streaming RPC response","duration":1703.5895000000019,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object array payload streams response items in order","status":"passed","title":"RpcServer.toHttpEffect Durable Object array payload streams response items in order","duration":1695.746800000008,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object concurrent unary calls do not hang","status":"passed","title":"RpcServer.toHttpEffect Durable Object concurrent unary calls do not hang","duration":1972.6087000000116,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object concurrent streaming calls do not hang","status":"passed","title":"RpcServer.toHttpEffect Durable Object concurrent streaming calls do not hang","duration":1207.1873999999953,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382816097,"endTime":1782382822482.6086,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/RpcHttp.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"RpcWorker: target worker exposes Greet","status":"passed","title":"RpcWorker: target worker exposes Greet","duration":348.27129999999306,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker.bind: caller proxies through service binding to target","status":"passed","title":"RpcWorker.bind: caller proxies through service binding to target","duration":771.0556999999972,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker.bind: 100 concurrent ProxyGreet calls do not hang","status":"passed","title":"RpcWorker.bind: 100 concurrent ProxyGreet calls do not hang","duration":2097.8773999999976,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382811041,"endTime":1782382813141.8774,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/RpcWorker.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"adopts the account's existing workers.dev subdomain without mutating it","status":"passed","title":"adopts the account's existing workers.dev subdomain without mutating it","duration":6237.183000000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account's workers.dev subdomain singleton","status":"passed","title":"list returns the account's workers.dev subdomain singleton","duration":5772.025699999998,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382842648,"endTime":1782382848885.183,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Subdomain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"D1 counter writes from WorkerA are visible from WorkerB (cross-script DO)","status":"skipped","title":"D1 counter writes from WorkerA are visible from WorkerB (cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"DO storage counter writes from WorkerA are visible from WorkerB (cross-script DO)","status":"skipped","title":"DO storage counter writes from WorkerA are visible from WorkerB (cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"WorkerC hosts its own isolated Counter (writes from A/B are not visible from C)","status":"skipped","title":"WorkerC hosts its own isolated Counter (writes from A/B are not visible from C)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Writes from WorkerB are visible from WorkerA (bidirectional cross-script DO)","status":"skipped","title":"Writes from WorkerB are visible from WorkerA (bidirectional cross-script DO)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/TaggedDO.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"RpcWorker WorkerA exposes the same RPC surface as the underlying DO","status":"skipped","title":"RpcWorker WorkerA exposes the same RPC surface as the underlying DO","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"D1 counter writes via WorkerA's RPC are visible from WorkerB (cross-script DO)","status":"skipped","title":"D1 counter writes via WorkerA's RPC are visible from WorkerB (cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"DO storage counter writes via WorkerA's RPC are visible from WorkerB (cross-script DO)","status":"skipped","title":"DO storage counter writes via WorkerA's RPC are visible from WorkerB (cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Writes from WorkerB are visible from WorkerA's RPC (bidirectional cross-script DO)","status":"skipped","title":"Writes from WorkerB are visible from WorkerA's RPC (bidirectional cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"WorkerC hosts its own isolated Counter (writes from A/B are not visible from C)","status":"skipped","title":"WorkerC hosts its own isolated Counter (writes from A/B are not visible from C)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/TaggedRpcDO.test.ts"},{"assertionResults":[{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker create, update, delete worker","status":"failed","title":"create, update, delete worker","duration":36765.02530000001,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker create, update, delete worker with assets","status":"passed","title":"create, update, delete worker with assets","duration":36779.25469999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker Worker assets: relocating to a fresh path with identical bytes preserves hash and keeps assets","status":"passed","title":"Worker assets: relocating to a fresh path with identical bytes preserves hash and keeps assets","duration":21164.892300000007,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker Worker assets: editing a file changes the hash and republishes the manifest","status":"passed","title":"Worker assets: editing a file changes the hash and republishes the manifest","duration":32782.667700000005,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker Worker assets: a bundle-only change keeps the asset manifest (hash.assets stable)","status":"passed","title":"Worker assets: a bundle-only change keeps the asset manifest (hash.assets stable)","duration":27697.807199999996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker create, update, delete internal worker","status":"passed","title":"create, update, delete internal worker","duration":61443.7926,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker owned worker (matching alchemy tags) is silently adopted without --adopt","status":"passed","title":"owned worker (matching alchemy tags) is silently adopted without --adopt","duration":25846.292000000016,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker adopt(true) takes over a foreign-tagged worker","status":"passed","title":"adopt(true) takes over a foreign-tagged worker","duration":23926.610700000005,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker url defaults to enabling the workers.dev subdomain on first deploy","status":"passed","title":"url defaults to enabling the workers.dev subdomain on first deploy","duration":14624.79419999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker url: false disables the workers.dev subdomain on first deploy","status":"passed","title":"url: false disables the workers.dev subdomain on first deploy","duration":13962.593999999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker toggling url between deploys flips the workers.dev subdomain","status":"passed","title":"toggling url between deploys flips the workers.dev subdomain","duration":11760.868199999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker redeploy re-enables previewsEnabled when externally disabled","status":"passed","title":"redeploy re-enables previewsEnabled when externally disabled","duration":24791.2702,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker domains reflects the workers.dev subdomain and tracks url","status":"passed","title":"domains reflects the workers.dev subdomain and tracks url","duration":25152.51329999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker domains puts custom domains before workers.dev and url is the first","status":"skipped","title":"domains puts custom domains before workers.dev and url is the first","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker list enumerates the deployed worker","status":"failed","title":"list enumerates the deployed worker","duration":1833.995500000019,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker downstream referencing worker.url is not re-updated when the worker changes","status":"failed","title":"downstream referencing worker.url is not re-updated when the worker changes","duration":2180.6567000000214,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker worker.durableObjectNamespaces stability across DO and worker changes","status":"failed","title":"worker.durableObjectNamespaces stability across DO and worker changes","duration":10279.993599999987,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782382661320,"endTime":1782382743931.7925,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Worker.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"target worker's own fetch handler responds","status":"skipped","title":"target worker's own fetch handler responds","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"async caller can call target's RPC method via service binding","status":"skipped","title":"async caller can call target's RPC method via service binding","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect caller can call target's RPC method via bindWorker","status":"skipped","title":"effect caller can call target's RPC method via bindWorker","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/WorkerBinding.test.ts"},{"assertionResults":[{"ancestorTitles":["Cloudflare.Worker env bindings"],"fullName":"Cloudflare.Worker env bindings async worker round-trips every supported binding shape","status":"skipped","title":"async worker round-trips every supported binding shape","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker env bindings"],"fullName":"Cloudflare.Worker env bindings effect worker round-trips env: literals and Redacted via WorkerEnvironment","status":"skipped","title":"effect worker round-trips env: literals and Redacted via WorkerEnvironment","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker env bindings"],"fullName":"Cloudflare.Worker env bindings effect worker resolves the yielded VersionMetadata binding","status":"skipped","title":"effect worker resolves the yielded VersionMetadata binding","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker env bindings"],"fullName":"Cloudflare.Worker env bindings effect worker round-trips Config.xxx bindings captured in Init","status":"skipped","title":"effect worker round-trips Config.xxx bindings captured in Init","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/WorkerEnv.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"async worker loads and proxies to a dynamic worker via env binding","status":"skipped","title":"async worker loads and proxies to a dynamic worker via env binding","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker loads and proxies to a dynamic worker via yield* loader","status":"skipped","title":"effect worker loads and proxies to a dynamic worker via yield* loader","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/WorkerLoader.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed worker can run a workflow to completion","status":"skipped","title":"deployed worker can run a workflow to completion","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed workflow","status":"skipped","title":"list enumerates the deployed workflow","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Workflow.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins the custom nameserver toggle and leaves the zone at its baseline on destroy","status":"skipped","title":"pins the custom nameserver toggle and leaves the zone at its baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"surfaces the typed CustomNameserverSetNotFound error when enabling without an account nameserver set","status":"skipped","title":"surfaces the typed CustomNameserverSetNotFound error when enabling without an account nameserver set","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates zones using account custom nameservers","status":"passed","title":"list enumerates zones using account custom nameservers","duration":1918.5256999999983,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a zone with account custom nameservers enabled","status":"skipped","title":"list includes a zone with account custom nameservers enabled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"enables account custom nameservers and restores the baseline on destroy","status":"skipped","title":"enables account custom nameservers and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382806432,"endTime":1782382808350.5256,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/CustomNameservers.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed ZoneHoldsRequireEnterprise error on non-Enterprise zones","status":"skipped","title":"surfaces the typed ZoneHoldsRequireEnterprise error on non-Enterprise zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the hold state across all zones","status":"skipped","title":"list enumerates the hold state across all zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"places a hold, updates includeSubdomains in place, and removes it on destroy","status":"skipped","title":"places a hold, updates includeSubdomains in place, and removes it on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/Hold.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins a toggle setting and restores the original value on destroy","status":"skipped","title":"pins a toggle setting and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updates a numeric setting in place and keeps the captured initial value","status":"skipped","title":"updates a numeric setting in place and keeps the captured initial value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing settingId replaces — old setting restored, new setting pinned","status":"skipped","title":"changing settingId replaces — old setting restored, new setting pinned","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed (zone, setting) pair","status":"skipped","title":"list enumerates the deployed (zone, setting) pair","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/Setting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create zone retains by default — destroy() opts in to deletion","status":"skipped","title":"create zone retains by default — destroy() opts in to deletion","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create zone retains by default — survives stack.destroy()","status":"skipped","title":"create zone retains by default — survives stack.destroy()","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing zone errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing zone errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates every zone in the account","status":"skipped","title":"list enumerates every zone in the account","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782382576944,"endTime":1782382576944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/Zone.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete webhook destination","status":"failed","title":"create, update, delete webhook destination","duration":29671.033,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed webhook destination","status":"failed","title":"list enumerates the deployed webhook destination","duration":29258.9583,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782383187511,"endTime":1782383217182.033,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Alerting/Webhook.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"D1Connection.bind exercises the full client surface","status":"passed","title":"D1Connection.bind exercises the full client surface","duration":26512.671800000004,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782383274353,"endTime":1782383300865.6719,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/D1/D1Binding.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update settings, replace script, delete","status":"failed","title":"create, update settings, replace script, delete","duration":8210.1687,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates consumer after out-of-band delete","status":"passed","title":"recreates consumer after out-of-band delete","duration":7202.320199999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopts existing consumer after local state loss","status":"passed","title":"adopts existing consumer after local state loss","duration":5910.3443,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"fails clearly when queue has consumer for different script","status":"passed","title":"fails clearly when queue has consumer for different script","duration":11779.0717,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"suppresses deletion of a dev-only consumer","status":"passed","title":"suppresses deletion of a dev-only consumer","duration":278.50639999999976,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"promotes a dev consumer to a live consumer on deploy","status":"passed","title":"promotes a dev consumer to a live consumer on deploy","duration":6682.724699999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed consumer","status":"failed","title":"list enumerates the deployed consumer","duration":2761.732400000001,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782383164051,"endTime":1782383175831.0718,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/QueueConsumer.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"Vite: editing a source file republishes the assets in a single deploy","status":"passed","title":"Vite: editing a source file republishes the assets in a single deploy","duration":15183.162,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Vite: class form deploys and serves the built assets","status":"passed","title":"Vite: class form deploys and serves the built assets","duration":10861.2202,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Vite: relocating rootDir (and deleting the old one) is a no-op when sources are identical","status":"passed","title":"Vite: relocating rootDir (and deleting the old one) is a no-op when sources are identical","duration":15135.9178,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Vite: `env` props are inlined as `import.meta.env.*` into the bundle","status":"passed","title":"Vite: `env` props are inlined as `import.meta.env.*` into the bundle","duration":9460.118600000002,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782383145253,"endTime":1782383160436.162,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Website/Vite.test.ts"},{"assertionResults":[{"ancestorTitles":["Cloudflare.Worker with bundle: false"],"fullName":"Cloudflare.Worker with bundle: false uploads a prebuilt module graph byte-for-byte","status":"passed","title":"uploads a prebuilt module graph byte-for-byte","duration":4649.4065,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782383179291,"endTime":1782383183940.4065,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/PrebuiltWorker.test.ts"},{"assertionResults":[{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker create, update, delete worker","status":"failed","title":"create, update, delete worker","duration":26796.176400000004,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker create, update, delete worker with assets","status":"passed","title":"create, update, delete worker with assets","duration":44677.729699999996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker Worker assets: relocating to a fresh path with identical bytes preserves hash and keeps assets","status":"passed","title":"Worker assets: relocating to a fresh path with identical bytes preserves hash and keeps assets","duration":11799.613900000004,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker Worker assets: editing a file changes the hash and republishes the manifest","status":"passed","title":"Worker assets: editing a file changes the hash and republishes the manifest","duration":16265.799500000008,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker Worker assets: a bundle-only change keeps the asset manifest (hash.assets stable)","status":"passed","title":"Worker assets: a bundle-only change keeps the asset manifest (hash.assets stable)","duration":26330.037200000006,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker create, update, delete internal worker","status":"passed","title":"create, update, delete internal worker","duration":11492.3756,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker owned worker (matching alchemy tags) is silently adopted without --adopt","status":"passed","title":"owned worker (matching alchemy tags) is silently adopted without --adopt","duration":14934.407500000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker adopt(true) takes over a foreign-tagged worker","status":"passed","title":"adopt(true) takes over a foreign-tagged worker","duration":7708.416799999992,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker url defaults to enabling the workers.dev subdomain on first deploy","status":"passed","title":"url defaults to enabling the workers.dev subdomain on first deploy","duration":3570.150199999989,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker url: false disables the workers.dev subdomain on first deploy","status":"passed","title":"url: false disables the workers.dev subdomain on first deploy","duration":3714.8110999999917,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker toggling url between deploys flips the workers.dev subdomain","status":"passed","title":"toggling url between deploys flips the workers.dev subdomain","duration":8358.019700000004,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker redeploy re-enables previewsEnabled when externally disabled","status":"passed","title":"redeploy re-enables previewsEnabled when externally disabled","duration":10162.229400000011,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker domains reflects the workers.dev subdomain and tracks url","status":"passed","title":"domains reflects the workers.dev subdomain and tracks url","duration":7107.179200000013,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker domains puts custom domains before workers.dev and url is the first","status":"skipped","title":"domains puts custom domains before workers.dev and url is the first","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker list enumerates the deployed worker","status":"passed","title":"list enumerates the deployed worker","duration":14278.448300000004,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker downstream referencing worker.url is not re-updated when the worker changes","status":"failed","title":"downstream referencing worker.url is not re-updated when the worker changes","duration":30504.045499999993,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker worker.durableObjectNamespaces stability across DO and worker changes","status":"failed","title":"worker.durableObjectNamespaces stability across DO and worker changes","duration":36251.6768,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782383065913,"endTime":1782383140423.6768,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Worker.test.ts"}],"numFailedTests":94,"numPassedTests":190,"numPendingTests":717} \ No newline at end of file diff --git a/cf-temp-results-4.json b/cf-temp-results-4.json new file mode 100644 index 000000000..701dc813d --- /dev/null +++ b/cf-temp-results-4.json @@ -0,0 +1 @@ +{"numTotalTestSuites":366,"numPassedTestSuites":341,"numFailedTestSuites":25,"numPendingTestSuites":0,"numTotalTests":958,"numPassedTests":155,"numFailedTests":18,"numPendingTests":785,"numTodoTests":0,"snapshot":{"added":0,"failure":false,"filesAdded":0,"filesRemoved":0,"filesRemovedList":[],"filesUnmatched":0,"filesUpdated":0,"matched":0,"total":0,"unchecked":0,"uncheckedKeysByFile":[],"unmatched":0,"updated":0,"didUpdate":false},"startTime":1782384949333,"success":false,"testResults":[{"assertionResults":[{"ancestorTitles":[],"fullName":"building the Cloudflare provider layers should not fail for unknown profile","status":"passed","title":"building the Cloudflare provider layers should not fail for unknown profile","duration":1145.3854999999894,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385231515,"endTime":1782385232660.3855,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Providers.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"read path: getAccount observes the testing account","status":"passed","title":"read path: getAccount observes the testing account","duration":1174.1581000000006,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"inaccessible account surfaces a typed tag","status":"passed","title":"inaccessible account surfaces a typed tag","duration":1076.4144000000015,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates accessible accounts (read-only)","status":"skipped","title":"list enumerates accessible accounts (read-only)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"createAccount is entitlement-gated: typed AccountCreationForbidden","status":"skipped","title":"createAccount is entitlement-gated: typed AccountCreationForbidden","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create subaccount, update name and settings in place, delete","status":"skipped","title":"create subaccount, update name and settings in place, delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385122004,"endTime":1782385123178.1582,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Account/Account.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the account members","status":"skipped","title":"list enumerates the account members","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create member, update roles in place, delete","status":"skipped","title":"create member, update roles in place, delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the member when the email changes","status":"skipped","title":"replaces the member when the email changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Account/Member.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","status":"skipped","title":"surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates trust store certificates across zones","status":"passed","title":"list enumerates trust store certificates across zones","duration":3902.5285000000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes the deployed trust store certificate on an entitled zone","status":"skipped","title":"list includes the deployed trust store certificate on an entitled zone","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"uploads a root CA, replaces it on certificate change, and deletes it","status":"skipped","title":"uploads a root CA, replaces it on certificate change, and deletes it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385174492,"endTime":1782385178394.5286,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Acm/CustomTrustStore.test.ts"},{"assertionResults":[{"ancestorTitles":["TotalTls"],"fullName":"TotalTls converges enabled:false as a no-op on a zone without the ACM entitlement","status":"skipped","title":"converges enabled:false as a no-op on a zone without the ACM entitlement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TotalTls"],"fullName":"TotalTls surfaces the typed AdvancedCertificateManagerRequired error when enabling without the entitlement","status":"skipped","title":"surfaces the typed AdvancedCertificateManagerRequired error when enabling without the entitlement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TotalTls"],"fullName":"TotalTls enables Total TLS, updates the CA in place, and restores the baseline on destroy","status":"skipped","title":"enables Total TLS, updates the CA in place, and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TotalTls"],"fullName":"TotalTls list enumerates the setting across all zones","status":"skipped","title":"list enumerates the setting across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Acm/TotalTls.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"address map lifecycle (typed FeatureNotEnabled on unentitled accounts)","status":"skipped","title":"address map lifecycle (typed FeatureNotEnabled on unentitled accounts)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed address map","status":"skipped","title":"list enumerates the deployed address map","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/AddressMap.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates BGP prefixes across BYOIP prefixes","status":"skipped","title":"list enumerates BGP prefixes across BYOIP prefixes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/BgpPrefix.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"lists the services catalog and prefixes (read-only)","status":"skipped","title":"lists the services catalog and prefixes (read-only)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account prefixes (read-only)","status":"skipped","title":"list enumerates account prefixes (read-only)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"prefix: create, patch description in place, destroy","status":"skipped","title":"prefix: create, patch description in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"bgp prefix: adopt-or-create by cidr, toggle advertisement, withdraw on destroy","status":"skipped","title":"bgp prefix: adopt-or-create by cidr, toggle advertisement, withdraw on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"prefix delegation: create and destroy (replace-only resource)","status":"skipped","title":"prefix delegation: create and destroy (replace-only resource)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"service binding: create against the CDN service and destroy","status":"skipped","title":"service binding: create against the CDN service and destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/Prefix.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates prefix delegations (read-only)","status":"skipped","title":"list enumerates prefix delegations (read-only)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/PrefixDelegation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates service bindings across prefixes (read-only)","status":"skipped","title":"list enumerates service bindings across prefixes (read-only)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/ServiceBinding.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a self_hosted application gated by a reusable policy","status":"skipped","title":"create and delete a self_hosted application gated by a reusable policy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create and delete a warp device-enrollment application","status":"passed","title":"create and delete a warp device-enrollment application","duration":1227.2475999999988,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed access application","status":"skipped","title":"list enumerates the deployed access application","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update policies in place keeps the applicationId stable","status":"skipped","title":"update policies in place keeps the applicationId stable","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385169947,"endTime":1782385171174.2476,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Application.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"legacy bookmark creation surfaces the typed AccessBookmarkNotFound error","status":"skipped","title":"legacy bookmark creation surfaces the typed AccessBookmarkNotFound error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy bookmark","status":"skipped","title":"create, update in place, and destroy bookmark","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates access bookmarks at the account scope","status":"skipped","title":"list enumerates access bookmarks at the account scope","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Bookmark.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed AccessCertificateQuotaExceeded error","status":"skipped","title":"unentitled accounts surface the typed AccessCertificateQuotaExceeded error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update hostnames, replace on PEM change, destroy","status":"skipped","title":"create, update hostnames, replace on PEM change, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account access certificates","status":"skipped","title":"list enumerates account access certificates","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Certificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed AccessCustomPagesNotEntitled error","status":"skipped","title":"unentitled accounts surface the typed AccessCustomPagesNotEntitled error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update html in place, replace on type change, destroy","status":"skipped","title":"create, update html in place, replace on type change, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates access custom pages at the account scope","status":"skipped","title":"list enumerates access custom pages at the account scope","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/CustomPage.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update rules, and delete group","status":"skipped","title":"create, update rules, and delete group","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"rename updates the group in place","status":"skipped","title":"rename updates the group in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed access group","status":"skipped","title":"list enumerates the deployed access group","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"group can be referenced from an access policy","status":"skipped","title":"group can be referenced from an access policy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Group.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy an OIDC IdP","status":"skipped","title":"create, verify, and destroy an OIDC IdP","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name and config in place (same id)","status":"skipped","title":"update name and config in place (same id)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed IdP","status":"skipped","title":"list enumerates the deployed IdP","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the type replaces the IdP","status":"skipped","title":"changing the type replaces the IdP","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/IdentityProvider.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed infrastructure target","status":"skipped","title":"list enumerates the deployed infrastructure target","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/InfrastructureTarget.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins the rotation interval, updates in place, and restores on destroy","status":"skipped","title":"pins the rotation interval, updates in place, and restores on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account key configuration singleton","status":"skipped","title":"list returns the account key configuration singleton","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/KeyConfiguration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and destroy an MCP portal","status":"skipped","title":"create, update in place, and destroy an MCP portal","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed MCP portal","status":"skipped","title":"list enumerates the deployed MCP portal","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/McpPortal.test.ts"},{"assertionResults":[{"ancestorTitles":["Organization"],"fullName":"Organization adopts the existing Access organization","status":"skipped","title":"adopts the existing Access organization","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Organization"],"fullName":"Organization toggles allow_authenticate_via_warp and restores","status":"skipped","title":"toggles allow_authenticate_via_warp and restores","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Organization"],"fullName":"Organization list returns the account Access organization","status":"skipped","title":"list returns the account Access organization","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Organization.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete basic allow policy","status":"skipped","title":"create and delete basic allow policy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutates includes without replacing","status":"skipped","title":"update mutates includes without replacing","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopts an out-of-band reusable policy","status":"skipped","title":"adopts an out-of-band reusable policy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed reusable policy","status":"skipped","title":"list enumerates the deployed reusable policy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Policy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update duration, and delete service token","status":"skipped","title":"create, update duration, and delete service token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed service token","status":"skipped","title":"list enumerates the deployed service token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"incrementing clientSecretVersion rotates the secret","status":"skipped","title":"incrementing clientSecretVersion rotates the secret","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/ServiceToken.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, and destroy tag","status":"skipped","title":"create, verify out-of-band, and destroy tag","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"rename replaces the tag","status":"skipped","title":"rename replaces the tag","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed access tag","status":"skipped","title":"list enumerates the deployed access tag","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Tag.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete ai gateway with default props","status":"skipped","title":"create and delete ai gateway with default props","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete ai gateway","status":"skipped","title":"create, update, delete ai gateway","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed ai gateway","status":"skipped","title":"list enumerates the deployed ai gateway","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"existing ai gateway (matching id) is silently adopted without --adopt","status":"skipped","title":"existing ai gateway (matching id) is silently adopted without --adopt","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"deployed worker can call AiGateway binding (effect-native getUrl)","status":"skipped","title":"deployed worker can call AiGateway binding (effect-native getUrl)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/AiGateway.test.ts"},{"assertionResults":[{"ancestorTitles":["AiGatewaySpendingLimit"],"fullName":"AiGatewaySpendingLimit create, read-back, and delete the account spending limit","status":"skipped","title":"create, read-back, and delete the account spending limit","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewaySpendingLimit"],"fullName":"AiGatewaySpendingLimit update the spending limit in place","status":"skipped","title":"update the spending limit in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewaySpendingLimit"],"fullName":"AiGatewaySpendingLimit list enumerates the deployed account spending limit","status":"skipped","title":"list enumerates the deployed account spending limit","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/AiGatewaySpendingLimit.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"first turn against a fresh thread persists user + assistant messages","status":"skipped","title":"first turn against a fresh thread persists user + assistant messages","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"persisted chat survives across DO invocations","status":"skipped","title":"persisted chat survives across DO invocations","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"distinct thread ids map to isolated histories","status":"skipped","title":"distinct thread ids map to isolated histories","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/ChatPersistence.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"send round-trips text and turns through the RpcWorker → DO","status":"skipped","title":"send round-trips text and turns through the RpcWorker → DO","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"streamMessage yields effect/ai Response parts that decode to real classes","status":"skipped","title":"streamMessage yields effect/ai Response parts that decode to real classes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"streamed turn is persisted: a follow-up send recalls it","status":"skipped","title":"streamed turn is persisted: a follow-up send recalls it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/ChatPersistenceRpc.test.ts"},{"assertionResults":[{"ancestorTitles":["AiGatewayDataset"],"fullName":"AiGatewayDataset create, update, delete a dataset on a gateway","status":"skipped","title":"create, update, delete a dataset on a gateway","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewayDataset"],"fullName":"AiGatewayDataset replaces dataset when the gateway changes","status":"skipped","title":"replaces dataset when the gateway changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewayDataset"],"fullName":"AiGatewayDataset recreates a dataset after out-of-band delete","status":"skipped","title":"recreates a dataset after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewayDataset"],"fullName":"AiGatewayDataset list enumerates datasets across gateways","status":"skipped","title":"list enumerates datasets across gateways","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/Dataset.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update elements (new deployed version), rename, delete","status":"skipped","title":"create, update elements (new deployed version), rename, delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces route when the gateway changes","status":"skipped","title":"replaces route when the gateway changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates a route after out-of-band delete","status":"skipped","title":"recreates a route after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates routes across all gateways","status":"skipped","title":"list enumerates routes across all gateways","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/DynamicRouting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, noop, replace, delete an evaluation","status":"skipped","title":"create, noop, replace, delete an evaluation","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed evaluation","status":"skipped","title":"list enumerates the deployed evaluation","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/Evaluation.test.ts"},{"assertionResults":[],"startTime":1782384949333,"endTime":1782384949333,"status":"failed","message":"test.skip.skipIf is not a function","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/LanguageModel.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, noop, replace, delete a BYOK provider config","status":"skipped","title":"create, noop, replace, delete a BYOK provider config","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed provider config","status":"skipped","title":"list enumerates the deployed provider config","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/ProviderConfig.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"construct auto-creates a managed token and wires it into the instance","status":"skipped","title":"construct auto-creates a managed token and wires it into the instance","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"web-crawler source skips token minting","status":"skipped","title":"web-crawler source skips token minting","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/AiSearch.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"worker deploys with ai_search + ai_search_namespace bindings injected","status":"skipped","title":"worker deploys with ai_search + ai_search_namespace bindings injected","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker resolves ai_search + ai_search_namespace via Effect clients","status":"skipped","title":"effect worker resolves ai_search + ai_search_namespace via Effect clients","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/Bindings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update mutable props, and delete an r2-backed instance","status":"skipped","title":"create, update mutable props, and delete an r2-backed instance","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the embedding model triggers a replacement","status":"skipped","title":"changing the embedding model triggers a replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed instance","status":"skipped","title":"list enumerates the deployed instance","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a web-crawler instance (no service token)","status":"skipped","title":"creates a web-crawler instance (no service token)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an instance in a custom namespace and moving namespaces replaces","status":"skipped","title":"creates an instance in a custom namespace and moving namespaces replaces","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/Instance.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update description in place, and delete a namespace","status":"skipped","title":"create, update description in place, and delete a namespace","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the name triggers a replacement","status":"skipped","title":"changing the name triggers a replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed namespace","status":"skipped","title":"list enumerates the deployed namespace","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopts the reserved default namespace without deleting it on teardown","status":"skipped","title":"adopts the reserved default namespace without deleting it on teardown","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/Namespace.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, rename in place, and delete a service token","status":"skipped","title":"create, rename in place, and delete a service token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed service token","status":"skipped","title":"list enumerates the deployed service token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"an AI Search instance syncs with a stack-minted service token","status":"skipped","title":"an AI Search instance syncs with a stack-minted service token","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/Token.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AiSecurityNotEntitled error on unentitled accounts","status":"skipped","title":"surfaces the typed AiSecurityNotEntitled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the custom topics across all zones","status":"passed","title":"list enumerates the custom topics across all zones","duration":1721.070599999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"sets topics, updates the list in place, and restores on destroy","status":"skipped","title":"sets topics, updates the list in place, and restores on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385167695,"endTime":1782385169416.0706,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSecurity/CustomTopics.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AiSecurityNotEntitled error on unentitled accounts","status":"skipped","title":"surfaces the typed AiSecurityNotEntitled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the setting across all entitled zones","status":"passed","title":"list enumerates the setting across all entitled zones","duration":1491.4926999999989,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"pins enabled, updates in place, and restores the original on destroy","status":"skipped","title":"pins enabled, updates in place, and restores the original on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385167543,"endTime":1782385169034.4927,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSecurity/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed NotEntitled error on unentitled zones","status":"skipped","title":"surfaces the typed NotEntitled error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the configuration across all zones","status":"passed","title":"list enumerates the configuration across all zones","duration":1894.2949000000008,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"sets session identifiers and restores the original value on destroy","status":"skipped","title":"sets session identifiers and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385167756,"endTime":1782385169650.295,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/Configuration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update description in place, destroy a label","status":"skipped","title":"create, update description in place, destroy a label","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"renaming a label triggers replacement","status":"skipped","title":"renaming a label triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"generated name respects Cloudflare's 24-character limit","status":"skipped","title":"generated name respects Cloudflare's 24-character limit","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed label","status":"skipped","title":"list enumerates the deployed label","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/Label.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, no-op redeploy, destroy an API Shield operation","status":"skipped","title":"create, no-op redeploy, destroy an API Shield operation","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the method triggers replacement","status":"skipped","title":"changing the method triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed API Shield operation","status":"skipped","title":"list enumerates the deployed API Shield operation","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/Operation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, enable validation in place, destroy a user schema","status":"skipped","title":"create, enable validation in place, destroy a user schema","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the schema source triggers replacement","status":"skipped","title":"changing the schema source triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed user schema","status":"skipped","title":"list enumerates the deployed user schema","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/UserSchema.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed worker can write data points through the Analytics Engine binding","status":"skipped","title":"deployed worker can write data points through the Analytics Engine binding","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AnalyticsEngine/AnalyticsEngineDataset.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed account token","status":"skipped","title":"list enumerates the deployed account token","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiToken/AccountApiToken.test.ts"},{"assertionResults":[{"ancestorTitles":["UserApiToken"],"fullName":"UserApiToken create and delete user token with default props","status":"skipped","title":"create and delete user token with default props","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UserApiToken"],"fullName":"UserApiToken create, update, delete user token","status":"skipped","title":"create, update, delete user token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UserApiToken list"],"fullName":"UserApiToken list list enumerates user tokens","status":"skipped","title":"list enumerates user tokens","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UserApiToken list probe"],"fullName":"UserApiToken list probe list rejects with typed Unauthorized under scoped token","status":"passed","title":"list rejects with typed Unauthorized under scoped token","duration":1544.6466,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385164027,"endTime":1782385165571.6465,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiToken/UserApiToken.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete notification policy","status":"skipped","title":"create, update, delete notification policy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces policy when alertType changes","status":"skipped","title":"replaces policy when alertType changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed notification policy","status":"skipped","title":"list enumerates the deployed notification policy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Alerting/NotificationPolicy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update window in place, delete silence","status":"skipped","title":"create, update window in place, delete silence","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces silence when the policy changes","status":"skipped","title":"replaces silence when the policy changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed silence","status":"skipped","title":"list enumerates the deployed silence","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Alerting/Silence.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete webhook destination","status":"skipped","title":"create, update, delete webhook destination","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed webhook destination","status":"skipped","title":"list enumerates the deployed webhook destination","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Alerting/Webhook.test.ts"},{"assertionResults":[{"ancestorTitles":["BotManagement"],"fullName":"BotManagement manages SBFM settings on the zone singleton and restores them on destroy","status":"skipped","title":"manages SBFM settings on the zone singleton and restores them on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["BotManagement"],"fullName":"BotManagement deploy with no settings set adopts the singleton without writing","status":"skipped","title":"deploy with no settings set adopts the singleton without writing","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["BotManagement"],"fullName":"BotManagement toggles a boolean SBFM field and restores it on destroy","status":"skipped","title":"toggles a boolean SBFM field and restores it on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["BotManagement"],"fullName":"BotManagement list enumerates bot management across all zones","status":"skipped","title":"list enumerates bot management across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/BotManagement/BotManagement.test.ts"},{"assertionResults":[{"ancestorTitles":["SmartRouting"],"fullName":"SmartRouting surfaces the typed NotAuthorized error on zones without the Argo add-on","status":"skipped","title":"surfaces the typed NotAuthorized error on zones without the Argo add-on","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SmartRouting"],"fullName":"SmartRouting enables Smart Routing and restores the original value on destroy","status":"skipped","title":"enables Smart Routing and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SmartRouting"],"fullName":"SmartRouting list enumerates Argo-entitled zones","status":"passed","title":"list enumerates Argo-entitled zones","duration":3355.0084000000024,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385159015,"endTime":1782385162370.0083,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Argo/SmartRouting.test.ts"},{"assertionResults":[{"ancestorTitles":["TieredCaching"],"fullName":"TieredCaching enables Tiered Caching and restores the original value on destroy","status":"skipped","title":"enables Tiered Caching and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TieredCaching"],"fullName":"TieredCaching updates enabled in place and keeps the captured initial value","status":"skipped","title":"updates enabled in place and keeps the captured initial value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TieredCaching"],"fullName":"TieredCaching list enumerates the setting across all zones","status":"skipped","title":"list enumerates the setting across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Argo/TieredCaching.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"async worker renders a page title through Browser Rendering","status":"skipped","title":"async worker renders a page title through Browser Rendering","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker exercises content via quickAction wrapper","status":"skipped","title":"effect worker exercises content via quickAction wrapper","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker converts a page to markdown","status":"skipped","title":"effect worker converts a page to markdown","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker extracts links","status":"skipped","title":"effect worker extracts links","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker scrapes elements by selector","status":"skipped","title":"effect worker scrapes elements by selector","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker takes a page snapshot","status":"skipped","title":"effect worker takes a page snapshot","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker streams a screenshot","status":"skipped","title":"effect worker streams a screenshot","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker streams a PDF","status":"skipped","title":"effect worker streams a PDF","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker extracts JSON with AI","status":"skipped","title":"effect worker extracts JSON with AI","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker calls the generic quickAction","status":"skipped","title":"effect worker calls the generic quickAction","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker exposes the raw BrowserRun binding","status":"skipped","title":"effect worker exposes the raw BrowserRun binding","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Browser/Browser.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an app with default name","status":"skipped","title":"create and delete an app with default name","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name in place (same appId, secret preserved)","status":"skipped","title":"update name in place (same appId, secret preserved)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed app","status":"skipped","title":"list enumerates the deployed app","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Calls/App.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a TURN key with default name","status":"skipped","title":"create and delete a TURN key with default name","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name in place (same keyId, key preserved)","status":"skipped","title":"update name in place (same keyId, key preserved)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed TURN key","status":"skipped","title":"list enumerates the deployed TURN key","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Calls/TurnKey.test.ts"},{"assertionResults":[{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation pins Managed CA hostnames, updates in place, and clears on destroy","status":"skipped","title":"pins Managed CA hostnames, updates in place, and clears on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation associates hostnames with an uploaded CA and destroys before the cert","status":"skipped","title":"associates hostnames with an uploaded CA and destroys before the cert","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation changing the certificate key replaces the association","status":"skipped","title":"changing the certificate key replaces the association","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CertificateAuthorities/HostnameAssociation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create signs the CSR, destroy revokes the certificate","status":"skipped","title":"create signs the CSR, destroy revokes the certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing validityDays replaces — new id issued, old certificate revoked","status":"skipped","title":"changing validityDays replaces — new id issued, old certificate revoked","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates client certificates across zones","status":"skipped","title":"list enumerates client certificates across zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ClientCertificate/ClientCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":["Rules"],"fullName":"Rules cloud connector rules — create, update in place, destroy clears the list","status":"skipped","title":"cloud connector rules — create, update in place, destroy clears the list","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Rules"],"fullName":"Rules no-op redeploy leaves the rule list untouched and ids stable","status":"skipped","title":"no-op redeploy leaves the rule list untouched and ids stable","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Rules"],"fullName":"Rules list enumerates rule lists across all zones","status":"skipped","title":"list enumerates rule lists across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CloudConnector/Rules.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Unauthorized error","status":"skipped","title":"unentitled accounts surface the typed Unauthorized error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create a scan config, update ports in place, delete","status":"skipped","title":"create a scan config, update ports in place, delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns an array of scan configs","status":"passed","title":"list returns an array of scan configs","duration":28049.267600000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed scan config","status":"skipped","title":"list enumerates the deployed scan config","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385086281,"endTime":1782385114330.2676,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CloudforceOne/ScanConfig.test.ts"},{"assertionResults":[{"ancestorTitles":["CacheReserve"],"fullName":"CacheReserve surfaces the typed SettingUnavailableForPlan error on unentitled zones","status":"skipped","title":"surfaces the typed SettingUnavailableForPlan error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["CacheReserve"],"fullName":"CacheReserve enables Cache Reserve and restores the original value on destroy","status":"skipped","title":"enables Cache Reserve and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["CacheReserve"],"fullName":"CacheReserve list enumerates Cache Reserve across all zones","status":"passed","title":"list enumerates Cache Reserve across all zones","duration":3314.3354,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["CacheReserve"],"fullName":"CacheReserve list includes the entitled zone's Cache Reserve setting","status":"skipped","title":"list includes the entitled zone's Cache Reserve setting","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385161412,"endTime":1782385164726.3354,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/CacheReserve.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates, updates in place, replaces on ip change, and deletes the mapping","status":"skipped","title":"creates, updates in place, replaces on ip change, and deletes the mapping","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/OriginCloudRegion.test.ts"},{"assertionResults":[{"ancestorTitles":["RegionalTieredCache"],"fullName":"RegionalTieredCache surfaces the typed SettingUnavailableForPlan error on non-Enterprise zones","status":"skipped","title":"surfaces the typed SettingUnavailableForPlan error on non-Enterprise zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RegionalTieredCache"],"fullName":"RegionalTieredCache enables Regional Tiered Cache and restores the original value on destroy","status":"skipped","title":"enables Regional Tiered Cache and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RegionalTieredCache"],"fullName":"RegionalTieredCache list enumerates the setting across entitled zones","status":"passed","title":"list enumerates the setting across entitled zones","duration":2249.7448000000004,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385153858,"endTime":1782385156107.7449,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/RegionalTieredCache.test.ts"},{"assertionResults":[{"ancestorTitles":["SmartTieredCache"],"fullName":"SmartTieredCache enables Smart Tiered Cache and restores the original value on destroy","status":"skipped","title":"enables Smart Tiered Cache and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SmartTieredCache"],"fullName":"SmartTieredCache updates the setting in place and keeps the captured initial value","status":"skipped","title":"updates the setting in place and keeps the captured initial value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SmartTieredCache"],"fullName":"SmartTieredCache list enumerates the setting across all zones","status":"skipped","title":"list enumerates the setting across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/SmartTieredCache.test.ts"},{"assertionResults":[{"ancestorTitles":["Variants"],"fullName":"Variants creates, updates in place, and deletes the variants setting","status":"skipped","title":"creates, updates in place, and deletes the variants setting","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Variants"],"fullName":"Variants deploy is idempotent and converges out-of-band drift","status":"skipped","title":"deploy is idempotent and converges out-of-band drift","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Variants"],"fullName":"Variants list enumerates the configured variants settings","status":"skipped","title":"list enumerates the configured variants settings","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/Variants.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"tcp service lifecycle: create, update, host switch","status":"skipped","title":"tcp service lifecycle: create, update, host switch","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"http service with explicit ports and default name","status":"skipped","title":"http service with explicit ports and default name","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed directory service","status":"skipped","title":"list enumerates the deployed directory service","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Connectivity/DirectoryService.test.ts"},{"assertionResults":[{"ancestorTitles":["effectful container (main)"],"fullName":"effectful container (main) deploys and serves over its TCP port","status":"skipped","title":"deploys and serves over its TCP port","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["external container (context/dockerfile)"],"fullName":"external container (context/dockerfile) builds the user Dockerfile and serves it over its TCP port","status":"skipped","title":"builds the user Dockerfile and serves it over its TCP port","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["remote container (image)"],"fullName":"remote container (image) pulls and re-pushes the remote image and serves it over its TCP port","status":"skipped","title":"pulls and re-pushes the remote image and serves it over its TCP port","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Container/Container.test.ts"},{"assertionResults":[{"ancestorTitles":["ContainerApplication"],"fullName":"ContainerApplication list enumerates container applications","status":"failed","title":"list enumerates container applications","duration":613.9043999999994,"failureMessages":["Provider not found for undefined"],"meta":{},"tags":[]}],"startTime":1782385076145,"endTime":1782385076758.9043,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Container/ContainerApplication.test.ts"},{"assertionResults":[{"ancestorTitles":["ContentScanning"],"fullName":"ContentScanning surfaces the typed ContentScanningNotEntitled error on unentitled zones","status":"skipped","title":"surfaces the typed ContentScanningNotEntitled error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ContentScanning"],"fullName":"ContentScanning pins Content Scanning off on an unentitled zone and destroys cleanly","status":"skipped","title":"pins Content Scanning off on an unentitled zone and destroys cleanly","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ContentScanning"],"fullName":"ContentScanning list enumerates the status across all zones","status":"skipped","title":"list enumerates the status across all zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ContentScanning"],"fullName":"ContentScanning enables Content Scanning and restores the original status on destroy","status":"skipped","title":"enables Content Scanning and restores the original status on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ContentScanning/ContentScanning.test.ts"},{"assertionResults":[{"ancestorTitles":["Expression"],"fullName":"Expression surfaces the typed ContentScanningNotEnabled error when scanning is disabled","status":"skipped","title":"surfaces the typed ContentScanningNotEnabled error when scanning is disabled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Expression"],"fullName":"Expression creates a custom expression, replaces on payload change, destroys cleanly","status":"skipped","title":"creates a custom expression, replaces on payload change, destroys cleanly","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Expression"],"fullName":"Expression list returns a well-typed array across all zones","status":"passed","title":"list returns a well-typed array across all zones","duration":1889.7962000000007,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Expression"],"fullName":"Expression list enumerates the deployed expression","status":"skipped","title":"list enumerates the deployed expression","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385165902,"endTime":1782385167791.7961,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ContentScanning/Expression.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a custom hostname with default ssl","status":"skipped","title":"create and delete a custom hostname with default ssl","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating ssl method patches in place","status":"skipped","title":"updating ssl method patches in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of custom hostnames","status":"passed","title":"list returns a well-typed array of custom hostnames","duration":1346.2353000000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed custom hostname","status":"skipped","title":"list enumerates the deployed custom hostname","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the hostname triggers replacement","status":"skipped","title":"changing the hostname triggers replacement","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385136240,"endTime":1782385137586.2354,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CustomHostname/CustomHostname.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates fallback origins across all zones","status":"passed","title":"list enumerates fallback origins across all zones","duration":2093.5879999999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list surfaces a deployed fallback origin","status":"skipped","title":"list surfaces a deployed fallback origin","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"set, update and delete the zone fallback origin","status":"skipped","title":"set, update and delete the zone fallback origin","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385165293,"endTime":1782385167386.588,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CustomHostname/FallbackOrigin.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed PlanLevelNotAllowed error on unentitled zones","status":"skipped","title":"surfaces the typed PlanLevelNotAllowed error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates custom certificates across zones","status":"passed","title":"list enumerates custom certificates across zones","duration":831.5570999999982,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"uploads, rotates in place, replaces on type change, and deletes","status":"skipped","title":"uploads, rotates in place, replaces on type change, and deletes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385132169,"endTime":1782385133000.5571,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CustomCertificate/CustomCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"D1Connection.bind exercises the full client surface","status":"skipped","title":"D1Connection.bind exercises the full client surface","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/D1/D1Binding.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed database","status":"passed","title":"list enumerates the deployed database","duration":5107.475000000002,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385157577,"endTime":1782385162684.475,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/D1/D1Database.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete database with default props","status":"passed","title":"create and delete database with default props","duration":8062.370200000005,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete database","status":"passed","title":"create, update, delete database","duration":7941.2629000000015,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"applies migrations from migrationsDir","status":"passed","title":"applies migrations from migrationsDir","duration":9411.726500000004,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"applies migrations using a custom migrationsTable","status":"passed","title":"applies migrations using a custom migrationsTable","duration":6380.826300000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"migrates legacy 2-column migration table to wrangler schema","status":"passed","title":"migrates legacy 2-column migration table to wrangler schema","duration":9315.924599999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"imports SQL files via importFiles","status":"passed","title":"imports SQL files via importFiles","duration":4028.567599999995,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"clones a database by databaseId","status":"skipped","title":"clones a database by databaseId","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"clones a database by name lookup","status":"skipped","title":"clones a database by name lookup","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"clones a database by passing the source resource directly","status":"skipped","title":"clones a database by passing the source resource directly","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"existing database (matching name) is silently adopted without --adopt","status":"failed","title":"existing database (matching name) is silently adopted without --adopt","duration":1034.983699999997,"failureMessages":["Forbidden: System limit reached: databases per account (1)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782385047777,"endTime":1782385058189.5676,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/D1/Database.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed CustomNameserversNotEnabled error on unentitled accounts","status":"passed","title":"surfaces the typed CustomNameserversNotEnabled error on unentitled accounts","duration":1838.8808999999965,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account custom nameservers","status":"passed","title":"list enumerates account custom nameservers","duration":1908.9995000000017,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed custom nameserver","status":"skipped","title":"list includes a deployed custom nameserver","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, replace on nsSet change, and destroy a custom nameserver","status":"skipped","title":"create, replace on nsSet change, and destroy a custom nameserver","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385137110,"endTime":1782385139021.9995,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CustomNameserver/CustomNameserver.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AdvancedTcpProtectionNotEntitled error without Magic Transit","status":"skipped","title":"surfaces the typed AdvancedTcpProtectionNotEntitled error without Magic Transit","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an allowlist entry, updates it in place, replaces on prefix change, and destroys","status":"skipped","title":"creates an allowlist entry, updates it in place, replaces on prefix change, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed empty array without Magic Transit","status":"skipped","title":"list returns a well-typed empty array without Magic Transit","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed allowlist entry","status":"skipped","title":"list enumerates the deployed allowlist entry","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/AllowlistEntry.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates a SYN protection filter, updates it in place, and destroys","status":"skipped","title":"creates a SYN protection filter, updates it in place, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of SYN protection filters","status":"passed","title":"list returns a well-typed array of SYN protection filters","duration":27800.1609,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed SYN protection filter","status":"skipped","title":"list enumerates the deployed SYN protection filter","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385109580,"endTime":1782385137380.161,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/SynProtectionFilter.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates a global SYN protection rule, updates it in place, and destroys","status":"skipped","title":"creates a global SYN protection rule, updates it in place, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of SYN protection rules","status":"passed","title":"list returns a well-typed array of SYN protection rules","duration":26726.1927,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed SYN protection rule","status":"skipped","title":"list enumerates the deployed SYN protection rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385113245,"endTime":1782385139971.1926,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/SynProtectionRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates a TCP flow protection filter, updates it in place, and destroys","status":"skipped","title":"creates a TCP flow protection filter, updates it in place, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account's TCP flow protection filters","status":"passed","title":"list returns the account's TCP flow protection filters","duration":27902.201999999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed TCP flow protection filter","status":"skipped","title":"list enumerates the deployed TCP flow protection filter","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385085498,"endTime":1782385113400.202,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/TcpFlowProtectionFilter.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates a global TCP flow protection rule, updates it in place, and destroys","status":"skipped","title":"creates a global TCP flow protection rule, updates it in place, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed empty array without Magic Transit","status":"passed","title":"list returns a well-typed empty array without Magic Transit","duration":27226.9482,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed TCP flow protection rule","status":"skipped","title":"list enumerates the deployed TCP flow protection rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385109548,"endTime":1782385136774.9482,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/TcpFlowProtectionRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"rejects public IPs with the typed InvalidHealthcheckEndpoint error","status":"skipped","title":"rejects public IPs with the typed InvalidHealthcheckEndpoint error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an endpoint healthcheck, updates in place, and destroys","status":"skipped","title":"creates an endpoint healthcheck, updates in place, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed endpoint healthcheck","status":"skipped","title":"list enumerates the deployed endpoint healthcheck","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Diagnostics/EndpointHealthcheck.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden error","status":"skipped","title":"unentitled accounts surface the typed Forbidden error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, and destroy a DLP profile with a standalone entry","status":"skipped","title":"create, update, and destroy a DLP profile with a standalone entry","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dlp/Dlp.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns the account's custom DLP entries","status":"skipped","title":"list returns the account's custom DLP entries","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed DLP entry","status":"skipped","title":"list enumerates the deployed DLP entry","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dlp/Entry.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates custom DLP profiles (read-only)","status":"skipped","title":"list enumerates custom DLP profiles (read-only)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed custom DLP profile","status":"skipped","title":"list includes a deployed custom DLP profile","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dlp/Profile.test.ts"},{"assertionResults":[{"ancestorTitles":["AccountSettings"],"fullName":"AccountSettings list returns the account's DNS settings singleton","status":"skipped","title":"list returns the account's DNS settings singleton","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AccountSettings"],"fullName":"AccountSettings pins a zone default and restores the pre-management value on destroy","status":"skipped","title":"pins a zone default and restores the pre-management value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AccountSettings"],"fullName":"AccountSettings updates in place, unions managedKeys, restores all managed fields","status":"skipped","title":"updates in place, unions managedKeys, restores all managed fields","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/AccountSettings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed worker drives the full DNS record CRUD surface via DnsReadWrite","status":"skipped","title":"deployed worker drives the full DNS record CRUD surface via DnsReadWrite","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/Dns.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an A record with default props","status":"skipped","title":"create and delete an A record with default props","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating mutable fields patches in place","status":"skipped","title":"updating mutable fields patches in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the record type triggers replacement","status":"skipped","title":"changing the record type triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing record errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing record errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed DNS record","status":"skipped","title":"list enumerates the deployed DNS record","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/DnsRecord.test.ts"},{"assertionResults":[{"ancestorTitles":["Dnssec"],"fullName":"Dnssec enables DNSSEC, captures the disabled baseline, destroy deactivates","status":"skipped","title":"enables DNSSEC, captures the disabled baseline, destroy deactivates","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Dnssec"],"fullName":"Dnssec updates status in place — same zone singleton, no replacement","status":"skipped","title":"updates status in place — same zone singleton, no replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Dnssec"],"fullName":"Dnssec list enumerates active DNSSEC across zones","status":"skipped","title":"list enumerates active DNSSEC across zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/Dnssec.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed InternalDnsNotAvailable error on unentitled accounts","status":"skipped","title":"surfaces the typed InternalDnsNotAvailable error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of internal DNS views","status":"skipped","title":"list returns a well-typed array of internal DNS views","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates a deployed internal DNS view","status":"skipped","title":"list enumerates a deployed internal DNS view","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update zones in place, and delete an internal DNS view","status":"skipped","title":"create, update zones in place, and delete an internal DNS view","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/View.test.ts"},{"assertionResults":[{"ancestorTitles":["ZoneSettings"],"fullName":"ZoneSettings pins flattenAllCnames and restores the pre-management value on destroy","status":"skipped","title":"pins flattenAllCnames and restores the pre-management value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ZoneSettings"],"fullName":"ZoneSettings updates in place, unions managedKeys, restores all managed fields","status":"skipped","title":"updates in place, unions managedKeys, restores all managed fields","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ZoneSettings"],"fullName":"ZoneSettings list enumerates DNS settings across all zones","status":"skipped","title":"list enumerates DNS settings across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneSettings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a zone transfer ACL","status":"skipped","title":"create, update in place, and delete a zone transfer ACL","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"generates a deterministic name when none is provided","status":"skipped","title":"generates a deterministic name when none is provided","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed zone transfer ACLs","status":"skipped","title":"list enumerates the deployed zone transfer ACLs","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferAcl.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates incoming configs across all zones","status":"passed","title":"list enumerates incoming configs across all zones","duration":4471.455099999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"incoming config does not persist on non-secondary zones (typed not-found)","status":"skipped","title":"incoming config does not persist on non-secondary zones (typed not-found)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update autoRefreshSeconds in place, and delete the incoming config","status":"skipped","title":"create, update autoRefreshSeconds in place, and delete the incoming config","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385157831,"endTime":1782385162302.455,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferIncoming.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed OutgoingZoneTransfersNotAllowed error on unentitled zones","status":"skipped","title":"surfaces the typed OutgoingZoneTransfersNotAllowed error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, sync peers and enabled state, and delete the outgoing config","status":"skipped","title":"create, sync peers and enabled state, and delete the outgoing config","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the outgoing transfer config per zone","status":"skipped","title":"list enumerates the outgoing transfer config per zone","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferOutgoing.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create with connection settings, update in place, and delete a peer","status":"skipped","title":"create with connection settings, update in place, and delete a peer","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"wires a TSIG reference through to the peer","status":"skipped","title":"wires a TSIG reference through to the peer","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed peer","status":"skipped","title":"list enumerates the deployed peer","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferPeer.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, rotate the secret in place, and delete a TSIG","status":"skipped","title":"create, rotate the secret in place, and delete a TSIG","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed TSIG","status":"skipped","title":"list enumerates the deployed TSIG","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferTsig.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed DnsFirewallNotEntitled error on unentitled accounts","status":"skipped","title":"surfaces the typed DnsFirewallNotEntitled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, verify out-of-band, destroy, and wait until gone","status":"skipped","title":"create, verify out-of-band, destroy, and wait until gone","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable settings in place, replace on rename","status":"skipped","title":"update mutable settings in place, replace on rename","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the DNS firewall cluster Attributes array","status":"skipped","title":"list returns the DNS firewall cluster Attributes array","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed DNS firewall cluster","status":"skipped","title":"list enumerates the deployed DNS firewall cluster","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DnsFirewall/DnsFirewall.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a custom device profile","status":"skipped","title":"create, update in place, and delete a custom device profile","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed custom device profile","status":"skipped","title":"list enumerates the deployed custom device profile","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/CustomProfile.test.ts"},{"assertionResults":[{"ancestorTitles":["DefaultProfile"],"fullName":"DefaultProfile reads the existing default device profile without mutating it","status":"skipped","title":"reads the existing default device profile without mutating it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DefaultProfile"],"fullName":"DefaultProfile toggles captivePortal and restores the original value","status":"skipped","title":"toggles captivePortal and restores the original value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DefaultProfile"],"fullName":"DefaultProfile list returns the singleton default device profile","status":"skipped","title":"list returns the singleton default device profile","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/DefaultProfile.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden entitlement error","status":"skipped","title":"unentitled accounts surface the typed Forbidden entitlement error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a DEX test","status":"skipped","title":"create, update in place, and destroy a DEX test","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed DEX test","status":"skipped","title":"list enumerates the deployed DEX test","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed empty array on unentitled accounts","status":"skipped","title":"list returns a well-typed empty array on unentitled accounts","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/DexTest.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a managed network","status":"skipped","title":"create, update in place, and delete a managed network","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed managed network","status":"skipped","title":"list enumerates the deployed managed network","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/ManagedNetwork.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates without a real provider surface the typed InvalidPostureIntegrationConfig error","status":"skipped","title":"creates without a real provider surface the typed InvalidPostureIntegrationConfig error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a posture integration","status":"skipped","title":"create, update in place, and destroy a posture integration","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates posture integrations in the account","status":"passed","title":"list enumerates posture integrations in the account","duration":27490.1985,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed posture integration","status":"skipped","title":"list includes a deployed posture integration","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385109677,"endTime":1782385137167.1985,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/PostureIntegration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a posture rule","status":"skipped","title":"create, update in place, and delete a posture rule","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the rule type triggers a replacement","status":"skipped","title":"changing the rule type triggers a replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed posture rule","status":"skipped","title":"list enumerates the deployed posture rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/PostureRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"patches disableForTime and restores the original value on destroy","status":"skipped","title":"patches disableForTime and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account's device settings singleton","status":"skipped","title":"list returns the account's device settings singleton","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed email address","status":"skipped","title":"list enumerates the deployed email address","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailAddress.test.ts"},{"assertionResults":[{"ancestorTitles":["EmailCatchAll"],"fullName":"EmailCatchAll configures the catch-all rule and restores the baseline on destroy","status":"skipped","title":"configures the catch-all rule and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["EmailCatchAll"],"fullName":"EmailCatchAll updates the catch-all rule in place and keeps the captured baseline","status":"skipped","title":"updates the catch-all rule in place and keeps the captured baseline","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["EmailCatchAll"],"fullName":"EmailCatchAll list enumerates the catch-all rule across all zones","status":"skipped","title":"list enumerates the catch-all rule across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailCatchAll.test.ts"},{"assertionResults":[{"ancestorTitles":["EmailRouting"],"fullName":"EmailRouting list enumerates email routing across all zones","status":"skipped","title":"list enumerates email routing across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailRouting.test.ts"},{"assertionResults":[{"ancestorTitles":["EmailRule"],"fullName":"EmailRule list enumerates the deployed email rule across all zones","status":"skipped","title":"list enumerates the deployed email rule across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"sends an email through the Worker send_email binding","status":"skipped","title":"sends an email through the Worker send_email binding","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/SendEmail.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and destroy a sending subdomain","status":"skipped","title":"create and destroy a sending subdomain","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the name triggers replacement","status":"skipped","title":"changing the name triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing subdomain errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing subdomain errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed sending subdomain","status":"skipped","title":"list enumerates the deployed sending subdomain","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/SendingSubdomain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed EmailSecurityNotEntitled error on unentitled accounts","status":"skipped","title":"surfaces the typed EmailSecurityNotEntitled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates allow policies (read-only)","status":"skipped","title":"list enumerates allow policies (read-only)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes the deployed allow policy","status":"skipped","title":"list includes the deployed allow policy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, destroy","status":"skipped","title":"create, update in place, destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/AllowPolicy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, destroy","status":"skipped","title":"create, update in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed block sender","status":"passed","title":"list enumerates the deployed block sender","duration":28032.281000000003,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385109596,"endTime":1782385137628.281,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/BlockSender.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"adopts an onboarded domain, patches settings in place, offboards on destroy","status":"skipped","title":"adopts an onboarded domain, patches settings in place, offboards on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates Email Security domains (or [] when unentitled)","status":"skipped","title":"list enumerates Email Security domains (or [] when unentitled)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/Domain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, destroy","status":"skipped","title":"create, update in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account impersonation registry","status":"skipped","title":"list returns the account impersonation registry","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed impersonation registry entry","status":"skipped","title":"list enumerates the deployed impersonation registry entry","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/ImpersonationRegistryEntry.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, destroy","status":"skipped","title":"create, update in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates trusted domains","status":"skipped","title":"list enumerates trusted domains","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/TrustedDomain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a zone-scoped ip rule","status":"skipped","title":"create and delete a zone-scoped ip rule","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mode and notes in place (same ruleId)","status":"skipped","title":"update mode and notes in place (same ruleId)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the configuration triggers replacement","status":"skipped","title":"changing the configuration triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed zone-scoped rule","status":"skipped","title":"list enumerates the deployed zone-scoped rule","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"account-scoped rule (no zoneId) create, update, delete","status":"skipped","title":"account-scoped rule (no zoneId) create, update, delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Firewall/AccessRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update urls/configurations/description/paused in place, destroy","status":"skipped","title":"create, update urls/configurations/description/paused in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed lockdown rule","status":"skipped","title":"list enumerates the deployed lockdown rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Firewall/Lockdown.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update mode/paused/description/userAgent in place, destroy","status":"skipped","title":"create, update mode/paused/description/userAgent in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates UA rules across all zones","status":"skipped","title":"list enumerates UA rules across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Firewall/UaRule.test.ts"},{"assertionResults":[{"ancestorTitles":["FraudDetectionSettings"],"fullName":"FraudDetectionSettings adopts the zone singleton without writing and restores nothing on destroy","status":"skipped","title":"adopts the zone singleton without writing and restores nothing on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["FraudDetectionSettings"],"fullName":"FraudDetectionSettings surfaces the typed FraudDetectionNotEntitled error on unentitled zones","status":"skipped","title":"surfaces the typed FraudDetectionNotEntitled error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["FraudDetectionSettings"],"fullName":"FraudDetectionSettings manages fraud detection settings and restores them on destroy","status":"skipped","title":"manages fraud detection settings and restores them on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["FraudDetectionSettings"],"fullName":"FraudDetectionSettings list enumerates the settings across all zones","status":"skipped","title":"list enumerates the settings across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Fraud/FraudDetectionSettings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create an activated certificate, deactivate in place, destroy","status":"skipped","title":"create an activated certificate, deactivate in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the validity period replaces the certificate","status":"skipped","title":"changing the validity period replaces the certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed gateway certificate","status":"skipped","title":"list enumerates the deployed gateway certificate","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Certificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"manage activityLog and protocolDetection, then restore on destroy","status":"skipped","title":"manage activityLog and protocolDetection, then restore on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account Gateway configuration singleton","status":"skipped","title":"list returns the account Gateway configuration singleton","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Configuration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy a DOMAIN list","status":"skipped","title":"create, verify, and destroy a DOMAIN list","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update items, description, and name in place","status":"skipped","title":"update items, description, and name in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the type replaces the list","status":"skipped","title":"changing the type replaces the list","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/List.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy a location","status":"skipped","title":"create, verify, and destroy a location","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name and ecsSupport in place (same id)","status":"skipped","title":"update name and ecsSupport in place (same id)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates deployed gateway locations","status":"skipped","title":"list enumerates deployed gateway locations","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Location.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"manage logging settings, update in place, restore on destroy","status":"skipped","title":"manage logging settings, update in place, restore on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account's Gateway logging singleton","status":"skipped","title":"list returns the account's Gateway logging singleton","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Logging.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, and destroy an identity-kind proxy endpoint","status":"skipped","title":"create, update, and destroy an identity-kind proxy endpoint","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"ip-kind endpoints surface the typed entitlement error","status":"skipped","title":"ip-kind endpoints surface the typed entitlement error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed proxy endpoint","status":"skipped","title":"list enumerates the deployed proxy endpoint","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/ProxyEndpoint.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed Gateway rule","status":"skipped","title":"list enumerates the deployed Gateway rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Rule.test.ts"},{"assertionResults":[{"ancestorTitles":["GoogleTagGateway"],"fullName":"GoogleTagGateway configures the gateway, updates in place, and restores the baseline on destroy","status":"skipped","title":"configures the gateway, updates in place, and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["GoogleTagGateway"],"fullName":"GoogleTagGateway no-op redeploy skips the PUT and destroy is idempotent","status":"skipped","title":"no-op redeploy skips the PUT and destroy is idempotent","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["GoogleTagGateway"],"fullName":"GoogleTagGateway list enumerates configured zones","status":"skipped","title":"list enumerates configured zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/GoogleTagGateway/GoogleTagGateway.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete a Flagship app","status":"skipped","title":"create, update, delete a Flagship app","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates an app after out-of-band delete","status":"skipped","title":"recreates an app after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed Flagship app","status":"skipped","title":"list enumerates the deployed Flagship app","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Flagship/App.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete a flag in an app","status":"skipped","title":"create, update, delete a flag in an app","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the flag when the key changes","status":"skipped","title":"replaces the flag when the key changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates a flag after out-of-band delete","status":"skipped","title":"recreates a flag after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed flag","status":"skipped","title":"list enumerates the deployed flag","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Flagship/Flag.test.ts"},{"assertionResults":[],"startTime":1782384949333,"endTime":1782384949333,"status":"failed","message":"FlagshipApp is not a function","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Flagship/Flagship.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an HTTP health check with default name","status":"skipped","title":"create and delete an HTTP health check with default name","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable props in place (same id), then no-op redeploy","status":"skipped","title":"update mutable props in place (same id), then no-op redeploy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing type HTTP→TCP updates in place; delete is idempotent","status":"skipped","title":"changing type HTTP→TCP updates in place; delete is idempotent","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing check errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing check errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed health check across zones","status":"skipped","title":"list enumerates the deployed health check across zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Healthcheck/Healthcheck.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"lists overrides and surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","status":"skipped","title":"lists overrides and surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates per-hostname TLS overrides","status":"passed","title":"list enumerates per-hostname TLS overrides","duration":1968.3135000000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a min_tls_version override","status":"skipped","title":"create, update in place, and destroy a min_tls_version override","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385141866,"endTime":1782385143834.3135,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/HostnameTlsSetting/HostnameTlsSetting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete hyperdrive with default props","status":"passed","title":"create and delete hyperdrive with default props","duration":11549.097499999996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete hyperdrive","status":"passed","title":"create, update, delete hyperdrive","duration":12341.469700000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed hyperdrive","status":"passed","title":"list enumerates the deployed hyperdrive","duration":9613.150100000006,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385121028,"endTime":1782385133371.4697,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Hyperdrive/Hyperdrive.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"async worker reads image info via env Images binding","status":"skipped","title":"async worker reads image info via env Images binding","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker reads image info via yield* Images","status":"skipped","title":"effect worker reads image info via yield* Images","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Images/Images.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the account's signing keys","status":"skipped","title":"list enumerates the account's signing keys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"surfaces the typed ImagesAccessNotEnabled error on unentitled accounts","status":"skipped","title":"surfaces the typed ImagesAccessNotEnabled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create a signing key, no-op redeploy keeps the value, delete","status":"skipped","title":"create a signing key, no-op redeploy keeps the value, delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Images/SigningKey.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a variant","status":"skipped","title":"create, update in place, and delete a variant","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replace a variant when the name changes","status":"skipped","title":"replace a variant when the name changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed variant","status":"skipped","title":"list enumerates the deployed variant","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Images/Variant.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed KeylessSslNotAvailable error on non-Enterprise zones","status":"skipped","title":"surfaces the typed KeylessSslNotAvailable error on non-Enterprise zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates keyless certificates across zones","status":"passed","title":"list enumerates keyless certificates across zones","duration":1043.5675999999985,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed keyless certificate","status":"skipped","title":"list includes a deployed keyless certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates, updates in place, replaces on certificate change, and destroys","status":"skipped","title":"creates, updates in place, replaces on certificate change, and destroys","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385134478,"endTime":1782385135521.5676,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/KeylessCertificate/KeylessCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed IndicatorFeedsNotEntitled error","status":"skipped","title":"unentitled accounts surface the typed IndicatorFeedsNotEntitled error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of indicator feeds","status":"passed","title":"list returns a well-typed array of indicator feeds","duration":26163.2362,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed indicator feed","status":"skipped","title":"list enumerates the deployed indicator feed","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create (or adopt), verify out-of-band, update in place, destroy","status":"skipped","title":"create (or adopt), verify out-of-band, update in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"uploads a STIX2 snapshot and grants/revokes a consumer permission","status":"skipped","title":"uploads a STIX2 snapshot and grants/revokes a consumer permission","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385113378,"endTime":1782385139541.236,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Intel/IndicatorFeed.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns [] for the non-listable IndicatorFeedPermission","status":"passed","title":"list returns [] for the non-listable IndicatorFeedPermission","duration":1328.3912000000018,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385173673,"endTime":1782385175001.391,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Intel/IndicatorFeedPermission.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, update name+scope in place, destroy","status":"skipped","title":"create, verify out-of-band, update name+scope in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed resource group","status":"skipped","title":"list enumerates the deployed resource group","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Iam/ResourceGroup.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create with a policy, verify out-of-band, update policies in place, destroy","status":"skipped","title":"create with a policy, verify out-of-band, update policies in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed user group","status":"skipped","title":"list enumerates the deployed user group","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Iam/UserGroup.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, replace when the user group changes, destroy","status":"skipped","title":"create, verify out-of-band, replace when the user group changes, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates memberships across all user groups in the account","status":"skipped","title":"list enumerates memberships across all user groups in the account","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Iam/UserGroupMembership.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete namespace with default props","status":"passed","title":"create and delete namespace with default props","duration":4024.3994999999995,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete namespace","status":"passed","title":"create, update, delete namespace","duration":4966.217499999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed namespace","status":"passed","title":"list enumerates the deployed namespace","duration":4315.253099999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"existing namespace (matching title) is silently adopted without --adopt","status":"passed","title":"existing namespace (matching title) is silently adopted without --adopt","duration":4911.264500000001,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385144306,"endTime":1782385149276.2175,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/KV/Namespace.test.ts"},{"assertionResults":[{"ancestorTitles":["LeakedCredentialDetection"],"fullName":"LeakedCredentialDetection list enumerates custom detections across all zones","status":"skipped","title":"list enumerates custom detections across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LeakedCredentialCheck/Detection.test.ts"},{"assertionResults":[{"ancestorTitles":["LeakedCredentialCheck"],"fullName":"LeakedCredentialCheck enables leaked credential checks and restores the baseline on destroy","status":"skipped","title":"enables leaked credential checks and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["LeakedCredentialCheck"],"fullName":"LeakedCredentialCheck surfaces the typed DetectionQuotaExceeded error on unentitled zones","status":"skipped","title":"surfaces the typed DetectionQuotaExceeded error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["LeakedCredentialCheck"],"fullName":"LeakedCredentialCheck list enumerates the check across all zones","status":"skipped","title":"list enumerates the check across all zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["LeakedCredentialCheck"],"fullName":"LeakedCredentialCheck creates, updates, and destroys a custom detection","status":"skipped","title":"creates, updates, and destroys a custom detection","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LeakedCredentialCheck/LeakedCredentialCheck.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed LoadBalancingNotEnabledForZone error without the LB subscription","status":"skipped","title":"surfaces the typed LoadBalancingNotEnabledForZone error without the LB subscription","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a load balancer","status":"skipped","title":"create, update in place, and destroy a load balancer","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of load balancers","status":"passed","title":"list returns a well-typed array of load balancers","duration":3123.6962999999996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed load balancer","status":"skipped","title":"list enumerates the deployed load balancer","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385158780,"endTime":1782385161903.6963,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/LoadBalancer.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed MonitorIntervalOutOfRange error without the LB subscription","status":"skipped","title":"surfaces the typed MonitorIntervalOutOfRange error without the LB subscription","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a monitor","status":"skipped","title":"create, update in place, and destroy a monitor","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns an array of monitor attributes","status":"skipped","title":"list returns an array of monitor attributes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed monitor","status":"skipped","title":"list enumerates the deployed monitor","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/Monitor.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed MonitorGroupsNotEnabled error on non-Enterprise accounts","status":"skipped","title":"surfaces the typed MonitorGroupsNotEnabled error on non-Enterprise accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns an array of monitor groups","status":"skipped","title":"list returns an array of monitor groups","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed monitor group","status":"skipped","title":"list enumerates the deployed monitor group","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a monitor group","status":"skipped","title":"create, update in place, and destroy a monitor group","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/MonitorGroup.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed PoolAccessFailed error without the LB subscription","status":"skipped","title":"surfaces the typed PoolAccessFailed error without the LB subscription","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a health-checked pool","status":"skipped","title":"create, update in place, and destroy a health-checked pool","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed pool","status":"skipped","title":"list enumerates the deployed pool","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/Pool.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"diag list","status":"skipped","title":"diag list","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/_diag.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed LogsControlNotAuthorized error on unentitled accounts","status":"skipped","title":"surfaces the typed LogsControlNotAuthorized error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, verify out-of-band, update in place, destroy, and wait until gone","status":"skipped","title":"create, verify out-of-band, update in place, destroy, and wait until gone","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list tolerates the typed LogsControlNotAuthorized error on unentitled accounts","status":"skipped","title":"list tolerates the typed LogsControlNotAuthorized error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the account CMB config singleton","status":"skipped","title":"list enumerates the account CMB config singleton","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LogsControl/CmbConfig.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed LogsControlNotAuthorized error on unentitled zones","status":"skipped","title":"surfaces the typed LogsControlNotAuthorized error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"pins the retention flag, updates in place, and restores on destroy","status":"skipped","title":"pins the retention flag, updates in place, and restores on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the retention flag across all zones","status":"passed","title":"list enumerates the retention flag across all zones","duration":854.4278999999988,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list contains the entitled test zone's retention flag","status":"skipped","title":"list contains the entitled test zone's retention flag","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385132549,"endTime":1782385133403.428,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LogsControl/RetentionFlag.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, and delete an account-scoped job pushing to R2","status":"skipped","title":"create, update, and delete an account-scoped job pushing to R2","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed Logpush job","status":"skipped","title":"list enumerates the deployed Logpush job","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the dataset triggers a replacement","status":"skipped","title":"changing the dataset triggers a replacement","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Logpush/Job.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed FeatureNotEnabled error","status":"skipped","title":"unentitled accounts surface the typed FeatureNotEnabled error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a sync, updates mutable props in place, and destroys it","status":"skipped","title":"creates a sync, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account catalog syncs","status":"skipped","title":"list enumerates account catalog syncs","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the sync when destinationType changes","status":"skipped","title":"replaces the sync when destinationType changes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicCloudNetworking/CatalogSync.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed FeatureNotEnabled error","status":"skipped","title":"unentitled accounts surface the typed FeatureNotEnabled error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of integrations","status":"skipped","title":"list returns a well-typed array of integrations","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed integration","status":"skipped","title":"list enumerates the deployed integration","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"registers an AWS integration, updates it in place, and destroys it","status":"skipped","title":"registers an AWS integration, updates it in place, and destroys it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the integration when cloudType changes","status":"skipped","title":"replaces the integration when cloudType changes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicCloudNetworking/CloudIntegration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed FeatureNotEnabled error","status":"skipped","title":"unentitled accounts surface the typed FeatureNotEnabled error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an on-ramp, updates mutable props in place, and destroys it","status":"skipped","title":"creates an on-ramp, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns on-ramps or a typed [] when unentitled","status":"skipped","title":"list returns on-ramps or a typed [] when unentitled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed on-ramp","status":"skipped","title":"list enumerates the deployed on-ramp","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicCloudNetworking/OnRamp.test.ts"},{"assertionResults":[{"ancestorTitles":["MagicNetworkMonitoring.Config list"],"fullName":"MagicNetworkMonitoring.Config list list enumerates the account MNM config","status":"skipped","title":"list enumerates the account MNM config","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/Config.test.ts"},{"assertionResults":[{"ancestorTitles":["MagicNetworkMonitoring"],"fullName":"MagicNetworkMonitoring creates, updates in place, and deletes the account config","status":"skipped","title":"creates, updates in place, and deletes the account config","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["MagicNetworkMonitoring"],"fullName":"MagicNetworkMonitoring creates a threshold rule, updates it in place, and replaces on type change","status":"skipped","title":"creates a threshold rule, updates it in place, and replaces on type change","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/MagicNetworkMonitoring.test.ts"},{"assertionResults":[{"ancestorTitles":["MagicNetworkMonitoring.Rule"],"fullName":"MagicNetworkMonitoring.Rule list enumerates the deployed rule","status":"skipped","title":"list enumerates the deployed rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/Rule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicWanUnauthorized error","status":"skipped","title":"unentitled accounts surface the typed MagicWanUnauthorized error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a custom app, updates mutable props in place, and destroys it","status":"skipped","title":"creates a custom app, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account apps (well-typed [] when unentitled)","status":"skipped","title":"list enumerates account apps (well-typed [] when unentitled)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed custom app","status":"skipped","title":"list includes a deployed custom app","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/App.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicTransitNotOnboarded error","status":"passed","title":"unentitled accounts surface the typed MagicTransitNotOnboarded error","duration":76610.1182,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed GRE tunnels","status":"passed","title":"list enumerates the deployed GRE tunnels","duration":26468.9581,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a GRE tunnel, updates mutable props in place, and destroys it","status":"skipped","title":"creates a GRE tunnel, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385077707,"endTime":1782385154317.1182,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/GreTunnel.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicTransitNotOnboarded error","status":"skipped","title":"unentitled accounts surface the typed MagicTransitNotOnboarded error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account IPsec tunnels (read-only [] when unentitled)","status":"passed","title":"list enumerates account IPsec tunnels (read-only [] when unentitled)","duration":27089.0079,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an IPsec tunnel, updates mutable props in place, and destroys it","status":"skipped","title":"creates an IPsec tunnel, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385110907,"endTime":1782385137996.0078,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/IpsecTunnel.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicWanUnauthorized error","status":"skipped","title":"unentitled accounts surface the typed MagicWanUnauthorized error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account Magic WAN sites","status":"skipped","title":"list enumerates account Magic WAN sites","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a site with WAN, LAN, and ACL, updates in place, and destroys","status":"skipped","title":"creates a site with WAN, LAN, and ACL, updates in place, and destroys","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/Site.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed site ACLs","status":"passed","title":"list enumerates the deployed site ACLs","duration":26139.808500000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Magic WAN error for ACL listing","status":"skipped","title":"unentitled accounts surface the typed Magic WAN error for ACL listing","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385112837,"endTime":1782385138976.8086,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/SiteAcl.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a well-typed array (empty on unentitled accounts)","status":"skipped","title":"list returns a well-typed array (empty on unentitled accounts)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed Magic WAN site LANs","status":"skipped","title":"list enumerates the deployed Magic WAN site LANs","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/SiteLan.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a well-typed array of site WANs","status":"passed","title":"list returns a well-typed array of site WANs","duration":26490.691100000004,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed site WAN","status":"skipped","title":"list enumerates the deployed site WAN","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385112848,"endTime":1782385139338.6912,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/SiteWan.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicTransitNotOnboarded error","status":"skipped","title":"unentitled accounts surface the typed MagicTransitNotOnboarded error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of routes","status":"skipped","title":"list returns a well-typed array of routes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed static route","status":"skipped","title":"list enumerates the deployed static route","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"routes a prefix over a GRE tunnel, updates in place, and destroys","status":"skipped","title":"routes a prefix over a GRE tunnel, updates in place, and destroys","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/StaticRoute.test.ts"},{"assertionResults":[{"ancestorTitles":["MtlsCertificate"],"fullName":"MtlsCertificate create and delete a CA certificate","status":"skipped","title":"create and delete a CA certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["MtlsCertificate"],"fullName":"MtlsCertificate create and delete a leaf certificate with private key","status":"skipped","title":"create and delete a leaf certificate with private key","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["MtlsCertificate"],"fullName":"MtlsCertificate replaces the certificate when the PEM changes","status":"skipped","title":"replaces the certificate when the PEM changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["MtlsCertificate"],"fullName":"MtlsCertificate list enumerates the deployed mTLS certificate","status":"skipped","title":"list enumerates the deployed mTLS certificate","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MtlsCertificate/MtlsCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins the default ASN, updates in place, and restores the original on destroy","status":"skipped","title":"pins the default ASN, updates in place, and restores the original on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the CNI settings singleton","status":"skipped","title":"list enumerates the CNI settings singleton","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/NetworkInterconnects/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden error","status":"skipped","title":"unentitled accounts surface the typed Forbidden error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list either enumerates organizations or tolerates the unentitled account","status":"skipped","title":"list either enumerates organizations or tolerates the unentitled account","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed organization","status":"skipped","title":"list enumerates the deployed organization","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, verify out-of-band, update in place, destroy","status":"skipped","title":"create, verify out-of-band, update in place, destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Organization/Organization.test.ts"},{"assertionResults":[{"ancestorTitles":["ManagedTransforms"],"fullName":"ManagedTransforms manages a named transform, updates in place, and restores it on destroy","status":"skipped","title":"manages a named transform, updates in place, and restores it on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ManagedTransforms"],"fullName":"ManagedTransforms destroy restores a transform that was enabled before management","status":"skipped","title":"destroy restores a transform that was enabled before management","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ManagedTransforms"],"fullName":"ManagedTransforms deploy with no transforms named adopts the singleton without writing","status":"skipped","title":"deploy with no transforms named adopts the singleton without writing","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ManagedTransforms"],"fullName":"ManagedTransforms list enumerates managed transforms across all zones","status":"skipped","title":"list enumerates managed transforms across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ManagedTransforms/ManagedTransforms.test.ts"},{"assertionResults":[{"ancestorTitles":["OriginPostQuantumEncryption"],"fullName":"OriginPostQuantumEncryption pins the setting and restores the original value on destroy","status":"skipped","title":"pins the setting and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["OriginPostQuantumEncryption"],"fullName":"OriginPostQuantumEncryption updates the value in place and keeps the captured initial value","status":"skipped","title":"updates the value in place and keeps the captured initial value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["OriginPostQuantumEncryption"],"fullName":"OriginPostQuantumEncryption no-op redeploy converges without changing the setting","status":"skipped","title":"no-op redeploy converges without changing the setting","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["OriginPostQuantumEncryption"],"fullName":"OriginPostQuantumEncryption list enumerates the setting across all zones","status":"skipped","title":"list enumerates the setting across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginPostQuantumEncryption/OriginPostQuantumEncryption.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"issue, verify, and revoke a certificate","status":"skipped","title":"issue, verify, and revoke a certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates issued certificates","status":"skipped","title":"list enumerates issued certificates","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replacement on requestedValidity change","status":"skipped","title":"replacement on requestedValidity change","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replacement on hostnames change","status":"skipped","title":"replacement on hostnames change","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginCaCertificate/OriginCaCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":["OriginTlsClientAuthCertificate"],"fullName":"OriginTlsClientAuthCertificate uploads and deletes a zone client certificate","status":"skipped","title":"uploads and deletes a zone client certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["OriginTlsClientAuthCertificate"],"fullName":"OriginTlsClientAuthCertificate replaces the certificate when the PEM changes","status":"skipped","title":"replaces the certificate when the PEM changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["OriginTlsClientAuthCertificate"],"fullName":"OriginTlsClientAuthCertificate list enumerates the deployed zone client certificate","status":"skipped","title":"list enumerates the deployed zone client certificate","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/Certificate.test.ts"},{"assertionResults":[{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation list returns [] for the non-listable association","status":"passed","title":"list returns [] for the non-listable association","duration":749.4066000000021,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation associates a hostname, updates cert and enablement in place, voids on destroy","status":"skipped","title":"associates a hostname, updates cert and enablement in place, voids on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation replaces the association when the hostname changes","status":"skipped","title":"replaces the association when the hostname changes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385179609,"endTime":1782385180358.4065,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/HostnameAssociation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"uploads and deletes a hostname client certificate","status":"skipped","title":"uploads and deletes a hostname client certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the hostname certificate when the PEM changes","status":"skipped","title":"replaces the hostname certificate when the PEM changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed hostname certificate","status":"skipped","title":"list enumerates the deployed hostname certificate","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/HostnameCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":["Setting"],"fullName":"Setting enables AOP and restores the original value on destroy","status":"skipped","title":"enables AOP and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Setting"],"fullName":"Setting updates the enabled flag in place","status":"skipped","title":"updates the enabled flag in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Setting"],"fullName":"Setting list enumerates the setting across all zones","status":"skipped","title":"list enumerates the setting across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/Setting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, replace on branch change, and destroy a deployment","status":"failed","title":"create, replace on branch change, and destroy a deployment","duration":120425.54870000001,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Deployment.test.ts:92:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates deployments across Pages projects","status":"skipped","title":"list enumerates deployments across Pages projects","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385047694,"endTime":1782385168119.5486,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Deployment.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"attach and detach a custom domain","status":"failed","title":"attach and detach a custom domain","duration":120382.38579999999,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Domain.test.ts:99:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the domain name triggers replacement","status":"failed","title":"changing the domain name triggers replacement","duration":120414.63690000001,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Domain.test.ts:166:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates domains across all Pages projects","status":"skipped","title":"list enumerates domains across all Pages projects","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385047642,"endTime":1782385168059.637,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Domain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a project with generated name","status":"failed","title":"create and delete a project with generated name","duration":32186.8551,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable props in place (same project id)","status":"failed","title":"update mutable props in place (same project id)","duration":120309.92129999999,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Project.test.ts:94:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed project","status":"failed","title":"list enumerates the deployed project","duration":120389.8469,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Project.test.ts:205:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the name triggers replacement","status":"failed","title":"changing the name triggers replacement","duration":120434.1963,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Project.test.ts:235:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]}],"startTime":1782385047779,"endTime":1782385168216.1963,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Project.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed PolicyQuotaExceeded error on unentitled zones","status":"skipped","title":"surfaces the typed PolicyQuotaExceeded error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates Page Shield policies across all zones","status":"passed","title":"list enumerates Page Shield policies across all zones","duration":1907.130799999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates, updates in place, and deletes a CSP policy","status":"skipped","title":"creates, updates in place, and deletes a CSP policy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385167246,"endTime":1782385169153.1309,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/PageShield/Policy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"enables Page Shield, updates in place, and restores the baseline on destroy","status":"skipped","title":"enables Page Shield, updates in place, and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"surfaces the typed NotEntitled error for plan-gated flags","status":"skipped","title":"surfaces the typed NotEntitled error for plan-gated flags","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates Page Shield settings across all zones","status":"skipped","title":"list enumerates Page Shield settings across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/PageShield/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"legacy pipeline: create, in-place update, replace on name change","status":"skipped","title":"legacy pipeline: create, in-place update, replace on name change","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed legacy pipeline","status":"skipped","title":"list enumerates the deployed legacy pipeline","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/LegacyPipeline.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed pipeline","status":"skipped","title":"list enumerates the deployed pipeline","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/Pipeline.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"stream: create with defaults, patch http in place, replace on schema change","status":"skipped","title":"stream: create with defaults, patch http in place, replace on schema change","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"end-to-end: stream → sql pipeline → r2 sink, replacement on sink change","status":"skipped","title":"end-to-end: stream → sql pipeline → r2 sink, replacement on sink change","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/Pipelines.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed sink","status":"skipped","title":"list enumerates the deployed sink","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/Sink.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed pipeline stream","status":"skipped","title":"list enumerates the deployed pipeline stream","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/Stream.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, and destroy a page rule","status":"skipped","title":"create, verify out-of-band, and destroy a page rule","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating actions, status, priority and target syncs in place","status":"skipped","title":"updating actions, status, priority and target syncs in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing rule errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing rule errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed page rule","status":"skipped","title":"list enumerates the deployed page rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/PageRule/PageRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"promotes a dev queue to a live queue on deploy","status":"skipped","title":"promotes a dev queue to a live queue on deploy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed queue","status":"skipped","title":"list enumerates the deployed queue","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"suppresses deletion of a dev-only queue","status":"skipped","title":"suppresses deletion of a dev-only queue","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/Queue.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update settings, replace script, delete","status":"skipped","title":"create, update settings, replace script, delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates consumer after out-of-band delete","status":"skipped","title":"recreates consumer after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopts existing consumer after local state loss","status":"skipped","title":"adopts existing consumer after local state loss","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"fails clearly when queue has consumer for different script","status":"skipped","title":"fails clearly when queue has consumer for different script","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"suppresses deletion of a dev-only consumer","status":"skipped","title":"suppresses deletion of a dev-only consumer","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"promotes a dev consumer to a live consumer on deploy","status":"skipped","title":"promotes a dev consumer to a live consumer on deploy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed consumer","status":"skipped","title":"list enumerates the deployed consumer","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/QueueConsumer.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"send → subscribe handler → DO state → polled by test client","status":"skipped","title":"send → subscribe handler → DO state → polled by test client","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/RoundTrip.test.ts"},{"assertionResults":[{"ancestorTitles":["Subscription"],"fullName":"Subscription create r2 event subscription into a queue and destroy it","status":"skipped","title":"create r2 event subscription into a queue and destroy it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Subscription"],"fullName":"Subscription update mutable props in place (same subscriptionId)","status":"skipped","title":"update mutable props in place (same subscriptionId)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Subscription"],"fullName":"Subscription replaces the subscription when the source changes","status":"skipped","title":"replaces the subscription when the source changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Subscription"],"fullName":"Subscription list enumerates the deployed subscription","status":"skipped","title":"list enumerates the deployed subscription","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/Subscription.test.ts"},{"assertionResults":[{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings passes scalar fields through unchanged","status":"passed","title":"passes scalar fields through unchanged","duration":14.842899999999645,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings converts maxWaitTime to whole milliseconds","status":"passed","title":"converts maxWaitTime to whole milliseconds","duration":12.233799999999974,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings rounds sub-millisecond maxWaitTime up","status":"passed","title":"rounds sub-millisecond maxWaitTime up","duration":11.855400000000373,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings converts retryDelay to whole seconds","status":"passed","title":"converts retryDelay to whole seconds","duration":11.686900000000605,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings rounds partial-second retryDelay up","status":"passed","title":"rounds partial-second retryDelay up","duration":11.577700000001641,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings leaves missing time fields undefined","status":"passed","title":"leaves missing time fields undefined","duration":1.2163000000000466,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385169797,"endTime":1782385169814.2163,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/toConsumerSettings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete bucket with default props","status":"skipped","title":"create and delete bucket with default props","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete bucket","status":"skipped","title":"create, update, delete bucket","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"existing bucket (matching name) is silently adopted without --adopt","status":"skipped","title":"existing bucket (matching name) is silently adopted without --adopt","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"destroying a bucket empties its objects first","status":"skipped","title":"destroying a bucket empties its objects first","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"lifecycle rules are added, updated, and removed","status":"skipped","title":"lifecycle rules are added, updated, and removed","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/Bucket.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update rules in place, and delete","status":"skipped","title":"create, update rules in place, and delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the queue triggers a replacement","status":"skipped","title":"changing the queue triggers a replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates deployed bucket event notifications","status":"skipped","title":"list enumerates deployed bucket event notifications","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/BucketEventNotification.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"reads the disabled baseline and surfaces typed errors without external creds","status":"skipped","title":"reads the disabled baseline and surfaces typed errors without external creds","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates buckets that have Sippy enabled","status":"skipped","title":"list enumerates buckets that have Sippy enabled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a bucket with Sippy enabled","status":"skipped","title":"list includes a bucket with Sippy enabled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"enable, no-op redeploy, disable on destroy","status":"skipped","title":"enable, no-op redeploy, disable on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/BucketSippy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates, updates, and deletes a bucket custom domain","status":"skipped","title":"creates, updates, and deletes a bucket custom domain","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates, updates, and deletes a bucket with multiple custom domains","status":"skipped","title":"creates, updates, and deletes a bucket with multiple custom domains","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/CustomDomain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed R2 bucket","status":"skipped","title":"list enumerates the deployed R2 bucket","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/R2Bucket.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"enable, sync maintenance config, register credential, destroy","status":"skipped","title":"enable, sync maintenance config, register credential, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed catalog","status":"skipped","title":"list enumerates the deployed catalog","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"re-enables after out-of-band disable","status":"skipped","title":"re-enables after out-of-band disable","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2DataCatalog/R2DataCatalog.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden error","status":"passed","title":"unentitled accounts surface the typed Forbidden error","duration":53150.91390000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create (or adopt), verify out-of-band, destroy forgets but keeps the app","status":"passed","title":"create (or adopt), verify out-of-band, destroy forgets but keeps the app","duration":27559.470699999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed RealtimeKit app","status":"passed","title":"list enumerates the deployed RealtimeKit app","duration":27893.337600000003,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385082261,"endTime":1782385135411.9138,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RealtimeKit/App.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create with defaults, verify out-of-band, update in place, destroy","status":"passed","title":"create with defaults, verify out-of-band, update in place, destroy","duration":26918.565999999995,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed preset","status":"passed","title":"list enumerates the deployed preset","duration":26748.291,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385110674,"endTime":1782385137592.566,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RealtimeKit/Preset.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, update in place, destroy","status":"passed","title":"create, verify out-of-band, update in place, destroy","duration":28138.6066,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates webhooks across the account's apps","status":"passed","title":"list enumerates webhooks across the account's apps","duration":52843.54770000001,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385083601,"endTime":1782385136448.5476,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RealtimeKit/Webhook.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"regional hostname lifecycle (typed error on unentitled zones)","status":"skipped","title":"regional hostname lifecycle (typed error on unentitled zones)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates regional hostnames across zones","status":"skipped","title":"list enumerates regional hostnames across zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RegionalHostname/RegionalHostname.test.ts"},{"assertionResults":[{"ancestorTitles":["Domain"],"fullName":"Domain adopts a registered domain, no-op syncs, and never releases it on destroy","status":"skipped","title":"adopts a registered domain, no-op syncs, and never releases it on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Domain"],"fullName":"Domain updates settings in place and restores the baseline on destroy","status":"skipped","title":"updates settings in place and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Domain"],"fullName":"Domain list enumerates registrar domains on the account","status":"skipped","title":"list enumerates registrar domains on the account","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Registrar/Domain.test.ts"},{"assertionResults":[{"ancestorTitles":["async worker (env binding)"],"fullName":"async worker (env binding) throttles requests past the configured limit","status":"skipped","title":"throttles requests past the configured limit","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["async worker (env binding)"],"fullName":"async worker (env binding) tracks each key independently","status":"skipped","title":"tracks each key independently","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["effect worker (yield* RateLimit)"],"fullName":"effect worker (yield* RateLimit) throttles requests past the configured limit","status":"skipped","title":"throttles requests past the configured limit","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["effect worker (yield* RateLimit)"],"fullName":"effect worker (yield* RateLimit) tracks each key independently","status":"skipped","title":"tracks each key independently","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RateLimit/RateLimit.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"reads succeed and write-blocked accounts surface the typed Forbidden error","status":"skipped","title":"reads succeed and write-blocked accounts surface the typed Forbidden error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the account's sent shares (typed Attributes), and includes a deployed share when entitled","status":"skipped","title":"list enumerates the account's sent shares (typed Attributes), and includes a deployed share when entitled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a share with a gateway policy, renames in place, adds and removes a resource, and destroys","status":"skipped","title":"creates a share with a gateway policy, renames in place, adds and removes a resource, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"manages standalone ShareResource and ShareRecipient on an existing share","status":"skipped","title":"manages standalone ShareResource and ShareRecipient on an existing share","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ResourceSharing/Share.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list() enumerates share recipients across the account's sent shares","status":"passed","title":"list() enumerates share recipients across the account's sent shares","duration":28904.0202,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list() includes a freshly deployed ShareRecipient","status":"skipped","title":"list() includes a freshly deployed ShareRecipient","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385109714,"endTime":1782385138618.0203,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ResourceSharing/ShareRecipient.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates share resources across the account's sent shares (typed Attributes), and includes a deployed entry when entitled","status":"skipped","title":"list enumerates share resources across the account's sent shares (typed Attributes), and includes a deployed entry when entitled","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ResourceSharing/ShareResource.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden error","status":"passed","title":"unentitled accounts surface the typed Forbidden error","duration":27254.641600000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, toggle active in place, and destroy a risk scoring integration","status":"skipped","title":"create, toggle active in place, and destroy a risk scoring integration","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates risk scoring integrations","status":"passed","title":"list enumerates risk scoring integrations","duration":27019.767,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a freshly deployed integration","status":"skipped","title":"list includes a freshly deployed integration","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385092087,"endTime":1782385119341.6416,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RiskScoring/Integration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an ip list with default name","status":"skipped","title":"create and delete an ip list with default name","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update description and items in place (same listId)","status":"skipped","title":"update description and items in place (same listId)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing kind replaces the list","status":"skipped","title":"changing kind replaces the list","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopts an existing list with the same name","status":"skipped","title":"adopts an existing list with the same name","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Rules/List.test.ts"},{"assertionResults":[{"ancestorTitles":["RumRule"],"fullName":"RumRule create, update in place, and delete a rule","status":"skipped","title":"create, update in place, and delete a rule","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RumRule"],"fullName":"RumRule list enumerates rules across all rulesets","status":"skipped","title":"list enumerates rules across all rulesets","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RumRule"],"fullName":"RumRule recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Rum/Rule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a host (gray-clouded) site","status":"skipped","title":"create and delete a host (gray-clouded) site","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable props in place (same siteTag)","status":"skipped","title":"update mutable props in place (same siteTag)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"zone (orange-clouded) site with autoInstall, replaced on identity flip","status":"skipped","title":"zone (orange-clouded) site with autoInstall, replaced on identity flip","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed RUM site","status":"skipped","title":"list enumerates the deployed RUM site","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Rum/Site.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"Config.redacted with literal default round-trips to runtime as Redacted","status":"skipped","title":"Config.redacted with literal default round-trips to runtime as Redacted","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Config.redacted resolved from env deploys as a secret_text and round-trips","status":"skipped","title":"Config.redacted resolved from env deploys as a secret_text and round-trips","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Config.string round-trips to runtime as a string","status":"skipped","title":"Config.string round-trips to runtime as a string","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Config.number round-trips to runtime preserving the number type","status":"skipped","title":"Config.number round-trips to runtime preserving the number type","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Config.string with object default round-trips to runtime preserving nested shape","status":"skipped","title":"Config.string with object default round-trips to runtime preserving nested shape","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Secret/Secret.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"sets a per-operation override, updates it in place, and clears it on destroy","status":"skipped","title":"sets a per-operation override, updates it in place, and clears it on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed per-operation override","status":"skipped","title":"list enumerates the deployed per-operation override","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SchemaValidation/OperationSetting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"uploads a schema, enables in place, replaces on disable and source change, destroys","status":"skipped","title":"uploads a schema, enables in place, replaces on disable and source change, destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed schema across all zones","status":"skipped","title":"list enumerates the deployed schema across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SchemaValidation/Schema.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins zone settings, updates in place, and restores the baseline on destroy","status":"skipped","title":"pins zone settings, updates in place, and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the schema validation settings across all zones","status":"skipped","title":"list enumerates the schema validation settings across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SchemaValidation/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"account entrypoint lifecycle (typed PhaseNotEntitled on unentitled accounts)","status":"skipped","title":"account entrypoint lifecycle (typed PhaseNotEntitled on unentitled accounts)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the account phase entrypoints","status":"skipped","title":"list enumerates the account phase entrypoints","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ruleset/AccountEntrypoint.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"custom ruleset lifecycle (typed PhaseNotEntitled on unentitled accounts)","status":"skipped","title":"custom ruleset lifecycle (typed PhaseNotEntitled on unentitled accounts)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed custom ruleset","status":"skipped","title":"list enumerates the deployed custom ruleset","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ruleset/CustomRuleset.test.ts"},{"assertionResults":[{"ancestorTitles":["Ruleset"],"fullName":"Ruleset creates, updates, and deletes a zone phase entrypoint ruleset","status":"skipped","title":"creates, updates, and deletes a zone phase entrypoint ruleset","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Ruleset"],"fullName":"Ruleset creates and tears down a ruleset whose zone is provisioned in the same deploy","status":"skipped","title":"creates and tears down a ruleset whose zone is provisioned in the same deploy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Ruleset"],"fullName":"Ruleset list enumerates the deployed zone phase entrypoint","status":"skipped","title":"list enumerates the deployed zone phase entrypoint","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ruleset/Ruleset.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"SecretsStore Secret declared on a Worker's env round-trips to runtime as a SecretsStoreSecret","status":"skipped","title":"SecretsStore Secret declared on a Worker's env round-trips to runtime as a SecretsStoreSecret","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SecretsStore/AsyncSecretBinding.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed secret across stores","status":"skipped","title":"list enumerates the deployed secret across stores","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SecretsStore/Secret.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"createStore POSTs a single JSON object body (regression: invalid_json_body)","status":"passed","title":"createStore POSTs a single JSON object body (regression: invalid_json_body)","duration":94.12589999999909,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"createStore surfaces MaximumStoresExceeded as a tagged error so callers can catchTag it","status":"passed","title":"createStore surfaces MaximumStoresExceeded as a tagged error so callers can catchTag it","duration":91.98150000000169,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed secrets store","status":"skipped","title":"list enumerates the deployed secrets store","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385187775,"endTime":1782385187869.126,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SecretsStore/SecretsStore.test.ts"},{"assertionResults":[{"ancestorTitles":["SecurityTxt"],"fullName":"SecurityTxt creates a security.txt, verifies out-of-band, and deletes on destroy","status":"skipped","title":"creates a security.txt, verifies out-of-band, and deletes on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SecurityTxt"],"fullName":"SecurityTxt updates mutable fields in place (full-replace PUT)","status":"skipped","title":"updates mutable fields in place (full-replace PUT)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SecurityTxt"],"fullName":"SecurityTxt disables the file without deleting it, then destroy removes it","status":"skipped","title":"disables the file without deleting it, then destroy removes it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SecurityTxt"],"fullName":"SecurityTxt list enumerates configured security.txt files","status":"skipped","title":"list enumerates configured security.txt files","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SecurityTxt/SecurityTxt.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create with generated name, update code in place, destroy","status":"skipped","title":"create with generated name, update code in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"renaming an explicit snippet triggers replacement","status":"skipped","title":"renaming an explicit snippet triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed snippet","status":"skipped","title":"list enumerates the deployed snippet","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Snippets/Snippet.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"snippet rules — create, update, list, and destroy in dependency order","status":"skipped","title":"snippet rules — create, update, list, and destroy in dependency order","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Snippets/SnippetRules.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a scheduled speed test","status":"skipped","title":"create and delete a scheduled speed test","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the frequency converges in place","status":"skipped","title":"changing the frequency converges in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the region triggers replacement","status":"skipped","title":"changing the region triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing schedule errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing schedule errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed schedule across zones","status":"skipped","title":"list enumerates the deployed schedule across zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Speed/TestSchedule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed SpectrumProtocolNotAvailable error on unentitled zones","status":"skipped","title":"surfaces the typed SpectrumProtocolNotAvailable error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a tcp/22 application","status":"skipped","title":"create, update in place, and destroy a tcp/22 application","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing app errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing app errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates Spectrum applications across all zones","status":"passed","title":"list enumerates Spectrum applications across all zones","duration":2426.006599999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list surfaces a deployed Spectrum application (entitled zone)","status":"skipped","title":"list surfaces a deployed Spectrum application (entitled zone)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385171777,"endTime":1782385174203.0066,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Spectrum/Application.test.ts"},{"assertionResults":[{"ancestorTitles":["State"],"fullName":"State getVersion returns the current STATE_STORE_VERSION","status":"skipped","title":"getVersion returns the current STATE_STORE_VERSION","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State DELETE /state/stacks/:stack wipes the test namespace (cleanup)","status":"skipped","title":"DELETE /state/stacks/:stack wipes the test namespace (cleanup)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State PUT /resources/:fqn (setState) persists a resource","status":"skipped","title":"PUT /resources/:fqn (setState) persists a resource","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /resources/:fqn (getState) reads back the persisted value","status":"skipped","title":"GET /resources/:fqn (getState) reads back the persisted value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /resources/:fqn returns undefined for a missing fqn","status":"skipped","title":"GET /resources/:fqn returns undefined for a missing fqn","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /resources (listResources) returns the FQNs in the stage","status":"skipped","title":"GET /resources (listResources) returns the FQNs in the stage","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /stacks (listStacks) includes the registered test stack","status":"skipped","title":"GET /stacks (listStacks) includes the registered test stack","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /stacks/:stack/stages (listStages) includes the test stage","status":"skipped","title":"GET /stacks/:stack/stages (listStages) includes the test stage","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State PUT /output (setStackOutput) persists a stack output","status":"skipped","title":"PUT /output (setStackOutput) persists a stack output","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /output (getStackOutput) reads back the persisted output","status":"skipped","title":"GET /output (getStackOutput) reads back the persisted output","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /output returns undefined for an un-deployed stage","status":"skipped","title":"GET /output returns undefined for an un-deployed stage","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /replaced-resources (getReplacedResources) returns status===replaced rows","status":"skipped","title":"GET /replaced-resources (getReplacedResources) returns status===replaced rows","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State DELETE /resources/:fqn (deleteState) removes a single resource","status":"skipped","title":"DELETE /resources/:fqn (deleteState) removes a single resource","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State DELETE /stacks/:stack?stage=... clears one stage but leaves the stack registered","status":"skipped","title":"DELETE /stacks/:stack?stage=... clears one stage but leaves the stack registered","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State DELETE /stacks/:stack (no stage) removes the stack from listStacks","status":"skipped","title":"DELETE /stacks/:stack (no stage) removes the stack from listStacks","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State setState 100x sequential — surfaces transient failures","status":"skipped","title":"setState 100x sequential — surfaces transient failures","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State setState 100x concurrent — surfaces racy transient failures","status":"skipped","title":"setState 100x concurrent — surfaces racy transient failures","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET+PUT interleaved 30x5 — engine traffic pattern","status":"skipped","title":"GET+PUT interleaved 30x5 — engine traffic pattern","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/StateStore/State.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","status":"skipped","title":"surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates advanced certificate packs across zones","status":"passed","title":"list enumerates advanced certificate packs across zones","duration":1829.6983999999975,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed advanced certificate pack","status":"skipped","title":"list includes a deployed advanced certificate pack","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"orders a pack, updates validation method in place, and deletes it","status":"skipped","title":"orders a pack, updates validation method in place, and deletes it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385166651,"endTime":1782385168480.6985,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ssl/CertificatePack.test.ts"},{"assertionResults":[{"ancestorTitles":["UniversalSsl"],"fullName":"UniversalSsl disables Universal SSL and restores the original value on destroy","status":"skipped","title":"disables Universal SSL and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UniversalSsl"],"fullName":"UniversalSsl updates enabled in place and keeps the captured initial value","status":"skipped","title":"updates enabled in place and keeps the captured initial value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UniversalSsl"],"fullName":"UniversalSsl destroy restores a disabled baseline when managing from a disabled zone","status":"skipped","title":"destroy restores a disabled baseline when managing from a disabled zone","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UniversalSsl"],"fullName":"UniversalSsl list enumerates the setting across all zones","status":"skipped","title":"list enumerates the setting across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ssl/UniversalSsl.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a live input","status":"skipped","title":"create, update in place, and delete a live input","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed live input","status":"skipped","title":"list enumerates the deployed live input","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/LiveInput.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, toggle enabled in place, replace destination, and delete","status":"skipped","title":"create, toggle enabled in place, replace destination, and delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates outputs across all live inputs","status":"skipped","title":"list enumerates outputs across all live inputs","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/LiveInputOutput.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a signing key","status":"skipped","title":"create and delete a signing key","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed signing key","status":"skipped","title":"list enumerates the deployed signing key","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/SigningKey.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a watermark with default name","status":"skipped","title":"create and delete a watermark with default name","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"prop change replaces the watermark (create-only resource)","status":"skipped","title":"prop change replaces the watermark (create-only resource)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed watermark","status":"skipped","title":"list enumerates the deployed watermark","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/Watermark.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"configure, update, and delete the account webhook","status":"skipped","title":"configure, update, and delete the account webhook","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the account Stream webhook","status":"skipped","title":"list enumerates the account Stream webhook","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/Webhook.test.ts"},{"assertionResults":[{"ancestorTitles":["Tunnel Configuration"],"fullName":"Tunnel Configuration list enumerates configurations across all tunnels","status":"skipped","title":"list enumerates configurations across all tunnels","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/Configuration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update comment in place, and destroy a hostname route","status":"skipped","title":"create, update comment in place, and destroy a hostname route","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed hostname route","status":"skipped","title":"list enumerates the deployed hostname route","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/HostnameRoute.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete route with default props","status":"skipped","title":"create and delete route with default props","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating the comment patches in place","status":"skipped","title":"updating the comment patches in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopt: takes over a pre-existing route","status":"skipped","title":"adopt: takes over a pre-existing route","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed route","status":"skipped","title":"list enumerates the deployed route","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/Route.test.ts"},{"assertionResults":[{"ancestorTitles":["Tunnel runtime bindings"],"fullName":"Tunnel runtime bindings TunnelRead lists tunnels with a read-scoped token","status":"skipped","title":"TunnelRead lists tunnels with a read-scoped token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Tunnel runtime bindings"],"fullName":"Tunnel runtime bindings TunnelWrite creates and deletes a tunnel with a write-scoped token","status":"skipped","title":"TunnelWrite creates and deletes a tunnel with a write-scoped token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Tunnel runtime bindings"],"fullName":"Tunnel runtime bindings TunnelReadWrite drives the full CRUD surface","status":"skipped","title":"TunnelReadWrite drives the full CRUD surface","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Tunnel.list"],"fullName":"Tunnel.list list enumerates the deployed tunnel","status":"skipped","title":"list enumerates the deployed tunnel","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/Tunnel.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy a virtual network","status":"skipped","title":"create, verify, and destroy a virtual network","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name and comment in place (same id)","status":"skipped","title":"update name and comment in place (same id)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed virtual network","status":"skipped","title":"list enumerates the deployed virtual network","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/VirtualNetwork.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, rename in place, and destroy a WARP Connector tunnel","status":"skipped","title":"create, rename in place, and destroy a WARP Connector tunnel","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed WARP Connector tunnel","status":"skipped","title":"list enumerates the deployed WARP Connector tunnel","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/WarpConnector.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a widget with default name","status":"skipped","title":"create and delete a widget with default name","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable props in place (same sitekey)","status":"skipped","title":"update mutable props in place (same sitekey)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed widget","status":"skipped","title":"list enumerates the deployed widget","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Turnstile/Widget.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, and clear tags on a KV namespace","status":"failed","title":"create, update, and clear tags on a KV namespace","duration":32404.463300000003,"failureMessages":["Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time.\n at Proxy. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Output.ts:494:19)\n at String ()\n at setQueryValue (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:729:23)\n at Module.buildRequestParts (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:785:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:631:30)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing resourceId triggers replacement","status":"failed","title":"changing resourceId triggers replacement","duration":30782.93800000001,"failureMessages":["Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time.\n at Proxy. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Output.ts:494:19)\n at String ()\n at setQueryValue (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:729:23)\n at Module.buildRequestParts (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:785:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:631:30)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing tags error without adopt, take over with adopt(true)","status":"failed","title":"adoption — existing tags error without adopt, take over with adopt(true)","duration":120329.7006,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tags/AccountResourceTags.test.ts:189:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account-wide tagged resources","status":"failed","title":"list enumerates account-wide tagged resources","duration":32466.764200000005,"failureMessages":["Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time.\n at Proxy. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Output.ts:494:19)\n at String ()\n at setQueryValue (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:729:23)\n at Module.buildRequestParts (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:785:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:631:30)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)"],"meta":{},"tags":[]}],"startTime":1782385047777,"endTime":1782385168109.7007,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tags/AccountResourceTags.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, and clear tags on a DNS record","status":"skipped","title":"create, update, and clear tags on a DNS record","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"tag the zone itself and clear on destroy","status":"skipped","title":"tag the zone itself and clear on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates tagged zone-scoped resources","status":"skipped","title":"list enumerates tagged zone-scoped resources","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tags/ZoneResourceTags.test.ts"},{"assertionResults":[{"ancestorTitles":["UrlNormalization"],"fullName":"UrlNormalization configures URL normalization and resets to defaults on destroy","status":"skipped","title":"configures URL normalization and resets to defaults on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UrlNormalization"],"fullName":"UrlNormalization updates scope and type in place","status":"skipped","title":"updates scope and type in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UrlNormalization"],"fullName":"UrlNormalization applies Cloudflare defaults when scope and type are omitted","status":"skipped","title":"applies Cloudflare defaults when scope and type are omitted","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UrlNormalization"],"fullName":"UrlNormalization list enumerates URL normalization across all zones","status":"skipped","title":"list enumerates URL normalization across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/UrlNormalization/UrlNormalization.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete vpc service","status":"skipped","title":"create, update, delete vpc service","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create vpc service with ipv4 host","status":"skipped","title":"create vpc service with ipv4 host","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create vpc service with dual-stack host","status":"skipped","title":"create vpc service with dual-stack host","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed vpc service","status":"skipped","title":"list enumerates the deployed vpc service","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VpcService/VpcService.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"reference vpc service by name and by id","status":"skipped","title":"reference vpc service by name and by id","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VpcService/VpcServiceRef.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete index with explicit dimensions","status":"skipped","title":"create and delete index with explicit dimensions","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create index from a preset","status":"skipped","title":"create index from a preset","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces index when dimensions change","status":"skipped","title":"replaces index when dimensions change","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed index","status":"skipped","title":"list enumerates the deployed index","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Vectorize/VectorizeIndex.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"VectorizeIndex.bind exercises the client surface","status":"skipped","title":"VectorizeIndex.bind exercises the client surface","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Vectorize/VectorizeIndexBinding.test.ts"},{"assertionResults":[{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex create and delete a metadata index","status":"skipped","title":"create and delete a metadata index","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex multiple metadata indexes on the same parent coexist","status":"skipped","title":"multiple metadata indexes on the same parent coexist","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex replacing the parent index also replaces the metadata index","status":"skipped","title":"replacing the parent index also replaces the metadata index","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex list enumerates the deployed metadata index","status":"skipped","title":"list enumerates the deployed metadata index","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex destroy is idempotent when the parent index was deleted out-of-band","status":"skipped","title":"destroy is idempotent when the parent index was deleted out-of-band","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Vectorize/VectorizeMetadataIndex.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed credential","status":"skipped","title":"list enumerates the deployed credential","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VulnerabilityScanner/Credential.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, rename, and destroy a credential set","status":"skipped","title":"create, rename, and destroy a credential set","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed credential set","status":"skipped","title":"list enumerates the deployed credential set","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"credential lifecycle — create, mutate in place, rotate value, destroy","status":"skipped","title":"credential lifecycle — create, mutate in place, rotate value, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"moving a credential to a different set triggers replacement","status":"skipped","title":"moving a credential to a different set triggers replacement","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VulnerabilityScanner/CredentialSet.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy a target environment","status":"skipped","title":"create, verify, and destroy a target environment","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating name and description patches in place; clearing description","status":"skipped","title":"updating name and description patches in place; clearing description","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed target environment","status":"skipped","title":"list enumerates the deployed target environment","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VulnerabilityScanner/TargetEnvironment.test.ts"},{"assertionResults":[{"ancestorTitles":["Settings"],"fullName":"Settings pins the settings to the default baseline without touching the API","status":"skipped","title":"pins the settings to the default baseline without touching the API","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Settings"],"fullName":"Settings surfaces the typed ZoneNotEntitled error when enabling on unentitled zones","status":"skipped","title":"surfaces the typed ZoneNotEntitled error when enabling on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Settings"],"fullName":"Settings enables the crawler bypass and restores the original value on destroy","status":"skipped","title":"enables the crawler bypass and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Settings"],"fullName":"Settings list enumerates the settings across all zones","status":"skipped","title":"list enumerates the settings across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/WaitingRoom/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed ZoneNotEntitled error on unentitled zones","status":"skipped","title":"surfaces the typed ZoneNotEntitled error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a waiting room","status":"skipped","title":"create, update in place, and destroy a waiting room","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates waiting rooms across zones","status":"passed","title":"list enumerates waiting rooms across zones","duration":3492.938000000002,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385169763,"endTime":1782385173255.938,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/WaitingRoom/WaitingRoom.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"StaticSite: editing a source file republishes the assets in a single deploy","status":"passed","title":"StaticSite: editing a source file republishes the assets in a single deploy","duration":35744.09390000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"StaticSite: class form deploys and serves the built assets","status":"passed","title":"StaticSite: class form deploys and serves the built assets","duration":14413.927200000006,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"StaticSite: relocating the project (and deleting the old one) preserves hash.assets","status":"passed","title":"StaticSite: relocating the project (and deleting the old one) preserves hash.assets","duration":32275.97600000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"StaticSite: a bundle-only change keeps the asset manifest (hash.assets stable)","status":"passed","title":"StaticSite: a bundle-only change keeps the asset manifest (hash.assets stable)","duration":26863.865399999995,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385048536,"endTime":1782385084280.094,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Website/StaticSite.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"Vite: editing a source file republishes the assets in a single deploy","status":"failed","title":"Vite: editing a source file republishes the assets in a single deploy","duration":2308.7316000000064,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Vite: class form deploys and serves the built assets","status":"failed","title":"Vite: class form deploys and serves the built assets","duration":2392.9879,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Vite: relocating rootDir (and deleting the old one) is a no-op when sources are identical","status":"failed","title":"Vite: relocating rootDir (and deleting the old one) is a no-op when sources are identical","duration":2510.962999999996,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Vite: `env` props are inlined as `import.meta.env.*` into the bundle","status":"failed","title":"Vite: `env` props are inlined as `import.meta.env.*` into the bundle","duration":2757.4001999999964,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782385114221,"endTime":1782385116981.4001,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Website/Vite.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a typed array of token validation rules","status":"passed","title":"list returns a typed array of token validation rules","duration":2114.285899999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed token validation rule","status":"skipped","title":"list enumerates the deployed token validation rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385147773,"endTime":1782385149887.286,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/TokenValidation/Rule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed TokenValidationNotEntitled error on unentitled accounts","status":"skipped","title":"surfaces the typed TokenValidationNotEntitled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates configurations across all zones","status":"passed","title":"list enumerates configurations across all zones","duration":2845.6463000000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"configuration + rule lifecycle: create, update in place, rotate keys, destroy","status":"skipped","title":"configuration + rule lifecycle: create, update in place, rotate keys, destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385169735,"endTime":1782385172580.6462,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/TokenValidation/TokenValidation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a well-typed array of web3 content lists","status":"passed","title":"list returns a well-typed array of web3 content lists","duration":2336.874599999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list surfaces a deployed web3 content list (entitled account)","status":"skipped","title":"list surfaces a deployed web3 content list (entitled account)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385163857,"endTime":1782385166193.8745,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Web3/ContentList.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a well-typed array of web3 hostnames","status":"passed","title":"list returns a well-typed array of web3 hostnames","duration":2929.8471000000027,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list surfaces a deployed web3 hostname (entitled account)","status":"skipped","title":"list surfaces a deployed web3 hostname (entitled account)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385162751,"endTime":1782385165680.8472,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Web3/Hostname.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed Web3HostnameNotEntitled error on unentitled accounts","status":"skipped","title":"surfaces the typed Web3HostnameNotEntitled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, replace on target change, destroy","status":"skipped","title":"create, update in place, replace on target change, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"content list — set entries, update declaratively, reset on destroy","status":"skipped","title":"content list — set entries, update declaratively, reset on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Web3/Web3Hostname.test.ts"},{"assertionResults":[{"ancestorTitles":["AccountSetting"],"fullName":"AccountSetting flips green compute, updates in place, and restores the baseline on destroy","status":"failed","title":"flips green compute, updates in place, and restores the baseline on destroy","duration":30425.091799999995,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":["AccountSetting"],"fullName":"AccountSetting no-op deploy when desired settings already match the live account","status":"passed","title":"no-op deploy when desired settings already match the live account","duration":1074.5227000000014,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AccountSetting"],"fullName":"AccountSetting list returns the account settings singleton","status":"passed","title":"list returns the account settings singleton","duration":987.2792000000045,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385047785,"endTime":1782385080273.2793,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/AccountSetting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed worker fires the scheduled handler on its cron trigger","status":"skipped","title":"deployed worker fires the scheduled handler on its cron trigger","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/CronEventSource.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"Drizzle.postgres query runs inside a Workflow task (ExecutionContext provided per run)","status":"skipped","title":"Drizzle.postgres query runs inside a Workflow task (ExecutionContext provided per run)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/DrizzleWorkflow.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"durable object methods can use binding clients","status":"skipped","title":"durable object methods can use binding clients","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"tick streams sequential values from a durable object (tutorial repro)","status":"skipped","title":"tick streams sequential values from a durable object (tutorial repro)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"async worker durable object binding accepts scriptName","status":"skipped","title":"async worker durable object binding accepts scriptName","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"durable object class migrations across redeploys","status":"skipped","title":"durable object class migrations across redeploys","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/DurableObjectNamespace.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed http-api worker handles createTask + getTask via typed HttpApiClient","status":"skipped","title":"deployed http-api worker handles createTask + getTask via typed HttpApiClient","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"cors middleware adds Access-Control-Allow-Origin header on preflight","status":"skipped","title":"cors middleware adds Access-Control-Allow-Origin header on preflight","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"cors middleware adds Access-Control-Allow-Origin header on actual requests","status":"skipped","title":"cors middleware adds Access-Control-Allow-Origin header on actual requests","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"concurrent createTask survives scope-lifecycle pressure","status":"skipped","title":"concurrent createTask survives scope-lifecycle pressure","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"createTaskDO + getTaskDO round-trip 100x in parallel through the DO HttpApi","status":"skipped","title":"createTaskDO + getTaskDO round-trip 100x in parallel through the DO HttpApi","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/HttpApi.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a destination with default name","status":"skipped","title":"create and delete a destination with default name","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update url, headers, and enabled in place (same slug)","status":"skipped","title":"update url, headers, and enabled in place (same slug)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replacement on name change (new slug, old destination removed)","status":"skipped","title":"replacement on name change (new slug, old destination removed)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed destination","status":"skipped","title":"list enumerates the deployed destination","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/ObservabilityDestination.test.ts"},{"assertionResults":[{"ancestorTitles":["Cloudflare.Worker with bundle: false"],"fullName":"Cloudflare.Worker with bundle: false uploads a prebuilt module graph byte-for-byte","status":"skipped","title":"uploads a prebuilt module graph byte-for-byte","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/PrebuiltWorker.test.ts"},{"assertionResults":[{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle collects modules matching the default rules, entry first, names POSIX-relative to the entry's directory","status":"passed","title":"collects modules matching the default rules, entry first, names POSIX-relative to the entry's directory","duration":2392.8338000000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle uploads file contents byte-for-byte","status":"passed","title":"uploads file contents byte-for-byte","duration":766.8310000000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle does not walk outside the entry's directory","status":"passed","title":"does not walk outside the entry's directory","duration":1030.2317999999996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle custom rules replace the defaults","status":"passed","title":"custom rules replace the defaults","duration":1416.262499999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle hash is stable across reads and sensitive to module changes","status":"passed","title":"hash is stable across reads and sensitive to module changes","duration":1822.8562999999995,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle fails with BundleError when the entry does not exist","status":"passed","title":"fails with BundleError when the entry does not exist","duration":260.20670000000064,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385151335,"endTime":1782385153727.8337,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/PrebuiltWorkerBundle.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an opt-out route (no script)","status":"skipped","title":"create and delete an opt-out route (no script)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"route to a Worker, then update pattern and script in place","status":"skipped","title":"route to a Worker, then update pattern and script in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing route errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing route errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed route across all zones","status":"skipped","title":"list enumerates the deployed route across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Route.test.ts"},{"assertionResults":[{"ancestorTitles":["RpcDecodeError"],"fullName":"RpcDecodeError message delegates to Error.message when cause is an Error","status":"passed","title":"message delegates to Error.message when cause is an Error","duration":102.26839999999993,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RpcDecodeError"],"fullName":"RpcDecodeError message stringifies non-Error cause","status":"passed","title":"message stringifies non-Error cause","duration":100.03819999999996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RpcCallError"],"fullName":"RpcCallError message includes method name and Error.message","status":"passed","title":"message includes method name and Error.message","duration":99.11040000000048,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RpcCallError"],"fullName":"RpcCallError message stringifies non-Error cause","status":"passed","title":"message stringifies non-Error cause","duration":98.80040000000008,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcErrorEnvelope"],"fullName":"isRpcErrorEnvelope detects valid envelope","status":"passed","title":"detects valid envelope","duration":98.31580000000031,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcErrorEnvelope"],"fullName":"isRpcErrorEnvelope rejects non-envelope values","status":"passed","title":"rejects non-envelope values","duration":99.1951999999992,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcStreamEnvelope"],"fullName":"isRpcStreamEnvelope detects valid bytes envelope","status":"passed","title":"detects valid bytes envelope","duration":99.04380000000037,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcStreamEnvelope"],"fullName":"isRpcStreamEnvelope detects valid jsonl envelope","status":"passed","title":"detects valid jsonl envelope","duration":99.2488999999996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcStreamEnvelope"],"fullName":"isRpcStreamEnvelope rejects missing or wrong fields","status":"passed","title":"rejects missing or wrong fields","duration":99.1247000000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["encodeRpcError"],"fullName":"encodeRpcError preserves tagged error fields","status":"passed","title":"preserves tagged error fields","duration":98.94650000000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["encodeRpcError"],"fullName":"encodeRpcError normalizes plain Error to name/message/stack","status":"passed","title":"normalizes plain Error to name/message/stack","duration":99.86470000000008,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["encodeRpcError"],"fullName":"encodeRpcError passes through primitives","status":"passed","title":"passes through primitives","duration":99.77369999999974,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["encodeRpcError"],"fullName":"encodeRpcError passes through plain objects","status":"passed","title":"passes through plain objects","duration":99.67570000000069,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcReadableStream"],"fullName":"fromRpcReadableStream bytes encoding passes raw Uint8Array chunks through","status":"passed","title":"bytes encoding passes raw Uint8Array chunks through","duration":90.69950000000063,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcReadableStream"],"fullName":"fromRpcReadableStream jsonl encoding parses JSON lines","status":"passed","title":"jsonl encoding parses JSON lines","duration":90.86250000000018,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcReadableStream"],"fullName":"fromRpcReadableStream jsonl encoding produces RpcDecodeError on malformed JSON","status":"passed","title":"jsonl encoding produces RpcDecodeError on malformed JSON","duration":90.33590000000004,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcReadableStream"],"fullName":"fromRpcReadableStream jsonl encoding skips empty lines","status":"passed","title":"jsonl encoding skips empty lines","duration":93.53830000000016,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcStreamEnvelope"],"fullName":"fromRpcStreamEnvelope delegates to fromRpcReadableStream","status":"passed","title":"delegates to fromRpcReadableStream","duration":93.50820000000022,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcValue"],"fullName":"decodeRpcValue passes through plain values","status":"passed","title":"passes through plain values","duration":43.40700000000015,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcValue"],"fullName":"decodeRpcValue converts stream envelope to Effect Stream","status":"passed","title":"converts stream envelope to Effect Stream","duration":44.99150000000009,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcValue"],"fullName":"decodeRpcValue converts bare ReadableStream to bytes Effect Stream","status":"passed","title":"converts bare ReadableStream to bytes Effect Stream","duration":44.891999999999825,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult succeeds for plain values","status":"passed","title":"succeeds for plain values","duration":43.71569999999974,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult succeeds for numeric values","status":"passed","title":"succeeds for numeric values","duration":43.69720000000052,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult fails for error envelopes with tagged error","status":"passed","title":"fails for error envelopes with tagged error","duration":44.807699999999386,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult fails with plain Error shape for error envelopes","status":"passed","title":"fails with plain Error shape for error envelopes","duration":44.77550000000065,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult wraps stream envelopes in succeed (stream passthrough)","status":"passed","title":"wraps stream envelopes in succeed (stream passthrough)","duration":45.49619999999959,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream selects jsonl encoding for non-byte data","status":"passed","title":"selects jsonl encoding for non-byte data","duration":35.73750000000018,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream roundtrips a single-element jsonl stream","status":"passed","title":"roundtrips a single-element jsonl stream","duration":50.717500000000655,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream selects bytes encoding for Uint8Array data","status":"passed","title":"selects bytes encoding for Uint8Array data","duration":36.36450000000059,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream roundtrips a single-element bytes stream","status":"passed","title":"roundtrips a single-element bytes stream","duration":52.98190000000068,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream handles empty stream as jsonl","status":"passed","title":"handles empty stream as jsonl","duration":53.65809999999965,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["makeRpcStub"],"fullName":"makeRpcStub proxies successful calls","status":"passed","title":"proxies successful calls","duration":41.3752999999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["makeRpcStub"],"fullName":"makeRpcStub wraps rejected promises as RpcCallError","status":"passed","title":"wraps rejected promises as RpcCallError","duration":41.14840000000004,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["makeRpcStub"],"fullName":"makeRpcStub decodes error envelopes into Effect.fail","status":"passed","title":"decodes error envelopes into Effect.fail","duration":41.08349999999973,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["makeRpcStub"],"fullName":"makeRpcStub decodes stream envelopes from successful calls","status":"passed","title":"decodes stream envelopes from successful calls","duration":41.08649999999943,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors toRpcStream encodes a stream that fails immediately","status":"passed","title":"toRpcStream encodes a stream that fails immediately","duration":40.94140000000061,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors toRpcStream encodes a stream that fails after elements","status":"passed","title":"toRpcStream encodes a stream that fails after elements","duration":41.02990000000045,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors fromRpcReadableStream decodes error marker in JSONL","status":"passed","title":"fromRpcReadableStream decodes error marker in JSONL","duration":40.952800000000025,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors fromRpcReadableStream yields elements before error marker","status":"passed","title":"fromRpcReadableStream yields elements before error marker","duration":40.90449999999964,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors makeRpcStub preserves stream errors (not collapsed to RpcCallError)","status":"passed","title":"makeRpcStub preserves stream errors (not collapsed to RpcCallError)","duration":40.88079999999991,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385159601,"endTime":1782385159852.0298,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Rpc.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"RpcDurableObjectNamespace: Increment / Get round-trip via Worker","status":"passed","title":"RpcDurableObjectNamespace: Increment / Get round-trip via Worker","duration":2714.2227999999886,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcDurableObjectNamespace: separate getByName(id) instances are isolated","status":"passed","title":"RpcDurableObjectNamespace: separate getByName(id) instances are isolated","duration":2669.1984999999986,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcDurableObjectNamespace: streaming RPC via getByName(id).CountUpTo","status":"passed","title":"RpcDurableObjectNamespace: streaming RPC via getByName(id).CountUpTo","duration":819.8761999999988,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker + RpcDurableObjectNamespace: Worker proxies *DO RPCs through the typed namespace","status":"passed","title":"RpcWorker + RpcDurableObjectNamespace: Worker proxies *DO RPCs through the typed namespace","duration":359.75520000001416,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcDurableObjectNamespace: 100 concurrent Increment calls do not hang","status":"passed","title":"RpcDurableObjectNamespace: 100 concurrent Increment calls do not hang","duration":3714.4653999999864,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent unary RPCs do not hang","status":"passed","title":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent unary RPCs do not hang","duration":1621.9150000000081,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent *DO unary RPCs do not hang","status":"passed","title":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent *DO unary RPCs do not hang","duration":2858.9741000000213,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent streaming *DO RPCs do not hang","status":"passed","title":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent streaming *DO RPCs do not hang","duration":1801.9476999999897,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385132491,"endTime":1782385136278.9478,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/RpcDurableObjectNamespace.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: unary RPC response","status":"passed","title":"RpcServer.toHttpEffect: unary RPC response","duration":1215.5836000000054,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: streaming RPC response","status":"passed","title":"RpcServer.toHttpEffect: streaming RPC response","duration":3457.830900000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: array payload streams response items in order","status":"passed","title":"RpcServer.toHttpEffect: array payload streams response items in order","duration":3467.3701999999976,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: 200 concurrent unary calls do not hang","status":"passed","title":"RpcServer.toHttpEffect: 200 concurrent unary calls do not hang","duration":5021.650699999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: concurrent streaming calls do not hang","status":"passed","title":"RpcServer.toHttpEffect: concurrent streaming calls do not hang","duration":4610.6391,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object unary RPC response","status":"passed","title":"RpcServer.toHttpEffect Durable Object unary RPC response","duration":3341.8078000000096,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object streaming RPC response","status":"passed","title":"RpcServer.toHttpEffect Durable Object streaming RPC response","duration":1367.7580000000016,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object array payload streams response items in order","status":"passed","title":"RpcServer.toHttpEffect Durable Object array payload streams response items in order","duration":1361.6865999999864,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object concurrent unary calls do not hang","status":"passed","title":"RpcServer.toHttpEffect Durable Object concurrent unary calls do not hang","duration":1473.8815999999933,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object concurrent streaming calls do not hang","status":"passed","title":"RpcServer.toHttpEffect Durable Object concurrent streaming calls do not hang","duration":1104.7505999999994,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385117206,"endTime":1782385123238.8816,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/RpcHttp.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"RpcWorker: target worker exposes Greet","status":"passed","title":"RpcWorker: target worker exposes Greet","duration":252.90989999999874,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker.bind: caller proxies through service binding to target","status":"passed","title":"RpcWorker.bind: caller proxies through service binding to target","duration":271.2774000000063,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker.bind: 100 concurrent ProxyGreet calls do not hang","status":"passed","title":"RpcWorker.bind: 100 concurrent ProxyGreet calls do not hang","duration":1556.704700000002,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385122632,"endTime":1782385124191.7046,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/RpcWorker.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"adopts the account's existing workers.dev subdomain without mutating it","status":"passed","title":"adopts the account's existing workers.dev subdomain without mutating it","duration":2279.7184000000016,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account's workers.dev subdomain singleton","status":"passed","title":"list returns the account's workers.dev subdomain singleton","duration":1886.354900000002,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385116473,"endTime":1782385118752.7185,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Subdomain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"D1 counter writes from WorkerA are visible from WorkerB (cross-script DO)","status":"skipped","title":"D1 counter writes from WorkerA are visible from WorkerB (cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"DO storage counter writes from WorkerA are visible from WorkerB (cross-script DO)","status":"skipped","title":"DO storage counter writes from WorkerA are visible from WorkerB (cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"WorkerC hosts its own isolated Counter (writes from A/B are not visible from C)","status":"skipped","title":"WorkerC hosts its own isolated Counter (writes from A/B are not visible from C)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Writes from WorkerB are visible from WorkerA (bidirectional cross-script DO)","status":"skipped","title":"Writes from WorkerB are visible from WorkerA (bidirectional cross-script DO)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/TaggedDO.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"RpcWorker WorkerA exposes the same RPC surface as the underlying DO","status":"skipped","title":"RpcWorker WorkerA exposes the same RPC surface as the underlying DO","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"D1 counter writes via WorkerA's RPC are visible from WorkerB (cross-script DO)","status":"skipped","title":"D1 counter writes via WorkerA's RPC are visible from WorkerB (cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"DO storage counter writes via WorkerA's RPC are visible from WorkerB (cross-script DO)","status":"skipped","title":"DO storage counter writes via WorkerA's RPC are visible from WorkerB (cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Writes from WorkerB are visible from WorkerA's RPC (bidirectional cross-script DO)","status":"skipped","title":"Writes from WorkerB are visible from WorkerA's RPC (bidirectional cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"WorkerC hosts its own isolated Counter (writes from A/B are not visible from C)","status":"skipped","title":"WorkerC hosts its own isolated Counter (writes from A/B are not visible from C)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/TaggedRpcDO.test.ts"},{"assertionResults":[{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker create, update, delete worker","status":"skipped","title":"create, update, delete worker","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker create, update, delete worker with assets","status":"skipped","title":"create, update, delete worker with assets","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker Worker assets: relocating to a fresh path with identical bytes preserves hash and keeps assets","status":"skipped","title":"Worker assets: relocating to a fresh path with identical bytes preserves hash and keeps assets","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker Worker assets: editing a file changes the hash and republishes the manifest","status":"skipped","title":"Worker assets: editing a file changes the hash and republishes the manifest","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker Worker assets: a bundle-only change keeps the asset manifest (hash.assets stable)","status":"skipped","title":"Worker assets: a bundle-only change keeps the asset manifest (hash.assets stable)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker create, update, delete internal worker","status":"skipped","title":"create, update, delete internal worker","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker owned worker (matching alchemy tags) is silently adopted without --adopt","status":"skipped","title":"owned worker (matching alchemy tags) is silently adopted without --adopt","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker adopt(true) takes over a foreign-tagged worker","status":"skipped","title":"adopt(true) takes over a foreign-tagged worker","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker url defaults to enabling the workers.dev subdomain on first deploy","status":"skipped","title":"url defaults to enabling the workers.dev subdomain on first deploy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker url: false disables the workers.dev subdomain on first deploy","status":"skipped","title":"url: false disables the workers.dev subdomain on first deploy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker toggling url between deploys flips the workers.dev subdomain","status":"skipped","title":"toggling url between deploys flips the workers.dev subdomain","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker redeploy re-enables previewsEnabled when externally disabled","status":"skipped","title":"redeploy re-enables previewsEnabled when externally disabled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker domains reflects the workers.dev subdomain and tracks url","status":"skipped","title":"domains reflects the workers.dev subdomain and tracks url","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker domains puts custom domains before workers.dev and url is the first","status":"skipped","title":"domains puts custom domains before workers.dev and url is the first","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker list enumerates the deployed worker","status":"skipped","title":"list enumerates the deployed worker","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker downstream referencing worker.url is not re-updated when the worker changes","status":"skipped","title":"downstream referencing worker.url is not re-updated when the worker changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker worker.durableObjectNamespaces stability across DO and worker changes","status":"skipped","title":"worker.durableObjectNamespaces stability across DO and worker changes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Worker.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"target worker's own fetch handler responds","status":"skipped","title":"target worker's own fetch handler responds","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"async caller can call target's RPC method via service binding","status":"skipped","title":"async caller can call target's RPC method via service binding","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect caller can call target's RPC method via bindWorker","status":"skipped","title":"effect caller can call target's RPC method via bindWorker","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/WorkerBinding.test.ts"},{"assertionResults":[{"ancestorTitles":["Cloudflare.Worker env bindings"],"fullName":"Cloudflare.Worker env bindings async worker round-trips every supported binding shape","status":"skipped","title":"async worker round-trips every supported binding shape","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker env bindings"],"fullName":"Cloudflare.Worker env bindings effect worker round-trips env: literals and Redacted via WorkerEnvironment","status":"skipped","title":"effect worker round-trips env: literals and Redacted via WorkerEnvironment","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker env bindings"],"fullName":"Cloudflare.Worker env bindings effect worker resolves the yielded VersionMetadata binding","status":"skipped","title":"effect worker resolves the yielded VersionMetadata binding","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker env bindings"],"fullName":"Cloudflare.Worker env bindings effect worker round-trips Config.xxx bindings captured in Init","status":"skipped","title":"effect worker round-trips Config.xxx bindings captured in Init","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/WorkerEnv.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"async worker loads and proxies to a dynamic worker via env binding","status":"skipped","title":"async worker loads and proxies to a dynamic worker via env binding","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker loads and proxies to a dynamic worker via yield* loader","status":"skipped","title":"effect worker loads and proxies to a dynamic worker via yield* loader","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/WorkerLoader.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed worker can run a workflow to completion","status":"skipped","title":"deployed worker can run a workflow to completion","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed workflow","status":"skipped","title":"list enumerates the deployed workflow","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Workflow.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, replace, and destroy a dispatch namespace","status":"skipped","title":"create, replace, and destroy a dispatch namespace","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"upload, update in place, and destroy a namespace script","status":"skipped","title":"upload, update in place, and destroy a namespace script","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed dispatch namespace","status":"skipped","title":"list enumerates the deployed dispatch namespace","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/WorkersForPlatforms/DispatchNamespace.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins the custom nameserver toggle and leaves the zone at its baseline on destroy","status":"skipped","title":"pins the custom nameserver toggle and leaves the zone at its baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"surfaces the typed CustomNameserverSetNotFound error when enabling without an account nameserver set","status":"skipped","title":"surfaces the typed CustomNameserverSetNotFound error when enabling without an account nameserver set","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates zones using account custom nameservers","status":"passed","title":"list enumerates zones using account custom nameservers","duration":3258.101200000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a zone with account custom nameservers enabled","status":"skipped","title":"list includes a zone with account custom nameservers enabled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"enables account custom nameservers and restores the baseline on destroy","status":"skipped","title":"enables account custom nameservers and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385169947,"endTime":1782385173205.1013,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/CustomNameservers.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed ZoneHoldsRequireEnterprise error on non-Enterprise zones","status":"skipped","title":"surfaces the typed ZoneHoldsRequireEnterprise error on non-Enterprise zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the hold state across all zones","status":"skipped","title":"list enumerates the hold state across all zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"places a hold, updates includeSubdomains in place, and removes it on destroy","status":"skipped","title":"places a hold, updates includeSubdomains in place, and removes it on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/Hold.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins a toggle setting and restores the original value on destroy","status":"skipped","title":"pins a toggle setting and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updates a numeric setting in place and keeps the captured initial value","status":"skipped","title":"updates a numeric setting in place and keeps the captured initial value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing settingId replaces — old setting restored, new setting pinned","status":"skipped","title":"changing settingId replaces — old setting restored, new setting pinned","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed (zone, setting) pair","status":"skipped","title":"list enumerates the deployed (zone, setting) pair","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/Setting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create zone retains by default — destroy() opts in to deletion","status":"skipped","title":"create zone retains by default — destroy() opts in to deletion","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create zone retains by default — survives stack.destroy()","status":"skipped","title":"create zone retains by default — survives stack.destroy()","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing zone errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing zone errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates every zone in the account","status":"skipped","title":"list enumerates every zone in the account","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/Zone.test.ts"},{"assertionResults":[{"ancestorTitles":["ZarazConfig"],"fullName":"ZarazConfig updates and retains a zone-level Zaraz config","status":"skipped","title":"updates and retains a zone-level Zaraz config","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ZarazConfig"],"fullName":"ZarazConfig delete true resets Zaraz config to defaults","status":"skipped","title":"delete true resets Zaraz config to defaults","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ZarazConfig"],"fullName":"ZarazConfig updates and retains Zaraz workflow mode","status":"skipped","title":"updates and retains Zaraz workflow mode","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ZarazConfig"],"fullName":"ZarazConfig list enumerates Zaraz config across all zones","status":"skipped","title":"list enumerates Zaraz config across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782384949333,"endTime":1782384949333,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zaraz/ZarazConfig.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"Zaraz event contracts are type-only","status":"passed","title":"Zaraz event contracts are type-only","duration":11.488800000000083,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782385166946,"endTime":1782385166957.4888,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zaraz/ZarazEventTypes.test.ts"}]} \ No newline at end of file diff --git a/cf-temp-results-5.json b/cf-temp-results-5.json new file mode 100644 index 000000000..25ae5cbfc --- /dev/null +++ b/cf-temp-results-5.json @@ -0,0 +1 @@ +{"numTotalTestSuites":366,"numPassedTestSuites":341,"numFailedTestSuites":25,"numPendingTestSuites":0,"numTotalTests":958,"numPassedTests":147,"numFailedTests":18,"numPendingTests":793,"numTodoTests":0,"snapshot":{"added":0,"failure":false,"filesAdded":0,"filesRemoved":0,"filesRemovedList":[],"filesUnmatched":0,"filesUpdated":0,"matched":0,"total":0,"unchecked":0,"uncheckedKeysByFile":[],"unmatched":0,"updated":0,"didUpdate":false},"startTime":1782386072728,"success":false,"testResults":[{"assertionResults":[{"ancestorTitles":[],"fullName":"building the Cloudflare provider layers should not fail for unknown profile","status":"passed","title":"building the Cloudflare provider layers should not fail for unknown profile","duration":358.57390000000305,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386248937,"endTime":1782386249295.574,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Providers.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a self_hosted application gated by a reusable policy","status":"skipped","title":"create and delete a self_hosted application gated by a reusable policy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create and delete a warp device-enrollment application","status":"passed","title":"create and delete a warp device-enrollment application","duration":690.922300000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed access application","status":"skipped","title":"list enumerates the deployed access application","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update policies in place keeps the applicationId stable","status":"skipped","title":"update policies in place keeps the applicationId stable","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386234826,"endTime":1782386235516.9224,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Application.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"legacy bookmark creation surfaces the typed AccessBookmarkNotFound error","status":"skipped","title":"legacy bookmark creation surfaces the typed AccessBookmarkNotFound error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy bookmark","status":"skipped","title":"create, update in place, and destroy bookmark","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates access bookmarks at the account scope","status":"skipped","title":"list enumerates access bookmarks at the account scope","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Bookmark.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed AccessCertificateQuotaExceeded error","status":"skipped","title":"unentitled accounts surface the typed AccessCertificateQuotaExceeded error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update hostnames, replace on PEM change, destroy","status":"skipped","title":"create, update hostnames, replace on PEM change, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account access certificates","status":"skipped","title":"list enumerates account access certificates","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Certificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed AccessCustomPagesNotEntitled error","status":"skipped","title":"unentitled accounts surface the typed AccessCustomPagesNotEntitled error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update html in place, replace on type change, destroy","status":"skipped","title":"create, update html in place, replace on type change, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates access custom pages at the account scope","status":"skipped","title":"list enumerates access custom pages at the account scope","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/CustomPage.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update rules, and delete group","status":"skipped","title":"create, update rules, and delete group","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"rename updates the group in place","status":"skipped","title":"rename updates the group in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed access group","status":"skipped","title":"list enumerates the deployed access group","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"group can be referenced from an access policy","status":"skipped","title":"group can be referenced from an access policy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Group.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy an OIDC IdP","status":"skipped","title":"create, verify, and destroy an OIDC IdP","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name and config in place (same id)","status":"skipped","title":"update name and config in place (same id)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed IdP","status":"skipped","title":"list enumerates the deployed IdP","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the type replaces the IdP","status":"skipped","title":"changing the type replaces the IdP","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/IdentityProvider.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed infrastructure target","status":"skipped","title":"list enumerates the deployed infrastructure target","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/InfrastructureTarget.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins the rotation interval, updates in place, and restores on destroy","status":"skipped","title":"pins the rotation interval, updates in place, and restores on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account key configuration singleton","status":"skipped","title":"list returns the account key configuration singleton","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/KeyConfiguration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and destroy an MCP portal","status":"skipped","title":"create, update in place, and destroy an MCP portal","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed MCP portal","status":"skipped","title":"list enumerates the deployed MCP portal","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/McpPortal.test.ts"},{"assertionResults":[{"ancestorTitles":["Organization"],"fullName":"Organization adopts the existing Access organization","status":"skipped","title":"adopts the existing Access organization","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Organization"],"fullName":"Organization toggles allow_authenticate_via_warp and restores","status":"skipped","title":"toggles allow_authenticate_via_warp and restores","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Organization"],"fullName":"Organization list returns the account Access organization","status":"skipped","title":"list returns the account Access organization","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Organization.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete basic allow policy","status":"skipped","title":"create and delete basic allow policy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutates includes without replacing","status":"skipped","title":"update mutates includes without replacing","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopts an out-of-band reusable policy","status":"skipped","title":"adopts an out-of-band reusable policy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed reusable policy","status":"skipped","title":"list enumerates the deployed reusable policy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Policy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update duration, and delete service token","status":"skipped","title":"create, update duration, and delete service token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed service token","status":"skipped","title":"list enumerates the deployed service token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"incrementing clientSecretVersion rotates the secret","status":"skipped","title":"incrementing clientSecretVersion rotates the secret","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/ServiceToken.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, and destroy tag","status":"skipped","title":"create, verify out-of-band, and destroy tag","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"rename replaces the tag","status":"skipped","title":"rename replaces the tag","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed access tag","status":"skipped","title":"list enumerates the deployed access tag","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Tag.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"read path: getAccount observes the testing account","status":"passed","title":"read path: getAccount observes the testing account","duration":2230.4843999999975,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"inaccessible account surfaces a typed tag","status":"passed","title":"inaccessible account surfaces a typed tag","duration":2178.423200000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates accessible accounts (read-only)","status":"skipped","title":"list enumerates accessible accounts (read-only)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"createAccount is entitlement-gated: typed AccountCreationForbidden","status":"skipped","title":"createAccount is entitlement-gated: typed AccountCreationForbidden","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create subaccount, update name and settings in place, delete","status":"skipped","title":"create subaccount, update name and settings in place, delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386236058,"endTime":1782386238288.4844,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Account/Account.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the account members","status":"skipped","title":"list enumerates the account members","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create member, update roles in place, delete","status":"skipped","title":"create member, update roles in place, delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the member when the email changes","status":"skipped","title":"replaces the member when the email changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Account/Member.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","status":"skipped","title":"surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates trust store certificates across zones","status":"passed","title":"list enumerates trust store certificates across zones","duration":1088.8661999999968,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes the deployed trust store certificate on an entitled zone","status":"skipped","title":"list includes the deployed trust store certificate on an entitled zone","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"uploads a root CA, replaces it on certificate change, and deletes it","status":"skipped","title":"uploads a root CA, replaces it on certificate change, and deletes it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386184849,"endTime":1782386185937.8662,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Acm/CustomTrustStore.test.ts"},{"assertionResults":[{"ancestorTitles":["TotalTls"],"fullName":"TotalTls converges enabled:false as a no-op on a zone without the ACM entitlement","status":"skipped","title":"converges enabled:false as a no-op on a zone without the ACM entitlement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TotalTls"],"fullName":"TotalTls surfaces the typed AdvancedCertificateManagerRequired error when enabling without the entitlement","status":"skipped","title":"surfaces the typed AdvancedCertificateManagerRequired error when enabling without the entitlement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TotalTls"],"fullName":"TotalTls enables Total TLS, updates the CA in place, and restores the baseline on destroy","status":"skipped","title":"enables Total TLS, updates the CA in place, and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TotalTls"],"fullName":"TotalTls list enumerates the setting across all zones","status":"skipped","title":"list enumerates the setting across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Acm/TotalTls.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"address map lifecycle (typed FeatureNotEnabled on unentitled accounts)","status":"skipped","title":"address map lifecycle (typed FeatureNotEnabled on unentitled accounts)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed address map","status":"skipped","title":"list enumerates the deployed address map","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/AddressMap.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates BGP prefixes across BYOIP prefixes","status":"skipped","title":"list enumerates BGP prefixes across BYOIP prefixes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/BgpPrefix.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"lists the services catalog and prefixes (read-only)","status":"skipped","title":"lists the services catalog and prefixes (read-only)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account prefixes (read-only)","status":"skipped","title":"list enumerates account prefixes (read-only)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"prefix: create, patch description in place, destroy","status":"skipped","title":"prefix: create, patch description in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"bgp prefix: adopt-or-create by cidr, toggle advertisement, withdraw on destroy","status":"skipped","title":"bgp prefix: adopt-or-create by cidr, toggle advertisement, withdraw on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"prefix delegation: create and destroy (replace-only resource)","status":"skipped","title":"prefix delegation: create and destroy (replace-only resource)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"service binding: create against the CDN service and destroy","status":"skipped","title":"service binding: create against the CDN service and destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/Prefix.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates prefix delegations (read-only)","status":"skipped","title":"list enumerates prefix delegations (read-only)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/PrefixDelegation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates service bindings across prefixes (read-only)","status":"skipped","title":"list enumerates service bindings across prefixes (read-only)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/ServiceBinding.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete ai gateway with default props","status":"skipped","title":"create and delete ai gateway with default props","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete ai gateway","status":"skipped","title":"create, update, delete ai gateway","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed ai gateway","status":"skipped","title":"list enumerates the deployed ai gateway","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"existing ai gateway (matching id) is silently adopted without --adopt","status":"skipped","title":"existing ai gateway (matching id) is silently adopted without --adopt","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"deployed worker can call AiGateway binding (effect-native getUrl)","status":"skipped","title":"deployed worker can call AiGateway binding (effect-native getUrl)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/AiGateway.test.ts"},{"assertionResults":[{"ancestorTitles":["AiGatewaySpendingLimit"],"fullName":"AiGatewaySpendingLimit create, read-back, and delete the account spending limit","status":"skipped","title":"create, read-back, and delete the account spending limit","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewaySpendingLimit"],"fullName":"AiGatewaySpendingLimit update the spending limit in place","status":"skipped","title":"update the spending limit in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewaySpendingLimit"],"fullName":"AiGatewaySpendingLimit list enumerates the deployed account spending limit","status":"skipped","title":"list enumerates the deployed account spending limit","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/AiGatewaySpendingLimit.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"first turn against a fresh thread persists user + assistant messages","status":"skipped","title":"first turn against a fresh thread persists user + assistant messages","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"persisted chat survives across DO invocations","status":"skipped","title":"persisted chat survives across DO invocations","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"distinct thread ids map to isolated histories","status":"skipped","title":"distinct thread ids map to isolated histories","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/ChatPersistence.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"send round-trips text and turns through the RpcWorker → DO","status":"skipped","title":"send round-trips text and turns through the RpcWorker → DO","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"streamMessage yields effect/ai Response parts that decode to real classes","status":"skipped","title":"streamMessage yields effect/ai Response parts that decode to real classes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"streamed turn is persisted: a follow-up send recalls it","status":"skipped","title":"streamed turn is persisted: a follow-up send recalls it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/ChatPersistenceRpc.test.ts"},{"assertionResults":[{"ancestorTitles":["AiGatewayDataset"],"fullName":"AiGatewayDataset create, update, delete a dataset on a gateway","status":"skipped","title":"create, update, delete a dataset on a gateway","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewayDataset"],"fullName":"AiGatewayDataset replaces dataset when the gateway changes","status":"skipped","title":"replaces dataset when the gateway changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewayDataset"],"fullName":"AiGatewayDataset recreates a dataset after out-of-band delete","status":"skipped","title":"recreates a dataset after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewayDataset"],"fullName":"AiGatewayDataset list enumerates datasets across gateways","status":"skipped","title":"list enumerates datasets across gateways","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/Dataset.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update elements (new deployed version), rename, delete","status":"skipped","title":"create, update elements (new deployed version), rename, delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces route when the gateway changes","status":"skipped","title":"replaces route when the gateway changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates a route after out-of-band delete","status":"skipped","title":"recreates a route after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates routes across all gateways","status":"skipped","title":"list enumerates routes across all gateways","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/DynamicRouting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, noop, replace, delete an evaluation","status":"skipped","title":"create, noop, replace, delete an evaluation","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed evaluation","status":"skipped","title":"list enumerates the deployed evaluation","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/Evaluation.test.ts"},{"assertionResults":[],"startTime":1782386072728,"endTime":1782386072728,"status":"failed","message":"test.skip.skipIf is not a function","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/LanguageModel.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, noop, replace, delete a BYOK provider config","status":"skipped","title":"create, noop, replace, delete a BYOK provider config","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed provider config","status":"skipped","title":"list enumerates the deployed provider config","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/ProviderConfig.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AiSecurityNotEntitled error on unentitled accounts","status":"skipped","title":"surfaces the typed AiSecurityNotEntitled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the custom topics across all zones","status":"passed","title":"list enumerates the custom topics across all zones","duration":3827.726599999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"sets topics, updates the list in place, and restores on destroy","status":"skipped","title":"sets topics, updates the list in place, and restores on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386228610,"endTime":1782386232437.7266,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSecurity/CustomTopics.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AiSecurityNotEntitled error on unentitled accounts","status":"skipped","title":"surfaces the typed AiSecurityNotEntitled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the setting across all entitled zones","status":"passed","title":"list enumerates the setting across all entitled zones","duration":2759.1264999999985,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"pins enabled, updates in place, and restores the original on destroy","status":"skipped","title":"pins enabled, updates in place, and restores the original on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386230894,"endTime":1782386233653.1265,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSecurity/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete notification policy","status":"skipped","title":"create, update, delete notification policy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces policy when alertType changes","status":"skipped","title":"replaces policy when alertType changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed notification policy","status":"skipped","title":"list enumerates the deployed notification policy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Alerting/NotificationPolicy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update window in place, delete silence","status":"skipped","title":"create, update window in place, delete silence","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces silence when the policy changes","status":"skipped","title":"replaces silence when the policy changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed silence","status":"skipped","title":"list enumerates the deployed silence","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Alerting/Silence.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete webhook destination","status":"skipped","title":"create, update, delete webhook destination","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed webhook destination","status":"skipped","title":"list enumerates the deployed webhook destination","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Alerting/Webhook.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed worker can write data points through the Analytics Engine binding","status":"skipped","title":"deployed worker can write data points through the Analytics Engine binding","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AnalyticsEngine/AnalyticsEngineDataset.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed NotEntitled error on unentitled zones","status":"skipped","title":"surfaces the typed NotEntitled error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the configuration across all zones","status":"passed","title":"list enumerates the configuration across all zones","duration":3110.1579999999994,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"sets session identifiers and restores the original value on destroy","status":"skipped","title":"sets session identifiers and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386228583,"endTime":1782386231693.158,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/Configuration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update description in place, destroy a label","status":"skipped","title":"create, update description in place, destroy a label","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"renaming a label triggers replacement","status":"skipped","title":"renaming a label triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"generated name respects Cloudflare's 24-character limit","status":"skipped","title":"generated name respects Cloudflare's 24-character limit","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed label","status":"skipped","title":"list enumerates the deployed label","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/Label.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, no-op redeploy, destroy an API Shield operation","status":"skipped","title":"create, no-op redeploy, destroy an API Shield operation","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the method triggers replacement","status":"skipped","title":"changing the method triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed API Shield operation","status":"skipped","title":"list enumerates the deployed API Shield operation","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/Operation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, enable validation in place, destroy a user schema","status":"skipped","title":"create, enable validation in place, destroy a user schema","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the schema source triggers replacement","status":"skipped","title":"changing the schema source triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed user schema","status":"skipped","title":"list enumerates the deployed user schema","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/UserSchema.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed account token","status":"skipped","title":"list enumerates the deployed account token","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiToken/AccountApiToken.test.ts"},{"assertionResults":[{"ancestorTitles":["UserApiToken"],"fullName":"UserApiToken create and delete user token with default props","status":"skipped","title":"create and delete user token with default props","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UserApiToken"],"fullName":"UserApiToken create, update, delete user token","status":"skipped","title":"create, update, delete user token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UserApiToken list"],"fullName":"UserApiToken list list enumerates user tokens","status":"skipped","title":"list enumerates user tokens","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UserApiToken list probe"],"fullName":"UserApiToken list probe list rejects with typed Unauthorized under scoped token","status":"passed","title":"list rejects with typed Unauthorized under scoped token","duration":3164.460899999998,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386230834,"endTime":1782386233998.461,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiToken/UserApiToken.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"construct auto-creates a managed token and wires it into the instance","status":"skipped","title":"construct auto-creates a managed token and wires it into the instance","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"web-crawler source skips token minting","status":"skipped","title":"web-crawler source skips token minting","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/AiSearch.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"worker deploys with ai_search + ai_search_namespace bindings injected","status":"skipped","title":"worker deploys with ai_search + ai_search_namespace bindings injected","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker resolves ai_search + ai_search_namespace via Effect clients","status":"skipped","title":"effect worker resolves ai_search + ai_search_namespace via Effect clients","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/Bindings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update mutable props, and delete an r2-backed instance","status":"skipped","title":"create, update mutable props, and delete an r2-backed instance","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the embedding model triggers a replacement","status":"skipped","title":"changing the embedding model triggers a replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed instance","status":"skipped","title":"list enumerates the deployed instance","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a web-crawler instance (no service token)","status":"skipped","title":"creates a web-crawler instance (no service token)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an instance in a custom namespace and moving namespaces replaces","status":"skipped","title":"creates an instance in a custom namespace and moving namespaces replaces","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/Instance.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update description in place, and delete a namespace","status":"skipped","title":"create, update description in place, and delete a namespace","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the name triggers a replacement","status":"skipped","title":"changing the name triggers a replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed namespace","status":"skipped","title":"list enumerates the deployed namespace","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopts the reserved default namespace without deleting it on teardown","status":"skipped","title":"adopts the reserved default namespace without deleting it on teardown","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/Namespace.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, rename in place, and delete a service token","status":"skipped","title":"create, rename in place, and delete a service token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed service token","status":"skipped","title":"list enumerates the deployed service token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"an AI Search instance syncs with a stack-minted service token","status":"skipped","title":"an AI Search instance syncs with a stack-minted service token","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/Token.test.ts"},{"assertionResults":[{"ancestorTitles":["SmartRouting"],"fullName":"SmartRouting surfaces the typed NotAuthorized error on zones without the Argo add-on","status":"skipped","title":"surfaces the typed NotAuthorized error on zones without the Argo add-on","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SmartRouting"],"fullName":"SmartRouting enables Smart Routing and restores the original value on destroy","status":"skipped","title":"enables Smart Routing and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SmartRouting"],"fullName":"SmartRouting list enumerates Argo-entitled zones","status":"passed","title":"list enumerates Argo-entitled zones","duration":1825.6346999999987,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386197308,"endTime":1782386199133.6348,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Argo/SmartRouting.test.ts"},{"assertionResults":[{"ancestorTitles":["TieredCaching"],"fullName":"TieredCaching enables Tiered Caching and restores the original value on destroy","status":"skipped","title":"enables Tiered Caching and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TieredCaching"],"fullName":"TieredCaching updates enabled in place and keeps the captured initial value","status":"skipped","title":"updates enabled in place and keeps the captured initial value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TieredCaching"],"fullName":"TieredCaching list enumerates the setting across all zones","status":"skipped","title":"list enumerates the setting across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Argo/TieredCaching.test.ts"},{"assertionResults":[{"ancestorTitles":["BotManagement"],"fullName":"BotManagement manages SBFM settings on the zone singleton and restores them on destroy","status":"skipped","title":"manages SBFM settings on the zone singleton and restores them on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["BotManagement"],"fullName":"BotManagement deploy with no settings set adopts the singleton without writing","status":"skipped","title":"deploy with no settings set adopts the singleton without writing","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["BotManagement"],"fullName":"BotManagement toggles a boolean SBFM field and restores it on destroy","status":"skipped","title":"toggles a boolean SBFM field and restores it on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["BotManagement"],"fullName":"BotManagement list enumerates bot management across all zones","status":"skipped","title":"list enumerates bot management across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/BotManagement/BotManagement.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"async worker renders a page title through Browser Rendering","status":"skipped","title":"async worker renders a page title through Browser Rendering","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker exercises content via quickAction wrapper","status":"skipped","title":"effect worker exercises content via quickAction wrapper","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker converts a page to markdown","status":"skipped","title":"effect worker converts a page to markdown","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker extracts links","status":"skipped","title":"effect worker extracts links","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker scrapes elements by selector","status":"skipped","title":"effect worker scrapes elements by selector","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker takes a page snapshot","status":"skipped","title":"effect worker takes a page snapshot","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker streams a screenshot","status":"skipped","title":"effect worker streams a screenshot","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker streams a PDF","status":"skipped","title":"effect worker streams a PDF","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker extracts JSON with AI","status":"skipped","title":"effect worker extracts JSON with AI","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker calls the generic quickAction","status":"skipped","title":"effect worker calls the generic quickAction","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker exposes the raw BrowserRun binding","status":"skipped","title":"effect worker exposes the raw BrowserRun binding","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Browser/Browser.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an app with default name","status":"skipped","title":"create and delete an app with default name","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name in place (same appId, secret preserved)","status":"skipped","title":"update name in place (same appId, secret preserved)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed app","status":"skipped","title":"list enumerates the deployed app","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Calls/App.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a TURN key with default name","status":"skipped","title":"create and delete a TURN key with default name","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name in place (same keyId, key preserved)","status":"skipped","title":"update name in place (same keyId, key preserved)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed TURN key","status":"skipped","title":"list enumerates the deployed TURN key","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Calls/TurnKey.test.ts"},{"assertionResults":[{"ancestorTitles":["CacheReserve"],"fullName":"CacheReserve surfaces the typed SettingUnavailableForPlan error on unentitled zones","status":"skipped","title":"surfaces the typed SettingUnavailableForPlan error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["CacheReserve"],"fullName":"CacheReserve enables Cache Reserve and restores the original value on destroy","status":"skipped","title":"enables Cache Reserve and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["CacheReserve"],"fullName":"CacheReserve list enumerates Cache Reserve across all zones","status":"passed","title":"list enumerates Cache Reserve across all zones","duration":1903.1431000000011,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["CacheReserve"],"fullName":"CacheReserve list includes the entitled zone's Cache Reserve setting","status":"skipped","title":"list includes the entitled zone's Cache Reserve setting","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386196934,"endTime":1782386198837.143,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/CacheReserve.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates, updates in place, replaces on ip change, and deletes the mapping","status":"skipped","title":"creates, updates in place, replaces on ip change, and deletes the mapping","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/OriginCloudRegion.test.ts"},{"assertionResults":[{"ancestorTitles":["RegionalTieredCache"],"fullName":"RegionalTieredCache surfaces the typed SettingUnavailableForPlan error on non-Enterprise zones","status":"skipped","title":"surfaces the typed SettingUnavailableForPlan error on non-Enterprise zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RegionalTieredCache"],"fullName":"RegionalTieredCache enables Regional Tiered Cache and restores the original value on destroy","status":"skipped","title":"enables Regional Tiered Cache and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RegionalTieredCache"],"fullName":"RegionalTieredCache list enumerates the setting across entitled zones","status":"passed","title":"list enumerates the setting across entitled zones","duration":7675.252800000002,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386222798,"endTime":1782386230473.2527,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/RegionalTieredCache.test.ts"},{"assertionResults":[{"ancestorTitles":["SmartTieredCache"],"fullName":"SmartTieredCache enables Smart Tiered Cache and restores the original value on destroy","status":"skipped","title":"enables Smart Tiered Cache and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SmartTieredCache"],"fullName":"SmartTieredCache updates the setting in place and keeps the captured initial value","status":"skipped","title":"updates the setting in place and keeps the captured initial value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SmartTieredCache"],"fullName":"SmartTieredCache list enumerates the setting across all zones","status":"skipped","title":"list enumerates the setting across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/SmartTieredCache.test.ts"},{"assertionResults":[{"ancestorTitles":["Variants"],"fullName":"Variants creates, updates in place, and deletes the variants setting","status":"skipped","title":"creates, updates in place, and deletes the variants setting","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Variants"],"fullName":"Variants deploy is idempotent and converges out-of-band drift","status":"skipped","title":"deploy is idempotent and converges out-of-band drift","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Variants"],"fullName":"Variants list enumerates the configured variants settings","status":"skipped","title":"list enumerates the configured variants settings","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/Variants.test.ts"},{"assertionResults":[{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation pins Managed CA hostnames, updates in place, and clears on destroy","status":"skipped","title":"pins Managed CA hostnames, updates in place, and clears on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation associates hostnames with an uploaded CA and destroys before the cert","status":"skipped","title":"associates hostnames with an uploaded CA and destroys before the cert","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation changing the certificate key replaces the association","status":"skipped","title":"changing the certificate key replaces the association","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CertificateAuthorities/HostnameAssociation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create signs the CSR, destroy revokes the certificate","status":"skipped","title":"create signs the CSR, destroy revokes the certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing validityDays replaces — new id issued, old certificate revoked","status":"skipped","title":"changing validityDays replaces — new id issued, old certificate revoked","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates client certificates across zones","status":"skipped","title":"list enumerates client certificates across zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ClientCertificate/ClientCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":["Rules"],"fullName":"Rules cloud connector rules — create, update in place, destroy clears the list","status":"skipped","title":"cloud connector rules — create, update in place, destroy clears the list","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Rules"],"fullName":"Rules no-op redeploy leaves the rule list untouched and ids stable","status":"skipped","title":"no-op redeploy leaves the rule list untouched and ids stable","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Rules"],"fullName":"Rules list enumerates rule lists across all zones","status":"skipped","title":"list enumerates rule lists across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CloudConnector/Rules.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Unauthorized error","status":"skipped","title":"unentitled accounts surface the typed Unauthorized error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create a scan config, update ports in place, delete","status":"skipped","title":"create a scan config, update ports in place, delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns an array of scan configs","status":"passed","title":"list returns an array of scan configs","duration":27829.699899999992,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed scan config","status":"skipped","title":"list enumerates the deployed scan config","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386147632,"endTime":1782386175461.7,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CloudforceOne/ScanConfig.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"tcp service lifecycle: create, update, host switch","status":"skipped","title":"tcp service lifecycle: create, update, host switch","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"http service with explicit ports and default name","status":"skipped","title":"http service with explicit ports and default name","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed directory service","status":"skipped","title":"list enumerates the deployed directory service","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Connectivity/DirectoryService.test.ts"},{"assertionResults":[{"ancestorTitles":["effectful container (main)"],"fullName":"effectful container (main) deploys and serves over its TCP port","status":"skipped","title":"deploys and serves over its TCP port","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["external container (context/dockerfile)"],"fullName":"external container (context/dockerfile) builds the user Dockerfile and serves it over its TCP port","status":"skipped","title":"builds the user Dockerfile and serves it over its TCP port","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["remote container (image)"],"fullName":"remote container (image) pulls and re-pushes the remote image and serves it over its TCP port","status":"skipped","title":"pulls and re-pushes the remote image and serves it over its TCP port","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Container/Container.test.ts"},{"assertionResults":[{"ancestorTitles":["ContainerApplication"],"fullName":"ContainerApplication list enumerates container applications","status":"failed","title":"list enumerates container applications","duration":1145.1693000000087,"failureMessages":["Provider not found for undefined"],"meta":{},"tags":[]}],"startTime":1782386147640,"endTime":1782386148785.1692,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Container/ContainerApplication.test.ts"},{"assertionResults":[{"ancestorTitles":["ContentScanning"],"fullName":"ContentScanning surfaces the typed ContentScanningNotEntitled error on unentitled zones","status":"skipped","title":"surfaces the typed ContentScanningNotEntitled error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ContentScanning"],"fullName":"ContentScanning pins Content Scanning off on an unentitled zone and destroys cleanly","status":"skipped","title":"pins Content Scanning off on an unentitled zone and destroys cleanly","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ContentScanning"],"fullName":"ContentScanning list enumerates the status across all zones","status":"skipped","title":"list enumerates the status across all zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ContentScanning"],"fullName":"ContentScanning enables Content Scanning and restores the original status on destroy","status":"skipped","title":"enables Content Scanning and restores the original status on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ContentScanning/ContentScanning.test.ts"},{"assertionResults":[{"ancestorTitles":["Expression"],"fullName":"Expression surfaces the typed ContentScanningNotEnabled error when scanning is disabled","status":"skipped","title":"surfaces the typed ContentScanningNotEnabled error when scanning is disabled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Expression"],"fullName":"Expression creates a custom expression, replaces on payload change, destroys cleanly","status":"skipped","title":"creates a custom expression, replaces on payload change, destroys cleanly","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Expression"],"fullName":"Expression list returns a well-typed array across all zones","status":"passed","title":"list returns a well-typed array across all zones","duration":2540.755400000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Expression"],"fullName":"Expression list enumerates the deployed expression","status":"skipped","title":"list enumerates the deployed expression","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386231081,"endTime":1782386233621.7554,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ContentScanning/Expression.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed PlanLevelNotAllowed error on unentitled zones","status":"skipped","title":"surfaces the typed PlanLevelNotAllowed error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates custom certificates across zones","status":"passed","title":"list enumerates custom certificates across zones","duration":2247.4026000000013,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"uploads, rotates in place, replaces on type change, and deletes","status":"skipped","title":"uploads, rotates in place, replaces on type change, and deletes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386235081,"endTime":1782386237328.4026,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CustomCertificate/CustomCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a custom hostname with default ssl","status":"skipped","title":"create and delete a custom hostname with default ssl","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating ssl method patches in place","status":"skipped","title":"updating ssl method patches in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of custom hostnames","status":"passed","title":"list returns a well-typed array of custom hostnames","duration":2748.2615000000005,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed custom hostname","status":"skipped","title":"list enumerates the deployed custom hostname","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the hostname triggers replacement","status":"skipped","title":"changing the hostname triggers replacement","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386235380,"endTime":1782386238128.2615,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CustomHostname/CustomHostname.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates fallback origins across all zones","status":"passed","title":"list enumerates fallback origins across all zones","duration":1872.1215999999986,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list surfaces a deployed fallback origin","status":"skipped","title":"list surfaces a deployed fallback origin","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"set, update and delete the zone fallback origin","status":"skipped","title":"set, update and delete the zone fallback origin","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386233111,"endTime":1782386234983.1216,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CustomHostname/FallbackOrigin.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed CustomNameserversNotEnabled error on unentitled accounts","status":"passed","title":"surfaces the typed CustomNameserversNotEnabled error on unentitled accounts","duration":2529.5570000000007,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account custom nameservers","status":"passed","title":"list enumerates account custom nameservers","duration":2677.0802000000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed custom nameserver","status":"skipped","title":"list includes a deployed custom nameserver","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, replace on nsSet change, and destroy a custom nameserver","status":"skipped","title":"create, replace on nsSet change, and destroy a custom nameserver","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386225270,"endTime":1782386227949.08,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CustomNameserver/CustomNameserver.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"D1Connection.bind exercises the full client surface","status":"skipped","title":"D1Connection.bind exercises the full client surface","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/D1/D1Binding.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed database","status":"passed","title":"list enumerates the deployed database","duration":4184.435799999999,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386178758,"endTime":1782386182942.4358,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/D1/D1Database.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete database with default props","status":"passed","title":"create and delete database with default props","duration":5234.165099999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete database","status":"failed","title":"create, update, delete database","duration":4753.0193,"failureMessages":["Forbidden: System limit reached: databases per account (1)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"applies migrations from migrationsDir","status":"failed","title":"applies migrations from migrationsDir","duration":4763.7774999999965,"failureMessages":["Forbidden: System limit reached: databases per account (1)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"applies migrations using a custom migrationsTable","status":"failed","title":"applies migrations using a custom migrationsTable","duration":4866.761699999988,"failureMessages":["Forbidden: System limit reached: databases per account (1)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"migrates legacy 2-column migration table to wrangler schema","status":"failed","title":"migrates legacy 2-column migration table to wrangler schema","duration":4784.55279999999,"failureMessages":["Forbidden: System limit reached: databases per account (1)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"imports SQL files via importFiles","status":"passed","title":"imports SQL files via importFiles","duration":4241.559000000008,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"clones a database by databaseId","status":"skipped","title":"clones a database by databaseId","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"clones a database by name lookup","status":"skipped","title":"clones a database by name lookup","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"clones a database by passing the source resource directly","status":"skipped","title":"clones a database by passing the source resource directly","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"existing database (matching name) is silently adopted without --adopt","status":"passed","title":"existing database (matching name) is silently adopted without --adopt","duration":3704.7341999999917,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386147655,"endTime":1782386156652.559,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/D1/Database.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AdvancedTcpProtectionNotEntitled error without Magic Transit","status":"skipped","title":"surfaces the typed AdvancedTcpProtectionNotEntitled error without Magic Transit","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an allowlist entry, updates it in place, replaces on prefix change, and destroys","status":"skipped","title":"creates an allowlist entry, updates it in place, replaces on prefix change, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed empty array without Magic Transit","status":"skipped","title":"list returns a well-typed empty array without Magic Transit","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed allowlist entry","status":"skipped","title":"list enumerates the deployed allowlist entry","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/AllowlistEntry.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates a SYN protection filter, updates it in place, and destroys","status":"skipped","title":"creates a SYN protection filter, updates it in place, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of SYN protection filters","status":"passed","title":"list returns a well-typed array of SYN protection filters","duration":30469.5429,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed SYN protection filter","status":"skipped","title":"list enumerates the deployed SYN protection filter","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386147619,"endTime":1782386178088.543,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/SynProtectionFilter.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates a global SYN protection rule, updates it in place, and destroys","status":"skipped","title":"creates a global SYN protection rule, updates it in place, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of SYN protection rules","status":"passed","title":"list returns a well-typed array of SYN protection rules","duration":25939.6887,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed SYN protection rule","status":"skipped","title":"list enumerates the deployed SYN protection rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386180694,"endTime":1782386206633.6887,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/SynProtectionRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates a TCP flow protection filter, updates it in place, and destroys","status":"skipped","title":"creates a TCP flow protection filter, updates it in place, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account's TCP flow protection filters","status":"passed","title":"list returns the account's TCP flow protection filters","duration":28052.76520000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed TCP flow protection filter","status":"skipped","title":"list enumerates the deployed TCP flow protection filter","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386147602,"endTime":1782386175654.7651,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/TcpFlowProtectionFilter.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates a global TCP flow protection rule, updates it in place, and destroys","status":"skipped","title":"creates a global TCP flow protection rule, updates it in place, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed empty array without Magic Transit","status":"passed","title":"list returns a well-typed empty array without Magic Transit","duration":26849.2672,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed TCP flow protection rule","status":"skipped","title":"list enumerates the deployed TCP flow protection rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386173888,"endTime":1782386200737.267,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/TcpFlowProtectionRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a custom device profile","status":"skipped","title":"create, update in place, and delete a custom device profile","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed custom device profile","status":"skipped","title":"list enumerates the deployed custom device profile","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/CustomProfile.test.ts"},{"assertionResults":[{"ancestorTitles":["DefaultProfile"],"fullName":"DefaultProfile reads the existing default device profile without mutating it","status":"skipped","title":"reads the existing default device profile without mutating it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DefaultProfile"],"fullName":"DefaultProfile toggles captivePortal and restores the original value","status":"skipped","title":"toggles captivePortal and restores the original value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DefaultProfile"],"fullName":"DefaultProfile list returns the singleton default device profile","status":"skipped","title":"list returns the singleton default device profile","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/DefaultProfile.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden entitlement error","status":"skipped","title":"unentitled accounts surface the typed Forbidden entitlement error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a DEX test","status":"skipped","title":"create, update in place, and destroy a DEX test","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed DEX test","status":"skipped","title":"list enumerates the deployed DEX test","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed empty array on unentitled accounts","status":"skipped","title":"list returns a well-typed empty array on unentitled accounts","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/DexTest.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a managed network","status":"skipped","title":"create, update in place, and delete a managed network","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed managed network","status":"skipped","title":"list enumerates the deployed managed network","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/ManagedNetwork.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates without a real provider surface the typed InvalidPostureIntegrationConfig error","status":"skipped","title":"creates without a real provider surface the typed InvalidPostureIntegrationConfig error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a posture integration","status":"skipped","title":"create, update in place, and destroy a posture integration","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates posture integrations in the account","status":"passed","title":"list enumerates posture integrations in the account","duration":27192.258900000004,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed posture integration","status":"skipped","title":"list includes a deployed posture integration","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386173786,"endTime":1782386200978.2588,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/PostureIntegration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a posture rule","status":"skipped","title":"create, update in place, and delete a posture rule","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the rule type triggers a replacement","status":"skipped","title":"changing the rule type triggers a replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed posture rule","status":"skipped","title":"list enumerates the deployed posture rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/PostureRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"patches disableForTime and restores the original value on destroy","status":"skipped","title":"patches disableForTime and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account's device settings singleton","status":"skipped","title":"list returns the account's device settings singleton","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"rejects public IPs with the typed InvalidHealthcheckEndpoint error","status":"skipped","title":"rejects public IPs with the typed InvalidHealthcheckEndpoint error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an endpoint healthcheck, updates in place, and destroys","status":"skipped","title":"creates an endpoint healthcheck, updates in place, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed endpoint healthcheck","status":"skipped","title":"list enumerates the deployed endpoint healthcheck","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Diagnostics/EndpointHealthcheck.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden error","status":"skipped","title":"unentitled accounts surface the typed Forbidden error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, and destroy a DLP profile with a standalone entry","status":"skipped","title":"create, update, and destroy a DLP profile with a standalone entry","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dlp/Dlp.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns the account's custom DLP entries","status":"skipped","title":"list returns the account's custom DLP entries","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed DLP entry","status":"skipped","title":"list enumerates the deployed DLP entry","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dlp/Entry.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates custom DLP profiles (read-only)","status":"skipped","title":"list enumerates custom DLP profiles (read-only)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed custom DLP profile","status":"skipped","title":"list includes a deployed custom DLP profile","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dlp/Profile.test.ts"},{"assertionResults":[{"ancestorTitles":["AccountSettings"],"fullName":"AccountSettings list returns the account's DNS settings singleton","status":"skipped","title":"list returns the account's DNS settings singleton","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AccountSettings"],"fullName":"AccountSettings pins a zone default and restores the pre-management value on destroy","status":"skipped","title":"pins a zone default and restores the pre-management value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AccountSettings"],"fullName":"AccountSettings updates in place, unions managedKeys, restores all managed fields","status":"skipped","title":"updates in place, unions managedKeys, restores all managed fields","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/AccountSettings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed worker drives the full DNS record CRUD surface via DnsReadWrite","status":"skipped","title":"deployed worker drives the full DNS record CRUD surface via DnsReadWrite","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/Dns.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an A record with default props","status":"skipped","title":"create and delete an A record with default props","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating mutable fields patches in place","status":"skipped","title":"updating mutable fields patches in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the record type triggers replacement","status":"skipped","title":"changing the record type triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing record errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing record errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed DNS record","status":"skipped","title":"list enumerates the deployed DNS record","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/DnsRecord.test.ts"},{"assertionResults":[{"ancestorTitles":["Dnssec"],"fullName":"Dnssec enables DNSSEC, captures the disabled baseline, destroy deactivates","status":"skipped","title":"enables DNSSEC, captures the disabled baseline, destroy deactivates","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Dnssec"],"fullName":"Dnssec updates status in place — same zone singleton, no replacement","status":"skipped","title":"updates status in place — same zone singleton, no replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Dnssec"],"fullName":"Dnssec list enumerates active DNSSEC across zones","status":"skipped","title":"list enumerates active DNSSEC across zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/Dnssec.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed InternalDnsNotAvailable error on unentitled accounts","status":"skipped","title":"surfaces the typed InternalDnsNotAvailable error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of internal DNS views","status":"skipped","title":"list returns a well-typed array of internal DNS views","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates a deployed internal DNS view","status":"skipped","title":"list enumerates a deployed internal DNS view","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update zones in place, and delete an internal DNS view","status":"skipped","title":"create, update zones in place, and delete an internal DNS view","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/View.test.ts"},{"assertionResults":[{"ancestorTitles":["ZoneSettings"],"fullName":"ZoneSettings pins flattenAllCnames and restores the pre-management value on destroy","status":"skipped","title":"pins flattenAllCnames and restores the pre-management value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ZoneSettings"],"fullName":"ZoneSettings updates in place, unions managedKeys, restores all managed fields","status":"skipped","title":"updates in place, unions managedKeys, restores all managed fields","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ZoneSettings"],"fullName":"ZoneSettings list enumerates DNS settings across all zones","status":"skipped","title":"list enumerates DNS settings across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneSettings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a zone transfer ACL","status":"skipped","title":"create, update in place, and delete a zone transfer ACL","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"generates a deterministic name when none is provided","status":"skipped","title":"generates a deterministic name when none is provided","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed zone transfer ACLs","status":"skipped","title":"list enumerates the deployed zone transfer ACLs","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferAcl.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates incoming configs across all zones","status":"passed","title":"list enumerates incoming configs across all zones","duration":905.5817000000025,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"incoming config does not persist on non-secondary zones (typed not-found)","status":"skipped","title":"incoming config does not persist on non-secondary zones (typed not-found)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update autoRefreshSeconds in place, and delete the incoming config","status":"skipped","title":"create, update autoRefreshSeconds in place, and delete the incoming config","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386180756,"endTime":1782386181661.5818,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferIncoming.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed OutgoingZoneTransfersNotAllowed error on unentitled zones","status":"skipped","title":"surfaces the typed OutgoingZoneTransfersNotAllowed error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, sync peers and enabled state, and delete the outgoing config","status":"skipped","title":"create, sync peers and enabled state, and delete the outgoing config","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the outgoing transfer config per zone","status":"skipped","title":"list enumerates the outgoing transfer config per zone","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferOutgoing.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create with connection settings, update in place, and delete a peer","status":"skipped","title":"create with connection settings, update in place, and delete a peer","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"wires a TSIG reference through to the peer","status":"skipped","title":"wires a TSIG reference through to the peer","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed peer","status":"skipped","title":"list enumerates the deployed peer","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferPeer.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, rotate the secret in place, and delete a TSIG","status":"skipped","title":"create, rotate the secret in place, and delete a TSIG","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed TSIG","status":"skipped","title":"list enumerates the deployed TSIG","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferTsig.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed DnsFirewallNotEntitled error on unentitled accounts","status":"skipped","title":"surfaces the typed DnsFirewallNotEntitled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, verify out-of-band, destroy, and wait until gone","status":"skipped","title":"create, verify out-of-band, destroy, and wait until gone","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable settings in place, replace on rename","status":"skipped","title":"update mutable settings in place, replace on rename","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the DNS firewall cluster Attributes array","status":"skipped","title":"list returns the DNS firewall cluster Attributes array","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed DNS firewall cluster","status":"skipped","title":"list enumerates the deployed DNS firewall cluster","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DnsFirewall/DnsFirewall.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed email address","status":"skipped","title":"list enumerates the deployed email address","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailAddress.test.ts"},{"assertionResults":[{"ancestorTitles":["EmailCatchAll"],"fullName":"EmailCatchAll configures the catch-all rule and restores the baseline on destroy","status":"skipped","title":"configures the catch-all rule and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["EmailCatchAll"],"fullName":"EmailCatchAll updates the catch-all rule in place and keeps the captured baseline","status":"skipped","title":"updates the catch-all rule in place and keeps the captured baseline","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["EmailCatchAll"],"fullName":"EmailCatchAll list enumerates the catch-all rule across all zones","status":"skipped","title":"list enumerates the catch-all rule across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailCatchAll.test.ts"},{"assertionResults":[{"ancestorTitles":["EmailRouting"],"fullName":"EmailRouting list enumerates email routing across all zones","status":"skipped","title":"list enumerates email routing across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailRouting.test.ts"},{"assertionResults":[{"ancestorTitles":["EmailRule"],"fullName":"EmailRule list enumerates the deployed email rule across all zones","status":"skipped","title":"list enumerates the deployed email rule across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"sends an email through the Worker send_email binding","status":"skipped","title":"sends an email through the Worker send_email binding","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/SendEmail.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and destroy a sending subdomain","status":"skipped","title":"create and destroy a sending subdomain","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the name triggers replacement","status":"skipped","title":"changing the name triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing subdomain errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing subdomain errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed sending subdomain","status":"skipped","title":"list enumerates the deployed sending subdomain","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/SendingSubdomain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed EmailSecurityNotEntitled error on unentitled accounts","status":"skipped","title":"surfaces the typed EmailSecurityNotEntitled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates allow policies (read-only)","status":"skipped","title":"list enumerates allow policies (read-only)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes the deployed allow policy","status":"skipped","title":"list includes the deployed allow policy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, destroy","status":"skipped","title":"create, update in place, destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/AllowPolicy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, destroy","status":"skipped","title":"create, update in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed block sender","status":"passed","title":"list enumerates the deployed block sender","duration":30014.336299999995,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386147593,"endTime":1782386177607.3362,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/BlockSender.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"adopts an onboarded domain, patches settings in place, offboards on destroy","status":"skipped","title":"adopts an onboarded domain, patches settings in place, offboards on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates Email Security domains (or [] when unentitled)","status":"skipped","title":"list enumerates Email Security domains (or [] when unentitled)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/Domain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, destroy","status":"skipped","title":"create, update in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account impersonation registry","status":"skipped","title":"list returns the account impersonation registry","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed impersonation registry entry","status":"skipped","title":"list enumerates the deployed impersonation registry entry","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/ImpersonationRegistryEntry.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, destroy","status":"skipped","title":"create, update in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates trusted domains","status":"skipped","title":"list enumerates trusted domains","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/TrustedDomain.test.ts"},{"assertionResults":[{"ancestorTitles":["FraudDetectionSettings"],"fullName":"FraudDetectionSettings adopts the zone singleton without writing and restores nothing on destroy","status":"skipped","title":"adopts the zone singleton without writing and restores nothing on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["FraudDetectionSettings"],"fullName":"FraudDetectionSettings surfaces the typed FraudDetectionNotEntitled error on unentitled zones","status":"skipped","title":"surfaces the typed FraudDetectionNotEntitled error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["FraudDetectionSettings"],"fullName":"FraudDetectionSettings manages fraud detection settings and restores them on destroy","status":"skipped","title":"manages fraud detection settings and restores them on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["FraudDetectionSettings"],"fullName":"FraudDetectionSettings list enumerates the settings across all zones","status":"skipped","title":"list enumerates the settings across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Fraud/FraudDetectionSettings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete a Flagship app","status":"skipped","title":"create, update, delete a Flagship app","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates an app after out-of-band delete","status":"skipped","title":"recreates an app after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed Flagship app","status":"skipped","title":"list enumerates the deployed Flagship app","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Flagship/App.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete a flag in an app","status":"skipped","title":"create, update, delete a flag in an app","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the flag when the key changes","status":"skipped","title":"replaces the flag when the key changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates a flag after out-of-band delete","status":"skipped","title":"recreates a flag after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed flag","status":"skipped","title":"list enumerates the deployed flag","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Flagship/Flag.test.ts"},{"assertionResults":[],"startTime":1782386072728,"endTime":1782386072728,"status":"failed","message":"FlagshipApp is not a function","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Flagship/Flagship.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create an activated certificate, deactivate in place, destroy","status":"skipped","title":"create an activated certificate, deactivate in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the validity period replaces the certificate","status":"skipped","title":"changing the validity period replaces the certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed gateway certificate","status":"skipped","title":"list enumerates the deployed gateway certificate","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Certificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"manage activityLog and protocolDetection, then restore on destroy","status":"skipped","title":"manage activityLog and protocolDetection, then restore on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account Gateway configuration singleton","status":"skipped","title":"list returns the account Gateway configuration singleton","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Configuration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy a DOMAIN list","status":"skipped","title":"create, verify, and destroy a DOMAIN list","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update items, description, and name in place","status":"skipped","title":"update items, description, and name in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the type replaces the list","status":"skipped","title":"changing the type replaces the list","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/List.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy a location","status":"skipped","title":"create, verify, and destroy a location","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name and ecsSupport in place (same id)","status":"skipped","title":"update name and ecsSupport in place (same id)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates deployed gateway locations","status":"skipped","title":"list enumerates deployed gateway locations","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Location.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"manage logging settings, update in place, restore on destroy","status":"skipped","title":"manage logging settings, update in place, restore on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account's Gateway logging singleton","status":"skipped","title":"list returns the account's Gateway logging singleton","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Logging.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, and destroy an identity-kind proxy endpoint","status":"skipped","title":"create, update, and destroy an identity-kind proxy endpoint","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"ip-kind endpoints surface the typed entitlement error","status":"skipped","title":"ip-kind endpoints surface the typed entitlement error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed proxy endpoint","status":"skipped","title":"list enumerates the deployed proxy endpoint","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/ProxyEndpoint.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed Gateway rule","status":"skipped","title":"list enumerates the deployed Gateway rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Rule.test.ts"},{"assertionResults":[{"ancestorTitles":["GoogleTagGateway"],"fullName":"GoogleTagGateway configures the gateway, updates in place, and restores the baseline on destroy","status":"skipped","title":"configures the gateway, updates in place, and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["GoogleTagGateway"],"fullName":"GoogleTagGateway no-op redeploy skips the PUT and destroy is idempotent","status":"skipped","title":"no-op redeploy skips the PUT and destroy is idempotent","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["GoogleTagGateway"],"fullName":"GoogleTagGateway list enumerates configured zones","status":"skipped","title":"list enumerates configured zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/GoogleTagGateway/GoogleTagGateway.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"lists overrides and surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","status":"skipped","title":"lists overrides and surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates per-hostname TLS overrides","status":"passed","title":"list enumerates per-hostname TLS overrides","duration":3545.944199999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a min_tls_version override","status":"skipped","title":"create, update in place, and destroy a min_tls_version override","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386229784,"endTime":1782386233329.944,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/HostnameTlsSetting/HostnameTlsSetting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a zone-scoped ip rule","status":"skipped","title":"create and delete a zone-scoped ip rule","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mode and notes in place (same ruleId)","status":"skipped","title":"update mode and notes in place (same ruleId)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the configuration triggers replacement","status":"skipped","title":"changing the configuration triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed zone-scoped rule","status":"skipped","title":"list enumerates the deployed zone-scoped rule","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"account-scoped rule (no zoneId) create, update, delete","status":"skipped","title":"account-scoped rule (no zoneId) create, update, delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Firewall/AccessRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update urls/configurations/description/paused in place, destroy","status":"skipped","title":"create, update urls/configurations/description/paused in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed lockdown rule","status":"skipped","title":"list enumerates the deployed lockdown rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Firewall/Lockdown.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update mode/paused/description/userAgent in place, destroy","status":"skipped","title":"create, update mode/paused/description/userAgent in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates UA rules across all zones","status":"skipped","title":"list enumerates UA rules across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Firewall/UaRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete hyperdrive with default props","status":"passed","title":"create and delete hyperdrive with default props","duration":10141.472899999993,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete hyperdrive","status":"failed","title":"create, update, delete hyperdrive","duration":8441.518300000003,"failureMessages":["BadRequest: This account has reached its limit for how many Hyperdrives it can have (2)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed hyperdrive","status":"passed","title":"list enumerates the deployed hyperdrive","duration":9155.1037,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386189893,"endTime":1782386200034.473,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Hyperdrive/Hyperdrive.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, update name+scope in place, destroy","status":"skipped","title":"create, verify out-of-band, update name+scope in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed resource group","status":"skipped","title":"list enumerates the deployed resource group","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Iam/ResourceGroup.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create with a policy, verify out-of-band, update policies in place, destroy","status":"skipped","title":"create with a policy, verify out-of-band, update policies in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed user group","status":"skipped","title":"list enumerates the deployed user group","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Iam/UserGroup.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, replace when the user group changes, destroy","status":"skipped","title":"create, verify out-of-band, replace when the user group changes, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates memberships across all user groups in the account","status":"skipped","title":"list enumerates memberships across all user groups in the account","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Iam/UserGroupMembership.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"async worker reads image info via env Images binding","status":"skipped","title":"async worker reads image info via env Images binding","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker reads image info via yield* Images","status":"skipped","title":"effect worker reads image info via yield* Images","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Images/Images.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the account's signing keys","status":"skipped","title":"list enumerates the account's signing keys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"surfaces the typed ImagesAccessNotEnabled error on unentitled accounts","status":"skipped","title":"surfaces the typed ImagesAccessNotEnabled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create a signing key, no-op redeploy keeps the value, delete","status":"skipped","title":"create a signing key, no-op redeploy keeps the value, delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Images/SigningKey.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a variant","status":"skipped","title":"create, update in place, and delete a variant","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replace a variant when the name changes","status":"skipped","title":"replace a variant when the name changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed variant","status":"skipped","title":"list enumerates the deployed variant","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Images/Variant.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed IndicatorFeedsNotEntitled error","status":"skipped","title":"unentitled accounts surface the typed IndicatorFeedsNotEntitled error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of indicator feeds","status":"passed","title":"list returns a well-typed array of indicator feeds","duration":27176.993799999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed indicator feed","status":"skipped","title":"list enumerates the deployed indicator feed","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create (or adopt), verify out-of-band, update in place, destroy","status":"skipped","title":"create (or adopt), verify out-of-band, update in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"uploads a STIX2 snapshot and grants/revokes a consumer permission","status":"skipped","title":"uploads a STIX2 snapshot and grants/revokes a consumer permission","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386178788,"endTime":1782386205964.994,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Intel/IndicatorFeed.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns [] for the non-listable IndicatorFeedPermission","status":"passed","title":"list returns [] for the non-listable IndicatorFeedPermission","duration":716.2498999999989,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386232829,"endTime":1782386233545.25,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Intel/IndicatorFeedPermission.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed KeylessSslNotAvailable error on non-Enterprise zones","status":"skipped","title":"surfaces the typed KeylessSslNotAvailable error on non-Enterprise zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates keyless certificates across zones","status":"passed","title":"list enumerates keyless certificates across zones","duration":3016.1937,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed keyless certificate","status":"skipped","title":"list includes a deployed keyless certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates, updates in place, replaces on certificate change, and destroys","status":"skipped","title":"creates, updates in place, replaces on certificate change, and destroys","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386231307,"endTime":1782386234323.1936,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/KeylessCertificate/KeylessCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete namespace with default props","status":"passed","title":"create and delete namespace with default props","duration":2564.7785000000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete namespace","status":"passed","title":"create, update, delete namespace","duration":2997.0545999999995,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed namespace","status":"passed","title":"list enumerates the deployed namespace","duration":2769.0950999999986,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"existing namespace (matching title) is silently adopted without --adopt","status":"passed","title":"existing namespace (matching title) is silently adopted without --adopt","duration":3059.955600000001,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386180648,"endTime":1782386183710.9556,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/KV/Namespace.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an HTTP health check with default name","status":"skipped","title":"create and delete an HTTP health check with default name","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable props in place (same id), then no-op redeploy","status":"skipped","title":"update mutable props in place (same id), then no-op redeploy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing type HTTP→TCP updates in place; delete is idempotent","status":"skipped","title":"changing type HTTP→TCP updates in place; delete is idempotent","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing check errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing check errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed health check across zones","status":"skipped","title":"list enumerates the deployed health check across zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Healthcheck/Healthcheck.test.ts"},{"assertionResults":[{"ancestorTitles":["LeakedCredentialDetection"],"fullName":"LeakedCredentialDetection list enumerates custom detections across all zones","status":"skipped","title":"list enumerates custom detections across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LeakedCredentialCheck/Detection.test.ts"},{"assertionResults":[{"ancestorTitles":["LeakedCredentialCheck"],"fullName":"LeakedCredentialCheck enables leaked credential checks and restores the baseline on destroy","status":"skipped","title":"enables leaked credential checks and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["LeakedCredentialCheck"],"fullName":"LeakedCredentialCheck surfaces the typed DetectionQuotaExceeded error on unentitled zones","status":"skipped","title":"surfaces the typed DetectionQuotaExceeded error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["LeakedCredentialCheck"],"fullName":"LeakedCredentialCheck list enumerates the check across all zones","status":"skipped","title":"list enumerates the check across all zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["LeakedCredentialCheck"],"fullName":"LeakedCredentialCheck creates, updates, and destroys a custom detection","status":"skipped","title":"creates, updates, and destroys a custom detection","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LeakedCredentialCheck/LeakedCredentialCheck.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, and delete an account-scoped job pushing to R2","status":"skipped","title":"create, update, and delete an account-scoped job pushing to R2","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed Logpush job","status":"skipped","title":"list enumerates the deployed Logpush job","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the dataset triggers a replacement","status":"skipped","title":"changing the dataset triggers a replacement","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Logpush/Job.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed LogsControlNotAuthorized error on unentitled accounts","status":"skipped","title":"surfaces the typed LogsControlNotAuthorized error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, verify out-of-band, update in place, destroy, and wait until gone","status":"skipped","title":"create, verify out-of-band, update in place, destroy, and wait until gone","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list tolerates the typed LogsControlNotAuthorized error on unentitled accounts","status":"skipped","title":"list tolerates the typed LogsControlNotAuthorized error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the account CMB config singleton","status":"skipped","title":"list enumerates the account CMB config singleton","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LogsControl/CmbConfig.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed LogsControlNotAuthorized error on unentitled zones","status":"skipped","title":"surfaces the typed LogsControlNotAuthorized error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"pins the retention flag, updates in place, and restores on destroy","status":"skipped","title":"pins the retention flag, updates in place, and restores on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the retention flag across all zones","status":"passed","title":"list enumerates the retention flag across all zones","duration":1738.259399999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list contains the entitled test zone's retention flag","status":"skipped","title":"list contains the entitled test zone's retention flag","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386235326,"endTime":1782386237064.2595,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LogsControl/RetentionFlag.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed LoadBalancingNotEnabledForZone error without the LB subscription","status":"skipped","title":"surfaces the typed LoadBalancingNotEnabledForZone error without the LB subscription","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a load balancer","status":"skipped","title":"create, update in place, and destroy a load balancer","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of load balancers","status":"passed","title":"list returns a well-typed array of load balancers","duration":1382.802599999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed load balancer","status":"skipped","title":"list enumerates the deployed load balancer","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386198139,"endTime":1782386199521.8025,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/LoadBalancer.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed MonitorIntervalOutOfRange error without the LB subscription","status":"skipped","title":"surfaces the typed MonitorIntervalOutOfRange error without the LB subscription","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a monitor","status":"skipped","title":"create, update in place, and destroy a monitor","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns an array of monitor attributes","status":"skipped","title":"list returns an array of monitor attributes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed monitor","status":"skipped","title":"list enumerates the deployed monitor","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/Monitor.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed MonitorGroupsNotEnabled error on non-Enterprise accounts","status":"skipped","title":"surfaces the typed MonitorGroupsNotEnabled error on non-Enterprise accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns an array of monitor groups","status":"skipped","title":"list returns an array of monitor groups","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed monitor group","status":"skipped","title":"list enumerates the deployed monitor group","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a monitor group","status":"skipped","title":"create, update in place, and destroy a monitor group","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/MonitorGroup.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed PoolAccessFailed error without the LB subscription","status":"skipped","title":"surfaces the typed PoolAccessFailed error without the LB subscription","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a health-checked pool","status":"skipped","title":"create, update in place, and destroy a health-checked pool","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed pool","status":"skipped","title":"list enumerates the deployed pool","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/Pool.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"diag list","status":"skipped","title":"diag list","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/_diag.test.ts"},{"assertionResults":[{"ancestorTitles":["MagicNetworkMonitoring.Config list"],"fullName":"MagicNetworkMonitoring.Config list list enumerates the account MNM config","status":"skipped","title":"list enumerates the account MNM config","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/Config.test.ts"},{"assertionResults":[{"ancestorTitles":["MagicNetworkMonitoring"],"fullName":"MagicNetworkMonitoring creates, updates in place, and deletes the account config","status":"skipped","title":"creates, updates in place, and deletes the account config","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["MagicNetworkMonitoring"],"fullName":"MagicNetworkMonitoring creates a threshold rule, updates it in place, and replaces on type change","status":"skipped","title":"creates a threshold rule, updates it in place, and replaces on type change","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/MagicNetworkMonitoring.test.ts"},{"assertionResults":[{"ancestorTitles":["MagicNetworkMonitoring.Rule"],"fullName":"MagicNetworkMonitoring.Rule list enumerates the deployed rule","status":"skipped","title":"list enumerates the deployed rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/Rule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicWanUnauthorized error","status":"skipped","title":"unentitled accounts surface the typed MagicWanUnauthorized error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a custom app, updates mutable props in place, and destroys it","status":"skipped","title":"creates a custom app, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account apps (well-typed [] when unentitled)","status":"skipped","title":"list enumerates account apps (well-typed [] when unentitled)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed custom app","status":"skipped","title":"list includes a deployed custom app","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/App.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicTransitNotOnboarded error","status":"passed","title":"unentitled accounts surface the typed MagicTransitNotOnboarded error","duration":78285.44930000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed GRE tunnels","status":"passed","title":"list enumerates the deployed GRE tunnels","duration":27342.242599999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a GRE tunnel, updates mutable props in place, and destroys it","status":"skipped","title":"creates a GRE tunnel, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386147612,"endTime":1782386225897.4492,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/GreTunnel.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicTransitNotOnboarded error","status":"skipped","title":"unentitled accounts surface the typed MagicTransitNotOnboarded error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account IPsec tunnels (read-only [] when unentitled)","status":"passed","title":"list enumerates account IPsec tunnels (read-only [] when unentitled)","duration":26984.226199999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an IPsec tunnel, updates mutable props in place, and destroys it","status":"skipped","title":"creates an IPsec tunnel, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386174609,"endTime":1782386201593.2263,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/IpsecTunnel.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicWanUnauthorized error","status":"skipped","title":"unentitled accounts surface the typed MagicWanUnauthorized error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account Magic WAN sites","status":"skipped","title":"list enumerates account Magic WAN sites","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a site with WAN, LAN, and ACL, updates in place, and destroys","status":"skipped","title":"creates a site with WAN, LAN, and ACL, updates in place, and destroys","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/Site.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed site ACLs","status":"passed","title":"list enumerates the deployed site ACLs","duration":26481.076100000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Magic WAN error for ACL listing","status":"skipped","title":"unentitled accounts surface the typed Magic WAN error for ACL listing","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386178602,"endTime":1782386205083.0762,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/SiteAcl.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a well-typed array (empty on unentitled accounts)","status":"skipped","title":"list returns a well-typed array (empty on unentitled accounts)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed Magic WAN site LANs","status":"skipped","title":"list enumerates the deployed Magic WAN site LANs","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/SiteLan.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a well-typed array of site WANs","status":"passed","title":"list returns a well-typed array of site WANs","duration":26464.1525,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed site WAN","status":"skipped","title":"list enumerates the deployed site WAN","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386178759,"endTime":1782386205223.1526,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/SiteWan.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicTransitNotOnboarded error","status":"skipped","title":"unentitled accounts surface the typed MagicTransitNotOnboarded error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of routes","status":"skipped","title":"list returns a well-typed array of routes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed static route","status":"skipped","title":"list enumerates the deployed static route","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"routes a prefix over a GRE tunnel, updates in place, and destroys","status":"skipped","title":"routes a prefix over a GRE tunnel, updates in place, and destroys","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/StaticRoute.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed FeatureNotEnabled error","status":"skipped","title":"unentitled accounts surface the typed FeatureNotEnabled error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a sync, updates mutable props in place, and destroys it","status":"skipped","title":"creates a sync, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account catalog syncs","status":"skipped","title":"list enumerates account catalog syncs","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the sync when destinationType changes","status":"skipped","title":"replaces the sync when destinationType changes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicCloudNetworking/CatalogSync.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed FeatureNotEnabled error","status":"skipped","title":"unentitled accounts surface the typed FeatureNotEnabled error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of integrations","status":"skipped","title":"list returns a well-typed array of integrations","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed integration","status":"skipped","title":"list enumerates the deployed integration","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"registers an AWS integration, updates it in place, and destroys it","status":"skipped","title":"registers an AWS integration, updates it in place, and destroys it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the integration when cloudType changes","status":"skipped","title":"replaces the integration when cloudType changes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicCloudNetworking/CloudIntegration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed FeatureNotEnabled error","status":"skipped","title":"unentitled accounts surface the typed FeatureNotEnabled error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an on-ramp, updates mutable props in place, and destroys it","status":"skipped","title":"creates an on-ramp, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns on-ramps or a typed [] when unentitled","status":"skipped","title":"list returns on-ramps or a typed [] when unentitled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed on-ramp","status":"skipped","title":"list enumerates the deployed on-ramp","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicCloudNetworking/OnRamp.test.ts"},{"assertionResults":[{"ancestorTitles":["ManagedTransforms"],"fullName":"ManagedTransforms manages a named transform, updates in place, and restores it on destroy","status":"skipped","title":"manages a named transform, updates in place, and restores it on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ManagedTransforms"],"fullName":"ManagedTransforms destroy restores a transform that was enabled before management","status":"skipped","title":"destroy restores a transform that was enabled before management","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ManagedTransforms"],"fullName":"ManagedTransforms deploy with no transforms named adopts the singleton without writing","status":"skipped","title":"deploy with no transforms named adopts the singleton without writing","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ManagedTransforms"],"fullName":"ManagedTransforms list enumerates managed transforms across all zones","status":"skipped","title":"list enumerates managed transforms across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ManagedTransforms/ManagedTransforms.test.ts"},{"assertionResults":[{"ancestorTitles":["MtlsCertificate"],"fullName":"MtlsCertificate create and delete a CA certificate","status":"skipped","title":"create and delete a CA certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["MtlsCertificate"],"fullName":"MtlsCertificate create and delete a leaf certificate with private key","status":"skipped","title":"create and delete a leaf certificate with private key","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["MtlsCertificate"],"fullName":"MtlsCertificate replaces the certificate when the PEM changes","status":"skipped","title":"replaces the certificate when the PEM changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["MtlsCertificate"],"fullName":"MtlsCertificate list enumerates the deployed mTLS certificate","status":"skipped","title":"list enumerates the deployed mTLS certificate","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MtlsCertificate/MtlsCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden error","status":"skipped","title":"unentitled accounts surface the typed Forbidden error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list either enumerates organizations or tolerates the unentitled account","status":"skipped","title":"list either enumerates organizations or tolerates the unentitled account","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed organization","status":"skipped","title":"list enumerates the deployed organization","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, verify out-of-band, update in place, destroy","status":"skipped","title":"create, verify out-of-band, update in place, destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Organization/Organization.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins the default ASN, updates in place, and restores the original on destroy","status":"skipped","title":"pins the default ASN, updates in place, and restores the original on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the CNI settings singleton","status":"skipped","title":"list enumerates the CNI settings singleton","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/NetworkInterconnects/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":["OriginTlsClientAuthCertificate"],"fullName":"OriginTlsClientAuthCertificate uploads and deletes a zone client certificate","status":"skipped","title":"uploads and deletes a zone client certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["OriginTlsClientAuthCertificate"],"fullName":"OriginTlsClientAuthCertificate replaces the certificate when the PEM changes","status":"skipped","title":"replaces the certificate when the PEM changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["OriginTlsClientAuthCertificate"],"fullName":"OriginTlsClientAuthCertificate list enumerates the deployed zone client certificate","status":"skipped","title":"list enumerates the deployed zone client certificate","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/Certificate.test.ts"},{"assertionResults":[{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation list returns [] for the non-listable association","status":"passed","title":"list returns [] for the non-listable association","duration":643.7504999999983,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation associates a hostname, updates cert and enablement in place, voids on destroy","status":"skipped","title":"associates a hostname, updates cert and enablement in place, voids on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation replaces the association when the hostname changes","status":"skipped","title":"replaces the association when the hostname changes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386235588,"endTime":1782386236231.7505,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/HostnameAssociation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"uploads and deletes a hostname client certificate","status":"skipped","title":"uploads and deletes a hostname client certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the hostname certificate when the PEM changes","status":"skipped","title":"replaces the hostname certificate when the PEM changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed hostname certificate","status":"skipped","title":"list enumerates the deployed hostname certificate","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/HostnameCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":["Setting"],"fullName":"Setting enables AOP and restores the original value on destroy","status":"skipped","title":"enables AOP and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Setting"],"fullName":"Setting updates the enabled flag in place","status":"skipped","title":"updates the enabled flag in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Setting"],"fullName":"Setting list enumerates the setting across all zones","status":"skipped","title":"list enumerates the setting across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/Setting.test.ts"},{"assertionResults":[{"ancestorTitles":["OriginPostQuantumEncryption"],"fullName":"OriginPostQuantumEncryption pins the setting and restores the original value on destroy","status":"skipped","title":"pins the setting and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["OriginPostQuantumEncryption"],"fullName":"OriginPostQuantumEncryption updates the value in place and keeps the captured initial value","status":"skipped","title":"updates the value in place and keeps the captured initial value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["OriginPostQuantumEncryption"],"fullName":"OriginPostQuantumEncryption no-op redeploy converges without changing the setting","status":"skipped","title":"no-op redeploy converges without changing the setting","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["OriginPostQuantumEncryption"],"fullName":"OriginPostQuantumEncryption list enumerates the setting across all zones","status":"skipped","title":"list enumerates the setting across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginPostQuantumEncryption/OriginPostQuantumEncryption.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, and destroy a page rule","status":"skipped","title":"create, verify out-of-band, and destroy a page rule","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating actions, status, priority and target syncs in place","status":"skipped","title":"updating actions, status, priority and target syncs in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing rule errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing rule errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed page rule","status":"skipped","title":"list enumerates the deployed page rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/PageRule/PageRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"issue, verify, and revoke a certificate","status":"skipped","title":"issue, verify, and revoke a certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates issued certificates","status":"skipped","title":"list enumerates issued certificates","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replacement on requestedValidity change","status":"skipped","title":"replacement on requestedValidity change","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replacement on hostnames change","status":"skipped","title":"replacement on hostnames change","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginCaCertificate/OriginCaCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, replace on branch change, and destroy a deployment","status":"failed","title":"create, replace on branch change, and destroy a deployment","duration":120132.67270000001,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Deployment.test.ts:92:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates deployments across Pages projects","status":"skipped","title":"list enumerates deployments across Pages projects","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386147626,"endTime":1782386267758.6726,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Deployment.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"attach and detach a custom domain","status":"failed","title":"attach and detach a custom domain","duration":120255.0738,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Domain.test.ts:99:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the domain name triggers replacement","status":"failed","title":"changing the domain name triggers replacement","duration":120289.9633,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Domain.test.ts:166:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates domains across all Pages projects","status":"skipped","title":"list enumerates domains across all Pages projects","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386147600,"endTime":1782386267892.9634,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Domain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a project with generated name","status":"failed","title":"create and delete a project with generated name","duration":27408.141000000003,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable props in place (same project id)","status":"failed","title":"update mutable props in place (same project id)","duration":120065.7073,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Project.test.ts:94:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed project","status":"failed","title":"list enumerates the deployed project","duration":120143.5726,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Project.test.ts:205:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the name triggers replacement","status":"failed","title":"changing the name triggers replacement","duration":120172.9614,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Project.test.ts:235:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]}],"startTime":1782386147599,"endTime":1782386267774.9614,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Project.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed PolicyQuotaExceeded error on unentitled zones","status":"skipped","title":"surfaces the typed PolicyQuotaExceeded error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates Page Shield policies across all zones","status":"passed","title":"list enumerates Page Shield policies across all zones","duration":2235.8392999999996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates, updates in place, and deletes a CSP policy","status":"skipped","title":"creates, updates in place, and deletes a CSP policy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386227473,"endTime":1782386229708.8394,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/PageShield/Policy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"enables Page Shield, updates in place, and restores the baseline on destroy","status":"skipped","title":"enables Page Shield, updates in place, and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"surfaces the typed NotEntitled error for plan-gated flags","status":"skipped","title":"surfaces the typed NotEntitled error for plan-gated flags","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates Page Shield settings across all zones","status":"skipped","title":"list enumerates Page Shield settings across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/PageShield/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"legacy pipeline: create, in-place update, replace on name change","status":"skipped","title":"legacy pipeline: create, in-place update, replace on name change","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed legacy pipeline","status":"skipped","title":"list enumerates the deployed legacy pipeline","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/LegacyPipeline.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed pipeline","status":"skipped","title":"list enumerates the deployed pipeline","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/Pipeline.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"stream: create with defaults, patch http in place, replace on schema change","status":"skipped","title":"stream: create with defaults, patch http in place, replace on schema change","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"end-to-end: stream → sql pipeline → r2 sink, replacement on sink change","status":"skipped","title":"end-to-end: stream → sql pipeline → r2 sink, replacement on sink change","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/Pipelines.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed sink","status":"skipped","title":"list enumerates the deployed sink","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/Sink.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed pipeline stream","status":"skipped","title":"list enumerates the deployed pipeline stream","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/Stream.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"promotes a dev queue to a live queue on deploy","status":"skipped","title":"promotes a dev queue to a live queue on deploy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed queue","status":"skipped","title":"list enumerates the deployed queue","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"suppresses deletion of a dev-only queue","status":"skipped","title":"suppresses deletion of a dev-only queue","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/Queue.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update settings, replace script, delete","status":"skipped","title":"create, update settings, replace script, delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates consumer after out-of-band delete","status":"skipped","title":"recreates consumer after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopts existing consumer after local state loss","status":"skipped","title":"adopts existing consumer after local state loss","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"fails clearly when queue has consumer for different script","status":"skipped","title":"fails clearly when queue has consumer for different script","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"suppresses deletion of a dev-only consumer","status":"skipped","title":"suppresses deletion of a dev-only consumer","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"promotes a dev consumer to a live consumer on deploy","status":"skipped","title":"promotes a dev consumer to a live consumer on deploy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed consumer","status":"skipped","title":"list enumerates the deployed consumer","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/QueueConsumer.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"send → subscribe handler → DO state → polled by test client","status":"skipped","title":"send → subscribe handler → DO state → polled by test client","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/RoundTrip.test.ts"},{"assertionResults":[{"ancestorTitles":["Subscription"],"fullName":"Subscription create r2 event subscription into a queue and destroy it","status":"skipped","title":"create r2 event subscription into a queue and destroy it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Subscription"],"fullName":"Subscription update mutable props in place (same subscriptionId)","status":"skipped","title":"update mutable props in place (same subscriptionId)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Subscription"],"fullName":"Subscription replaces the subscription when the source changes","status":"skipped","title":"replaces the subscription when the source changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Subscription"],"fullName":"Subscription list enumerates the deployed subscription","status":"skipped","title":"list enumerates the deployed subscription","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/Subscription.test.ts"},{"assertionResults":[{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings passes scalar fields through unchanged","status":"passed","title":"passes scalar fields through unchanged","duration":15.70890000000145,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings converts maxWaitTime to whole milliseconds","status":"passed","title":"converts maxWaitTime to whole milliseconds","duration":13.332099999999627,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings rounds sub-millisecond maxWaitTime up","status":"passed","title":"rounds sub-millisecond maxWaitTime up","duration":13.048499999998967,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings converts retryDelay to whole seconds","status":"passed","title":"converts retryDelay to whole seconds","duration":12.804799999999886,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings rounds partial-second retryDelay up","status":"passed","title":"rounds partial-second retryDelay up","duration":12.671699999998964,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings leaves missing time fields undefined","status":"passed","title":"leaves missing time fields undefined","duration":1.2870999999995547,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386230403,"endTime":1782386230420.287,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/toConsumerSettings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"enable, sync maintenance config, register credential, destroy","status":"skipped","title":"enable, sync maintenance config, register credential, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed catalog","status":"skipped","title":"list enumerates the deployed catalog","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"re-enables after out-of-band disable","status":"skipped","title":"re-enables after out-of-band disable","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2DataCatalog/R2DataCatalog.test.ts"},{"assertionResults":[{"ancestorTitles":["async worker (env binding)"],"fullName":"async worker (env binding) throttles requests past the configured limit","status":"skipped","title":"throttles requests past the configured limit","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["async worker (env binding)"],"fullName":"async worker (env binding) tracks each key independently","status":"skipped","title":"tracks each key independently","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["effect worker (yield* RateLimit)"],"fullName":"effect worker (yield* RateLimit) throttles requests past the configured limit","status":"skipped","title":"throttles requests past the configured limit","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["effect worker (yield* RateLimit)"],"fullName":"effect worker (yield* RateLimit) tracks each key independently","status":"skipped","title":"tracks each key independently","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RateLimit/RateLimit.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"regional hostname lifecycle (typed error on unentitled zones)","status":"skipped","title":"regional hostname lifecycle (typed error on unentitled zones)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates regional hostnames across zones","status":"skipped","title":"list enumerates regional hostnames across zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RegionalHostname/RegionalHostname.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete bucket with default props","status":"skipped","title":"create and delete bucket with default props","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete bucket","status":"skipped","title":"create, update, delete bucket","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"existing bucket (matching name) is silently adopted without --adopt","status":"skipped","title":"existing bucket (matching name) is silently adopted without --adopt","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"destroying a bucket empties its objects first","status":"skipped","title":"destroying a bucket empties its objects first","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"lifecycle rules are added, updated, and removed","status":"skipped","title":"lifecycle rules are added, updated, and removed","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/Bucket.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update rules in place, and delete","status":"skipped","title":"create, update rules in place, and delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the queue triggers a replacement","status":"skipped","title":"changing the queue triggers a replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates deployed bucket event notifications","status":"skipped","title":"list enumerates deployed bucket event notifications","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/BucketEventNotification.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"reads the disabled baseline and surfaces typed errors without external creds","status":"skipped","title":"reads the disabled baseline and surfaces typed errors without external creds","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates buckets that have Sippy enabled","status":"skipped","title":"list enumerates buckets that have Sippy enabled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a bucket with Sippy enabled","status":"skipped","title":"list includes a bucket with Sippy enabled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"enable, no-op redeploy, disable on destroy","status":"skipped","title":"enable, no-op redeploy, disable on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/BucketSippy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates, updates, and deletes a bucket custom domain","status":"skipped","title":"creates, updates, and deletes a bucket custom domain","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates, updates, and deletes a bucket with multiple custom domains","status":"skipped","title":"creates, updates, and deletes a bucket with multiple custom domains","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/CustomDomain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed R2 bucket","status":"skipped","title":"list enumerates the deployed R2 bucket","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/R2Bucket.test.ts"},{"assertionResults":[{"ancestorTitles":["Domain"],"fullName":"Domain adopts a registered domain, no-op syncs, and never releases it on destroy","status":"skipped","title":"adopts a registered domain, no-op syncs, and never releases it on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Domain"],"fullName":"Domain updates settings in place and restores the baseline on destroy","status":"skipped","title":"updates settings in place and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Domain"],"fullName":"Domain list enumerates registrar domains on the account","status":"skipped","title":"list enumerates registrar domains on the account","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Registrar/Domain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an ip list with default name","status":"skipped","title":"create and delete an ip list with default name","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update description and items in place (same listId)","status":"skipped","title":"update description and items in place (same listId)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing kind replaces the list","status":"skipped","title":"changing kind replaces the list","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopts an existing list with the same name","status":"skipped","title":"adopts an existing list with the same name","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Rules/List.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden error","status":"passed","title":"unentitled accounts surface the typed Forbidden error","duration":27755.6894,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, toggle active in place, and destroy a risk scoring integration","status":"skipped","title":"create, toggle active in place, and destroy a risk scoring integration","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates risk scoring integrations","status":"passed","title":"list enumerates risk scoring integrations","duration":28352.0756,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a freshly deployed integration","status":"skipped","title":"list includes a freshly deployed integration","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386175411,"endTime":1782386203765.0757,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RiskScoring/Integration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"reads succeed and write-blocked accounts surface the typed Forbidden error","status":"skipped","title":"reads succeed and write-blocked accounts surface the typed Forbidden error","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the account's sent shares (typed Attributes), and includes a deployed share when entitled","status":"skipped","title":"list enumerates the account's sent shares (typed Attributes), and includes a deployed share when entitled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a share with a gateway policy, renames in place, adds and removes a resource, and destroys","status":"skipped","title":"creates a share with a gateway policy, renames in place, adds and removes a resource, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"manages standalone ShareResource and ShareRecipient on an existing share","status":"skipped","title":"manages standalone ShareResource and ShareRecipient on an existing share","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ResourceSharing/Share.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list() enumerates share recipients across the account's sent shares","status":"passed","title":"list() enumerates share recipients across the account's sent shares","duration":30277.9632,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list() includes a freshly deployed ShareRecipient","status":"skipped","title":"list() includes a freshly deployed ShareRecipient","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386147604,"endTime":1782386177881.9631,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ResourceSharing/ShareRecipient.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates share resources across the account's sent shares (typed Attributes), and includes a deployed entry when entitled","status":"skipped","title":"list enumerates share resources across the account's sent shares (typed Attributes), and includes a deployed entry when entitled","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ResourceSharing/ShareResource.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"account entrypoint lifecycle (typed PhaseNotEntitled on unentitled accounts)","status":"skipped","title":"account entrypoint lifecycle (typed PhaseNotEntitled on unentitled accounts)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the account phase entrypoints","status":"skipped","title":"list enumerates the account phase entrypoints","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ruleset/AccountEntrypoint.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"custom ruleset lifecycle (typed PhaseNotEntitled on unentitled accounts)","status":"skipped","title":"custom ruleset lifecycle (typed PhaseNotEntitled on unentitled accounts)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed custom ruleset","status":"skipped","title":"list enumerates the deployed custom ruleset","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ruleset/CustomRuleset.test.ts"},{"assertionResults":[{"ancestorTitles":["Ruleset"],"fullName":"Ruleset creates, updates, and deletes a zone phase entrypoint ruleset","status":"skipped","title":"creates, updates, and deletes a zone phase entrypoint ruleset","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Ruleset"],"fullName":"Ruleset creates and tears down a ruleset whose zone is provisioned in the same deploy","status":"skipped","title":"creates and tears down a ruleset whose zone is provisioned in the same deploy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Ruleset"],"fullName":"Ruleset list enumerates the deployed zone phase entrypoint","status":"skipped","title":"list enumerates the deployed zone phase entrypoint","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ruleset/Ruleset.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"sets a per-operation override, updates it in place, and clears it on destroy","status":"skipped","title":"sets a per-operation override, updates it in place, and clears it on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed per-operation override","status":"skipped","title":"list enumerates the deployed per-operation override","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SchemaValidation/OperationSetting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"uploads a schema, enables in place, replaces on disable and source change, destroys","status":"skipped","title":"uploads a schema, enables in place, replaces on disable and source change, destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed schema across all zones","status":"skipped","title":"list enumerates the deployed schema across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SchemaValidation/Schema.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins zone settings, updates in place, and restores the baseline on destroy","status":"skipped","title":"pins zone settings, updates in place, and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the schema validation settings across all zones","status":"skipped","title":"list enumerates the schema validation settings across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SchemaValidation/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"Config.redacted with literal default round-trips to runtime as Redacted","status":"skipped","title":"Config.redacted with literal default round-trips to runtime as Redacted","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Config.redacted resolved from env deploys as a secret_text and round-trips","status":"skipped","title":"Config.redacted resolved from env deploys as a secret_text and round-trips","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Config.string round-trips to runtime as a string","status":"skipped","title":"Config.string round-trips to runtime as a string","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Config.number round-trips to runtime preserving the number type","status":"skipped","title":"Config.number round-trips to runtime preserving the number type","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Config.string with object default round-trips to runtime preserving nested shape","status":"skipped","title":"Config.string with object default round-trips to runtime preserving nested shape","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Secret/Secret.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden error","status":"passed","title":"unentitled accounts surface the typed Forbidden error","duration":53620.18000000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create (or adopt), verify out-of-band, destroy forgets but keeps the app","status":"passed","title":"create (or adopt), verify out-of-band, destroy forgets but keeps the app","duration":28126.08129999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed RealtimeKit app","status":"passed","title":"list enumerates the deployed RealtimeKit app","duration":28321.180300000007,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386147608,"endTime":1782386201228.18,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RealtimeKit/App.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create with defaults, verify out-of-band, update in place, destroy","status":"passed","title":"create with defaults, verify out-of-band, update in place, destroy","duration":26475.508399999995,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed preset","status":"passed","title":"list enumerates the deployed preset","duration":27742.608900000003,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386178598,"endTime":1782386206344.609,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RealtimeKit/Preset.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, update in place, destroy","status":"passed","title":"create, verify out-of-band, update in place, destroy","duration":27862.137100000007,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates webhooks across the account's apps","status":"passed","title":"list enumerates webhooks across the account's apps","duration":54139.792100000006,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386147613,"endTime":1782386201755.792,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RealtimeKit/Webhook.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"SecretsStore Secret declared on a Worker's env round-trips to runtime as a SecretsStoreSecret","status":"skipped","title":"SecretsStore Secret declared on a Worker's env round-trips to runtime as a SecretsStoreSecret","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SecretsStore/AsyncSecretBinding.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed secret across stores","status":"skipped","title":"list enumerates the deployed secret across stores","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SecretsStore/Secret.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"createStore POSTs a single JSON object body (regression: invalid_json_body)","status":"passed","title":"createStore POSTs a single JSON object body (regression: invalid_json_body)","duration":102.7368000000024,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"createStore surfaces MaximumStoresExceeded as a tagged error so callers can catchTag it","status":"passed","title":"createStore surfaces MaximumStoresExceeded as a tagged error so callers can catchTag it","duration":97.66159999999945,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed secrets store","status":"skipped","title":"list enumerates the deployed secrets store","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386243777,"endTime":1782386243879.7368,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SecretsStore/SecretsStore.test.ts"},{"assertionResults":[{"ancestorTitles":["SecurityTxt"],"fullName":"SecurityTxt creates a security.txt, verifies out-of-band, and deletes on destroy","status":"skipped","title":"creates a security.txt, verifies out-of-band, and deletes on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SecurityTxt"],"fullName":"SecurityTxt updates mutable fields in place (full-replace PUT)","status":"skipped","title":"updates mutable fields in place (full-replace PUT)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SecurityTxt"],"fullName":"SecurityTxt disables the file without deleting it, then destroy removes it","status":"skipped","title":"disables the file without deleting it, then destroy removes it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SecurityTxt"],"fullName":"SecurityTxt list enumerates configured security.txt files","status":"skipped","title":"list enumerates configured security.txt files","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SecurityTxt/SecurityTxt.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed SpectrumProtocolNotAvailable error on unentitled zones","status":"skipped","title":"surfaces the typed SpectrumProtocolNotAvailable error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a tcp/22 application","status":"skipped","title":"create, update in place, and destroy a tcp/22 application","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing app errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing app errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates Spectrum applications across all zones","status":"passed","title":"list enumerates Spectrum applications across all zones","duration":1451.8019999999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list surfaces a deployed Spectrum application (entitled zone)","status":"skipped","title":"list surfaces a deployed Spectrum application (entitled zone)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386203272,"endTime":1782386204723.802,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Spectrum/Application.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a scheduled speed test","status":"skipped","title":"create and delete a scheduled speed test","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the frequency converges in place","status":"skipped","title":"changing the frequency converges in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the region triggers replacement","status":"skipped","title":"changing the region triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing schedule errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing schedule errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed schedule across zones","status":"skipped","title":"list enumerates the deployed schedule across zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Speed/TestSchedule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create with generated name, update code in place, destroy","status":"skipped","title":"create with generated name, update code in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"renaming an explicit snippet triggers replacement","status":"skipped","title":"renaming an explicit snippet triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed snippet","status":"skipped","title":"list enumerates the deployed snippet","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Snippets/Snippet.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"snippet rules — create, update, list, and destroy in dependency order","status":"skipped","title":"snippet rules — create, update, list, and destroy in dependency order","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Snippets/SnippetRules.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","status":"skipped","title":"surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates advanced certificate packs across zones","status":"passed","title":"list enumerates advanced certificate packs across zones","duration":2486.8173000000024,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed advanced certificate pack","status":"skipped","title":"list includes a deployed advanced certificate pack","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"orders a pack, updates validation method in place, and deletes it","status":"skipped","title":"orders a pack, updates validation method in place, and deletes it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386231922,"endTime":1782386234408.8174,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ssl/CertificatePack.test.ts"},{"assertionResults":[{"ancestorTitles":["UniversalSsl"],"fullName":"UniversalSsl disables Universal SSL and restores the original value on destroy","status":"skipped","title":"disables Universal SSL and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UniversalSsl"],"fullName":"UniversalSsl updates enabled in place and keeps the captured initial value","status":"skipped","title":"updates enabled in place and keeps the captured initial value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UniversalSsl"],"fullName":"UniversalSsl destroy restores a disabled baseline when managing from a disabled zone","status":"skipped","title":"destroy restores a disabled baseline when managing from a disabled zone","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UniversalSsl"],"fullName":"UniversalSsl list enumerates the setting across all zones","status":"skipped","title":"list enumerates the setting across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ssl/UniversalSsl.test.ts"},{"assertionResults":[{"ancestorTitles":["State"],"fullName":"State getVersion returns the current STATE_STORE_VERSION","status":"skipped","title":"getVersion returns the current STATE_STORE_VERSION","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State DELETE /state/stacks/:stack wipes the test namespace (cleanup)","status":"skipped","title":"DELETE /state/stacks/:stack wipes the test namespace (cleanup)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State PUT /resources/:fqn (setState) persists a resource","status":"skipped","title":"PUT /resources/:fqn (setState) persists a resource","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /resources/:fqn (getState) reads back the persisted value","status":"skipped","title":"GET /resources/:fqn (getState) reads back the persisted value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /resources/:fqn returns undefined for a missing fqn","status":"skipped","title":"GET /resources/:fqn returns undefined for a missing fqn","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /resources (listResources) returns the FQNs in the stage","status":"skipped","title":"GET /resources (listResources) returns the FQNs in the stage","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /stacks (listStacks) includes the registered test stack","status":"skipped","title":"GET /stacks (listStacks) includes the registered test stack","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /stacks/:stack/stages (listStages) includes the test stage","status":"skipped","title":"GET /stacks/:stack/stages (listStages) includes the test stage","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State PUT /output (setStackOutput) persists a stack output","status":"skipped","title":"PUT /output (setStackOutput) persists a stack output","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /output (getStackOutput) reads back the persisted output","status":"skipped","title":"GET /output (getStackOutput) reads back the persisted output","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /output returns undefined for an un-deployed stage","status":"skipped","title":"GET /output returns undefined for an un-deployed stage","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /replaced-resources (getReplacedResources) returns status===replaced rows","status":"skipped","title":"GET /replaced-resources (getReplacedResources) returns status===replaced rows","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State DELETE /resources/:fqn (deleteState) removes a single resource","status":"skipped","title":"DELETE /resources/:fqn (deleteState) removes a single resource","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State DELETE /stacks/:stack?stage=... clears one stage but leaves the stack registered","status":"skipped","title":"DELETE /stacks/:stack?stage=... clears one stage but leaves the stack registered","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State DELETE /stacks/:stack (no stage) removes the stack from listStacks","status":"skipped","title":"DELETE /stacks/:stack (no stage) removes the stack from listStacks","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State setState 100x sequential — surfaces transient failures","status":"skipped","title":"setState 100x sequential — surfaces transient failures","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State setState 100x concurrent — surfaces racy transient failures","status":"skipped","title":"setState 100x concurrent — surfaces racy transient failures","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET+PUT interleaved 30x5 — engine traffic pattern","status":"skipped","title":"GET+PUT interleaved 30x5 — engine traffic pattern","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/StateStore/State.test.ts"},{"assertionResults":[{"ancestorTitles":["RumRule"],"fullName":"RumRule create, update in place, and delete a rule","status":"skipped","title":"create, update in place, and delete a rule","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RumRule"],"fullName":"RumRule list enumerates rules across all rulesets","status":"skipped","title":"list enumerates rules across all rulesets","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RumRule"],"fullName":"RumRule recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Rum/Rule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a host (gray-clouded) site","status":"skipped","title":"create and delete a host (gray-clouded) site","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable props in place (same siteTag)","status":"skipped","title":"update mutable props in place (same siteTag)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"zone (orange-clouded) site with autoInstall, replaced on identity flip","status":"skipped","title":"zone (orange-clouded) site with autoInstall, replaced on identity flip","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed RUM site","status":"skipped","title":"list enumerates the deployed RUM site","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Rum/Site.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, and clear tags on a KV namespace","status":"failed","title":"create, update, and clear tags on a KV namespace","duration":28569.9188,"failureMessages":["Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time.\n at Proxy. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Output.ts:494:19)\n at String ()\n at setQueryValue (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:729:23)\n at Module.buildRequestParts (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:785:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:631:30)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing resourceId triggers replacement","status":"failed","title":"changing resourceId triggers replacement","duration":28607.8281,"failureMessages":["Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time.\n at Proxy. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Output.ts:494:19)\n at String ()\n at setQueryValue (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:729:23)\n at Module.buildRequestParts (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:785:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:631:30)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing tags error without adopt, take over with adopt(true)","status":"failed","title":"adoption — existing tags error without adopt, take over with adopt(true)","duration":120059.96830000001,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tags/AccountResourceTags.test.ts:189:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account-wide tagged resources","status":"failed","title":"list enumerates account-wide tagged resources","duration":30229.580199999997,"failureMessages":["Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time.\n at Proxy. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Output.ts:494:19)\n at String ()\n at setQueryValue (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:729:23)\n at Module.buildRequestParts (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:785:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:631:30)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)"],"meta":{},"tags":[]}],"startTime":1782386147624,"endTime":1782386267686.9683,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tags/AccountResourceTags.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, and clear tags on a DNS record","status":"skipped","title":"create, update, and clear tags on a DNS record","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"tag the zone itself and clear on destroy","status":"skipped","title":"tag the zone itself and clear on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates tagged zone-scoped resources","status":"skipped","title":"list enumerates tagged zone-scoped resources","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tags/ZoneResourceTags.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a typed array of token validation rules","status":"passed","title":"list returns a typed array of token validation rules","duration":2992.174500000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed token validation rule","status":"skipped","title":"list enumerates the deployed token validation rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386227154,"endTime":1782386230146.1746,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/TokenValidation/Rule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed TokenValidationNotEntitled error on unentitled accounts","status":"skipped","title":"surfaces the typed TokenValidationNotEntitled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates configurations across all zones","status":"passed","title":"list enumerates configurations across all zones","duration":2246.117299999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"configuration + rule lifecycle: create, update in place, rotate keys, destroy","status":"skipped","title":"configuration + rule lifecycle: create, update in place, rotate keys, destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386201474,"endTime":1782386203720.1172,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/TokenValidation/TokenValidation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a widget with default name","status":"skipped","title":"create and delete a widget with default name","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable props in place (same sitekey)","status":"skipped","title":"update mutable props in place (same sitekey)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed widget","status":"skipped","title":"list enumerates the deployed widget","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Turnstile/Widget.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a live input","status":"skipped","title":"create, update in place, and delete a live input","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed live input","status":"skipped","title":"list enumerates the deployed live input","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/LiveInput.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, toggle enabled in place, replace destination, and delete","status":"skipped","title":"create, toggle enabled in place, replace destination, and delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates outputs across all live inputs","status":"skipped","title":"list enumerates outputs across all live inputs","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/LiveInputOutput.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a signing key","status":"skipped","title":"create and delete a signing key","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed signing key","status":"skipped","title":"list enumerates the deployed signing key","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/SigningKey.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a watermark with default name","status":"skipped","title":"create and delete a watermark with default name","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"prop change replaces the watermark (create-only resource)","status":"skipped","title":"prop change replaces the watermark (create-only resource)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed watermark","status":"skipped","title":"list enumerates the deployed watermark","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/Watermark.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"configure, update, and delete the account webhook","status":"skipped","title":"configure, update, and delete the account webhook","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the account Stream webhook","status":"skipped","title":"list enumerates the account Stream webhook","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/Webhook.test.ts"},{"assertionResults":[{"ancestorTitles":["UrlNormalization"],"fullName":"UrlNormalization configures URL normalization and resets to defaults on destroy","status":"skipped","title":"configures URL normalization and resets to defaults on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UrlNormalization"],"fullName":"UrlNormalization updates scope and type in place","status":"skipped","title":"updates scope and type in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UrlNormalization"],"fullName":"UrlNormalization applies Cloudflare defaults when scope and type are omitted","status":"skipped","title":"applies Cloudflare defaults when scope and type are omitted","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UrlNormalization"],"fullName":"UrlNormalization list enumerates URL normalization across all zones","status":"skipped","title":"list enumerates URL normalization across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/UrlNormalization/UrlNormalization.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete vpc service","status":"skipped","title":"create, update, delete vpc service","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create vpc service with ipv4 host","status":"skipped","title":"create vpc service with ipv4 host","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create vpc service with dual-stack host","status":"skipped","title":"create vpc service with dual-stack host","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed vpc service","status":"skipped","title":"list enumerates the deployed vpc service","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VpcService/VpcService.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"reference vpc service by name and by id","status":"skipped","title":"reference vpc service by name and by id","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VpcService/VpcServiceRef.test.ts"},{"assertionResults":[{"ancestorTitles":["Tunnel Configuration"],"fullName":"Tunnel Configuration list enumerates configurations across all tunnels","status":"skipped","title":"list enumerates configurations across all tunnels","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/Configuration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update comment in place, and destroy a hostname route","status":"skipped","title":"create, update comment in place, and destroy a hostname route","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed hostname route","status":"skipped","title":"list enumerates the deployed hostname route","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/HostnameRoute.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete route with default props","status":"skipped","title":"create and delete route with default props","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating the comment patches in place","status":"skipped","title":"updating the comment patches in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopt: takes over a pre-existing route","status":"skipped","title":"adopt: takes over a pre-existing route","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed route","status":"skipped","title":"list enumerates the deployed route","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/Route.test.ts"},{"assertionResults":[{"ancestorTitles":["Tunnel runtime bindings"],"fullName":"Tunnel runtime bindings TunnelRead lists tunnels with a read-scoped token","status":"skipped","title":"TunnelRead lists tunnels with a read-scoped token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Tunnel runtime bindings"],"fullName":"Tunnel runtime bindings TunnelWrite creates and deletes a tunnel with a write-scoped token","status":"skipped","title":"TunnelWrite creates and deletes a tunnel with a write-scoped token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Tunnel runtime bindings"],"fullName":"Tunnel runtime bindings TunnelReadWrite drives the full CRUD surface","status":"skipped","title":"TunnelReadWrite drives the full CRUD surface","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Tunnel.list"],"fullName":"Tunnel.list list enumerates the deployed tunnel","status":"skipped","title":"list enumerates the deployed tunnel","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/Tunnel.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy a virtual network","status":"skipped","title":"create, verify, and destroy a virtual network","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name and comment in place (same id)","status":"skipped","title":"update name and comment in place (same id)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"skipped","title":"recreates after out-of-band delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed virtual network","status":"skipped","title":"list enumerates the deployed virtual network","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/VirtualNetwork.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, rename in place, and destroy a WARP Connector tunnel","status":"skipped","title":"create, rename in place, and destroy a WARP Connector tunnel","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed WARP Connector tunnel","status":"skipped","title":"list enumerates the deployed WARP Connector tunnel","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/WarpConnector.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed credential","status":"skipped","title":"list enumerates the deployed credential","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VulnerabilityScanner/Credential.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, rename, and destroy a credential set","status":"skipped","title":"create, rename, and destroy a credential set","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed credential set","status":"skipped","title":"list enumerates the deployed credential set","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"credential lifecycle — create, mutate in place, rotate value, destroy","status":"skipped","title":"credential lifecycle — create, mutate in place, rotate value, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"moving a credential to a different set triggers replacement","status":"skipped","title":"moving a credential to a different set triggers replacement","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VulnerabilityScanner/CredentialSet.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy a target environment","status":"skipped","title":"create, verify, and destroy a target environment","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating name and description patches in place; clearing description","status":"skipped","title":"updating name and description patches in place; clearing description","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed target environment","status":"skipped","title":"list enumerates the deployed target environment","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VulnerabilityScanner/TargetEnvironment.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete index with explicit dimensions","status":"skipped","title":"create and delete index with explicit dimensions","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create index from a preset","status":"skipped","title":"create index from a preset","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces index when dimensions change","status":"skipped","title":"replaces index when dimensions change","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed index","status":"skipped","title":"list enumerates the deployed index","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Vectorize/VectorizeIndex.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"VectorizeIndex.bind exercises the client surface","status":"skipped","title":"VectorizeIndex.bind exercises the client surface","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Vectorize/VectorizeIndexBinding.test.ts"},{"assertionResults":[{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex create and delete a metadata index","status":"skipped","title":"create and delete a metadata index","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex multiple metadata indexes on the same parent coexist","status":"skipped","title":"multiple metadata indexes on the same parent coexist","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex replacing the parent index also replaces the metadata index","status":"skipped","title":"replacing the parent index also replaces the metadata index","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex list enumerates the deployed metadata index","status":"skipped","title":"list enumerates the deployed metadata index","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex destroy is idempotent when the parent index was deleted out-of-band","status":"skipped","title":"destroy is idempotent when the parent index was deleted out-of-band","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Vectorize/VectorizeMetadataIndex.test.ts"},{"assertionResults":[{"ancestorTitles":["Settings"],"fullName":"Settings pins the settings to the default baseline without touching the API","status":"skipped","title":"pins the settings to the default baseline without touching the API","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Settings"],"fullName":"Settings surfaces the typed ZoneNotEntitled error when enabling on unentitled zones","status":"skipped","title":"surfaces the typed ZoneNotEntitled error when enabling on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Settings"],"fullName":"Settings enables the crawler bypass and restores the original value on destroy","status":"skipped","title":"enables the crawler bypass and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Settings"],"fullName":"Settings list enumerates the settings across all zones","status":"skipped","title":"list enumerates the settings across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/WaitingRoom/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed ZoneNotEntitled error on unentitled zones","status":"skipped","title":"surfaces the typed ZoneNotEntitled error on unentitled zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a waiting room","status":"skipped","title":"create, update in place, and destroy a waiting room","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates waiting rooms across zones","status":"passed","title":"list enumerates waiting rooms across zones","duration":2154.7730999999985,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386199182,"endTime":1782386201336.7732,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/WaitingRoom/WaitingRoom.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"StaticSite: editing a source file republishes the assets in a single deploy","status":"skipped","title":"StaticSite: editing a source file republishes the assets in a single deploy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"StaticSite: class form deploys and serves the built assets","status":"skipped","title":"StaticSite: class form deploys and serves the built assets","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"StaticSite: relocating the project (and deleting the old one) preserves hash.assets","status":"skipped","title":"StaticSite: relocating the project (and deleting the old one) preserves hash.assets","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"StaticSite: a bundle-only change keeps the asset manifest (hash.assets stable)","status":"skipped","title":"StaticSite: a bundle-only change keeps the asset manifest (hash.assets stable)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Website/StaticSite.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"Vite: editing a source file republishes the assets in a single deploy","status":"skipped","title":"Vite: editing a source file republishes the assets in a single deploy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Vite: class form deploys and serves the built assets","status":"skipped","title":"Vite: class form deploys and serves the built assets","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Vite: relocating rootDir (and deleting the old one) is a no-op when sources are identical","status":"skipped","title":"Vite: relocating rootDir (and deleting the old one) is a no-op when sources are identical","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Vite: `env` props are inlined as `import.meta.env.*` into the bundle","status":"skipped","title":"Vite: `env` props are inlined as `import.meta.env.*` into the bundle","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Website/Vite.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, replace, and destroy a dispatch namespace","status":"skipped","title":"create, replace, and destroy a dispatch namespace","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"upload, update in place, and destroy a namespace script","status":"skipped","title":"upload, update in place, and destroy a namespace script","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed dispatch namespace","status":"skipped","title":"list enumerates the deployed dispatch namespace","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/WorkersForPlatforms/DispatchNamespace.test.ts"},{"assertionResults":[{"ancestorTitles":["ZarazConfig"],"fullName":"ZarazConfig updates and retains a zone-level Zaraz config","status":"skipped","title":"updates and retains a zone-level Zaraz config","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ZarazConfig"],"fullName":"ZarazConfig delete true resets Zaraz config to defaults","status":"skipped","title":"delete true resets Zaraz config to defaults","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ZarazConfig"],"fullName":"ZarazConfig updates and retains Zaraz workflow mode","status":"skipped","title":"updates and retains Zaraz workflow mode","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ZarazConfig"],"fullName":"ZarazConfig list enumerates Zaraz config across all zones","status":"skipped","title":"list enumerates Zaraz config across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zaraz/ZarazConfig.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"Zaraz event contracts are type-only","status":"passed","title":"Zaraz event contracts are type-only","duration":8.45720000000074,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386231754,"endTime":1782386231762.4573,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zaraz/ZarazEventTypes.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins the custom nameserver toggle and leaves the zone at its baseline on destroy","status":"skipped","title":"pins the custom nameserver toggle and leaves the zone at its baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"surfaces the typed CustomNameserverSetNotFound error when enabling without an account nameserver set","status":"skipped","title":"surfaces the typed CustomNameserverSetNotFound error when enabling without an account nameserver set","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates zones using account custom nameservers","status":"passed","title":"list enumerates zones using account custom nameservers","duration":2475.6251999999986,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a zone with account custom nameservers enabled","status":"skipped","title":"list includes a zone with account custom nameservers enabled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"enables account custom nameservers and restores the baseline on destroy","status":"skipped","title":"enables account custom nameservers and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386198979,"endTime":1782386201454.6252,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/CustomNameservers.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed ZoneHoldsRequireEnterprise error on non-Enterprise zones","status":"skipped","title":"surfaces the typed ZoneHoldsRequireEnterprise error on non-Enterprise zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the hold state across all zones","status":"skipped","title":"list enumerates the hold state across all zones","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"places a hold, updates includeSubdomains in place, and removes it on destroy","status":"skipped","title":"places a hold, updates includeSubdomains in place, and removes it on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/Hold.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins a toggle setting and restores the original value on destroy","status":"skipped","title":"pins a toggle setting and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updates a numeric setting in place and keeps the captured initial value","status":"skipped","title":"updates a numeric setting in place and keeps the captured initial value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing settingId replaces — old setting restored, new setting pinned","status":"skipped","title":"changing settingId replaces — old setting restored, new setting pinned","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed (zone, setting) pair","status":"skipped","title":"list enumerates the deployed (zone, setting) pair","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/Setting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create zone retains by default — destroy() opts in to deletion","status":"skipped","title":"create zone retains by default — destroy() opts in to deletion","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create zone retains by default — survives stack.destroy()","status":"skipped","title":"create zone retains by default — survives stack.destroy()","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing zone errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing zone errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates every zone in the account","status":"skipped","title":"list enumerates every zone in the account","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/Zone.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a well-typed array of web3 content lists","status":"passed","title":"list returns a well-typed array of web3 content lists","duration":3251.351600000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list surfaces a deployed web3 content list (entitled account)","status":"skipped","title":"list surfaces a deployed web3 content list (entitled account)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386205793,"endTime":1782386209044.3516,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Web3/ContentList.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a well-typed array of web3 hostnames","status":"passed","title":"list returns a well-typed array of web3 hostnames","duration":1792.6238999999987,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list surfaces a deployed web3 hostname (entitled account)","status":"skipped","title":"list surfaces a deployed web3 hostname (entitled account)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386198817,"endTime":1782386200609.6238,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Web3/Hostname.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed Web3HostnameNotEntitled error on unentitled accounts","status":"skipped","title":"surfaces the typed Web3HostnameNotEntitled error on unentitled accounts","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, replace on target change, destroy","status":"skipped","title":"create, update in place, replace on target change, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"content list — set entries, update declaratively, reset on destroy","status":"skipped","title":"content list — set entries, update declaratively, reset on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Web3/Web3Hostname.test.ts"},{"assertionResults":[{"ancestorTitles":["AccountSetting"],"fullName":"AccountSetting flips green compute, updates in place, and restores the baseline on destroy","status":"failed","title":"flips green compute, updates in place, and restores the baseline on destroy","duration":28791.771900000007,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":["AccountSetting"],"fullName":"AccountSetting no-op deploy when desired settings already match the live account","status":"passed","title":"no-op deploy when desired settings already match the live account","duration":1402.0978000000032,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["AccountSetting"],"fullName":"AccountSetting list returns the account settings singleton","status":"passed","title":"list returns the account settings singleton","duration":370.6735999999946,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386147627,"endTime":1782386178192.6736,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/AccountSetting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed worker fires the scheduled handler on its cron trigger","status":"skipped","title":"deployed worker fires the scheduled handler on its cron trigger","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/CronEventSource.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"Drizzle.postgres query runs inside a Workflow task (ExecutionContext provided per run)","status":"skipped","title":"Drizzle.postgres query runs inside a Workflow task (ExecutionContext provided per run)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/DrizzleWorkflow.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"durable object methods can use binding clients","status":"skipped","title":"durable object methods can use binding clients","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"tick streams sequential values from a durable object (tutorial repro)","status":"skipped","title":"tick streams sequential values from a durable object (tutorial repro)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"async worker durable object binding accepts scriptName","status":"skipped","title":"async worker durable object binding accepts scriptName","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"durable object class migrations across redeploys","status":"skipped","title":"durable object class migrations across redeploys","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/DurableObjectNamespace.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed http-api worker handles createTask + getTask via typed HttpApiClient","status":"skipped","title":"deployed http-api worker handles createTask + getTask via typed HttpApiClient","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"cors middleware adds Access-Control-Allow-Origin header on preflight","status":"skipped","title":"cors middleware adds Access-Control-Allow-Origin header on preflight","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"cors middleware adds Access-Control-Allow-Origin header on actual requests","status":"skipped","title":"cors middleware adds Access-Control-Allow-Origin header on actual requests","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"concurrent createTask survives scope-lifecycle pressure","status":"skipped","title":"concurrent createTask survives scope-lifecycle pressure","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"createTaskDO + getTaskDO round-trip 100x in parallel through the DO HttpApi","status":"skipped","title":"createTaskDO + getTaskDO round-trip 100x in parallel through the DO HttpApi","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/HttpApi.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a destination with default name","status":"skipped","title":"create and delete a destination with default name","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update url, headers, and enabled in place (same slug)","status":"skipped","title":"update url, headers, and enabled in place (same slug)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replacement on name change (new slug, old destination removed)","status":"skipped","title":"replacement on name change (new slug, old destination removed)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed destination","status":"skipped","title":"list enumerates the deployed destination","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/ObservabilityDestination.test.ts"},{"assertionResults":[{"ancestorTitles":["Cloudflare.Worker with bundle: false"],"fullName":"Cloudflare.Worker with bundle: false uploads a prebuilt module graph byte-for-byte","status":"skipped","title":"uploads a prebuilt module graph byte-for-byte","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/PrebuiltWorker.test.ts"},{"assertionResults":[{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle collects modules matching the default rules, entry first, names POSIX-relative to the entry's directory","status":"passed","title":"collects modules matching the default rules, entry first, names POSIX-relative to the entry's directory","duration":2533.9073000000008,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle uploads file contents byte-for-byte","status":"passed","title":"uploads file contents byte-for-byte","duration":699.356600000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle does not walk outside the entry's directory","status":"passed","title":"does not walk outside the entry's directory","duration":1131.2955000000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle custom rules replace the defaults","status":"passed","title":"custom rules replace the defaults","duration":1132.8775999999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle hash is stable across reads and sensitive to module changes","status":"passed","title":"hash is stable across reads and sensitive to module changes","duration":1363.3156,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle fails with BundleError when the entry does not exist","status":"passed","title":"fails with BundleError when the entry does not exist","duration":358.5034000000014,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386191868,"endTime":1782386194401.9072,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/PrebuiltWorkerBundle.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an opt-out route (no script)","status":"skipped","title":"create and delete an opt-out route (no script)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"route to a Worker, then update pattern and script in place","status":"skipped","title":"route to a Worker, then update pattern and script in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing route errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing route errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed route across all zones","status":"skipped","title":"list enumerates the deployed route across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Route.test.ts"},{"assertionResults":[{"ancestorTitles":["RpcDecodeError"],"fullName":"RpcDecodeError message delegates to Error.message when cause is an Error","status":"passed","title":"message delegates to Error.message when cause is an Error","duration":111.78709999999955,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RpcDecodeError"],"fullName":"RpcDecodeError message stringifies non-Error cause","status":"passed","title":"message stringifies non-Error cause","duration":109.77070000000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RpcCallError"],"fullName":"RpcCallError message includes method name and Error.message","status":"passed","title":"message includes method name and Error.message","duration":109.58410000000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RpcCallError"],"fullName":"RpcCallError message stringifies non-Error cause","status":"passed","title":"message stringifies non-Error cause","duration":109.41619999999966,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcErrorEnvelope"],"fullName":"isRpcErrorEnvelope detects valid envelope","status":"passed","title":"detects valid envelope","duration":109.27660000000014,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcErrorEnvelope"],"fullName":"isRpcErrorEnvelope rejects non-envelope values","status":"passed","title":"rejects non-envelope values","duration":110.60619999999926,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcStreamEnvelope"],"fullName":"isRpcStreamEnvelope detects valid bytes envelope","status":"passed","title":"detects valid bytes envelope","duration":110.53120000000035,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcStreamEnvelope"],"fullName":"isRpcStreamEnvelope detects valid jsonl envelope","status":"passed","title":"detects valid jsonl envelope","duration":110.60210000000006,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcStreamEnvelope"],"fullName":"isRpcStreamEnvelope rejects missing or wrong fields","status":"passed","title":"rejects missing or wrong fields","duration":110.51980000000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["encodeRpcError"],"fullName":"encodeRpcError preserves tagged error fields","status":"passed","title":"preserves tagged error fields","duration":110.36530000000039,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["encodeRpcError"],"fullName":"encodeRpcError normalizes plain Error to name/message/stack","status":"passed","title":"normalizes plain Error to name/message/stack","duration":111.31800000000021,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["encodeRpcError"],"fullName":"encodeRpcError passes through primitives","status":"passed","title":"passes through primitives","duration":111.21680000000015,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["encodeRpcError"],"fullName":"encodeRpcError passes through plain objects","status":"passed","title":"passes through plain objects","duration":111.16239999999925,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcReadableStream"],"fullName":"fromRpcReadableStream bytes encoding passes raw Uint8Array chunks through","status":"passed","title":"bytes encoding passes raw Uint8Array chunks through","duration":78.72220000000016,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcReadableStream"],"fullName":"fromRpcReadableStream jsonl encoding parses JSON lines","status":"passed","title":"jsonl encoding parses JSON lines","duration":78.85240000000067,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcReadableStream"],"fullName":"fromRpcReadableStream jsonl encoding produces RpcDecodeError on malformed JSON","status":"passed","title":"jsonl encoding produces RpcDecodeError on malformed JSON","duration":78.2496000000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcReadableStream"],"fullName":"fromRpcReadableStream jsonl encoding skips empty lines","status":"passed","title":"jsonl encoding skips empty lines","duration":81.38749999999982,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcStreamEnvelope"],"fullName":"fromRpcStreamEnvelope delegates to fromRpcReadableStream","status":"passed","title":"delegates to fromRpcReadableStream","duration":81.45559999999932,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcValue"],"fullName":"decodeRpcValue passes through plain values","status":"passed","title":"passes through plain values","duration":42.96319999999923,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcValue"],"fullName":"decodeRpcValue converts stream envelope to Effect Stream","status":"passed","title":"converts stream envelope to Effect Stream","duration":43.77149999999983,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcValue"],"fullName":"decodeRpcValue converts bare ReadableStream to bytes Effect Stream","status":"passed","title":"converts bare ReadableStream to bytes Effect Stream","duration":43.698599999999715,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult succeeds for plain values","status":"passed","title":"succeeds for plain values","duration":43.32789999999932,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult succeeds for numeric values","status":"passed","title":"succeeds for numeric values","duration":43.27430000000004,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult fails for error envelopes with tagged error","status":"passed","title":"fails for error envelopes with tagged error","duration":43.54730000000018,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult fails with plain Error shape for error envelopes","status":"passed","title":"fails with plain Error shape for error envelopes","duration":43.51549999999952,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult wraps stream envelopes in succeed (stream passthrough)","status":"passed","title":"wraps stream envelopes in succeed (stream passthrough)","duration":44.0586000000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream selects jsonl encoding for non-byte data","status":"passed","title":"selects jsonl encoding for non-byte data","duration":33.87159999999949,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream roundtrips a single-element jsonl stream","status":"passed","title":"roundtrips a single-element jsonl stream","duration":47.28049999999985,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream selects bytes encoding for Uint8Array data","status":"passed","title":"selects bytes encoding for Uint8Array data","duration":34.37119999999959,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream roundtrips a single-element bytes stream","status":"passed","title":"roundtrips a single-element bytes stream","duration":48.82679999999982,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream handles empty stream as jsonl","status":"passed","title":"handles empty stream as jsonl","duration":49.189000000000306,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["makeRpcStub"],"fullName":"makeRpcStub proxies successful calls","status":"passed","title":"proxies successful calls","duration":30.484400000000278,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["makeRpcStub"],"fullName":"makeRpcStub wraps rejected promises as RpcCallError","status":"passed","title":"wraps rejected promises as RpcCallError","duration":30.376800000000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["makeRpcStub"],"fullName":"makeRpcStub decodes error envelopes into Effect.fail","status":"passed","title":"decodes error envelopes into Effect.fail","duration":30.327800000000025,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["makeRpcStub"],"fullName":"makeRpcStub decodes stream envelopes from successful calls","status":"passed","title":"decodes stream envelopes from successful calls","duration":30.330299999999625,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors toRpcStream encodes a stream that fails immediately","status":"passed","title":"toRpcStream encodes a stream that fails immediately","duration":30.255200000000514,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors toRpcStream encodes a stream that fails after elements","status":"passed","title":"toRpcStream encodes a stream that fails after elements","duration":30.319400000000314,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors fromRpcReadableStream decodes error marker in JSONL","status":"passed","title":"fromRpcReadableStream decodes error marker in JSONL","duration":30.24420000000009,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors fromRpcReadableStream yields elements before error marker","status":"passed","title":"fromRpcReadableStream yields elements before error marker","duration":30.193599999999606,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors makeRpcStub preserves stream errors (not collapsed to RpcCallError)","status":"passed","title":"makeRpcStub preserves stream errors (not collapsed to RpcCallError)","duration":30.082000000000335,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386215883,"endTime":1782386216121.082,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Rpc.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"RpcDurableObjectNamespace: Increment / Get round-trip via Worker","status":"passed","title":"RpcDurableObjectNamespace: Increment / Get round-trip via Worker","duration":3051.1375999999873,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcDurableObjectNamespace: separate getByName(id) instances are isolated","status":"passed","title":"RpcDurableObjectNamespace: separate getByName(id) instances are isolated","duration":11509.869799999986,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcDurableObjectNamespace: streaming RPC via getByName(id).CountUpTo","status":"passed","title":"RpcDurableObjectNamespace: streaming RPC via getByName(id).CountUpTo","duration":1047.9543000000122,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker + RpcDurableObjectNamespace: Worker proxies *DO RPCs through the typed namespace","status":"passed","title":"RpcWorker + RpcDurableObjectNamespace: Worker proxies *DO RPCs through the typed namespace","duration":584.8598999999813,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcDurableObjectNamespace: 100 concurrent Increment calls do not hang","status":"passed","title":"RpcDurableObjectNamespace: 100 concurrent Increment calls do not hang","duration":10883.786300000007,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent unary RPCs do not hang","status":"passed","title":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent unary RPCs do not hang","duration":2447.0160000000033,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent *DO unary RPCs do not hang","status":"passed","title":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent *DO unary RPCs do not hang","duration":3411.4587000000174,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent streaming *DO RPCs do not hang","status":"passed","title":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent streaming *DO RPCs do not hang","duration":2130.569199999998,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386217256,"endTime":1782386228773.8699,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/RpcDurableObjectNamespace.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: unary RPC response","status":"passed","title":"RpcServer.toHttpEffect: unary RPC response","duration":998.7232999999978,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: streaming RPC response","status":"passed","title":"RpcServer.toHttpEffect: streaming RPC response","duration":2550.0391999999993,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: array payload streams response items in order","status":"passed","title":"RpcServer.toHttpEffect: array payload streams response items in order","duration":2558.853900000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: 200 concurrent unary calls do not hang","status":"passed","title":"RpcServer.toHttpEffect: 200 concurrent unary calls do not hang","duration":4906.46160000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: concurrent streaming calls do not hang","status":"passed","title":"RpcServer.toHttpEffect: concurrent streaming calls do not hang","duration":4275.984399999987,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object unary RPC response","status":"passed","title":"RpcServer.toHttpEffect Durable Object unary RPC response","duration":4516.302899999995,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object streaming RPC response","status":"passed","title":"RpcServer.toHttpEffect Durable Object streaming RPC response","duration":2981.2183000000077,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object array payload streams response items in order","status":"passed","title":"RpcServer.toHttpEffect Durable Object array payload streams response items in order","duration":2949.0858000000007,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object concurrent unary calls do not hang","status":"passed","title":"RpcServer.toHttpEffect Durable Object concurrent unary calls do not hang","duration":1909.4630000000034,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object concurrent streaming calls do not hang","status":"passed","title":"RpcServer.toHttpEffect Durable Object concurrent streaming calls do not hang","duration":923.744399999996,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386188485,"endTime":1782386194674.463,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/RpcHttp.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"RpcWorker: target worker exposes Greet","status":"passed","title":"RpcWorker: target worker exposes Greet","duration":338.60269999998854,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker.bind: caller proxies through service binding to target","status":"passed","title":"RpcWorker.bind: caller proxies through service binding to target","duration":704.3788000000059,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker.bind: 100 concurrent ProxyGreet calls do not hang","status":"passed","title":"RpcWorker.bind: 100 concurrent ProxyGreet calls do not hang","duration":2007.2301999999909,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386206605,"endTime":1782386208616.2302,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/RpcWorker.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"adopts the account's existing workers.dev subdomain without mutating it","status":"passed","title":"adopts the account's existing workers.dev subdomain without mutating it","duration":3588.517200000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account's workers.dev subdomain singleton","status":"passed","title":"list returns the account's workers.dev subdomain singleton","duration":2888.386999999999,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386216281,"endTime":1782386219869.517,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Subdomain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"D1 counter writes from WorkerA are visible from WorkerB (cross-script DO)","status":"skipped","title":"D1 counter writes from WorkerA are visible from WorkerB (cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"DO storage counter writes from WorkerA are visible from WorkerB (cross-script DO)","status":"skipped","title":"DO storage counter writes from WorkerA are visible from WorkerB (cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"WorkerC hosts its own isolated Counter (writes from A/B are not visible from C)","status":"skipped","title":"WorkerC hosts its own isolated Counter (writes from A/B are not visible from C)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Writes from WorkerB are visible from WorkerA (bidirectional cross-script DO)","status":"skipped","title":"Writes from WorkerB are visible from WorkerA (bidirectional cross-script DO)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/TaggedDO.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"RpcWorker WorkerA exposes the same RPC surface as the underlying DO","status":"skipped","title":"RpcWorker WorkerA exposes the same RPC surface as the underlying DO","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"D1 counter writes via WorkerA's RPC are visible from WorkerB (cross-script DO)","status":"skipped","title":"D1 counter writes via WorkerA's RPC are visible from WorkerB (cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"DO storage counter writes via WorkerA's RPC are visible from WorkerB (cross-script DO)","status":"skipped","title":"DO storage counter writes via WorkerA's RPC are visible from WorkerB (cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Writes from WorkerB are visible from WorkerA's RPC (bidirectional cross-script DO)","status":"skipped","title":"Writes from WorkerB are visible from WorkerA's RPC (bidirectional cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"WorkerC hosts its own isolated Counter (writes from A/B are not visible from C)","status":"skipped","title":"WorkerC hosts its own isolated Counter (writes from A/B are not visible from C)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/TaggedRpcDO.test.ts"},{"assertionResults":[{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker create, update, delete worker","status":"skipped","title":"create, update, delete worker","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker create, update, delete worker with assets","status":"skipped","title":"create, update, delete worker with assets","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker Worker assets: relocating to a fresh path with identical bytes preserves hash and keeps assets","status":"skipped","title":"Worker assets: relocating to a fresh path with identical bytes preserves hash and keeps assets","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker Worker assets: editing a file changes the hash and republishes the manifest","status":"skipped","title":"Worker assets: editing a file changes the hash and republishes the manifest","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker Worker assets: a bundle-only change keeps the asset manifest (hash.assets stable)","status":"skipped","title":"Worker assets: a bundle-only change keeps the asset manifest (hash.assets stable)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker create, update, delete internal worker","status":"skipped","title":"create, update, delete internal worker","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker owned worker (matching alchemy tags) is silently adopted without --adopt","status":"skipped","title":"owned worker (matching alchemy tags) is silently adopted without --adopt","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker adopt(true) takes over a foreign-tagged worker","status":"skipped","title":"adopt(true) takes over a foreign-tagged worker","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker url defaults to enabling the workers.dev subdomain on first deploy","status":"skipped","title":"url defaults to enabling the workers.dev subdomain on first deploy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker url: false disables the workers.dev subdomain on first deploy","status":"skipped","title":"url: false disables the workers.dev subdomain on first deploy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker toggling url between deploys flips the workers.dev subdomain","status":"skipped","title":"toggling url between deploys flips the workers.dev subdomain","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker redeploy re-enables previewsEnabled when externally disabled","status":"skipped","title":"redeploy re-enables previewsEnabled when externally disabled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker domains reflects the workers.dev subdomain and tracks url","status":"skipped","title":"domains reflects the workers.dev subdomain and tracks url","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker domains puts custom domains before workers.dev and url is the first","status":"skipped","title":"domains puts custom domains before workers.dev and url is the first","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker list enumerates the deployed worker","status":"skipped","title":"list enumerates the deployed worker","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker downstream referencing worker.url is not re-updated when the worker changes","status":"skipped","title":"downstream referencing worker.url is not re-updated when the worker changes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker worker.durableObjectNamespaces stability across DO and worker changes","status":"skipped","title":"worker.durableObjectNamespaces stability across DO and worker changes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Worker.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"target worker's own fetch handler responds","status":"skipped","title":"target worker's own fetch handler responds","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"async caller can call target's RPC method via service binding","status":"skipped","title":"async caller can call target's RPC method via service binding","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect caller can call target's RPC method via bindWorker","status":"skipped","title":"effect caller can call target's RPC method via bindWorker","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/WorkerBinding.test.ts"},{"assertionResults":[{"ancestorTitles":["Cloudflare.Worker env bindings"],"fullName":"Cloudflare.Worker env bindings async worker round-trips every supported binding shape","status":"skipped","title":"async worker round-trips every supported binding shape","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker env bindings"],"fullName":"Cloudflare.Worker env bindings effect worker round-trips env: literals and Redacted via WorkerEnvironment","status":"skipped","title":"effect worker round-trips env: literals and Redacted via WorkerEnvironment","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker env bindings"],"fullName":"Cloudflare.Worker env bindings effect worker resolves the yielded VersionMetadata binding","status":"skipped","title":"effect worker resolves the yielded VersionMetadata binding","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker env bindings"],"fullName":"Cloudflare.Worker env bindings effect worker round-trips Config.xxx bindings captured in Init","status":"skipped","title":"effect worker round-trips Config.xxx bindings captured in Init","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/WorkerEnv.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"async worker loads and proxies to a dynamic worker via env binding","status":"skipped","title":"async worker loads and proxies to a dynamic worker via env binding","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker loads and proxies to a dynamic worker via yield* loader","status":"skipped","title":"effect worker loads and proxies to a dynamic worker via yield* loader","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/WorkerLoader.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed worker can run a workflow to completion","status":"skipped","title":"deployed worker can run a workflow to completion","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed workflow","status":"skipped","title":"list enumerates the deployed workflow","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782386072728,"endTime":1782386072728,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Workflow.test.ts"}]} \ No newline at end of file diff --git a/cf-temp-results.json b/cf-temp-results.json new file mode 100644 index 000000000..6bb7ac7cf --- /dev/null +++ b/cf-temp-results.json @@ -0,0 +1 @@ +{"numTotalTestSuites":366,"numPassedTestSuites":54,"numFailedTestSuites":312,"numPendingTestSuites":0,"numTotalTests":969,"numPassedTests":165,"numFailedTests":551,"numPendingTests":253,"numTodoTests":0,"snapshot":{"added":0,"failure":false,"filesAdded":0,"filesRemoved":0,"filesRemovedList":[],"filesUnmatched":0,"filesUpdated":0,"matched":0,"total":0,"unchecked":0,"uncheckedKeysByFile":[],"unmatched":0,"updated":0,"didUpdate":false},"startTime":1782379347422,"success":false,"testResults":[{"assertionResults":[{"ancestorTitles":[],"fullName":"building the Cloudflare provider layers should not fail for unknown profile","status":"passed","title":"building the Cloudflare provider layers should not fail for unknown profile","duration":73.58020000000033,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379748915,"endTime":1782379748988.58,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Providers.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a self_hosted application gated by a reusable policy","status":"failed","title":"create and delete a self_hosted application gated by a reusable policy","duration":120023.55029999999,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Application.test.ts:27:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create and delete a warp device-enrollment application","status":"passed","title":"create and delete a warp device-enrollment application","duration":810.2758000000031,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed access application","status":"failed","title":"list enumerates the deployed access application","duration":97387.87890000001,"failureMessages":["Forbidden: Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update policies in place keeps the applicationId stable","status":"failed","title":"update policies in place keeps the applicationId stable","duration":50920.23759999999,"failureMessages":["Forbidden: Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379416253,"endTime":1782379536276.5503,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Application.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"legacy bookmark creation surfaces the typed AccessBookmarkNotFound error","status":"failed","title":"legacy bookmark creation surfaces the typed AccessBookmarkNotFound error","duration":24084.2999,"failureMessages":["AssertionError: expected 'Unauthorized' to deeply equal 'AccessBookmarkNotFound'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Bookmark.test.ts:47:26)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy bookmark","status":"skipped","title":"create, update in place, and destroy bookmark","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates access bookmarks at the account scope","status":"failed","title":"list enumerates access bookmarks at the account scope","duration":23953.056,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379518374,"endTime":1782379542458.2998,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Bookmark.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed AccessCertificateQuotaExceeded error","status":"failed","title":"unentitled accounts surface the typed AccessCertificateQuotaExceeded error","duration":24299.4712,"failureMessages":["AssertionError: expected 'Unauthorized' to deeply equal 'AccessCertificateQuotaExceeded'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Certificate.test.ts:43:26)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update hostnames, replace on PEM change, destroy","status":"skipped","title":"create, update hostnames, replace on PEM change, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account access certificates","status":"failed","title":"list enumerates account access certificates","duration":24258.1063,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379601497,"endTime":1782379625796.4712,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Certificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed AccessCustomPagesNotEntitled error","status":"failed","title":"unentitled accounts surface the typed AccessCustomPagesNotEntitled error","duration":24346.6068,"failureMessages":["AssertionError: expected 'Unauthorized' to deeply equal 'AccessCustomPagesNotEntitled'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/CustomPage.test.ts:46:26)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update html in place, replace on type change, destroy","status":"skipped","title":"create, update html in place, replace on type change, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates access custom pages at the account scope","status":"failed","title":"list enumerates access custom pages at the account scope","duration":24156.051400000004,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379592276,"endTime":1782379616622.6067,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/CustomPage.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update rules, and delete group","status":"failed","title":"create, update rules, and delete group","duration":120023.4136,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Group.test.ts:20:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"rename updates the group in place","status":"failed","title":"rename updates the group in place","duration":120024.12890000001,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Group.test.ts:81:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed access group","status":"failed","title":"list enumerates the deployed access group","duration":50763.6222,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"group can be referenced from an access policy","status":"failed","title":"group can be referenced from an access policy","duration":74131.72959999999,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379416253,"endTime":1782379536279.129,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Group.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy an OIDC IdP","status":"failed","title":"create, verify, and destroy an OIDC IdP","duration":23759.9544,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name and config in place (same id)","status":"failed","title":"update name and config in place (same id)","duration":23726.6453,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed IdP","status":"failed","title":"list enumerates the deployed IdP","duration":23742.8873,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the type replaces the IdP","status":"failed","title":"changing the type replaces the IdP","duration":23712.6016,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379557937,"endTime":1782379581696.9543,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/IdentityProvider.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed infrastructure target","status":"failed","title":"list enumerates the deployed infrastructure target","duration":24353.9291,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]}],"startTime":1782379590208,"endTime":1782379614561.9292,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/InfrastructureTarget.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins the rotation interval, updates in place, and restores on destroy","status":"failed","title":"pins the rotation interval, updates in place, and restores on destroy","duration":23757.4712,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account key configuration singleton","status":"failed","title":"list returns the account key configuration singleton","duration":23716.6383,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379567096,"endTime":1782379590853.4712,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/KeyConfiguration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and destroy an MCP portal","status":"failed","title":"create, update in place, and destroy an MCP portal","duration":38741.6835,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed MCP portal","status":"failed","title":"list enumerates the deployed MCP portal","duration":38802.9509,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379603206,"endTime":1782379642010.951,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/McpPortal.test.ts"},{"assertionResults":[{"ancestorTitles":["Organization"],"fullName":"Organization adopts the existing Access organization","status":"skipped","title":"adopts the existing Access organization","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Organization"],"fullName":"Organization toggles allow_authenticate_via_warp and restores","status":"skipped","title":"toggles allow_authenticate_via_warp and restores","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Organization"],"fullName":"Organization list returns the account Access organization","status":"failed","title":"list returns the account Access organization","duration":24822.7247,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379532638,"endTime":1782379557460.7246,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Organization.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete basic allow policy","status":"failed","title":"create and delete basic allow policy","duration":50772.0223,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutates includes without replacing","status":"failed","title":"update mutates includes without replacing","duration":97470.67270000001,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopts an out-of-band reusable policy","status":"failed","title":"adopts an out-of-band reusable policy","duration":26401.117200000008,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed reusable policy","status":"failed","title":"list enumerates the deployed reusable policy","duration":74215.97090000001,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379416327,"endTime":1782379513800.6726,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Policy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update duration, and delete service token","status":"failed","title":"create, update duration, and delete service token","duration":50518.9078,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed service token","status":"failed","title":"list enumerates the deployed service token","duration":50234.104600000006,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"incrementing clientSecretVersion rotates the secret","status":"failed","title":"incrementing clientSecretVersion rotates the secret","duration":50161.854300000006,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379416339,"endTime":1782379466857.9077,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/ServiceToken.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, and destroy tag","status":"failed","title":"create, verify out-of-band, and destroy tag","duration":23683.3389,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"rename replaces the tag","status":"failed","title":"rename replaces the tag","duration":23761.6505,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed access tag","status":"failed","title":"list enumerates the deployed access tag","duration":23826.463799999998,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379550863,"endTime":1782379574690.4639,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Access/Tag.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","status":"failed","title":"surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","duration":991.2689000000028,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Acm/CustomTrustStore.test.ts:43:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates trust store certificates across zones","status":"passed","title":"list enumerates trust store certificates across zones","duration":957.6527999999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes the deployed trust store certificate on an entitled zone","status":"skipped","title":"list includes the deployed trust store certificate on an entitled zone","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"uploads a root CA, replaces it on certificate change, and deletes it","status":"skipped","title":"uploads a root CA, replaces it on certificate change, and deletes it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379641334,"endTime":1782379642325.2688,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Acm/CustomTrustStore.test.ts"},{"assertionResults":[{"ancestorTitles":["TotalTls"],"fullName":"TotalTls converges enabled:false as a no-op on a zone without the ACM entitlement","status":"failed","title":"converges enabled:false as a no-op on a zone without the ACM entitlement","duration":927.4395000000004,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Acm/TotalTls.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["TotalTls"],"fullName":"TotalTls surfaces the typed AdvancedCertificateManagerRequired error when enabling without the entitlement","status":"failed","title":"surfaces the typed AdvancedCertificateManagerRequired error when enabling without the entitlement","duration":284.15560000000187,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Acm/TotalTls.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["TotalTls"],"fullName":"TotalTls enables Total TLS, updates the CA in place, and restores the baseline on destroy","status":"skipped","title":"enables Total TLS, updates the CA in place, and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["TotalTls"],"fullName":"TotalTls list enumerates the setting across all zones","status":"failed","title":"list enumerates the setting across all zones","duration":241.08610000000044,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Acm/TotalTls.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379632269,"endTime":1782379633722.0862,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Acm/TotalTls.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"read path: getAccount observes the testing account","status":"passed","title":"read path: getAccount observes the testing account","duration":2023.0145000000048,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"inaccessible account surfaces a typed tag","status":"passed","title":"inaccessible account surfaces a typed tag","duration":1503.1174000000028,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates accessible accounts (read-only)","status":"failed","title":"list enumerates accessible accounts (read-only)","duration":73259.9124,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"createAccount is entitlement-gated: typed AccountCreationForbidden","status":"failed","title":"createAccount is entitlement-gated: typed AccountCreationForbidden","duration":26131.670700000002,"failureMessages":["AssertionError: expected 'InternalServerError' to deeply equal 'AccountCreationForbidden'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Account/Account.test.ts:97:26)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create subaccount, update name and settings in place, delete","status":"skipped","title":"create subaccount, update name and settings in place, delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379416239,"endTime":1782379489502.9124,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Account/Account.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the account members","status":"failed","title":"list enumerates the account members","duration":23889.067300000002,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create member, update roles in place, delete","status":"failed","title":"create member, update roles in place, delete","duration":23941.82,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the member when the email changes","status":"failed","title":"replaces the member when the email changes","duration":23763.9037,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":23737.0626,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379564000,"endTime":1782379587942.82,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Account/Member.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"address map lifecycle (typed FeatureNotEnabled on unentitled accounts)","status":"failed","title":"address map lifecycle (typed FeatureNotEnabled on unentitled accounts)","duration":2808.3937000000005,"failureMessages":["AssertionError: expected 'CloudflareHttpError' to deeply equal 'FeatureNotEnabled'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/AddressMap.test.ts:69:36)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:627:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed address map","status":"failed","title":"list enumerates the deployed address map","duration":2846.6525999999994,"failureMessages":["AssertionError: expected 'CloudflareHttpError' to deeply equal 'FeatureNotEnabled'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/AddressMap.test.ts:153:36)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:627:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379545593,"endTime":1782379548440.6526,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/AddressMap.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates BGP prefixes across BYOIP prefixes","status":"failed","title":"list enumerates BGP prefixes across BYOIP prefixes","duration":24237.2082,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379591724,"endTime":1782379615961.2083,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/BgpPrefix.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"lists the services catalog and prefixes (read-only)","status":"failed","title":"lists the services catalog and prefixes (read-only)","duration":23615.064400000003,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account prefixes (read-only)","status":"failed","title":"list enumerates account prefixes (read-only)","duration":23544.799300000002,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"prefix: create, patch description in place, destroy","status":"skipped","title":"prefix: create, patch description in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"bgp prefix: adopt-or-create by cidr, toggle advertisement, withdraw on destroy","status":"skipped","title":"bgp prefix: adopt-or-create by cidr, toggle advertisement, withdraw on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"prefix delegation: create and destroy (replace-only resource)","status":"skipped","title":"prefix delegation: create and destroy (replace-only resource)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"service binding: create against the CDN service and destroy","status":"skipped","title":"service binding: create against the CDN service and destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379559102,"endTime":1782379582717.0645,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/Prefix.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates prefix delegations (read-only)","status":"failed","title":"list enumerates prefix delegations (read-only)","duration":24259.213600000003,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379593831,"endTime":1782379618090.2136,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/PrefixDelegation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates service bindings across prefixes (read-only)","status":"failed","title":"list enumerates service bindings across prefixes (read-only)","duration":9231.207999999999,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379541716,"endTime":1782379550947.208,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Addressing/ServiceBinding.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete ai gateway with default props","status":"skipped","title":"create and delete ai gateway with default props","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete ai gateway","status":"skipped","title":"create, update, delete ai gateway","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed ai gateway","status":"skipped","title":"list enumerates the deployed ai gateway","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"existing ai gateway (matching id) is silently adopted without --adopt","status":"skipped","title":"existing ai gateway (matching id) is silently adopted without --adopt","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"deployed worker can call AiGateway binding (effect-native getUrl)","status":"skipped","title":"deployed worker can call AiGateway binding (effect-native getUrl)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/AiGateway.test.ts"},{"assertionResults":[{"ancestorTitles":["AiGatewaySpendingLimit"],"fullName":"AiGatewaySpendingLimit create, read-back, and delete the account spending limit","status":"failed","title":"create, read-back, and delete the account spending limit","duration":26415.8661,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewaySpendingLimit"],"fullName":"AiGatewaySpendingLimit update the spending limit in place","status":"failed","title":"update the spending limit in place","duration":24360.259000000005,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewaySpendingLimit"],"fullName":"AiGatewaySpendingLimit list enumerates the deployed account spending limit","status":"failed","title":"list enumerates the deployed account spending limit","duration":23554.77530000001,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379416310,"endTime":1782379490641.7754,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/AiGatewaySpendingLimit.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"first turn against a fresh thread persists user + assistant messages","status":"skipped","title":"first turn against a fresh thread persists user + assistant messages","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"persisted chat survives across DO invocations","status":"skipped","title":"persisted chat survives across DO invocations","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"distinct thread ids map to isolated histories","status":"skipped","title":"distinct thread ids map to isolated histories","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"null","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/ChatPersistence.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"send round-trips text and turns through the RpcWorker → DO","status":"skipped","title":"send round-trips text and turns through the RpcWorker → DO","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"streamMessage yields effect/ai Response parts that decode to real classes","status":"skipped","title":"streamMessage yields effect/ai Response parts that decode to real classes","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"streamed turn is persisted: a follow-up send recalls it","status":"skipped","title":"streamed turn is persisted: a follow-up send recalls it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"Please wait and consider throttling your request speed","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/ChatPersistenceRpc.test.ts"},{"assertionResults":[{"ancestorTitles":["AiGatewayDataset"],"fullName":"AiGatewayDataset create, update, delete a dataset on a gateway","status":"failed","title":"create, update, delete a dataset on a gateway","duration":26386.9963,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewayDataset"],"fullName":"AiGatewayDataset replaces dataset when the gateway changes","status":"failed","title":"replaces dataset when the gateway changes","duration":24412.965599999996,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewayDataset"],"fullName":"AiGatewayDataset recreates a dataset after out-of-band delete","status":"failed","title":"recreates a dataset after out-of-band delete","duration":23367.363000000012,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":["AiGatewayDataset"],"fullName":"AiGatewayDataset list enumerates datasets across gateways","status":"failed","title":"list enumerates datasets across gateways","duration":23245.291800000006,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379416254,"endTime":1782379513666.2917,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/Dataset.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update elements (new deployed version), rename, delete","status":"failed","title":"create, update elements (new deployed version), rename, delete","duration":24812.113000000005,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces route when the gateway changes","status":"failed","title":"replaces route when the gateway changes","duration":24713.3932,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates a route after out-of-band delete","status":"failed","title":"recreates a route after out-of-band delete","duration":24918.577800000003,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates routes across all gateways","status":"failed","title":"list enumerates routes across all gateways","duration":24274.0584,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379601356,"endTime":1782379626275.578,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/DynamicRouting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, noop, replace, delete an evaluation","status":"failed","title":"create, noop, replace, delete an evaluation","duration":24566.6458,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed evaluation","status":"failed","title":"list enumerates the deployed evaluation","duration":25228.273199999996,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379602470,"endTime":1782379627700.2732,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/Evaluation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed worker generates text via AiGateway-backed LanguageModel","status":"skipped","title":"deployed worker generates text via AiGateway-backed LanguageModel","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"deployed worker streams text via AiGateway-backed LanguageModel","status":"skipped","title":"deployed worker streams text via AiGateway-backed LanguageModel","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"streamed parts respect ordering: text-start → text-delta+ → text-end → finish","status":"skipped","title":"streamed parts respect ordering: text-start → text-delta+ → text-end → finish","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"DEBUG: dump raw Workers AI SSE stream","status":"skipped","title":"DEBUG: dump raw Workers AI SSE stream","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"stream finish part reports the real token counts and a `stop` reason","status":"skipped","title":"stream finish part reports the real token counts and a `stop` reason","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"stream emits multiple text-delta chunks for a long-form response","status":"skipped","title":"stream emits multiple text-delta chunks for a long-form response","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"persisted chat survives across DO invocations","status":"skipped","title":"persisted chat survives across DO invocations","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"deployed worker invokes a tool via AiGateway-backed LanguageModel","status":"skipped","title":"deployed worker invokes a tool via AiGateway-backed LanguageModel","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"streams tool-call parts via AiGateway-backed LanguageModel","status":"skipped","title":"streams tool-call parts via AiGateway-backed LanguageModel","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"concatenated tool-params-delta payloads parse back into the requested arguments","status":"skipped","title":"concatenated tool-params-delta payloads parse back into the requested arguments","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"streams Effect-native parts and prints them live","status":"skipped","title":"streams Effect-native parts and prints them live","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/LanguageModel.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, noop, replace, delete a BYOK provider config","status":"failed","title":"create, noop, replace, delete a BYOK provider config","duration":2442.8050999999996,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed provider config","status":"failed","title":"list enumerates the deployed provider config","duration":2313.6219999999994,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379639228,"endTime":1782379641670.8052,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiGateway/ProviderConfig.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"construct auto-creates a managed token and wires it into the instance","status":"failed","title":"construct auto-creates a managed token and wires it into the instance","duration":25364.3031,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"web-crawler source skips token minting","status":"failed","title":"web-crawler source skips token minting","duration":53690.8015,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379446851,"endTime":1782379500542.8015,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/AiSearch.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"worker deploys with ai_search + ai_search_namespace bindings injected","status":"skipped","title":"worker deploys with ai_search + ai_search_namespace bindings injected","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker resolves ai_search + ai_search_namespace via Effect clients","status":"skipped","title":"effect worker resolves ai_search + ai_search_namespace via Effect clients","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/Bindings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update mutable props, and delete an r2-backed instance","status":"failed","title":"create, update mutable props, and delete an r2-backed instance","duration":25708.237600000008,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the embedding model triggers a replacement","status":"failed","title":"changing the embedding model triggers a replacement","duration":25843.2301,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":27728.55159999999,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed instance","status":"failed","title":"list enumerates the deployed instance","duration":27066.901199999993,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a web-crawler instance (no service token)","status":"failed","title":"creates a web-crawler instance (no service token)","duration":48940.00199999999,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an instance in a custom namespace and moving namespaces replaces","status":"failed","title":"creates an instance in a custom namespace and moving namespaces replaces","duration":24422.804000000004,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379416421,"endTime":1782379466551.804,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/Instance.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update description in place, and delete a namespace","status":"failed","title":"create, update description in place, and delete a namespace","duration":23560.8793,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the name triggers a replacement","status":"failed","title":"changing the name triggers a replacement","duration":23585.9749,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed namespace","status":"failed","title":"list enumerates the deployed namespace","duration":23668.272,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopts the reserved default namespace without deleting it on teardown","status":"failed","title":"adopts the reserved default namespace without deleting it on teardown","duration":23604.5048,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":23637.5545,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379558960,"endTime":1782379582629.272,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/Namespace.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, rename in place, and delete a service token","status":"failed","title":"create, rename in place, and delete a service token","duration":46949.6945,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":47056.0374,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed service token","status":"failed","title":"list enumerates the deployed service token","duration":46971.6418,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"an AI Search instance syncs with a stack-minted service token","status":"failed","title":"an AI Search instance syncs with a stack-minted service token","duration":23633.0181,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379476756,"endTime":1782379523814.0374,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSearch/Token.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AiSecurityNotEntitled error on unentitled accounts","status":"failed","title":"surfaces the typed AiSecurityNotEntitled error on unentitled accounts","duration":2492.993300000002,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSecurity/CustomTopics.test.ts:34:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the custom topics across all zones","status":"passed","title":"list enumerates the custom topics across all zones","duration":2537.7596999999987,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"sets topics, updates the list in place, and restores on destroy","status":"skipped","title":"sets topics, updates the list in place, and restores on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379695863,"endTime":1782379698402.7598,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSecurity/CustomTopics.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AiSecurityNotEntitled error on unentitled accounts","status":"failed","title":"surfaces the typed AiSecurityNotEntitled error on unentitled accounts","duration":3085.7998000000007,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSecurity/Settings.test.ts:34:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the setting across all entitled zones","status":"passed","title":"list enumerates the setting across all entitled zones","duration":3128.1650999999983,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"pins enabled, updates in place, and restores the original on destroy","status":"skipped","title":"pins enabled, updates in place, and restores the original on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379646744,"endTime":1782379649879.165,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AiSecurity/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed worker can write data points through the Analytics Engine binding","status":"skipped","title":"deployed worker can write data points through the Analytics Engine binding","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/AnalyticsEngine/AnalyticsEngineDataset.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed NotEntitled error on unentitled zones","status":"failed","title":"surfaces the typed NotEntitled error on unentitled zones","duration":934.7052000000003,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/Configuration.test.ts:34:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the configuration across all zones","status":"passed","title":"list enumerates the configuration across all zones","duration":987.7023000000008,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"sets session identifiers and restores the original value on destroy","status":"skipped","title":"sets session identifiers and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379681192,"endTime":1782379682181.7024,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/Configuration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update description in place, destroy a label","status":"failed","title":"create, update description in place, destroy a label","duration":3351.0779,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/Label.test.ts:34:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"renaming a label triggers replacement","status":"failed","title":"renaming a label triggers replacement","duration":3424.9243000000024,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/Label.test.ts:34:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"generated name respects Cloudflare's 24-character limit","status":"failed","title":"generated name respects Cloudflare's 24-character limit","duration":3618.344799999999,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/Label.test.ts:34:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed label","status":"failed","title":"list enumerates the deployed label","duration":3388.8109000000004,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/Label.test.ts:34:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379668176,"endTime":1782379671796.3447,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/Label.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, no-op redeploy, destroy an API Shield operation","status":"failed","title":"create, no-op redeploy, destroy an API Shield operation","duration":2845.1313999999984,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/Operation.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the method triggers replacement","status":"failed","title":"changing the method triggers replacement","duration":2884.550299999999,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/Operation.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed API Shield operation","status":"failed","title":"list enumerates the deployed API Shield operation","duration":2866.3889000000017,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/Operation.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379653485,"endTime":1782379656371.5503,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/Operation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, enable validation in place, destroy a user schema","status":"failed","title":"create, enable validation in place, destroy a user schema","duration":2646.360499999999,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/UserSchema.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the schema source triggers replacement","status":"failed","title":"changing the schema source triggers replacement","duration":2612.492399999999,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/UserSchema.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed user schema","status":"failed","title":"list enumerates the deployed user schema","duration":4322.532199999998,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/UserSchema.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379688818,"endTime":1782379693142.5322,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiShield/UserSchema.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete notification policy","status":"failed","title":"create, update, delete notification policy","duration":23621.863100000002,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces policy when alertType changes","status":"failed","title":"replaces policy when alertType changes","duration":23644.978000000003,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed notification policy","status":"failed","title":"list enumerates the deployed notification policy","duration":23633.778100000003,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379477521,"endTime":1782379501166.978,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Alerting/NotificationPolicy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update window in place, delete silence","status":"failed","title":"create, update window in place, delete silence","duration":23351.576500000003,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces silence when the policy changes","status":"failed","title":"replaces silence when the policy changes","duration":23344.346899999997,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed silence","status":"failed","title":"list enumerates the deployed silence","duration":23366.7573,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379499736,"endTime":1782379523103.7573,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Alerting/Silence.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete webhook destination","status":"failed","title":"create, update, delete webhook destination","duration":2936.3292,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed webhook destination","status":"failed","title":"list enumerates the deployed webhook destination","duration":2620.6949999999997,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379545549,"endTime":1782379548485.329,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Alerting/Webhook.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed account token","status":"failed","title":"list enumerates the deployed account token","duration":2524.762899999998,"failureMessages":["Unauthorized: Unauthorized to access requested resource\n at Object.9109 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:91:22)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379678420,"endTime":1782379680944.763,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiToken/AccountApiToken.test.ts"},{"assertionResults":[{"ancestorTitles":["UserApiToken"],"fullName":"UserApiToken create and delete user token with default props","status":"skipped","title":"create and delete user token with default props","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UserApiToken"],"fullName":"UserApiToken create, update, delete user token","status":"skipped","title":"create, update, delete user token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UserApiToken list"],"fullName":"UserApiToken list list enumerates user tokens","status":"skipped","title":"list enumerates user tokens","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["UserApiToken list probe"],"fullName":"UserApiToken list probe list rejects with typed Unauthorized under scoped token","status":"passed","title":"list rejects with typed Unauthorized under scoped token","duration":496.35699999999997,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379536870,"endTime":1782379537366.357,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ApiToken/UserApiToken.test.ts"},{"assertionResults":[{"ancestorTitles":["SmartRouting"],"fullName":"SmartRouting surfaces the typed NotAuthorized error on zones without the Argo add-on","status":"failed","title":"surfaces the typed NotAuthorized error on zones without the Argo add-on","duration":2276.056199999999,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Argo/SmartRouting.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["SmartRouting"],"fullName":"SmartRouting enables Smart Routing and restores the original value on destroy","status":"skipped","title":"enables Smart Routing and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["SmartRouting"],"fullName":"SmartRouting list enumerates Argo-entitled zones","status":"passed","title":"list enumerates Argo-entitled zones","duration":1266.5074999999997,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379661262,"endTime":1782379664805.5076,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Argo/SmartRouting.test.ts"},{"assertionResults":[{"ancestorTitles":["TieredCaching"],"fullName":"TieredCaching enables Tiered Caching and restores the original value on destroy","status":"failed","title":"enables Tiered Caching and restores the original value on destroy","duration":1040.5800000000017,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Argo/TieredCaching.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["TieredCaching"],"fullName":"TieredCaching updates enabled in place and keeps the captured initial value","status":"failed","title":"updates enabled in place and keeps the captured initial value","duration":601.4963000000025,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Argo/TieredCaching.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["TieredCaching"],"fullName":"TieredCaching list enumerates the setting across all zones","status":"failed","title":"list enumerates the setting across all zones","duration":223.8117999999995,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Argo/TieredCaching.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379663865,"endTime":1782379665731.8118,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Argo/TieredCaching.test.ts"},{"assertionResults":[{"ancestorTitles":["BotManagement"],"fullName":"BotManagement manages SBFM settings on the zone singleton and restores them on destroy","status":"failed","title":"manages SBFM settings on the zone singleton and restores them on destroy","duration":2619.3723000000027,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/BotManagement/BotManagement.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["BotManagement"],"fullName":"BotManagement deploy with no settings set adopts the singleton without writing","status":"failed","title":"deploy with no settings set adopts the singleton without writing","duration":987.0400000000009,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/BotManagement/BotManagement.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["BotManagement"],"fullName":"BotManagement toggles a boolean SBFM field and restores it on destroy","status":"failed","title":"toggles a boolean SBFM field and restores it on destroy","duration":785.3667999999998,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/BotManagement/BotManagement.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["BotManagement"],"fullName":"BotManagement list enumerates bot management across all zones","status":"failed","title":"list enumerates bot management across all zones","duration":333.3581000000013,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/BotManagement/BotManagement.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379701287,"endTime":1782379706013.3582,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/BotManagement/BotManagement.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"async worker renders a page title through Browser Rendering","status":"skipped","title":"async worker renders a page title through Browser Rendering","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker exercises content via quickAction wrapper","status":"skipped","title":"effect worker exercises content via quickAction wrapper","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker converts a page to markdown","status":"skipped","title":"effect worker converts a page to markdown","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker extracts links","status":"skipped","title":"effect worker extracts links","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker scrapes elements by selector","status":"skipped","title":"effect worker scrapes elements by selector","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker takes a page snapshot","status":"skipped","title":"effect worker takes a page snapshot","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker streams a screenshot","status":"skipped","title":"effect worker streams a screenshot","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker streams a PDF","status":"skipped","title":"effect worker streams a PDF","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker extracts JSON with AI","status":"skipped","title":"effect worker extracts JSON with AI","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker calls the generic quickAction","status":"skipped","title":"effect worker calls the generic quickAction","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker exposes the raw BrowserRun binding","status":"skipped","title":"effect worker exposes the raw BrowserRun binding","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"Please wait and consider throttling your request speed","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Browser/Browser.test.ts"},{"assertionResults":[{"ancestorTitles":["CacheReserve"],"fullName":"CacheReserve surfaces the typed SettingUnavailableForPlan error on unentitled zones","status":"failed","title":"surfaces the typed SettingUnavailableForPlan error on unentitled zones","duration":807.7436999999991,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/CacheReserve.test.ts:35:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["CacheReserve"],"fullName":"CacheReserve enables Cache Reserve and restores the original value on destroy","status":"skipped","title":"enables Cache Reserve and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["CacheReserve"],"fullName":"CacheReserve list enumerates Cache Reserve across all zones","status":"passed","title":"list enumerates Cache Reserve across all zones","duration":303.9753000000019,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["CacheReserve"],"fullName":"CacheReserve list includes the entitled zone's Cache Reserve setting","status":"skipped","title":"list includes the entitled zone's Cache Reserve setting","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379665632,"endTime":1782379666743.9753,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/CacheReserve.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates, updates in place, replaces on ip change, and deletes the mapping","status":"failed","title":"creates, updates in place, replaces on ip change, and deletes the mapping","duration":2214.582299999998,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/OriginCloudRegion.test.ts:26:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379721659,"endTime":1782379723873.5823,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/OriginCloudRegion.test.ts"},{"assertionResults":[{"ancestorTitles":["RegionalTieredCache"],"fullName":"RegionalTieredCache surfaces the typed SettingUnavailableForPlan error on non-Enterprise zones","status":"failed","title":"surfaces the typed SettingUnavailableForPlan error on non-Enterprise zones","duration":1700.5188000000016,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/RegionalTieredCache.test.ts:35:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["RegionalTieredCache"],"fullName":"RegionalTieredCache enables Regional Tiered Cache and restores the original value on destroy","status":"skipped","title":"enables Regional Tiered Cache and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RegionalTieredCache"],"fullName":"RegionalTieredCache list enumerates the setting across entitled zones","status":"passed","title":"list enumerates the setting across entitled zones","duration":747.386599999998,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379634286,"endTime":1782379636734.3867,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/RegionalTieredCache.test.ts"},{"assertionResults":[{"ancestorTitles":["SmartTieredCache"],"fullName":"SmartTieredCache enables Smart Tiered Cache and restores the original value on destroy","status":"failed","title":"enables Smart Tiered Cache and restores the original value on destroy","duration":2605.8606,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/SmartTieredCache.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["SmartTieredCache"],"fullName":"SmartTieredCache updates the setting in place and keeps the captured initial value","status":"failed","title":"updates the setting in place and keeps the captured initial value","duration":1306.2662999999993,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/SmartTieredCache.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["SmartTieredCache"],"fullName":"SmartTieredCache list enumerates the setting across all zones","status":"failed","title":"list enumerates the setting across all zones","duration":257.33550000000105,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/SmartTieredCache.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379647567,"endTime":1782379651736.3354,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/SmartTieredCache.test.ts"},{"assertionResults":[{"ancestorTitles":["Variants"],"fullName":"Variants creates, updates in place, and deletes the variants setting","status":"failed","title":"creates, updates in place, and deletes the variants setting","duration":1563.4634000000005,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/Variants.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["Variants"],"fullName":"Variants deploy is idempotent and converges out-of-band drift","status":"failed","title":"deploy is idempotent and converges out-of-band drift","duration":374.1189000000013,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/Variants.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["Variants"],"fullName":"Variants list enumerates the configured variants settings","status":"failed","title":"list enumerates the configured variants settings","duration":317.1166999999987,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/Variants.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379655296,"endTime":1782379657551.1167,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Cache/Variants.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an app with default name","status":"failed","title":"create and delete an app with default name","duration":23644.242,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name in place (same appId, secret preserved)","status":"failed","title":"update name in place (same appId, secret preserved)","duration":23695.543100000003,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed app","status":"failed","title":"list enumerates the deployed app","duration":23659.9266,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":23677.648800000003,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379573431,"endTime":1782379597127.5432,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Calls/App.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a TURN key with default name","status":"failed","title":"create and delete a TURN key with default name","duration":23521.9148,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name in place (same keyId, key preserved)","status":"failed","title":"update name in place (same keyId, key preserved)","duration":23548.8328,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":23491.6228,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed TURN key","status":"failed","title":"list enumerates the deployed TURN key","duration":23532.147200000003,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379495338,"endTime":1782379518887.8328,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Calls/TurnKey.test.ts"},{"assertionResults":[{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation pins Managed CA hostnames, updates in place, and clears on destroy","status":"failed","title":"pins Managed CA hostnames, updates in place, and clears on destroy","duration":2239.2091,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CertificateAuthorities/HostnameAssociation.test.ts:30:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation associates hostnames with an uploaded CA and destroys before the cert","status":"failed","title":"associates hostnames with an uploaded CA and destroys before the cert","duration":972.4154999999992,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CertificateAuthorities/HostnameAssociation.test.ts:30:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation changing the certificate key replaces the association","status":"failed","title":"changing the certificate key replaces the association","duration":284.2089999999989,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CertificateAuthorities/HostnameAssociation.test.ts:30:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379660911,"endTime":1782379664407.209,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CertificateAuthorities/HostnameAssociation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create signs the CSR, destroy revokes the certificate","status":"failed","title":"create signs the CSR, destroy revokes the certificate","duration":1399.2845000000016,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ClientCertificate/ClientCertificate.test.ts:29:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing validityDays replaces — new id issued, old certificate revoked","status":"failed","title":"changing validityDays replaces — new id issued, old certificate revoked","duration":1364.2652000000016,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ClientCertificate/ClientCertificate.test.ts:29:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates client certificates across zones","status":"failed","title":"list enumerates client certificates across zones","duration":1441.7246000000014,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ClientCertificate/ClientCertificate.test.ts:29:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379664945,"endTime":1782379666388.7246,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ClientCertificate/ClientCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":["Rules"],"fullName":"Rules cloud connector rules — create, update in place, destroy clears the list","status":"failed","title":"cloud connector rules — create, update in place, destroy clears the list","duration":1412.1650000000009,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CloudConnector/Rules.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["Rules"],"fullName":"Rules no-op redeploy leaves the rule list untouched and ids stable","status":"failed","title":"no-op redeploy leaves the rule list untouched and ids stable","duration":582.5286000000015,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CloudConnector/Rules.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["Rules"],"fullName":"Rules list enumerates rule lists across all zones","status":"failed","title":"list enumerates rule lists across all zones","duration":243.86989999999787,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CloudConnector/Rules.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379637384,"endTime":1782379639623.8699,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CloudConnector/Rules.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Unauthorized error","status":"failed","title":"unentitled accounts surface the typed Unauthorized error","duration":1068.6695,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create a scan config, update ports in place, delete","status":"skipped","title":"create a scan config, update ports in place, delete","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns an array of scan configs","status":"failed","title":"list returns an array of scan configs","duration":1050.228500000001,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed scan config","status":"skipped","title":"list enumerates the deployed scan config","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379549444,"endTime":1782379550512.6694,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CloudforceOne/ScanConfig.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"tcp service lifecycle: create, update, host switch","status":"failed","title":"tcp service lifecycle: create, update, host switch","duration":23744.3772,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"http service with explicit ports and default name","status":"failed","title":"http service with explicit ports and default name","duration":23759.6501,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed directory service","status":"failed","title":"list enumerates the deployed directory service","duration":23643.0658,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":23884.3682,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379473415,"endTime":1782379497301.3682,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Connectivity/DirectoryService.test.ts"},{"assertionResults":[{"ancestorTitles":["effectful container (main)"],"fullName":"effectful container (main) deploys and serves over its TCP port","status":"skipped","title":"deploys and serves over its TCP port","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["external container (context/dockerfile)"],"fullName":"external container (context/dockerfile) builds the user Dockerfile and serves it over its TCP port","status":"skipped","title":"builds the user Dockerfile and serves it over its TCP port","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["remote container (image)"],"fullName":"remote container (image) pulls and re-pushes the remote image and serves it over its TCP port","status":"skipped","title":"pulls and re-pushes the remote image and serves it over its TCP port","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Container/Container.test.ts"},{"assertionResults":[{"ancestorTitles":["ContainerApplication"],"fullName":"ContainerApplication list enumerates container applications","status":"failed","title":"list enumerates container applications","duration":353.8166999999994,"failureMessages":["Provider not found for undefined"],"meta":{},"tags":[]}],"startTime":1782379722955,"endTime":1782379723308.8167,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Container/ContainerApplication.test.ts"},{"assertionResults":[{"ancestorTitles":["ContentScanning"],"fullName":"ContentScanning surfaces the typed ContentScanningNotEntitled error on unentitled zones","status":"failed","title":"surfaces the typed ContentScanningNotEntitled error on unentitled zones","duration":2805.6597,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ContentScanning/ContentScanning.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["ContentScanning"],"fullName":"ContentScanning pins Content Scanning off on an unentitled zone and destroys cleanly","status":"failed","title":"pins Content Scanning off on an unentitled zone and destroys cleanly","duration":645.1274999999987,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ContentScanning/ContentScanning.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["ContentScanning"],"fullName":"ContentScanning list enumerates the status across all zones","status":"failed","title":"list enumerates the status across all zones","duration":854.4763999999996,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ContentScanning/ContentScanning.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["ContentScanning"],"fullName":"ContentScanning enables Content Scanning and restores the original status on destroy","status":"skipped","title":"enables Content Scanning and restores the original status on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379666930,"endTime":1782379671236.4763,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ContentScanning/ContentScanning.test.ts"},{"assertionResults":[{"ancestorTitles":["Expression"],"fullName":"Expression surfaces the typed ContentScanningNotEnabled error when scanning is disabled","status":"failed","title":"surfaces the typed ContentScanningNotEnabled error when scanning is disabled","duration":1311.6042000000016,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ContentScanning/Expression.test.ts:35:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["Expression"],"fullName":"Expression creates a custom expression, replaces on payload change, destroys cleanly","status":"skipped","title":"creates a custom expression, replaces on payload change, destroys cleanly","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Expression"],"fullName":"Expression list returns a well-typed array across all zones","status":"passed","title":"list returns a well-typed array across all zones","duration":335.2610999999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Expression"],"fullName":"Expression list enumerates the deployed expression","status":"skipped","title":"list enumerates the deployed expression","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379664645,"endTime":1782379666292.261,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ContentScanning/Expression.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed PlanLevelNotAllowed error on unentitled zones","status":"failed","title":"surfaces the typed PlanLevelNotAllowed error on unentitled zones","duration":1588.6598000000013,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CustomCertificate/CustomCertificate.test.ts:37:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates custom certificates across zones","status":"passed","title":"list enumerates custom certificates across zones","duration":1621.6686000000009,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"uploads, rotates in place, replaces on type change, and deletes","status":"skipped","title":"uploads, rotates in place, replaces on type change, and deletes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379642846,"endTime":1782379644469.6687,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CustomCertificate/CustomCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a custom hostname with default ssl","status":"skipped","title":"create and delete a custom hostname with default ssl","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating ssl method patches in place","status":"skipped","title":"updating ssl method patches in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of custom hostnames","status":"passed","title":"list returns a well-typed array of custom hostnames","duration":1072.3407000000007,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed custom hostname","status":"skipped","title":"list enumerates the deployed custom hostname","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the hostname triggers replacement","status":"skipped","title":"changing the hostname triggers replacement","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379734726,"endTime":1782379735798.3408,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CustomHostname/CustomHostname.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates fallback origins across all zones","status":"passed","title":"list enumerates fallback origins across all zones","duration":1068.3551999999981,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list surfaces a deployed fallback origin","status":"skipped","title":"list surfaces a deployed fallback origin","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"set, update and delete the zone fallback origin","status":"skipped","title":"set, update and delete the zone fallback origin","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379734775,"endTime":1782379735843.3552,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CustomHostname/FallbackOrigin.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed CustomNameserversNotEnabled error on unentitled accounts","status":"passed","title":"surfaces the typed CustomNameserversNotEnabled error on unentitled accounts","duration":2020.816999999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account custom nameservers","status":"passed","title":"list enumerates account custom nameservers","duration":1239.5972000000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed custom nameserver","status":"skipped","title":"list includes a deployed custom nameserver","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, replace on nsSet change, and destroy a custom nameserver","status":"skipped","title":"create, replace on nsSet change, and destroy a custom nameserver","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379732837,"endTime":1782379734857.817,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/CustomNameserver/CustomNameserver.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"D1Connection.bind exercises the full client surface","status":"passed","title":"D1Connection.bind exercises the full client surface","duration":24893.4657,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379726007,"endTime":1782379750900.4658,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/D1/D1Binding.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed database","status":"failed","title":"list enumerates the deployed database","duration":1844.9033000000018,"failureMessages":["Forbidden: System limit reached: databases per account (1)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379733958,"endTime":1782379735802.9033,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/D1/D1Database.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete database with default props","status":"passed","title":"create and delete database with default props","duration":4646.931299999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete database","status":"passed","title":"create, update, delete database","duration":5461.746500000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"applies migrations from migrationsDir","status":"passed","title":"applies migrations from migrationsDir","duration":6060.144599999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"applies migrations using a custom migrationsTable","status":"passed","title":"applies migrations using a custom migrationsTable","duration":4827.759599999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"migrates legacy 2-column migration table to wrangler schema","status":"passed","title":"migrates legacy 2-column migration table to wrangler schema","duration":6230.200000000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"imports SQL files via importFiles","status":"passed","title":"imports SQL files via importFiles","duration":4098.039499999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"clones a database by databaseId","status":"passed","title":"clones a database by databaseId","duration":7866.301200000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"clones a database by name lookup","status":"passed","title":"clones a database by name lookup","duration":7279.739099999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"clones a database by passing the source resource directly","status":"passed","title":"clones a database by passing the source resource directly","duration":7527.3318,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"existing database (matching name) is silently adopted without --adopt","status":"passed","title":"existing database (matching name) is silently adopted without --adopt","duration":3460.4022000000004,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379628859,"endTime":1782379642449.3318,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/D1/Database.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a custom device profile","status":"failed","title":"create, update in place, and delete a custom device profile","duration":23676.3252,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed custom device profile","status":"failed","title":"list enumerates the deployed custom device profile","duration":23725.1508,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379570481,"endTime":1782379594207.151,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/CustomProfile.test.ts"},{"assertionResults":[{"ancestorTitles":["DefaultProfile"],"fullName":"DefaultProfile reads the existing default device profile without mutating it","status":"skipped","title":"reads the existing default device profile without mutating it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DefaultProfile"],"fullName":"DefaultProfile toggles captivePortal and restores the original value","status":"skipped","title":"toggles captivePortal and restores the original value","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["DefaultProfile"],"fullName":"DefaultProfile list returns the singleton default device profile","status":"failed","title":"list returns the singleton default device profile","duration":23540.5688,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379562627,"endTime":1782379586167.5688,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/DefaultProfile.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden entitlement error","status":"failed","title":"unentitled accounts surface the typed Forbidden entitlement error","duration":24135.179500000002,"failureMessages":["AssertionError: expected 'Authentication error' to contain 'dex.api.entitlements.missing'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/DexTest.test.ts:47:31)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a DEX test","status":"skipped","title":"create, update in place, and destroy a DEX test","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed DEX test","status":"skipped","title":"list enumerates the deployed DEX test","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed empty array on unentitled accounts","status":"passed","title":"list returns a well-typed empty array on unentitled accounts","duration":24226.223300000005,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379592447,"endTime":1782379616675.2234,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/DexTest.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a managed network","status":"failed","title":"create, update in place, and delete a managed network","duration":23499.8954,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed managed network","status":"failed","title":"list enumerates the deployed managed network","duration":23520.7741,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]}],"startTime":1782379504254,"endTime":1782379527774.7742,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/ManagedNetwork.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates without a real provider surface the typed InvalidPostureIntegrationConfig error","status":"failed","title":"creates without a real provider surface the typed InvalidPostureIntegrationConfig error","duration":90016.08599999998,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:163:10\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/PostureIntegration.test.ts:31:30\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a posture integration","status":"skipped","title":"create, update in place, and destroy a posture integration","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates posture integrations in the account","status":"passed","title":"list enumerates posture integrations in the account","duration":26416.985700000005,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed posture integration","status":"skipped","title":"list includes a deployed posture integration","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379416384,"endTime":1782379506400.086,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/PostureIntegration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a posture rule","status":"failed","title":"create, update in place, and delete a posture rule","duration":23766.747199999998,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the rule type triggers a replacement","status":"failed","title":"changing the rule type triggers a replacement","duration":23729.768799999998,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed posture rule","status":"failed","title":"list enumerates the deployed posture rule","duration":23748.668099999995,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379557640,"endTime":1782379581406.7473,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/PostureRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"patches disableForTime and restores the original value on destroy","status":"failed","title":"patches disableForTime and restores the original value on destroy","duration":50900.7157,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account's device settings singleton","status":"failed","title":"list returns the account's device settings singleton","duration":26130.553400000004,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]}],"startTime":1782379416240,"endTime":1782379467140.7158,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Devices/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AdvancedTcpProtectionNotEntitled error without Magic Transit","status":"failed","title":"surfaces the typed AdvancedTcpProtectionNotEntitled error without Magic Transit","duration":24808.170399999995,"failureMessages":["AssertionError: expected 'TooManyRequests' to deeply equal 'AdvancedTcpProtectionNotEntitled'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/AllowlistEntry.test.ts:60:26)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an allowlist entry, updates it in place, replaces on prefix change, and destroys","status":"skipped","title":"creates an allowlist entry, updates it in place, replaces on prefix change, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed empty array without Magic Transit","status":"failed","title":"list returns a well-typed empty array without Magic Transit","duration":24697.157900000006,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed allowlist entry","status":"skipped","title":"list enumerates the deployed allowlist entry","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379537935,"endTime":1782379562743.1704,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/AllowlistEntry.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates a SYN protection filter, updates it in place, and destroys","status":"skipped","title":"creates a SYN protection filter, updates it in place, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of SYN protection filters","status":"passed","title":"list returns a well-typed array of SYN protection filters","duration":26914.5551,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed SYN protection filter","status":"skipped","title":"list enumerates the deployed SYN protection filter","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379706512,"endTime":1782379733426.5552,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/SynProtectionFilter.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates a global SYN protection rule, updates it in place, and destroys","status":"skipped","title":"creates a global SYN protection rule, updates it in place, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of SYN protection rules","status":"passed","title":"list returns a well-typed array of SYN protection rules","duration":27387.054799999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed SYN protection rule","status":"skipped","title":"list enumerates the deployed SYN protection rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379724082,"endTime":1782379751469.0547,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/SynProtectionRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates a TCP flow protection filter, updates it in place, and destroys","status":"skipped","title":"creates a TCP flow protection filter, updates it in place, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account's TCP flow protection filters","status":"passed","title":"list returns the account's TCP flow protection filters","duration":26716.9431,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed TCP flow protection filter","status":"skipped","title":"list enumerates the deployed TCP flow protection filter","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379725943,"endTime":1782379752659.943,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/TcpFlowProtectionFilter.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates a global TCP flow protection rule, updates it in place, and destroys","status":"skipped","title":"creates a global TCP flow protection rule, updates it in place, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed empty array without Magic Transit","status":"failed","title":"list returns a well-typed empty array without Magic Transit","duration":995.3613999999998,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed TCP flow protection rule","status":"skipped","title":"list enumerates the deployed TCP flow protection rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379549445,"endTime":1782379550440.3613,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DdosProtection/TcpFlowProtectionRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"rejects public IPs with the typed InvalidHealthcheckEndpoint error","status":"failed","title":"rejects public IPs with the typed InvalidHealthcheckEndpoint error","duration":14582.177,"failureMessages":["AssertionError: expected 'CloudflareHttpError' to deeply equal 'InvalidHealthcheckEndpoint'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Diagnostics/EndpointHealthcheck.test.ts:70:26)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an endpoint healthcheck, updates in place, and destroys","status":"failed","title":"creates an endpoint healthcheck, updates in place, and destroys","duration":14732.678800000002,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed endpoint healthcheck","status":"failed","title":"list enumerates the deployed endpoint healthcheck","duration":14611.424399999998,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379534234,"endTime":1782379548967.6787,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Diagnostics/EndpointHealthcheck.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden error","status":"failed","title":"unentitled accounts surface the typed Forbidden error","duration":49331.5695,"failureMessages":["AssertionError: expected 'CloudflareHttpError' to deeply equal 'Forbidden'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dlp/Dlp.test.ts:54:31)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, and destroy a DLP profile with a standalone entry","status":"skipped","title":"create, update, and destroy a DLP profile with a standalone entry","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379592887,"endTime":1782379642218.5696,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dlp/Dlp.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns the account's custom DLP entries","status":"failed","title":"list returns the account's custom DLP entries","duration":23639.5821,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed DLP entry","status":"skipped","title":"list enumerates the deployed DLP entry","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379562679,"endTime":1782379586318.582,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dlp/Entry.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates custom DLP profiles (read-only)","status":"failed","title":"list enumerates custom DLP profiles (read-only)","duration":24294.5773,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed custom DLP profile","status":"skipped","title":"list includes a deployed custom DLP profile","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379600198,"endTime":1782379624492.5774,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dlp/Profile.test.ts"},{"assertionResults":[{"ancestorTitles":["AccountSettings"],"fullName":"AccountSettings list returns the account's DNS settings singleton","status":"failed","title":"list returns the account's DNS settings singleton","duration":26332.80679999999,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":["AccountSettings"],"fullName":"AccountSettings pins a zone default and restores the pre-management value on destroy","status":"failed","title":"pins a zone default and restores the pre-management value on destroy","duration":24430.70550000001,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["AccountSettings"],"fullName":"AccountSettings updates in place, unions managedKeys, restores all managed fields","status":"failed","title":"updates in place, unions managedKeys, restores all managed fields","duration":23312.944800000012,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379416265,"endTime":1782379490340.9448,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/AccountSettings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed worker drives the full DNS record CRUD surface via DnsReadWrite","status":"skipped","title":"deployed worker drives the full DNS record CRUD surface via DnsReadWrite","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"Please wait and consider throttling your request speed","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/Dns.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an A record with default props","status":"failed","title":"create and delete an A record with default props","duration":3609.0573000000004,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/DnsRecord.test.ts:39:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating mutable fields patches in place","status":"failed","title":"updating mutable fields patches in place","duration":3237.8872999999985,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/DnsRecord.test.ts:39:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the record type triggers replacement","status":"failed","title":"changing the record type triggers replacement","duration":3207.157299999999,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/DnsRecord.test.ts:39:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing record errors without adopt, takes over with adopt(true)","status":"failed","title":"adoption — existing record errors without adopt, takes over with adopt(true)","duration":3923.4821999999986,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/DnsRecord.test.ts:39:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed DNS record","status":"failed","title":"list enumerates the deployed DNS record","duration":3177.721800000003,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/DnsRecord.test.ts:39:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379684218,"endTime":1782379688143.4822,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/DnsRecord.test.ts"},{"assertionResults":[{"ancestorTitles":["Dnssec"],"fullName":"Dnssec enables DNSSEC, captures the disabled baseline, destroy deactivates","status":"failed","title":"enables DNSSEC, captures the disabled baseline, destroy deactivates","duration":818.3029999999981,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/Dnssec.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["Dnssec"],"fullName":"Dnssec updates status in place — same zone singleton, no replacement","status":"failed","title":"updates status in place — same zone singleton, no replacement","duration":1566.0203999999976,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/Dnssec.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["Dnssec"],"fullName":"Dnssec list enumerates active DNSSEC across zones","status":"failed","title":"list enumerates active DNSSEC across zones","duration":284.20839999999953,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/Dnssec.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379633296,"endTime":1782379635965.2085,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/Dnssec.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed InternalDnsNotAvailable error on unentitled accounts","status":"failed","title":"surfaces the typed InternalDnsNotAvailable error on unentitled accounts","duration":415.8247999999985,"failureMessages":["Error: Please wait and consider throttling your request speed\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/Zone/lookup.ts:88:9)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of internal DNS views","status":"failed","title":"list returns a well-typed array of internal DNS views","duration":23686.1258,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates a deployed internal DNS view","status":"skipped","title":"list enumerates a deployed internal DNS view","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update zones in place, and delete an internal DNS view","status":"skipped","title":"create, update zones in place, and delete an internal DNS view","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379556620,"endTime":1782379580308.1257,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/View.test.ts"},{"assertionResults":[{"ancestorTitles":["ZoneSettings"],"fullName":"ZoneSettings pins flattenAllCnames and restores the pre-management value on destroy","status":"failed","title":"pins flattenAllCnames and restores the pre-management value on destroy","duration":1026.7629000000015,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneSettings.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["ZoneSettings"],"fullName":"ZoneSettings updates in place, unions managedKeys, restores all managed fields","status":"failed","title":"updates in place, unions managedKeys, restores all managed fields","duration":358.8774999999987,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneSettings.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["ZoneSettings"],"fullName":"ZoneSettings list enumerates DNS settings across all zones","status":"failed","title":"list enumerates DNS settings across all zones","duration":374.1769000000022,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneSettings.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379672529,"endTime":1782379674289.177,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneSettings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a zone transfer ACL","status":"failed","title":"create, update in place, and delete a zone transfer ACL","duration":15773.066200000001,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"generates a deterministic name when none is provided","status":"failed","title":"generates a deterministic name when none is provided","duration":49172.737499999996,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed zone transfer ACLs","status":"failed","title":"list enumerates the deployed zone transfer ACLs","duration":15175.543100000003,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379533362,"endTime":1782379582536.7375,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferAcl.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates incoming configs across all zones","status":"passed","title":"list enumerates incoming configs across all zones","duration":2504.6121000000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"incoming config does not persist on non-secondary zones (typed not-found)","status":"failed","title":"incoming config does not persist on non-secondary zones (typed not-found)","duration":2546.3662999999997,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferIncoming.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update autoRefreshSeconds in place, and delete the incoming config","status":"skipped","title":"create, update autoRefreshSeconds in place, and delete the incoming config","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379627274,"endTime":1782379629822.3662,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferIncoming.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed OutgoingZoneTransfersNotAllowed error on unentitled zones","status":"failed","title":"surfaces the typed OutgoingZoneTransfersNotAllowed error on unentitled zones","duration":2029.7365999999965,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferOutgoing.test.ts:35:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, sync peers and enabled state, and delete the outgoing config","status":"skipped","title":"create, sync peers and enabled state, and delete the outgoing config","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the outgoing transfer config per zone","status":"failed","title":"list enumerates the outgoing transfer config per zone","duration":2054.5574000000015,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferOutgoing.test.ts:35:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379691110,"endTime":1782379693166.5574,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferOutgoing.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create with connection settings, update in place, and delete a peer","status":"failed","title":"create with connection settings, update in place, and delete a peer","duration":56285.1446,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"wires a TSIG reference through to the peer","status":"failed","title":"wires a TSIG reference through to the peer","duration":50987.1519,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed peer","status":"failed","title":"list enumerates the deployed peer","duration":50948.733199999995,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379523239,"endTime":1782379579524.1445,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferPeer.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, rotate the secret in place, and delete a TSIG","status":"failed","title":"create, rotate the secret in place, and delete a TSIG","duration":71303.802,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed TSIG","status":"failed","title":"list enumerates the deployed TSIG","duration":66692.55979999999,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379508380,"endTime":1782379579683.802,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Dns/ZoneTransferTsig.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed DnsFirewallNotEntitled error on unentitled accounts","status":"failed","title":"surfaces the typed DnsFirewallNotEntitled error on unentitled accounts","duration":23776.4988,"failureMessages":["AssertionError: expected 'TooManyRequests' to deeply equal 'DnsFirewallNotEntitled'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DnsFirewall/DnsFirewall.test.ts:69:26)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, verify out-of-band, destroy, and wait until gone","status":"skipped","title":"create, verify out-of-band, destroy, and wait until gone","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable settings in place, replace on rename","status":"skipped","title":"update mutable settings in place, replace on rename","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the DNS firewall cluster Attributes array","status":"failed","title":"list returns the DNS firewall cluster Attributes array","duration":23810.0599,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed DNS firewall cluster","status":"skipped","title":"list enumerates the deployed DNS firewall cluster","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379564796,"endTime":1782379588608.0598,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/DnsFirewall/DnsFirewall.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed email address","status":"failed","title":"list enumerates the deployed email address","duration":120022.4535,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailAddress.test.ts:27:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]}],"startTime":1782379416384,"endTime":1782379536406.4536,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailAddress.test.ts"},{"assertionResults":[{"ancestorTitles":["EmailCatchAll"],"fullName":"EmailCatchAll configures the catch-all rule and restores the baseline on destroy","status":"failed","title":"configures the catch-all rule and restores the baseline on destroy","duration":2520.3927000000003,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailCatchAll.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["EmailCatchAll"],"fullName":"EmailCatchAll updates the catch-all rule in place and keeps the captured baseline","status":"failed","title":"updates the catch-all rule in place and keeps the captured baseline","duration":627.8004000000001,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailCatchAll.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["EmailCatchAll"],"fullName":"EmailCatchAll list enumerates the catch-all rule across all zones","status":"failed","title":"list enumerates the catch-all rule across all zones","duration":1502.4615000000013,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailCatchAll.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379719983,"endTime":1782379724634.4614,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailCatchAll.test.ts"},{"assertionResults":[{"ancestorTitles":["EmailRouting"],"fullName":"EmailRouting list enumerates email routing across all zones","status":"failed","title":"list enumerates email routing across all zones","duration":865.5352999999996,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailRouting.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379707774,"endTime":1782379708639.5354,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailRouting.test.ts"},{"assertionResults":[{"ancestorTitles":["EmailRule"],"fullName":"EmailRule list enumerates the deployed email rule across all zones","status":"failed","title":"list enumerates the deployed email rule across all zones","duration":2586.447899999999,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailRule.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379695508,"endTime":1782379698094.448,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/EmailRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"sends an email through the Worker send_email binding","status":"skipped","title":"sends an email through the Worker send_email binding","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/SendEmail.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and destroy a sending subdomain","status":"failed","title":"create and destroy a sending subdomain","duration":3910.5,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/SendingSubdomain.test.ts:40:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the name triggers replacement","status":"failed","title":"changing the name triggers replacement","duration":3252.397499999999,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/SendingSubdomain.test.ts:40:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing subdomain errors without adopt, takes over with adopt(true)","status":"failed","title":"adoption — existing subdomain errors without adopt, takes over with adopt(true)","duration":3206.209499999997,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/SendingSubdomain.test.ts:40:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed sending subdomain","status":"failed","title":"list enumerates the deployed sending subdomain","duration":3235.0465000000004,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/SendingSubdomain.test.ts:40:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379693293,"endTime":1782379697203.5,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Email/SendingSubdomain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed EmailSecurityNotEntitled error on unentitled accounts","status":"failed","title":"surfaces the typed EmailSecurityNotEntitled error on unentitled accounts","duration":23710.8819,"failureMessages":["AssertionError: expected 'TooManyRequests' to deeply equal 'EmailSecurityNotEntitled'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/AllowPolicy.test.ts:66:26)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates allow policies (read-only)","status":"failed","title":"list enumerates allow policies (read-only)","duration":23668.0659,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes the deployed allow policy","status":"skipped","title":"list includes the deployed allow policy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, destroy","status":"skipped","title":"create, update in place, destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379570820,"endTime":1782379594530.8818,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/AllowPolicy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, destroy","status":"skipped","title":"create, update in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed block sender","status":"passed","title":"list enumerates the deployed block sender","duration":26739.7742,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379721949,"endTime":1782379748688.7742,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/BlockSender.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"adopts an onboarded domain, patches settings in place, offboards on destroy","status":"skipped","title":"adopts an onboarded domain, patches settings in place, offboards on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates Email Security domains (or [] when unentitled)","status":"failed","title":"list enumerates Email Security domains (or [] when unentitled)","duration":24345.6409,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379542165,"endTime":1782379566510.6409,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/Domain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, destroy","status":"skipped","title":"create, update in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account impersonation registry","status":"failed","title":"list returns the account impersonation registry","duration":23616.070200000002,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed impersonation registry entry","status":"skipped","title":"list enumerates the deployed impersonation registry entry","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379504024,"endTime":1782379527640.0703,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/ImpersonationRegistryEntry.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, destroy","status":"skipped","title":"create, update in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates trusted domains","status":"failed","title":"list enumerates trusted domains","duration":23721.833599999998,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379578156,"endTime":1782379601877.8335,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/EmailSecurity/TrustedDomain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a zone-scoped ip rule","status":"failed","title":"create and delete a zone-scoped ip rule","duration":391.2234999999996,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Firewall/AccessRule.test.ts:39:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mode and notes in place (same ruleId)","status":"failed","title":"update mode and notes in place (same ruleId)","duration":619.4322999999999,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Firewall/AccessRule.test.ts:39:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the configuration triggers replacement","status":"failed","title":"changing the configuration triggers replacement","duration":531.0519000000004,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Firewall/AccessRule.test.ts:39:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed zone-scoped rule","status":"failed","title":"list enumerates the deployed zone-scoped rule","duration":815.1505000000002,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Firewall/AccessRule.test.ts:39:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"account-scoped rule (no zoneId) create, update, delete","status":"failed","title":"account-scoped rule (no zoneId) create, update, delete","duration":39236.6805,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379426549,"endTime":1782379465787.6804,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Firewall/AccessRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update urls/configurations/description/paused in place, destroy","status":"failed","title":"create, update urls/configurations/description/paused in place, destroy","duration":1309.5803000000014,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Firewall/Lockdown.test.ts:42:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed lockdown rule","status":"failed","title":"list enumerates the deployed lockdown rule","duration":1262.7235999999975,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Firewall/Lockdown.test.ts:42:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379652200,"endTime":1782379653509.5803,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Firewall/Lockdown.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update mode/paused/description/userAgent in place, destroy","status":"failed","title":"create, update mode/paused/description/userAgent in place, destroy","duration":2419.404700000003,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Firewall/UaRule.test.ts:37:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates UA rules across all zones","status":"failed","title":"list enumerates UA rules across all zones","duration":2442.773399999998,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Firewall/UaRule.test.ts:37:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379689986,"endTime":1782379692430.7734,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Firewall/UaRule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete a Flagship app","status":"failed","title":"create, update, delete a Flagship app","duration":23876.8101,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates an app after out-of-band delete","status":"failed","title":"recreates an app after out-of-band delete","duration":23894.3389,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed Flagship app","status":"failed","title":"list enumerates the deployed Flagship app","duration":23906.7877,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379535383,"endTime":1782379559290.7876,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Flagship/App.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete a flag in an app","status":"failed","title":"create, update, delete a flag in an app","duration":23946.757900000004,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the flag when the key changes","status":"failed","title":"replaces the flag when the key changes","duration":23992.826800000003,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates a flag after out-of-band delete","status":"failed","title":"recreates a flag after out-of-band delete","duration":24019.506899999997,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed flag","status":"failed","title":"list enumerates the deployed flag","duration":23923.253899999996,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379594665,"endTime":1782379618686.5068,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Flagship/Flag.test.ts"},{"assertionResults":[],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"FlagshipApp is not a function","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Flagship/Flagship.test.ts"},{"assertionResults":[{"ancestorTitles":["FraudDetectionSettings"],"fullName":"FraudDetectionSettings adopts the zone singleton without writing and restores nothing on destroy","status":"failed","title":"adopts the zone singleton without writing and restores nothing on destroy","duration":1888.858900000003,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Fraud/FraudDetectionSettings.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["FraudDetectionSettings"],"fullName":"FraudDetectionSettings surfaces the typed FraudDetectionNotEntitled error on unentitled zones","status":"failed","title":"surfaces the typed FraudDetectionNotEntitled error on unentitled zones","duration":742.2955000000002,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Fraud/FraudDetectionSettings.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["FraudDetectionSettings"],"fullName":"FraudDetectionSettings manages fraud detection settings and restores them on destroy","status":"skipped","title":"manages fraud detection settings and restores them on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["FraudDetectionSettings"],"fullName":"FraudDetectionSettings list enumerates the settings across all zones","status":"failed","title":"list enumerates the settings across all zones","duration":338.4900999999991,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Fraud/FraudDetectionSettings.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379649338,"endTime":1782379652308.49,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Fraud/FraudDetectionSettings.test.ts"},{"assertionResults":[{"ancestorTitles":["GoogleTagGateway"],"fullName":"GoogleTagGateway configures the gateway, updates in place, and restores the baseline on destroy","status":"failed","title":"configures the gateway, updates in place, and restores the baseline on destroy","duration":2655.9045000000006,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/GoogleTagGateway/GoogleTagGateway.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["GoogleTagGateway"],"fullName":"GoogleTagGateway no-op redeploy skips the PUT and destroy is idempotent","status":"failed","title":"no-op redeploy skips the PUT and destroy is idempotent","duration":487.46290000000226,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/GoogleTagGateway/GoogleTagGateway.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["GoogleTagGateway"],"fullName":"GoogleTagGateway list enumerates configured zones","status":"skipped","title":"list enumerates configured zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379669469,"endTime":1782379672612.463,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/GoogleTagGateway/GoogleTagGateway.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create an activated certificate, deactivate in place, destroy","status":"failed","title":"create an activated certificate, deactivate in place, destroy","duration":9377.3414,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the validity period replaces the certificate","status":"skipped","title":"changing the validity period replaces the certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed gateway certificate","status":"failed","title":"list enumerates the deployed gateway certificate","duration":9213.234999999999,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379538388,"endTime":1782379547765.3413,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Certificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"manage activityLog and protocolDetection, then restore on destroy","status":"failed","title":"manage activityLog and protocolDetection, then restore on destroy","duration":23684.3321,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account Gateway configuration singleton","status":"failed","title":"list returns the account Gateway configuration singleton","duration":23609.6244,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379569326,"endTime":1782379593010.332,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Configuration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy a DOMAIN list","status":"failed","title":"create, verify, and destroy a DOMAIN list","duration":23532.91,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update items, description, and name in place","status":"failed","title":"update items, description, and name in place","duration":23589.7418,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the type replaces the list","status":"failed","title":"changing the type replaces the list","duration":23511.8269,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379504689,"endTime":1782379528279.7417,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/List.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy a location","status":"failed","title":"create, verify, and destroy a location","duration":23855.7669,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name and ecsSupport in place (same id)","status":"failed","title":"update name and ecsSupport in place (same id)","duration":23835.3322,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":23815.1482,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates deployed gateway locations","status":"failed","title":"list enumerates deployed gateway locations","duration":23826.6912,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379550748,"endTime":1782379574603.7668,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Location.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"manage logging settings, update in place, restore on destroy","status":"failed","title":"manage logging settings, update in place, restore on destroy","duration":23581.3454,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account's Gateway logging singleton","status":"failed","title":"list returns the account's Gateway logging singleton","duration":23622.6487,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379557229,"endTime":1782379580853.6487,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Logging.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, and destroy an identity-kind proxy endpoint","status":"failed","title":"create, update, and destroy an identity-kind proxy endpoint","duration":23676.9335,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"ip-kind endpoints surface the typed entitlement error","status":"failed","title":"ip-kind endpoints surface the typed entitlement error","duration":23732.03,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed proxy endpoint","status":"failed","title":"list enumerates the deployed proxy endpoint","duration":23749.632700000002,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379476385,"endTime":1782379500136.6328,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/ProxyEndpoint.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed Gateway rule","status":"failed","title":"list enumerates the deployed Gateway rule","duration":25308.841199999995,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379536068,"endTime":1782379561376.8413,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Gateway/Rule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an HTTP health check with default name","status":"failed","title":"create and delete an HTTP health check with default name","duration":2861.2703999999976,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Healthcheck/Healthcheck.test.ts:39:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable props in place (same id), then no-op redeploy","status":"failed","title":"update mutable props in place (same id), then no-op redeploy","duration":2813.346999999998,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Healthcheck/Healthcheck.test.ts:39:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing type HTTP→TCP updates in place; delete is idempotent","status":"failed","title":"changing type HTTP→TCP updates in place; delete is idempotent","duration":3011.4002999999975,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Healthcheck/Healthcheck.test.ts:39:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing check errors without adopt, takes over with adopt(true)","status":"failed","title":"adoption — existing check errors without adopt, takes over with adopt(true)","duration":3148.8587000000007,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Healthcheck/Healthcheck.test.ts:39:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed health check across zones","status":"failed","title":"list enumerates the deployed health check across zones","duration":3128.721700000002,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Healthcheck/Healthcheck.test.ts:39:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379704949,"endTime":1782379708100.8586,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Healthcheck/Healthcheck.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"lists overrides and surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","status":"failed","title":"lists overrides and surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","duration":1190.6389999999992,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/HostnameTlsSetting/HostnameTlsSetting.test.ts:35:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates per-hostname TLS overrides","status":"passed","title":"list enumerates per-hostname TLS overrides","duration":1234.4867000000013,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a min_tls_version override","status":"skipped","title":"create, update in place, and destroy a min_tls_version override","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379665178,"endTime":1782379666413.4868,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/HostnameTlsSetting/HostnameTlsSetting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete hyperdrive with default props","status":"passed","title":"create and delete hyperdrive with default props","duration":9888.170999999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete hyperdrive","status":"passed","title":"create, update, delete hyperdrive","duration":10471.342999999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed hyperdrive","status":"passed","title":"list enumerates the deployed hyperdrive","duration":11976.152599999998,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379728382,"endTime":1782379740360.1526,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Hyperdrive/Hyperdrive.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, update name+scope in place, destroy","status":"failed","title":"create, verify out-of-band, update name+scope in place, destroy","duration":596.4431999999997,"failureMessages":["Error: Please wait and consider throttling your request speed\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/Zone/lookup.ts:88:9)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed resource group","status":"failed","title":"list enumerates the deployed resource group","duration":40031.297600000005,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379597860,"endTime":1782379637893.2976,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Iam/ResourceGroup.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create with a policy, verify out-of-band, update policies in place, destroy","status":"failed","title":"create with a policy, verify out-of-band, update policies in place, destroy","duration":49831.1995,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed user group","status":"failed","title":"list enumerates the deployed user group","duration":21025.4286,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379530434,"endTime":1782379580265.1995,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Iam/UserGroup.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, replace when the user group changes, destroy","status":"failed","title":"create, verify out-of-band, replace when the user group changes, destroy","duration":1761.0028999999995,"failureMessages":["Error: no accepted account members found\n at Object.~effect/Effect/successCont (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Iam/UserGroupMembership.test.ts:32:22)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates memberships across all user groups in the account","status":"failed","title":"list enumerates memberships across all user groups in the account","duration":1733.7996999999996,"failureMessages":["Error: no accepted account members found\n at Object.~effect/Effect/successCont (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Iam/UserGroupMembership.test.ts:32:22)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379672531,"endTime":1782379674292.003,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Iam/UserGroupMembership.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed IndicatorFeedsNotEntitled error","status":"failed","title":"unentitled accounts surface the typed IndicatorFeedsNotEntitled error","duration":24393.290499999996,"failureMessages":["AssertionError: expected 'Forbidden' to deeply equal 'IndicatorFeedsNotEntitled'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Intel/IndicatorFeed.test.ts:68:26)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of indicator feeds","status":"passed","title":"list returns a well-typed array of indicator feeds","duration":24463.4029,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed indicator feed","status":"skipped","title":"list enumerates the deployed indicator feed","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create (or adopt), verify out-of-band, update in place, destroy","status":"skipped","title":"create (or adopt), verify out-of-band, update in place, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"uploads a STIX2 snapshot and grants/revokes a consumer permission","status":"skipped","title":"uploads a STIX2 snapshot and grants/revokes a consumer permission","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379599844,"endTime":1782379624308.4028,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Intel/IndicatorFeed.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns [] for the non-listable IndicatorFeedPermission","status":"passed","title":"list returns [] for the non-listable IndicatorFeedPermission","duration":288.0797999999995,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379737728,"endTime":1782379738016.0798,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Intel/IndicatorFeedPermission.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"async worker reads image info via env Images binding","status":"skipped","title":"async worker reads image info via env Images binding","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker reads image info via yield* Images","status":"skipped","title":"effect worker reads image info via yield* Images","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Images/Images.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the account's signing keys","status":"failed","title":"list enumerates the account's signing keys","duration":23625.928399999997,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"surfaces the typed ImagesAccessNotEnabled error on unentitled accounts","status":"failed","title":"surfaces the typed ImagesAccessNotEnabled error on unentitled accounts","duration":23660.522,"failureMessages":["AssertionError: expected 'TooManyRequests' to deeply equal 'ImagesAccessNotEnabled'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Images/SigningKey.test.ts:86:26)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create a signing key, no-op redeploy keeps the value, delete","status":"skipped","title":"create a signing key, no-op redeploy keeps the value, delete","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379557938,"endTime":1782379581600.522,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Images/SigningKey.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a variant","status":"failed","title":"create, update in place, and delete a variant","duration":24251.748700000004,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replace a variant when the name changes","status":"failed","title":"replace a variant when the name changes","duration":24217.0156,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed variant","status":"skipped","title":"list enumerates the deployed variant","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379593010,"endTime":1782379617261.7488,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Images/Variant.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed KeylessSslNotAvailable error on non-Enterprise zones","status":"failed","title":"surfaces the typed KeylessSslNotAvailable error on non-Enterprise zones","duration":220.2505000000001,"failureMessages":["Error: Please wait and consider throttling your request speed\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/Zone/lookup.ts:88:9)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates keyless certificates across zones","status":"passed","title":"list enumerates keyless certificates across zones","duration":8795.980200000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed keyless certificate","status":"skipped","title":"list includes a deployed keyless certificate","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates, updates in place, replaces on certificate change, and destroys","status":"skipped","title":"creates, updates in place, replaces on certificate change, and destroys","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379522674,"endTime":1782379531470.9802,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/KeylessCertificate/KeylessCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete namespace with default props","status":"passed","title":"create and delete namespace with default props","duration":2026.7698999999993,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete namespace","status":"passed","title":"create, update, delete namespace","duration":3073.0918,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed namespace","status":"passed","title":"list enumerates the deployed namespace","duration":2714.072,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"existing namespace (matching title) is silently adopted without --adopt","status":"passed","title":"existing namespace (matching title) is silently adopted without --adopt","duration":3100.7564,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379534917,"endTime":1782379538019.7563,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/KV/Namespace.test.ts"},{"assertionResults":[{"ancestorTitles":["LeakedCredentialDetection"],"fullName":"LeakedCredentialDetection list enumerates custom detections across all zones","status":"failed","title":"list enumerates custom detections across all zones","duration":4125.1443,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LeakedCredentialCheck/Detection.test.ts:34:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379688327,"endTime":1782379692452.1443,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LeakedCredentialCheck/Detection.test.ts"},{"assertionResults":[{"ancestorTitles":["LeakedCredentialCheck"],"fullName":"LeakedCredentialCheck enables leaked credential checks and restores the baseline on destroy","status":"failed","title":"enables leaked credential checks and restores the baseline on destroy","duration":2122.0190000000002,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LeakedCredentialCheck/LeakedCredentialCheck.test.ts:35:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["LeakedCredentialCheck"],"fullName":"LeakedCredentialCheck surfaces the typed DetectionQuotaExceeded error on unentitled zones","status":"failed","title":"surfaces the typed DetectionQuotaExceeded error on unentitled zones","duration":986.2455000000009,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LeakedCredentialCheck/LeakedCredentialCheck.test.ts:35:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["LeakedCredentialCheck"],"fullName":"LeakedCredentialCheck list enumerates the check across all zones","status":"failed","title":"list enumerates the check across all zones","duration":1218.4746000000014,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LeakedCredentialCheck/LeakedCredentialCheck.test.ts:35:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["LeakedCredentialCheck"],"fullName":"LeakedCredentialCheck creates, updates, and destroys a custom detection","status":"skipped","title":"creates, updates, and destroys a custom detection","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379702905,"endTime":1782379707232.4746,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LeakedCredentialCheck/LeakedCredentialCheck.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed LoadBalancingNotEnabledForZone error without the LB subscription","status":"failed","title":"surfaces the typed LoadBalancingNotEnabledForZone error without the LB subscription","duration":1063.1336999999985,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/LoadBalancer.test.ts:39:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a load balancer","status":"skipped","title":"create, update in place, and destroy a load balancer","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of load balancers","status":"passed","title":"list returns a well-typed array of load balancers","duration":1027.0951999999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed load balancer","status":"skipped","title":"list enumerates the deployed load balancer","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379707774,"endTime":1782379708837.1338,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/LoadBalancer.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed MonitorIntervalOutOfRange error without the LB subscription","status":"failed","title":"surfaces the typed MonitorIntervalOutOfRange error without the LB subscription","duration":23580.642600000003,"failureMessages":["AssertionError: expected 'TooManyRequests' to deeply equal 'MonitorIntervalOutOfRange'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/Monitor.test.ts:84:26)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a monitor","status":"skipped","title":"create, update in place, and destroy a monitor","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns an array of monitor attributes","status":"failed","title":"list returns an array of monitor attributes","duration":23531.7362,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed monitor","status":"skipped","title":"list enumerates the deployed monitor","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379578614,"endTime":1782379602194.6426,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/Monitor.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed MonitorGroupsNotEnabled error on non-Enterprise accounts","status":"failed","title":"surfaces the typed MonitorGroupsNotEnabled error on non-Enterprise accounts","duration":52970.44,"failureMessages":["AssertionError: expected 'CloudflareHttpError' to deeply equal 'MonitorGroupsNotEnabled'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/MonitorGroup.test.ts:92:26)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns an array of monitor groups","status":"failed","title":"list returns an array of monitor groups","duration":24950.8408,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed monitor group","status":"skipped","title":"list enumerates the deployed monitor group","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a monitor group","status":"skipped","title":"create, update in place, and destroy a monitor group","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379588925,"endTime":1782379641895.44,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/MonitorGroup.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed PoolAccessFailed error without the LB subscription","status":"failed","title":"surfaces the typed PoolAccessFailed error without the LB subscription","duration":23711.878899999996,"failureMessages":["AssertionError: expected 'TooManyRequests' to deeply equal 'PoolAccessFailed'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/Pool.test.ts:81:26)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a health-checked pool","status":"skipped","title":"create, update in place, and destroy a health-checked pool","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed pool","status":"skipped","title":"list enumerates the deployed pool","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379557725,"endTime":1782379581436.879,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/Pool.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"diag list","status":"failed","title":"diag list","duration":24117.1141,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379594914,"endTime":1782379619031.114,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LoadBalancer/_diag.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, and delete an account-scoped job pushing to R2","status":"failed","title":"create, update, and delete an account-scoped job pushing to R2","duration":2971.8652,"failureMessages":["UnknownCloudflareError: Invalid API Token\n at Object.1000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:122:9)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed Logpush job","status":"failed","title":"list enumerates the deployed Logpush job","duration":3332.148500000003,"failureMessages":["UnknownCloudflareError: Invalid API Token\n at Object.1000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:122:9)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the dataset triggers a replacement","status":"skipped","title":"changing the dataset triggers a replacement","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379668390,"endTime":1782379671724.1484,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Logpush/Job.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed LogsControlNotAuthorized error on unentitled accounts","status":"failed","title":"surfaces the typed LogsControlNotAuthorized error on unentitled accounts","duration":23925.6112,"failureMessages":["AssertionError: expected 'Forbidden' to deeply equal 'LogsControlNotAuthorized'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LogsControl/CmbConfig.test.ts:52:30)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, verify out-of-band, update in place, destroy, and wait until gone","status":"skipped","title":"create, verify out-of-band, update in place, destroy, and wait until gone","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list tolerates the typed LogsControlNotAuthorized error on unentitled accounts","status":"failed","title":"list tolerates the typed LogsControlNotAuthorized error on unentitled accounts","duration":23954.8089,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the account CMB config singleton","status":"skipped","title":"list enumerates the account CMB config singleton","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379517135,"endTime":1782379541090.8088,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LogsControl/CmbConfig.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed LogsControlNotAuthorized error on unentitled zones","status":"failed","title":"surfaces the typed LogsControlNotAuthorized error on unentitled zones","duration":991.0766000000003,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LogsControl/RetentionFlag.test.ts:27:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"pins the retention flag, updates in place, and restores on destroy","status":"skipped","title":"pins the retention flag, updates in place, and restores on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the retention flag across all zones","status":"passed","title":"list enumerates the retention flag across all zones","duration":1034.8053999999975,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list contains the entitled test zone's retention flag","status":"skipped","title":"list contains the entitled test zone's retention flag","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379665414,"endTime":1782379666450.8054,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/LogsControl/RetentionFlag.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed FeatureNotEnabled error","status":"failed","title":"unentitled accounts surface the typed FeatureNotEnabled error","duration":1570.3829000000005,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a sync, updates mutable props in place, and destroys it","status":"skipped","title":"creates a sync, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account catalog syncs","status":"failed","title":"list enumerates account catalog syncs","duration":1470.5300000000007,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the sync when destinationType changes","status":"skipped","title":"replaces the sync when destinationType changes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379546605,"endTime":1782379548175.3828,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicCloudNetworking/CatalogSync.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed FeatureNotEnabled error","status":"failed","title":"unentitled accounts surface the typed FeatureNotEnabled error","duration":2803.2921000000006,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of integrations","status":"failed","title":"list returns a well-typed array of integrations","duration":2818.1677999999993,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed integration","status":"skipped","title":"list enumerates the deployed integration","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"registers an AWS integration, updates it in place, and destroys it","status":"skipped","title":"registers an AWS integration, updates it in place, and destroys it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the integration when cloudType changes","status":"skipped","title":"replaces the integration when cloudType changes","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379545416,"endTime":1782379548236.1677,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicCloudNetworking/CloudIntegration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed FeatureNotEnabled error","status":"failed","title":"unentitled accounts surface the typed FeatureNotEnabled error","duration":9543.2287,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an on-ramp, updates mutable props in place, and destroys it","status":"skipped","title":"creates an on-ramp, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns on-ramps or a typed [] when unentitled","status":"failed","title":"list returns on-ramps or a typed [] when unentitled","duration":24726.765099999997,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed on-ramp","status":"skipped","title":"list enumerates the deployed on-ramp","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379540574,"endTime":1782379565302.7651,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicCloudNetworking/OnRamp.test.ts"},{"assertionResults":[{"ancestorTitles":["MagicNetworkMonitoring.Config list"],"fullName":"MagicNetworkMonitoring.Config list list enumerates the account MNM config","status":"failed","title":"list enumerates the account MNM config","duration":50793.41339999999,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379416374,"endTime":1782379467167.4133,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/Config.test.ts"},{"assertionResults":[{"ancestorTitles":["MagicNetworkMonitoring"],"fullName":"MagicNetworkMonitoring creates, updates in place, and deletes the account config","status":"failed","title":"creates, updates in place, and deletes the account config","duration":51685.6572,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":["MagicNetworkMonitoring"],"fullName":"MagicNetworkMonitoring creates a threshold rule, updates it in place, and replaces on type change","status":"failed","title":"creates a threshold rule, updates it in place, and replaces on type change","duration":23333.420599999998,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379416355,"endTime":1782379491374.4207,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/MagicNetworkMonitoring.test.ts"},{"assertionResults":[{"ancestorTitles":["MagicNetworkMonitoring.Rule"],"fullName":"MagicNetworkMonitoring.Rule list enumerates the deployed rule","status":"failed","title":"list enumerates the deployed rule","duration":52176.62670000001,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379416263,"endTime":1782379468439.6267,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/Rule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicWanUnauthorized error","status":"failed","title":"unentitled accounts surface the typed MagicWanUnauthorized error","duration":24443.805800000002,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a custom app, updates mutable props in place, and destroys it","status":"skipped","title":"creates a custom app, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account apps (well-typed [] when unentitled)","status":"failed","title":"list enumerates account apps (well-typed [] when unentitled)","duration":24469.2211,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed custom app","status":"skipped","title":"list includes a deployed custom app","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379448294,"endTime":1782379472764.2212,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/App.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicTransitNotOnboarded error","status":"passed","title":"unentitled accounts surface the typed MagicTransitNotOnboarded error","duration":74411.7149,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed GRE tunnels","status":"passed","title":"list enumerates the deployed GRE tunnels","duration":24872.6881,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a GRE tunnel, updates mutable props in place, and destroys it","status":"skipped","title":"creates a GRE tunnel, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379594770,"endTime":1782379669181.7148,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/GreTunnel.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicTransitNotOnboarded error","status":"failed","title":"unentitled accounts surface the typed MagicTransitNotOnboarded error","duration":50092.6552,"failureMessages":["AssertionError: expected [ 'MagicTransitNotOnboarded', …(1) ] to include 'CloudflareHttpError'\n at Proxy. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+expect@4.1.9/node_modules/@vitest/expect/dist/index.js:1319:15)\n at Proxy. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+expect@4.1.9/node_modules/@vitest/expect/dist/index.js:1156:15)\n at Proxy.methodWrapper (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/chai@6.2.2/node_modules/chai/index.js:1700:25)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/IpsecTunnel.test.ts:85:57)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account IPsec tunnels (read-only [] when unentitled)","status":"passed","title":"list enumerates account IPsec tunnels (read-only [] when unentitled)","duration":24453.232600000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates an IPsec tunnel, updates mutable props in place, and destroys it","status":"skipped","title":"creates an IPsec tunnel, updates mutable props in place, and destroys it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379591686,"endTime":1782379641778.6553,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/IpsecTunnel.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicWanUnauthorized error","status":"failed","title":"unentitled accounts surface the typed MagicWanUnauthorized error","duration":50897.5556,"failureMessages":["AssertionError: expected [ 'MagicWanUnauthorized', 'Forbidden' ] to include 'CloudflareHttpError'\n at Proxy. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+expect@4.1.9/node_modules/@vitest/expect/dist/index.js:1319:15)\n at Proxy. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+expect@4.1.9/node_modules/@vitest/expect/dist/index.js:1156:15)\n at Proxy.methodWrapper (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/chai@6.2.2/node_modules/chai/index.js:1700:25)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/Site.test.ts:74:53)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account Magic WAN sites","status":"failed","title":"list enumerates account Magic WAN sites","duration":24933.9847,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a site with WAN, LAN, and ACL, updates in place, and destroys","status":"skipped","title":"creates a site with WAN, LAN, and ACL, updates in place, and destroys","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379593787,"endTime":1782379644684.5557,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/Site.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed site ACLs","status":"passed","title":"list enumerates the deployed site ACLs","duration":26285.6921,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Magic WAN error for ACL listing","status":"failed","title":"unentitled accounts surface the typed Magic WAN error for ACL listing","duration":51477.167799999996,"failureMessages":["AssertionError: expected [ 'MagicWanUnauthorized', 'Forbidden' ] to include 'TooManyRequests'\n at Proxy. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+expect@4.1.9/node_modules/@vitest/expect/dist/index.js:1319:15)\n at Proxy. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+expect@4.1.9/node_modules/@vitest/expect/dist/index.js:1156:15)\n at Proxy.methodWrapper (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/chai@6.2.2/node_modules/chai/index.js:1700:25)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/SiteAcl.test.ts:122:53)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15"],"meta":{},"tags":[]}],"startTime":1782379416279,"endTime":1782379467759.1677,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/SiteAcl.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a well-typed array (empty on unentitled accounts)","status":"failed","title":"list returns a well-typed array (empty on unentitled accounts)","duration":23820.6689,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed Magic WAN site LANs","status":"skipped","title":"list enumerates the deployed Magic WAN site LANs","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379567445,"endTime":1782379591265.669,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/SiteLan.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a well-typed array of site WANs","status":"passed","title":"list returns a well-typed array of site WANs","duration":26392.4542,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed site WAN","status":"skipped","title":"list enumerates the deployed site WAN","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379722547,"endTime":1782379748939.454,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/SiteWan.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed MagicTransitNotOnboarded error","status":"failed","title":"unentitled accounts surface the typed MagicTransitNotOnboarded error","duration":48911.6486,"failureMessages":["AssertionError: expected [ 'MagicTransitNotOnboarded', …(1) ] to include 'TooManyRequests'\n at Proxy. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+expect@4.1.9/node_modules/@vitest/expect/dist/index.js:1319:15)\n at Proxy. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+expect@4.1.9/node_modules/@vitest/expect/dist/index.js:1156:15)\n at Proxy.methodWrapper (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/chai@6.2.2/node_modules/chai/index.js:1700:25)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/StaticRoute.test.ts:67:57)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns a well-typed array of routes","status":"failed","title":"list returns a well-typed array of routes","duration":23943.6005,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed static route","status":"skipped","title":"list enumerates the deployed static route","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"routes a prefix over a GRE tunnel, updates in place, and destroys","status":"skipped","title":"routes a prefix over a GRE tunnel, updates in place, and destroys","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379517962,"endTime":1782379566873.6487,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MagicTransit/StaticRoute.test.ts"},{"assertionResults":[{"ancestorTitles":["ManagedTransforms"],"fullName":"ManagedTransforms manages a named transform, updates in place, and restores it on destroy","status":"failed","title":"manages a named transform, updates in place, and restores it on destroy","duration":1694.029599999998,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ManagedTransforms/ManagedTransforms.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["ManagedTransforms"],"fullName":"ManagedTransforms destroy restores a transform that was enabled before management","status":"failed","title":"destroy restores a transform that was enabled before management","duration":517.4056000000019,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ManagedTransforms/ManagedTransforms.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["ManagedTransforms"],"fullName":"ManagedTransforms deploy with no transforms named adopts the singleton without writing","status":"failed","title":"deploy with no transforms named adopts the singleton without writing","duration":256.02240000000165,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ManagedTransforms/ManagedTransforms.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["ManagedTransforms"],"fullName":"ManagedTransforms list enumerates managed transforms across all zones","status":"skipped","title":"list enumerates managed transforms across all zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379680175,"endTime":1782379682643.0225,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ManagedTransforms/ManagedTransforms.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins the default ASN, updates in place, and restores the original on destroy","status":"failed","title":"pins the default ASN, updates in place, and restores the original on destroy","duration":25484.7239,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the CNI settings singleton","status":"failed","title":"list enumerates the CNI settings singleton","duration":25015.038099999998,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379532167,"endTime":1782379557651.7239,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/NetworkInterconnects/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":["MtlsCertificate"],"fullName":"MtlsCertificate create and delete a CA certificate","status":"failed","title":"create and delete a CA certificate","duration":27414.874599999996,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":["MtlsCertificate"],"fullName":"MtlsCertificate create and delete a leaf certificate with private key","status":"failed","title":"create and delete a leaf certificate with private key","duration":24368.2325,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":["MtlsCertificate"],"fullName":"MtlsCertificate replaces the certificate when the PEM changes","status":"failed","title":"replaces the certificate when the PEM changes","duration":23361.913700000005,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":["MtlsCertificate"],"fullName":"MtlsCertificate list enumerates the deployed mTLS certificate","status":"failed","title":"list enumerates the deployed mTLS certificate","duration":23421.229300000006,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379416482,"endTime":1782379515047.2292,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/MtlsCertificate/MtlsCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden error","status":"failed","title":"unentitled accounts surface the typed Forbidden error","duration":952.3853999999992,"failureMessages":["Unauthorized: Authentication failed (status: 400)\n at Object.9106 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:89:22)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list either enumerates organizations or tolerates the unentitled account","status":"failed","title":"list either enumerates organizations or tolerates the unentitled account","duration":893.0819999999985,"failureMessages":["Unauthorized: Authentication failed (status: 400)\n at Object.9106 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:89:22)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed organization","status":"skipped","title":"list enumerates the deployed organization","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, verify out-of-band, update in place, destroy","status":"skipped","title":"create, verify out-of-band, update in place, destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379708150,"endTime":1782379709102.3855,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Organization/Organization.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"issue, verify, and revoke a certificate","status":"failed","title":"issue, verify, and revoke a certificate","duration":3733.7765,"failureMessages":["HostnameNotAuthorized: Failed to validate requested hostname origin.alchemy-test-2.us: This zone is either not part of your account, or you do not have access to it. Please contact support if using a multi-user organization.\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates issued certificates","status":"failed","title":"list enumerates issued certificates","duration":3559.3993999999984,"failureMessages":["HostnameNotAuthorized: Failed to validate requested hostname origin.alchemy-test-2.us: This zone is either not part of your account, or you do not have access to it. Please contact support if using a multi-user organization.\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replacement on requestedValidity change","status":"failed","title":"replacement on requestedValidity change","duration":4040.975699999999,"failureMessages":["HostnameNotAuthorized: Failed to validate requested hostname origin.alchemy-test-2.us: This zone is either not part of your account, or you do not have access to it. Please contact support if using a multi-user organization.\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replacement on hostnames change","status":"failed","title":"replacement on hostnames change","duration":3056.9576000000015,"failureMessages":["HostnameNotAuthorized: Failed to validate requested hostname origin.alchemy-test-2.us: This zone is either not part of your account, or you do not have access to it. Please contact support if using a multi-user organization.\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]}],"startTime":1782379709946,"endTime":1782379713989.9756,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginCaCertificate/OriginCaCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":["OriginPostQuantumEncryption"],"fullName":"OriginPostQuantumEncryption pins the setting and restores the original value on destroy","status":"failed","title":"pins the setting and restores the original value on destroy","duration":2543.9231,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginPostQuantumEncryption/OriginPostQuantumEncryption.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["OriginPostQuantumEncryption"],"fullName":"OriginPostQuantumEncryption updates the value in place and keeps the captured initial value","status":"failed","title":"updates the value in place and keeps the captured initial value","duration":304.21710000000166,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginPostQuantumEncryption/OriginPostQuantumEncryption.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["OriginPostQuantumEncryption"],"fullName":"OriginPostQuantumEncryption no-op redeploy converges without changing the setting","status":"failed","title":"no-op redeploy converges without changing the setting","duration":330.0939999999973,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginPostQuantumEncryption/OriginPostQuantumEncryption.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["OriginPostQuantumEncryption"],"fullName":"OriginPostQuantumEncryption list enumerates the setting across all zones","status":"failed","title":"list enumerates the setting across all zones","duration":270.46970000000147,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginPostQuantumEncryption/OriginPostQuantumEncryption.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379705549,"endTime":1782379708998.4697,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginPostQuantumEncryption/OriginPostQuantumEncryption.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, and destroy a page rule","status":"failed","title":"create, verify out-of-band, and destroy a page rule","duration":2132.2161000000015,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/PageRule/PageRule.test.ts:39:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating actions, status, priority and target syncs in place","status":"failed","title":"updating actions, status, priority and target syncs in place","duration":1973.2299999999996,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/PageRule/PageRule.test.ts:39:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing rule errors without adopt, takes over with adopt(true)","status":"failed","title":"adoption — existing rule errors without adopt, takes over with adopt(true)","duration":2038.0506999999998,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/PageRule/PageRule.test.ts:39:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed page rule","status":"failed","title":"list enumerates the deployed page rule","duration":2063.1837000000014,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/PageRule/PageRule.test.ts:39:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379695823,"endTime":1782379697955.216,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/PageRule/PageRule.test.ts"},{"assertionResults":[{"ancestorTitles":["OriginTlsClientAuthCertificate"],"fullName":"OriginTlsClientAuthCertificate uploads and deletes a zone client certificate","status":"failed","title":"uploads and deletes a zone client certificate","duration":2167.045900000001,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/Certificate.test.ts:37:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["OriginTlsClientAuthCertificate"],"fullName":"OriginTlsClientAuthCertificate replaces the certificate when the PEM changes","status":"failed","title":"replaces the certificate when the PEM changes","duration":850.7065000000002,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/Certificate.test.ts:37:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["OriginTlsClientAuthCertificate"],"fullName":"OriginTlsClientAuthCertificate list enumerates the deployed zone client certificate","status":"failed","title":"list enumerates the deployed zone client certificate","duration":331.32670000000144,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/Certificate.test.ts:37:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379704952,"endTime":1782379708301.3267,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/Certificate.test.ts"},{"assertionResults":[{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation list returns [] for the non-listable association","status":"passed","title":"list returns [] for the non-listable association","duration":722.5820000000022,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation associates a hostname, updates cert and enablement in place, voids on destroy","status":"failed","title":"associates a hostname, updates cert and enablement in place, voids on destroy","duration":1559.7086999999992,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/HostnameAssociation.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["HostnameAssociation"],"fullName":"HostnameAssociation replaces the association when the hostname changes","status":"failed","title":"replaces the association when the hostname changes","duration":346.2626999999993,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/HostnameAssociation.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379714464,"endTime":1782379717452.2627,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/HostnameAssociation.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"uploads and deletes a hostname client certificate","status":"failed","title":"uploads and deletes a hostname client certificate","duration":2062.5769,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/HostnameCertificate.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces the hostname certificate when the PEM changes","status":"failed","title":"replaces the hostname certificate when the PEM changes","duration":2231.8472999999976,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/HostnameCertificate.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed hostname certificate","status":"failed","title":"list enumerates the deployed hostname certificate","duration":1907.4621000000006,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/HostnameCertificate.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379642936,"endTime":1782379645169.8474,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/HostnameCertificate.test.ts"},{"assertionResults":[{"ancestorTitles":["Setting"],"fullName":"Setting enables AOP and restores the original value on destroy","status":"failed","title":"enables AOP and restores the original value on destroy","duration":997.3574999999983,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/Setting.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["Setting"],"fullName":"Setting updates the enabled flag in place","status":"failed","title":"updates the enabled flag in place","duration":460.1820999999982,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/Setting.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["Setting"],"fullName":"Setting list enumerates the setting across all zones","status":"failed","title":"list enumerates the setting across all zones","duration":371.9050000000025,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/Setting.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379671592,"endTime":1782379673422.905,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/Setting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed PolicyQuotaExceeded error on unentitled zones","status":"failed","title":"surfaces the typed PolicyQuotaExceeded error on unentitled zones","duration":2624.9972000000016,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/PageShield/Policy.test.ts:34:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates Page Shield policies across all zones","status":"passed","title":"list enumerates Page Shield policies across all zones","duration":2807.320300000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates, updates in place, and deletes a CSP policy","status":"skipped","title":"creates, updates in place, and deletes a CSP policy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379678726,"endTime":1782379681535.3203,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/PageShield/Policy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"enables Page Shield, updates in place, and restores the baseline on destroy","status":"failed","title":"enables Page Shield, updates in place, and restores the baseline on destroy","duration":3284.4768000000004,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/PageShield/Settings.test.ts:27:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"surfaces the typed NotEntitled error for plan-gated flags","status":"failed","title":"surfaces the typed NotEntitled error for plan-gated flags","duration":3071.2085000000006,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/PageShield/Settings.test.ts:27:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates Page Shield settings across all zones","status":"failed","title":"list enumerates Page Shield settings across all zones","duration":3328.5419,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/PageShield/Settings.test.ts:27:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379717954,"endTime":1782379721284.542,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/PageShield/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, replace on branch change, and destroy a deployment","status":"failed","title":"create, replace on branch change, and destroy a deployment","duration":120097.0058,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Deployment.test.ts:92:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates deployments across Pages projects","status":"skipped","title":"list enumerates deployments across Pages projects","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379416419,"endTime":1782379536516.0059,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Deployment.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"attach and detach a custom domain","status":"failed","title":"attach and detach a custom domain","duration":120055.8795,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Domain.test.ts:99:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the domain name triggers replacement","status":"failed","title":"changing the domain name triggers replacement","duration":120071.7102,"failureMessages":["Error: STACK_TRACE_ERROR\n at task (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1784:27)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1817:16)\n at Object. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:1563:28)\n at chain (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@vitest+runner@4.1.9/node_modules/@vitest/runner/dist/chunk-artifact.js:599:14)\n at Proxy.f (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/@effect+vitest@4.0.0-beta.84+c97c99a2a4ebebdd/node_modules/@effect/vitest/dist/internal/internal.js:69:38)\n at test.provider (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Test/Vitest.ts:154:8)\n at D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Domain.test.ts:166:6\n at processTicksAndRejections (node:internal/process/task_queues:104:5)\n at VitestModuleEvaluator._runInlinedModule (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vitest@4.1.9+2d161875cf265f3d/node_modules/vitest/dist/module-evaluator.js:206:4)\n at VitestModuleRunner.directRequest (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/vite@8.0.16+3502fc6112a1ac00/node_modules/vite/dist/node/module-runner.js:1259:59)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates domains across all Pages projects","status":"skipped","title":"list enumerates domains across all Pages projects","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379416255,"endTime":1782379536329.7102,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Domain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a project with generated name","status":"failed","title":"create and delete a project with generated name","duration":26368.007299999997,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable props in place (same project id)","status":"failed","title":"update mutable props in place (same project id)","duration":50738.06809999999,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed project","status":"failed","title":"list enumerates the deployed project","duration":50579.2191,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the name triggers replacement","status":"failed","title":"changing the name triggers replacement","duration":50596.0012,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379416360,"endTime":1782379467100.068,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pages/Project.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"legacy pipeline: create, in-place update, replace on name change","status":"failed","title":"legacy pipeline: create, in-place update, replace on name change","duration":1942.4589000000014,"failureMessages":["UnknownCloudflareError: Invalid API Token\n at Object.1000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:122:9)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed legacy pipeline","status":"skipped","title":"list enumerates the deployed legacy pipeline","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379696007,"endTime":1782379697949.459,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/LegacyPipeline.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed pipeline","status":"failed","title":"list enumerates the deployed pipeline","duration":710.0171,"failureMessages":["UnknownCloudflareError: Invalid API Token\n at Object.1000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:122:9)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379531203,"endTime":1782379531913.017,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/Pipeline.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"stream: create with defaults, patch http in place, replace on schema change","status":"failed","title":"stream: create with defaults, patch http in place, replace on schema change","duration":23690.2278,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"end-to-end: stream → sql pipeline → r2 sink, replacement on sink change","status":"failed","title":"end-to-end: stream → sql pipeline → r2 sink, replacement on sink change","duration":23700.589099999997,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379554881,"endTime":1782379578582.589,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/Pipelines.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed sink","status":"failed","title":"list enumerates the deployed sink","duration":1191.9271000000008,"failureMessages":["UnknownCloudflareError: Invalid API Token\n at Object.1000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:122:9)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379622374,"endTime":1782379623565.927,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/Sink.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed pipeline stream","status":"failed","title":"list enumerates the deployed pipeline stream","duration":23635.0716,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379578614,"endTime":1782379602249.0715,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Pipelines/Stream.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete bucket with default props","status":"failed","title":"create and delete bucket with default props","duration":24636.061999999998,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update, delete bucket","status":"failed","title":"create, update, delete bucket","duration":24652.9185,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"existing bucket (matching name) is silently adopted without --adopt","status":"failed","title":"existing bucket (matching name) is silently adopted without --adopt","duration":24675.761399999996,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"destroying a bucket empties its objects first","status":"failed","title":"destroying a bucket empties its objects first","duration":24815.309,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"lifecycle rules are added, updated, and removed","status":"failed","title":"lifecycle rules are added, updated, and removed","duration":24834.6731,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379537541,"endTime":1782379562377.673,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/Bucket.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update rules in place, and delete","status":"failed","title":"create, update rules in place, and delete","duration":24222.2067,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the queue triggers a replacement","status":"failed","title":"changing the queue triggers a replacement","duration":24201.324700000005,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates deployed bucket event notifications","status":"failed","title":"list enumerates deployed bucket event notifications","duration":24329.9761,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379545662,"endTime":1782379569992.976,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/BucketEventNotification.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"reads the disabled baseline and surfaces typed errors without external creds","status":"failed","title":"reads the disabled baseline and surfaces typed errors without external creds","duration":23560.3973,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates buckets that have Sippy enabled","status":"failed","title":"list enumerates buckets that have Sippy enabled","duration":23579.5288,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a bucket with Sippy enabled","status":"skipped","title":"list includes a bucket with Sippy enabled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"enable, no-op redeploy, disable on destroy","status":"skipped","title":"enable, no-op redeploy, disable on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379558800,"endTime":1782379582380.5288,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/BucketSippy.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"creates, updates, and deletes a bucket custom domain","status":"failed","title":"creates, updates, and deletes a bucket custom domain","duration":23471.0721,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates, updates, and deletes a bucket with multiple custom domains","status":"failed","title":"creates, updates, and deletes a bucket with multiple custom domains","duration":23464.5234,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379495132,"endTime":1782379518603.072,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/CustomDomain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed R2 bucket","status":"failed","title":"list enumerates the deployed R2 bucket","duration":23618.5309,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379573362,"endTime":1782379596980.531,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2/R2Bucket.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"promotes a dev queue to a live queue on deploy","status":"passed","title":"promotes a dev queue to a live queue on deploy","duration":3820.8051000000014,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed queue","status":"passed","title":"list enumerates the deployed queue","duration":3925.5422,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"suppresses deletion of a dev-only queue","status":"passed","title":"suppresses deletion of a dev-only queue","duration":1449.2170000000006,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379731526,"endTime":1782379735453.5422,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/Queue.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update settings, replace script, delete","status":"failed","title":"create, update settings, replace script, delete","duration":11028.762700000001,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates consumer after out-of-band delete","status":"passed","title":"recreates consumer after out-of-band delete","duration":10238.062600000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopts existing consumer after local state loss","status":"passed","title":"adopts existing consumer after local state loss","duration":8771.315799999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"fails clearly when queue has consumer for different script","status":"failed","title":"fails clearly when queue has consumer for different script","duration":8820.4354,"failureMessages":["AssertionError: expected '{\"_id\":\"Exit\",\"_tag\":\"Failure\",\"cause…' to contain 'fails-clearly-when-queue-has-consumer…'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/QueueConsumer.test.ts:375:23)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at Array. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1972:15)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:425:25)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"suppresses deletion of a dev-only consumer","status":"passed","title":"suppresses deletion of a dev-only consumer","duration":2183.4357,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"promotes a dev consumer to a live consumer on deploy","status":"passed","title":"promotes a dev consumer to a live consumer on deploy","duration":9123.1315,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed consumer","status":"failed","title":"list enumerates the deployed consumer","duration":2941.3922999999995,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379629807,"endTime":1782379641521.3923,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/QueueConsumer.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"send → subscribe handler → DO state → polled by test client","status":"failed","title":"send → subscribe handler → DO state → polled by test client","duration":72630.41769999999,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379540709,"endTime":1782379613339.4177,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/RoundTrip.test.ts"},{"assertionResults":[{"ancestorTitles":["Subscription"],"fullName":"Subscription create r2 event subscription into a queue and destroy it","status":"failed","title":"create r2 event subscription into a queue and destroy it","duration":26301.495899999994,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":["Subscription"],"fullName":"Subscription update mutable props in place (same subscriptionId)","status":"passed","title":"update mutable props in place (same subscriptionId)","duration":4113.700500000006,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Subscription"],"fullName":"Subscription replaces the subscription when the source changes","status":"passed","title":"replaces the subscription when the source changes","duration":3686.935599999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Subscription"],"fullName":"Subscription list enumerates the deployed subscription","status":"passed","title":"list enumerates the deployed subscription","duration":2968.5674,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379416366,"endTime":1782379453436.5674,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/Subscription.test.ts"},{"assertionResults":[{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings passes scalar fields through unchanged","status":"passed","title":"passes scalar fields through unchanged","duration":11.627700000000004,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings converts maxWaitTime to whole milliseconds","status":"passed","title":"converts maxWaitTime to whole milliseconds","duration":9.965499999999338,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings rounds sub-millisecond maxWaitTime up","status":"passed","title":"rounds sub-millisecond maxWaitTime up","duration":9.730800000000272,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings converts retryDelay to whole seconds","status":"passed","title":"converts retryDelay to whole seconds","duration":9.615199999999277,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings rounds partial-second retryDelay up","status":"passed","title":"rounds partial-second retryDelay up","duration":9.519199999999728,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toConsumerSettings"],"fullName":"toConsumerSettings leaves missing time fields undefined","status":"passed","title":"leaves missing time fields undefined","duration":1.022700000000441,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379730810,"endTime":1782379730823.0227,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Queue/toConsumerSettings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"enable, sync maintenance config, register credential, destroy","status":"failed","title":"enable, sync maintenance config, register credential, destroy","duration":24779.214999999997,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed catalog","status":"failed","title":"list enumerates the deployed catalog","duration":24527.8828,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"re-enables after out-of-band disable","status":"failed","title":"re-enables after out-of-band disable","duration":24365.603199999998,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379545385,"endTime":1782379570164.215,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/R2DataCatalog/R2DataCatalog.test.ts"},{"assertionResults":[{"ancestorTitles":["async worker (env binding)"],"fullName":"async worker (env binding) throttles requests past the configured limit","status":"skipped","title":"throttles requests past the configured limit","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["async worker (env binding)"],"fullName":"async worker (env binding) tracks each key independently","status":"skipped","title":"tracks each key independently","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["effect worker (yield* RateLimit)"],"fullName":"effect worker (yield* RateLimit) throttles requests past the configured limit","status":"skipped","title":"throttles requests past the configured limit","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["effect worker (yield* RateLimit)"],"fullName":"effect worker (yield* RateLimit) tracks each key independently","status":"skipped","title":"tracks each key independently","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RateLimit/RateLimit.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden error","status":"failed","title":"unentitled accounts surface the typed Forbidden error","duration":23646.6951,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create (or adopt), verify out-of-band, destroy forgets but keeps the app","status":"failed","title":"create (or adopt), verify out-of-band, destroy forgets but keeps the app","duration":23698.8744,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed RealtimeKit app","status":"failed","title":"list enumerates the deployed RealtimeKit app","duration":23680.254399999998,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379557230,"endTime":1782379580929.8745,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RealtimeKit/App.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create with defaults, verify out-of-band, update in place, destroy","status":"passed","title":"create with defaults, verify out-of-band, update in place, destroy","duration":27914.376000000004,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed preset","status":"passed","title":"list enumerates the deployed preset","duration":27810.3418,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379724465,"endTime":1782379752379.376,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RealtimeKit/Preset.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify out-of-band, update in place, destroy","status":"passed","title":"create, verify out-of-band, update in place, destroy","duration":27416.293800000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates webhooks across the account's apps","status":"passed","title":"list enumerates webhooks across the account's apps","duration":52500.794200000004,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379719866,"endTime":1782379772368.7942,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RealtimeKit/Webhook.test.ts"},{"assertionResults":[{"ancestorTitles":["Domain"],"fullName":"Domain adopts a registered domain, no-op syncs, and never releases it on destroy","status":"failed","title":"adopts a registered domain, no-op syncs, and never releases it on destroy","duration":51451.37289999999,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":["Domain"],"fullName":"Domain updates settings in place and restores the baseline on destroy","status":"failed","title":"updates settings in place and restores the baseline on destroy","duration":23321.28480000001,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":["Domain"],"fullName":"Domain list enumerates registrar domains on the account","status":"failed","title":"list enumerates registrar domains on the account","duration":23327.6936,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379416347,"endTime":1782379514447.6936,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Registrar/Domain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"regional hostname lifecycle (typed error on unentitled zones)","status":"failed","title":"regional hostname lifecycle (typed error on unentitled zones)","duration":2387.9572000000007,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RegionalHostname/RegionalHostname.test.ts:33:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates regional hostnames across zones","status":"skipped","title":"list enumerates regional hostnames across zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379678125,"endTime":1782379680512.9573,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RegionalHostname/RegionalHostname.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"reads succeed and write-blocked accounts surface the typed Forbidden error","status":"failed","title":"reads succeed and write-blocked accounts surface the typed Forbidden error","duration":24307.8905,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the account's sent shares (typed Attributes), and includes a deployed share when entitled","status":"failed","title":"list enumerates the account's sent shares (typed Attributes), and includes a deployed share when entitled","duration":24345.067,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"creates a share with a gateway policy, renames in place, adds and removes a resource, and destroys","status":"skipped","title":"creates a share with a gateway policy, renames in place, adds and removes a resource, and destroys","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"manages standalone ShareResource and ShareRecipient on an existing share","status":"skipped","title":"manages standalone ShareResource and ShareRecipient on an existing share","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379598557,"endTime":1782379622904.067,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ResourceSharing/Share.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list() enumerates share recipients across the account's sent shares","status":"passed","title":"list() enumerates share recipients across the account's sent shares","duration":28288.552699999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list() includes a freshly deployed ShareRecipient","status":"skipped","title":"list() includes a freshly deployed ShareRecipient","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379722926,"endTime":1782379751214.5527,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ResourceSharing/ShareRecipient.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates share resources across the account's sent shares (typed Attributes), and includes a deployed entry when entitled","status":"failed","title":"list enumerates share resources across the account's sent shares (typed Attributes), and includes a deployed entry when entitled","duration":23577.759700000002,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379557130,"endTime":1782379580707.7598,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/ResourceSharing/ShareResource.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"unentitled accounts surface the typed Forbidden error","status":"passed","title":"unentitled accounts surface the typed Forbidden error","duration":26544.327200000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, toggle active in place, and destroy a risk scoring integration","status":"skipped","title":"create, toggle active in place, and destroy a risk scoring integration","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates risk scoring integrations","status":"passed","title":"list enumerates risk scoring integrations","duration":26581.190400000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a freshly deployed integration","status":"skipped","title":"list includes a freshly deployed integration","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379726811,"endTime":1782379753394.1904,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/RiskScoring/Integration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an ip list with default name","status":"failed","title":"create and delete an ip list with default name","duration":26357.4142,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update description and items in place (same listId)","status":"failed","title":"update description and items in place (same listId)","duration":26418.550099999993,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing kind replaces the list","status":"failed","title":"changing kind replaces the list","duration":26407.771200000003,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":26366.237299999993,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopts an existing list with the same name","status":"failed","title":"adopts an existing list with the same name","duration":52344.78319999999,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379416298,"endTime":1782379468645.7832,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Rules/List.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"account entrypoint lifecycle (typed PhaseNotEntitled on unentitled accounts)","status":"failed","title":"account entrypoint lifecycle (typed PhaseNotEntitled on unentitled accounts)","duration":51405.018,"failureMessages":["AssertionError: expected 'TooManyRequests' to deeply equal 'PhaseNotEntitled'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ruleset/AccountEntrypoint.test.ts:73:36)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:627:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the account phase entrypoints","status":"failed","title":"list enumerates the account phase entrypoints","duration":51002.537500000006,"failureMessages":["AssertionError: expected 'TooManyRequests' to deeply equal 'PhaseNotEntitled'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ruleset/AccountEntrypoint.test.ts:186:36)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379416252,"endTime":1782379467657.018,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ruleset/AccountEntrypoint.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"custom ruleset lifecycle (typed PhaseNotEntitled on unentitled accounts)","status":"failed","title":"custom ruleset lifecycle (typed PhaseNotEntitled on unentitled accounts)","duration":51142.28599999999,"failureMessages":["AssertionError: expected 'TooManyRequests' to deeply equal 'PhaseNotEntitled'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ruleset/CustomRuleset.test.ts:73:36)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:627:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed custom ruleset","status":"failed","title":"list enumerates the deployed custom ruleset","duration":51276.5315,"failureMessages":["AssertionError: expected 'TooManyRequests' to deeply equal 'PhaseNotEntitled'\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ruleset/CustomRuleset.test.ts:177:36)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:627:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379416267,"endTime":1782379467546.5315,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ruleset/CustomRuleset.test.ts"},{"assertionResults":[{"ancestorTitles":["Ruleset"],"fullName":"Ruleset creates, updates, and deletes a zone phase entrypoint ruleset","status":"failed","title":"creates, updates, and deletes a zone phase entrypoint ruleset","duration":2335.2704000000012,"failureMessages":["Forbidden: Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["Ruleset"],"fullName":"Ruleset creates and tears down a ruleset whose zone is provisioned in the same deploy","status":"failed","title":"creates and tears down a ruleset whose zone is provisioned in the same deploy","duration":814.8368999999984,"failureMessages":["Forbidden: Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["Ruleset"],"fullName":"Ruleset list enumerates the deployed zone phase entrypoint","status":"skipped","title":"list enumerates the deployed zone phase entrypoint","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379680006,"endTime":1782379683156.837,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ruleset/Ruleset.test.ts"},{"assertionResults":[{"ancestorTitles":["RumRule"],"fullName":"RumRule create, update in place, and delete a rule","status":"failed","title":"create, update in place, and delete a rule","duration":2427.3856000000014,"failureMessages":["Error: zone \"alchemy-test-3.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Rum/Rule.test.ts:157:11)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["RumRule"],"fullName":"RumRule list enumerates rules across all rulesets","status":"failed","title":"list enumerates rules across all rulesets","duration":282.79479999999967,"failureMessages":["Error: zone \"alchemy-test-3.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Rum/Rule.test.ts:245:11)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["RumRule"],"fullName":"RumRule recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":323.9712999999974,"failureMessages":["Error: zone \"alchemy-test-3.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Rum/Rule.test.ts:284:11)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379696310,"endTime":1782379699344.9712,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Rum/Rule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a host (gray-clouded) site","status":"failed","title":"create and delete a host (gray-clouded) site","duration":23397.298799999997,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable props in place (same siteTag)","status":"failed","title":"update mutable props in place (same siteTag)","duration":23449.9643,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"zone (orange-clouded) site with autoInstall, replaced on identity flip","status":"failed","title":"zone (orange-clouded) site with autoInstall, replaced on identity flip","duration":172.2415000000001,"failureMessages":["Error: Please wait and consider throttling your request speed\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/Zone/lookup.ts:88:9)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed RUM site","status":"failed","title":"list enumerates the deployed RUM site","duration":23519.338499999998,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":23427.6311,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379496063,"endTime":1782379519583.3384,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Rum/Site.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"sets a per-operation override, updates it in place, and clears it on destroy","status":"failed","title":"sets a per-operation override, updates it in place, and clears it on destroy","duration":2091.6273,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SchemaValidation/OperationSetting.test.ts:27:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed per-operation override","status":"failed","title":"list enumerates the deployed per-operation override","duration":2328.4540000000015,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SchemaValidation/OperationSetting.test.ts:27:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379694881,"endTime":1782379697211.454,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SchemaValidation/OperationSetting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"uploads a schema, enables in place, replaces on disable and source change, destroys","status":"failed","title":"uploads a schema, enables in place, replaces on disable and source change, destroys","duration":2710.1045999999988,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SchemaValidation/Schema.test.ts:29:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed schema across all zones","status":"failed","title":"list enumerates the deployed schema across all zones","duration":3024.6159000000007,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SchemaValidation/Schema.test.ts:29:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379694617,"endTime":1782379697643.616,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SchemaValidation/Schema.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins zone settings, updates in place, and restores the baseline on destroy","status":"failed","title":"pins zone settings, updates in place, and restores the baseline on destroy","duration":1233.8546999999999,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SchemaValidation/Settings.test.ts:27:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the schema validation settings across all zones","status":"failed","title":"list enumerates the schema validation settings across all zones","duration":1295.7720000000008,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SchemaValidation/Settings.test.ts:27:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379651578,"endTime":1782379652875.772,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SchemaValidation/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"Config.redacted with literal default round-trips to runtime as Redacted","status":"skipped","title":"Config.redacted with literal default round-trips to runtime as Redacted","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Config.redacted resolved from env deploys as a secret_text and round-trips","status":"skipped","title":"Config.redacted resolved from env deploys as a secret_text and round-trips","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Config.string round-trips to runtime as a string","status":"skipped","title":"Config.string round-trips to runtime as a string","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Config.number round-trips to runtime preserving the number type","status":"skipped","title":"Config.number round-trips to runtime preserving the number type","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Config.string with object default round-trips to runtime preserving nested shape","status":"skipped","title":"Config.string with object default round-trips to runtime preserving nested shape","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"Please wait and consider throttling your request speed","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Secret/Secret.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"SecretsStore Secret declared on a Worker's env round-trips to runtime as a SecretsStoreSecret","status":"skipped","title":"SecretsStore Secret declared on a Worker's env round-trips to runtime as a SecretsStoreSecret","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"Please wait and consider throttling your request speed","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SecretsStore/AsyncSecretBinding.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed secret across stores","status":"failed","title":"list enumerates the deployed secret across stores","duration":24539.736,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379584774,"endTime":1782379609313.736,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SecretsStore/Secret.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"createStore POSTs a single JSON object body (regression: invalid_json_body)","status":"passed","title":"createStore POSTs a single JSON object body (regression: invalid_json_body)","duration":29.499899999999798,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"createStore surfaces MaximumStoresExceeded as a tagged error so callers can catchTag it","status":"passed","title":"createStore surfaces MaximumStoresExceeded as a tagged error so callers can catchTag it","duration":28.91319999999996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed secrets store","status":"failed","title":"list enumerates the deployed secrets store","duration":23856.4462,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379517276,"endTime":1782379541132.4463,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SecretsStore/SecretsStore.test.ts"},{"assertionResults":[{"ancestorTitles":["SecurityTxt"],"fullName":"SecurityTxt creates a security.txt, verifies out-of-band, and deletes on destroy","status":"failed","title":"creates a security.txt, verifies out-of-band, and deletes on destroy","duration":2428.238699999998,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SecurityTxt/SecurityTxt.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["SecurityTxt"],"fullName":"SecurityTxt updates mutable fields in place (full-replace PUT)","status":"failed","title":"updates mutable fields in place (full-replace PUT)","duration":519.0116999999991,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SecurityTxt/SecurityTxt.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["SecurityTxt"],"fullName":"SecurityTxt disables the file without deleting it, then destroy removes it","status":"failed","title":"disables the file without deleting it, then destroy removes it","duration":232.20260000000053,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SecurityTxt/SecurityTxt.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["SecurityTxt"],"fullName":"SecurityTxt list enumerates configured security.txt files","status":"failed","title":"list enumerates configured security.txt files","duration":252.26800000000003,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SecurityTxt/SecurityTxt.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379711397,"endTime":1782379714829.268,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/SecurityTxt/SecurityTxt.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed SpectrumProtocolNotAvailable error on unentitled zones","status":"failed","title":"surfaces the typed SpectrumProtocolNotAvailable error on unentitled zones","duration":518.6926999999996,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Spectrum/Application.test.ts:38:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a tcp/22 application","status":"skipped","title":"create, update in place, and destroy a tcp/22 application","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing app errors without adopt, takes over with adopt(true)","status":"skipped","title":"adoption — existing app errors without adopt, takes over with adopt(true)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates Spectrum applications across all zones","status":"passed","title":"list enumerates Spectrum applications across all zones","duration":545.7928000000011,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list surfaces a deployed Spectrum application (entitled zone)","status":"skipped","title":"list surfaces a deployed Spectrum application (entitled zone)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379541853,"endTime":1782379542399.7927,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Spectrum/Application.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create with generated name, update code in place, destroy","status":"failed","title":"create with generated name, update code in place, destroy","duration":1503.4045000000006,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Snippets/Snippet.test.ts:47:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"renaming an explicit snippet triggers replacement","status":"failed","title":"renaming an explicit snippet triggers replacement","duration":1822.730599999999,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Snippets/Snippet.test.ts:47:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed snippet","status":"skipped","title":"list enumerates the deployed snippet","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379705756,"endTime":1782379707580.7307,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Snippets/Snippet.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"snippet rules — create, update, list, and destroy in dependency order","status":"failed","title":"snippet rules — create, update, list, and destroy in dependency order","duration":1648.9350000000013,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Snippets/SnippetRules.test.ts:48:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379679667,"endTime":1782379681315.935,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Snippets/SnippetRules.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a scheduled speed test","status":"skipped","title":"create and delete a scheduled speed test","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the frequency converges in place","status":"skipped","title":"changing the frequency converges in place","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing the region triggers replacement","status":"skipped","title":"changing the region triggers replacement","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing schedule errors without adopt, takes over with adopt(true)","status":"failed","title":"adoption — existing schedule errors without adopt, takes over with adopt(true)","duration":2251.7952000000005,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Speed/TestSchedule.test.ts:53:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed schedule across zones","status":"skipped","title":"list enumerates the deployed schedule across zones","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379709521,"endTime":1782379711772.7952,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Speed/TestSchedule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","status":"failed","title":"surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones","duration":2100.694499999998,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ssl/CertificatePack.test.ts:34:32)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates advanced certificate packs across zones","status":"passed","title":"list enumerates advanced certificate packs across zones","duration":2148.3990999999987,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a deployed advanced certificate pack","status":"skipped","title":"list includes a deployed advanced certificate pack","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"orders a pack, updates validation method in place, and deletes it","status":"skipped","title":"orders a pack, updates validation method in place, and deletes it","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379679338,"endTime":1782379681488.3992,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ssl/CertificatePack.test.ts"},{"assertionResults":[{"ancestorTitles":["UniversalSsl"],"fullName":"UniversalSsl disables Universal SSL and restores the original value on destroy","status":"failed","title":"disables Universal SSL and restores the original value on destroy","duration":1646.9707999999991,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ssl/UniversalSsl.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["UniversalSsl"],"fullName":"UniversalSsl updates enabled in place and keeps the captured initial value","status":"failed","title":"updates enabled in place and keeps the captured initial value","duration":274.88970000000336,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ssl/UniversalSsl.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["UniversalSsl"],"fullName":"UniversalSsl destroy restores a disabled baseline when managing from a disabled zone","status":"failed","title":"destroy restores a disabled baseline when managing from a disabled zone","duration":247.75810000000274,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ssl/UniversalSsl.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["UniversalSsl"],"fullName":"UniversalSsl list enumerates the setting across all zones","status":"failed","title":"list enumerates the setting across all zones","duration":711.5275000000001,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ssl/UniversalSsl.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379662815,"endTime":1782379665696.5276,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Ssl/UniversalSsl.test.ts"},{"assertionResults":[{"ancestorTitles":["State"],"fullName":"State getVersion returns the current STATE_STORE_VERSION","status":"failed","title":"getVersion returns the current STATE_STORE_VERSION","duration":1906.7258000000002,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State DELETE /state/stacks/:stack wipes the test namespace (cleanup)","status":"failed","title":"DELETE /state/stacks/:stack wipes the test namespace (cleanup)","duration":360.6154999999999,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State PUT /resources/:fqn (setState) persists a resource","status":"failed","title":"PUT /resources/:fqn (setState) persists a resource","duration":195.34470000000147,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /resources/:fqn (getState) reads back the persisted value","status":"failed","title":"GET /resources/:fqn (getState) reads back the persisted value","duration":223.1316999999981,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /resources/:fqn returns undefined for a missing fqn","status":"failed","title":"GET /resources/:fqn returns undefined for a missing fqn","duration":212.22490000000107,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /resources (listResources) returns the FQNs in the stage","status":"failed","title":"GET /resources (listResources) returns the FQNs in the stage","duration":184.97819999999774,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /stacks (listStacks) includes the registered test stack","status":"failed","title":"GET /stacks (listStacks) includes the registered test stack","duration":185.00269999999728,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /stacks/:stack/stages (listStages) includes the test stage","status":"failed","title":"GET /stacks/:stack/stages (listStages) includes the test stage","duration":205.50810000000638,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State PUT /output (setStackOutput) persists a stack output","status":"failed","title":"PUT /output (setStackOutput) persists a stack output","duration":178.86049999999523,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /output (getStackOutput) reads back the persisted output","status":"failed","title":"GET /output (getStackOutput) reads back the persisted output","duration":207.7850999999937,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /output returns undefined for an un-deployed stage","status":"failed","title":"GET /output returns undefined for an un-deployed stage","duration":166.00819999999658,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET /replaced-resources (getReplacedResources) returns status===replaced rows","status":"failed","title":"GET /replaced-resources (getReplacedResources) returns status===replaced rows","duration":181.15879999999015,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State DELETE /resources/:fqn (deleteState) removes a single resource","status":"failed","title":"DELETE /resources/:fqn (deleteState) removes a single resource","duration":168.45660000000498,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State DELETE /stacks/:stack?stage=... clears one stage but leaves the stack registered","status":"failed","title":"DELETE /stacks/:stack?stage=... clears one stage but leaves the stack registered","duration":164.61179999999877,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State DELETE /stacks/:stack (no stage) removes the stack from listStacks","status":"failed","title":"DELETE /stacks/:stack (no stage) removes the stack from listStacks","duration":187.51260000000184,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State setState 100x sequential — surfaces transient failures","status":"failed","title":"setState 100x sequential — surfaces transient failures","duration":531.0632999999943,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State setState 100x concurrent — surfaces racy transient failures","status":"failed","title":"setState 100x concurrent — surfaces racy transient failures","duration":558.4580999999889,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["State"],"fullName":"State GET+PUT interleaved 30x5 — engine traffic pattern","status":"failed","title":"GET+PUT interleaved 30x5 — engine traffic pattern","duration":303.417300000001,"failureMessages":["AuthError: Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Cloudflare/StateStore/State.ts:165:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379416259,"endTime":1782379422381.4172,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/StateStore/State.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update in place, and delete a live input","status":"failed","title":"create, update in place, and delete a live input","duration":27045.167599999997,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":27733.6982,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed live input","status":"skipped","title":"list enumerates the deployed live input","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379636972,"endTime":1782379664707.6982,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/LiveInput.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, toggle enabled in place, replace destination, and delete","status":"failed","title":"create, toggle enabled in place, replace destination, and delete","duration":24207.3488,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":24170.6026,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates outputs across all live inputs","status":"skipped","title":"list enumerates outputs across all live inputs","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379593818,"endTime":1782379618025.3489,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/LiveInputOutput.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a signing key","status":"failed","title":"create and delete a signing key","duration":23819.0226,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed signing key","status":"failed","title":"list enumerates the deployed signing key","duration":23844.953999999998,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379566236,"endTime":1782379590080.954,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/SigningKey.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a watermark with default name","status":"failed","title":"create and delete a watermark with default name","duration":14549.1335,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"prop change replaces the watermark (create-only resource)","status":"failed","title":"prop change replaces the watermark (create-only resource)","duration":14701.5086,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed watermark","status":"failed","title":"list enumerates the deployed watermark","duration":14532.8669,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379533433,"endTime":1782379548135.5085,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/Watermark.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"configure, update, and delete the account webhook","status":"failed","title":"configure, update, and delete the account webhook","duration":24863.1068,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the account Stream webhook","status":"failed","title":"list enumerates the account Stream webhook","duration":24760.3819,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379535215,"endTime":1782379560078.1067,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Stream/Webhook.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, and clear tags on a KV namespace","status":"failed","title":"create, update, and clear tags on a KV namespace","duration":28039.257599999997,"failureMessages":["Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time.\n at Proxy. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Output.ts:494:19)\n at String ()\n at setQueryValue (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:729:23)\n at Module.buildRequestParts (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:785:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:631:30)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing resourceId triggers replacement","status":"failed","title":"changing resourceId triggers replacement","duration":28085.990300000005,"failureMessages":["Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time.\n at Proxy. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Output.ts:494:19)\n at String ()\n at setQueryValue (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:729:23)\n at Module.buildRequestParts (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:785:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:631:30)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing tags error without adopt, take over with adopt(true)","status":"failed","title":"adoption — existing tags error without adopt, take over with adopt(true)","duration":51995.66859999999,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates account-wide tagged resources","status":"failed","title":"list enumerates account-wide tagged resources","duration":27737.48210000001,"failureMessages":["Error: Cannot coerce Output to a string via JS coercion. Use Output.interpolate`...` or Output.map(output, fn) to compose Outputs — the value isn't known until deploy time.\n at Proxy. (D:/code/alchemy/alchemy-effect/packages/alchemy/src/Output.ts:494:19)\n at String ()\n at setQueryValue (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:729:23)\n at Module.buildRequestParts (D:/code/alchemy/alchemy-effect/distilled/packages/core/src/traits.ts:785:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:631:30)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)"],"meta":{},"tags":[]}],"startTime":1782379416301,"endTime":1782379468298.6687,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tags/AccountResourceTags.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, and clear tags on a DNS record","status":"failed","title":"create, update, and clear tags on a DNS record","duration":1099.7069999999985,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tags/ZoneResourceTags.test.ts:37:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"tag the zone itself and clear on destroy","status":"failed","title":"tag the zone itself and clear on destroy","duration":1439.0357000000004,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tags/ZoneResourceTags.test.ts:37:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates tagged zone-scoped resources","status":"failed","title":"list enumerates tagged zone-scoped resources","duration":1131.4638000000014,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tags/ZoneResourceTags.test.ts:37:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379697525,"endTime":1782379698965.0356,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tags/ZoneResourceTags.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a typed array of token validation rules","status":"passed","title":"list returns a typed array of token validation rules","duration":1064.4332000000031,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed token validation rule","status":"skipped","title":"list enumerates the deployed token validation rule","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379733436,"endTime":1782379734500.433,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/TokenValidation/Rule.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed TokenValidationNotEntitled error on unentitled accounts","status":"failed","title":"surfaces the typed TokenValidationNotEntitled error on unentitled accounts","duration":1713.2392,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/TokenValidation/TokenValidation.test.ts:36:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates configurations across all zones","status":"passed","title":"list enumerates configurations across all zones","duration":1677.5449999999983,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"configuration + rule lifecycle: create, update in place, rotate keys, destroy","status":"skipped","title":"configuration + rule lifecycle: create, update in place, rotate keys, destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379643776,"endTime":1782379645489.2393,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/TokenValidation/TokenValidation.test.ts"},{"assertionResults":[{"ancestorTitles":["Tunnel Configuration"],"fullName":"Tunnel Configuration list enumerates configurations across all tunnels","status":"failed","title":"list enumerates configurations across all tunnels","duration":27530.05679999999,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379416337,"endTime":1782379443867.057,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/Configuration.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update comment in place, and destroy a hostname route","status":"failed","title":"create, update comment in place, and destroy a hostname route","duration":29068.932700000005,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed hostname route","status":"failed","title":"list enumerates the deployed hostname route","duration":26904.9669,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379416249,"endTime":1782379445317.9326,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/HostnameRoute.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete route with default props","status":"failed","title":"create and delete route with default props","duration":26905.6483,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating the comment patches in place","status":"failed","title":"updating the comment patches in place","duration":26339.5389,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adopt: takes over a pre-existing route","status":"failed","title":"adopt: takes over a pre-existing route","duration":27317.027700000006,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed route","status":"failed","title":"list enumerates the deployed route","duration":26212.413100000005,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379416359,"endTime":1782379443679.0276,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/Route.test.ts"},{"assertionResults":[{"ancestorTitles":["Tunnel runtime bindings"],"fullName":"Tunnel runtime bindings TunnelRead lists tunnels with a read-scoped token","status":"skipped","title":"TunnelRead lists tunnels with a read-scoped token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Tunnel runtime bindings"],"fullName":"Tunnel runtime bindings TunnelWrite creates and deletes a tunnel with a write-scoped token","status":"skipped","title":"TunnelWrite creates and deletes a tunnel with a write-scoped token","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Tunnel runtime bindings"],"fullName":"Tunnel runtime bindings TunnelReadWrite drives the full CRUD surface","status":"skipped","title":"TunnelReadWrite drives the full CRUD surface","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Tunnel.list"],"fullName":"Tunnel.list list enumerates the deployed tunnel","status":"skipped","title":"list enumerates the deployed tunnel","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/Tunnel.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy a virtual network","status":"failed","title":"create, verify, and destroy a virtual network","duration":24233.135000000002,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update name and comment in place (same id)","status":"failed","title":"update name and comment in place (same id)","duration":24138.4162,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":24081.3472,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed virtual network","status":"failed","title":"list enumerates the deployed virtual network","duration":24101.157499999998,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379522629,"endTime":1782379546862.135,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/VirtualNetwork.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, rename in place, and destroy a WARP Connector tunnel","status":"failed","title":"create, rename in place, and destroy a WARP Connector tunnel","duration":47731.7216,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed WARP Connector tunnel","status":"failed","title":"list enumerates the deployed WARP Connector tunnel","duration":5922.2437,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379457975,"endTime":1782379505706.7217,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Tunnel/WarpConnector.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a widget with default name","status":"failed","title":"create and delete a widget with default name","duration":23505.399699999998,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update mutable props in place (same sitekey)","status":"failed","title":"update mutable props in place (same sitekey)","duration":23467.940700000003,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"recreates after out-of-band delete","status":"failed","title":"recreates after out-of-band delete","duration":23560.2778,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed widget","status":"failed","title":"list enumerates the deployed widget","duration":23538.5869,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379503300,"endTime":1782379526860.2778,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Turnstile/Widget.test.ts"},{"assertionResults":[{"ancestorTitles":["UrlNormalization"],"fullName":"UrlNormalization configures URL normalization and resets to defaults on destroy","status":"failed","title":"configures URL normalization and resets to defaults on destroy","duration":1371.5064999999995,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/UrlNormalization/UrlNormalization.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["UrlNormalization"],"fullName":"UrlNormalization updates scope and type in place","status":"failed","title":"updates scope and type in place","duration":299.59340000000157,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/UrlNormalization/UrlNormalization.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["UrlNormalization"],"fullName":"UrlNormalization applies Cloudflare defaults when scope and type are omitted","status":"failed","title":"applies Cloudflare defaults when scope and type are omitted","duration":242.7045999999973,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/UrlNormalization/UrlNormalization.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["UrlNormalization"],"fullName":"UrlNormalization list enumerates URL normalization across all zones","status":"failed","title":"list enumerates URL normalization across all zones","duration":248.61840000000302,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/UrlNormalization/UrlNormalization.test.ts:28:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379680129,"endTime":1782379682291.6184,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/UrlNormalization/UrlNormalization.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete index with explicit dimensions","status":"failed","title":"create and delete index with explicit dimensions","duration":23550.4028,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create index from a preset","status":"failed","title":"create index from a preset","duration":23712.832599999998,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replaces index when dimensions change","status":"failed","title":"replaces index when dimensions change","duration":23528.0794,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed index","status":"failed","title":"list enumerates the deployed index","duration":23579.1502,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379509147,"endTime":1782379532859.8325,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Vectorize/VectorizeIndex.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"VectorizeIndex.bind exercises the client surface","status":"failed","title":"VectorizeIndex.bind exercises the client surface","duration":27292.8834,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379624040,"endTime":1782379651332.8833,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Vectorize/VectorizeIndexBinding.test.ts"},{"assertionResults":[{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex create and delete a metadata index","status":"failed","title":"create and delete a metadata index","duration":1548.1400000000012,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex multiple metadata indexes on the same parent coexist","status":"failed","title":"multiple metadata indexes on the same parent coexist","duration":1560.7282999999989,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex replacing the parent index also replaces the metadata index","status":"failed","title":"replacing the parent index also replaces the metadata index","duration":3052.7191999999995,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex list enumerates the deployed metadata index","status":"failed","title":"list enumerates the deployed metadata index","duration":1633.447900000001,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.VectorizeMetadataIndex"],"fullName":"Cloudflare.VectorizeMetadataIndex destroy is idempotent when the parent index was deleted out-of-band","status":"failed","title":"destroy is idempotent when the parent index was deleted out-of-band","duration":1573.5172999999995,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379546116,"endTime":1782379549169.7192,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Vectorize/VectorizeMetadataIndex.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, update, delete vpc service","status":"failed","title":"create, update, delete vpc service","duration":20109.748900000002,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create vpc service with ipv4 host","status":"failed","title":"create vpc service with ipv4 host","duration":71885.5957,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create vpc service with dual-stack host","status":"skipped","title":"create vpc service with dual-stack host","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed vpc service","status":"failed","title":"list enumerates the deployed vpc service","duration":20064.652000000002,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379447027,"endTime":1782379518913.5957,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VpcService/VpcService.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"reference vpc service by name and by id","status":"failed","title":"reference vpc service by name and by id","duration":23743.4703,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379475780,"endTime":1782379499523.4702,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VpcService/VpcServiceRef.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list enumerates the deployed credential","status":"failed","title":"list enumerates the deployed credential","duration":24678.241499999996,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379608431,"endTime":1782379633109.2415,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VulnerabilityScanner/Credential.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, rename, and destroy a credential set","status":"failed","title":"create, rename, and destroy a credential set","duration":24292.013699999996,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed credential set","status":"failed","title":"list enumerates the deployed credential set","duration":25180.9746,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"credential lifecycle — create, mutate in place, rotate value, destroy","status":"failed","title":"credential lifecycle — create, mutate in place, rotate value, destroy","duration":24263.827400000002,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"moving a credential to a different set triggers replacement","status":"failed","title":"moving a credential to a different set triggers replacement","duration":24313.8826,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379600676,"endTime":1782379625858.9746,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VulnerabilityScanner/CredentialSet.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, verify, and destroy a target environment","status":"failed","title":"create, verify, and destroy a target environment","duration":1177.0527000000002,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VulnerabilityScanner/TargetEnvironment.test.ts:33:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updating name and description patches in place; clearing description","status":"failed","title":"updating name and description patches in place; clearing description","duration":1153.4694999999992,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VulnerabilityScanner/TargetEnvironment.test.ts:33:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed target environment","status":"failed","title":"list enumerates the deployed target environment","duration":1191.5437000000002,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VulnerabilityScanner/TargetEnvironment.test.ts:33:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379632317,"endTime":1782379633511.5437,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/VulnerabilityScanner/TargetEnvironment.test.ts"},{"assertionResults":[{"ancestorTitles":["Settings"],"fullName":"Settings pins the settings to the default baseline without touching the API","status":"failed","title":"pins the settings to the default baseline without touching the API","duration":1912.9015999999974,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/WaitingRoom/Settings.test.ts:35:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["Settings"],"fullName":"Settings surfaces the typed ZoneNotEntitled error when enabling on unentitled zones","status":"failed","title":"surfaces the typed ZoneNotEntitled error when enabling on unentitled zones","duration":338.2066999999988,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/WaitingRoom/Settings.test.ts:35:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["Settings"],"fullName":"Settings enables the crawler bypass and restores the original value on destroy","status":"skipped","title":"enables the crawler bypass and restores the original value on destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Settings"],"fullName":"Settings list enumerates the settings across all zones","status":"failed","title":"list enumerates the settings across all zones","duration":417.2474000000002,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/WaitingRoom/Settings.test.ts:35:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379650370,"endTime":1782379653039.2473,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/WaitingRoom/Settings.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed ZoneNotEntitled error on unentitled zones","status":"failed","title":"surfaces the typed ZoneNotEntitled error on unentitled zones","duration":2351.0162,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/WaitingRoom/WaitingRoom.test.ts:40:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, and destroy a waiting room","status":"skipped","title":"create, update in place, and destroy a waiting room","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates waiting rooms across zones","status":"passed","title":"list enumerates waiting rooms across zones","duration":2380.362799999999,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379649431,"endTime":1782379651812.3628,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/WaitingRoom/WaitingRoom.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a well-typed array of web3 content lists","status":"passed","title":"list returns a well-typed array of web3 content lists","duration":1389.9992999999995,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list surfaces a deployed web3 content list (entitled account)","status":"skipped","title":"list surfaces a deployed web3 content list (entitled account)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379733237,"endTime":1782379734626.9993,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Web3/ContentList.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"list returns a well-typed array of web3 hostnames","status":"passed","title":"list returns a well-typed array of web3 hostnames","duration":1106.4267,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list surfaces a deployed web3 hostname (entitled account)","status":"skipped","title":"list surfaces a deployed web3 hostname (entitled account)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379734540,"endTime":1782379735646.4268,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Web3/Hostname.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed Web3HostnameNotEntitled error on unentitled accounts","status":"failed","title":"surfaces the typed Web3HostnameNotEntitled error on unentitled accounts","duration":1972.2965000000004,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Web3/Web3Hostname.test.ts:35:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create, update in place, replace on target change, destroy","status":"skipped","title":"create, update in place, replace on target change, destroy","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"content list — set entries, update declaratively, reset on destroy","status":"skipped","title":"content list — set entries, update declaratively, reset on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379630817,"endTime":1782379632789.2964,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Web3/Web3Hostname.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"StaticSite: editing a source file republishes the assets in a single deploy","status":"failed","title":"StaticSite: editing a source file republishes the assets in a single deploy","duration":70991.75570000001,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"StaticSite: class form deploys and serves the built assets","status":"failed","title":"StaticSite: class form deploys and serves the built assets","duration":71000.6104,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"StaticSite: relocating the project (and deleting the old one) preserves hash.assets","status":"failed","title":"StaticSite: relocating the project (and deleting the old one) preserves hash.assets","duration":71049.61720000001,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"StaticSite: a bundle-only change keeps the asset manifest (hash.assets stable)","status":"failed","title":"StaticSite: a bundle-only change keeps the asset manifest (hash.assets stable)","duration":5938.776,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379461476,"endTime":1782379532525.6172,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Website/StaticSite.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"Vite: editing a source file republishes the assets in a single deploy","status":"passed","title":"Vite: editing a source file republishes the assets in a single deploy","duration":35637.2617,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Vite: class form deploys and serves the built assets","status":"passed","title":"Vite: class form deploys and serves the built assets","duration":35508.7333,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Vite: relocating rootDir (and deleting the old one) is a no-op when sources are identical","status":"failed","title":"Vite: relocating rootDir (and deleting the old one) is a no-op when sources are identical","duration":25966.1835,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Vite: `env` props are inlined as `import.meta.env.*` into the bundle","status":"passed","title":"Vite: `env` props are inlined as `import.meta.env.*` into the bundle","duration":34459.960999999996,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379592321,"endTime":1782379627958.2617,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Website/Vite.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create, replace, and destroy a dispatch namespace","status":"failed","title":"create, replace, and destroy a dispatch namespace","duration":1345.305699999999,"failureMessages":["Forbidden: You do not have access to dispatch namespaces. You can purchase it within the Cloudflare dashboard here: https://dash.cloudflare.com?to=/:account/workers-for-platforms - if you are an Enterprise customer please contact your account team.\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"upload, update in place, and destroy a namespace script","status":"failed","title":"upload, update in place, and destroy a namespace script","duration":1436.0170999999991,"failureMessages":["Forbidden: You do not have access to dispatch namespaces. You can purchase it within the Cloudflare dashboard here: https://dash.cloudflare.com?to=/:account/workers-for-platforms - if you are an Enterprise customer please contact your account team.\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed dispatch namespace","status":"failed","title":"list enumerates the deployed dispatch namespace","duration":1417.3408999999992,"failureMessages":["Forbidden: You do not have access to dispatch namespaces. You can purchase it within the Cloudflare dashboard here: https://dash.cloudflare.com?to=/:account/workers-for-platforms - if you are an Enterprise customer please contact your account team.\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]}],"startTime":1782379634112,"endTime":1782379635549.017,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/WorkersForPlatforms/DispatchNamespace.test.ts"},{"assertionResults":[{"ancestorTitles":["AccountSetting"],"fullName":"AccountSetting flips green compute, updates in place, and restores the baseline on destroy","status":"failed","title":"flips green compute, updates in place, and restores the baseline on destroy","duration":28556.3695,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["AccountSetting"],"fullName":"AccountSetting no-op deploy when desired settings already match the live account","status":"failed","title":"no-op deploy when desired settings already match the live account","duration":181.25730000000112,"failureMessages":["CloudflareHttpError: null\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:409:7)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["AccountSetting"],"fullName":"AccountSetting list returns the account settings singleton","status":"failed","title":"list returns the account settings singleton","duration":23270.7838,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379522251,"endTime":1782379574259.7837,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/AccountSetting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed worker fires the scheduled handler on its cron trigger","status":"skipped","title":"deployed worker fires the scheduled handler on its cron trigger","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/CronEventSource.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"Drizzle.postgres query runs inside a Workflow task (ExecutionContext provided per run)","status":"skipped","title":"Drizzle.postgres query runs inside a Workflow task (ExecutionContext provided per run)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/DrizzleWorkflow.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"durable object methods can use binding clients","status":"skipped","title":"durable object methods can use binding clients","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"tick streams sequential values from a durable object (tutorial repro)","status":"skipped","title":"tick streams sequential values from a durable object (tutorial repro)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"async worker durable object binding accepts scriptName","status":"skipped","title":"async worker durable object binding accepts scriptName","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"durable object class migrations across redeploys","status":"skipped","title":"durable object class migrations across redeploys","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/DurableObjectNamespace.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed http-api worker handles createTask + getTask via typed HttpApiClient","status":"skipped","title":"deployed http-api worker handles createTask + getTask via typed HttpApiClient","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"cors middleware adds Access-Control-Allow-Origin header on preflight","status":"skipped","title":"cors middleware adds Access-Control-Allow-Origin header on preflight","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"cors middleware adds Access-Control-Allow-Origin header on actual requests","status":"skipped","title":"cors middleware adds Access-Control-Allow-Origin header on actual requests","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"concurrent createTask survives scope-lifecycle pressure","status":"skipped","title":"concurrent createTask survives scope-lifecycle pressure","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"createTaskDO + getTaskDO round-trip 100x in parallel through the DO HttpApi","status":"skipped","title":"createTaskDO + getTaskDO round-trip 100x in parallel through the DO HttpApi","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"Please wait and consider throttling your request speed","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/HttpApi.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete a destination with default name","status":"failed","title":"create and delete a destination with default name","duration":26726.89069999999,"failureMessages":["Forbidden: Authentication error\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:7505:82\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Option.js:877:83\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:81:18\n at Getter.run (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaGetter.js:441:42)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:57\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:1019:38\n at Module. (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Function.js:78:18)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:884:25\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/SchemaParser.js:788:50\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/Schema.js:640:48"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"update url, headers, and enabled in place (same slug)","status":"failed","title":"update url, headers, and enabled in place (same slug)","duration":51321.6434,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"replacement on name change (new slug, old destination removed)","status":"failed","title":"replacement on name change (new slug, old destination removed)","duration":51852.9347,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed destination","status":"failed","title":"list enumerates the deployed destination","duration":51621.8873,"failureMessages":["TooManyRequests: Please wait and consider throttling your request speed\n at Object.971 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:78:5)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]}],"startTime":1782379416397,"endTime":1782379468253.9348,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/ObservabilityDestination.test.ts"},{"assertionResults":[{"ancestorTitles":["Cloudflare.Worker with bundle: false"],"fullName":"Cloudflare.Worker with bundle: false uploads a prebuilt module graph byte-for-byte","status":"failed","title":"uploads a prebuilt module graph byte-for-byte","duration":3506.962599999999,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379728602,"endTime":1782379732108.9626,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/PrebuiltWorker.test.ts"},{"assertionResults":[{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle collects modules matching the default rules, entry first, names POSIX-relative to the entry's directory","status":"passed","title":"collects modules matching the default rules, entry first, names POSIX-relative to the entry's directory","duration":1965.482,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle uploads file contents byte-for-byte","status":"passed","title":"uploads file contents byte-for-byte","duration":687.2734,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle does not walk outside the entry's directory","status":"passed","title":"does not walk outside the entry's directory","duration":864.9611000000004,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle custom rules replace the defaults","status":"passed","title":"custom rules replace the defaults","duration":1032.9205999999995,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle hash is stable across reads and sensitive to module changes","status":"passed","title":"hash is stable across reads and sensitive to module changes","duration":1438.9146,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["readPrebuiltWorkerBundle"],"fullName":"readPrebuiltWorkerBundle fails with BundleError when the entry does not exist","status":"passed","title":"fails with BundleError when the entry does not exist","duration":16.711300000000847,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379728833,"endTime":1782379730798.482,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/PrebuiltWorkerBundle.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create and delete an opt-out route (no script)","status":"failed","title":"create and delete an opt-out route (no script)","duration":2365.2916000000005,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Route.test.ts:42:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"route to a Worker, then update pattern and script in place","status":"failed","title":"route to a Worker, then update pattern and script in place","duration":2554.4004999999997,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Route.test.ts:42:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing route errors without adopt, takes over with adopt(true)","status":"failed","title":"adoption — existing route errors without adopt, takes over with adopt(true)","duration":2579.207199999997,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Route.test.ts:42:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed route across all zones","status":"failed","title":"list enumerates the deployed route across all zones","duration":2531.076799999999,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Route.test.ts:42:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379654050,"endTime":1782379656631.2073,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Route.test.ts"},{"assertionResults":[{"ancestorTitles":["RpcDecodeError"],"fullName":"RpcDecodeError message delegates to Error.message when cause is an Error","status":"passed","title":"message delegates to Error.message when cause is an Error","duration":381.78999999999996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RpcDecodeError"],"fullName":"RpcDecodeError message stringifies non-Error cause","status":"passed","title":"message stringifies non-Error cause","duration":380.2384000000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RpcCallError"],"fullName":"RpcCallError message includes method name and Error.message","status":"passed","title":"message includes method name and Error.message","duration":380.04280000000017,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["RpcCallError"],"fullName":"RpcCallError message stringifies non-Error cause","status":"passed","title":"message stringifies non-Error cause","duration":379.8823000000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcErrorEnvelope"],"fullName":"isRpcErrorEnvelope detects valid envelope","status":"passed","title":"detects valid envelope","duration":379.7451000000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcErrorEnvelope"],"fullName":"isRpcErrorEnvelope rejects non-envelope values","status":"passed","title":"rejects non-envelope values","duration":381.16640000000007,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcStreamEnvelope"],"fullName":"isRpcStreamEnvelope detects valid bytes envelope","status":"passed","title":"detects valid bytes envelope","duration":381.0718999999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcStreamEnvelope"],"fullName":"isRpcStreamEnvelope detects valid jsonl envelope","status":"passed","title":"detects valid jsonl envelope","duration":381.08100000000013,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["isRpcStreamEnvelope"],"fullName":"isRpcStreamEnvelope rejects missing or wrong fields","status":"passed","title":"rejects missing or wrong fields","duration":380.9232000000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["encodeRpcError"],"fullName":"encodeRpcError preserves tagged error fields","status":"passed","title":"preserves tagged error fields","duration":380.8049000000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["encodeRpcError"],"fullName":"encodeRpcError normalizes plain Error to name/message/stack","status":"passed","title":"normalizes plain Error to name/message/stack","duration":381.4300000000003,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["encodeRpcError"],"fullName":"encodeRpcError passes through primitives","status":"passed","title":"passes through primitives","duration":381.3607000000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["encodeRpcError"],"fullName":"encodeRpcError passes through plain objects","status":"passed","title":"passes through plain objects","duration":381.28720000000067,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcReadableStream"],"fullName":"fromRpcReadableStream bytes encoding passes raw Uint8Array chunks through","status":"passed","title":"bytes encoding passes raw Uint8Array chunks through","duration":389.08100000000013,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcReadableStream"],"fullName":"fromRpcReadableStream jsonl encoding parses JSON lines","status":"passed","title":"jsonl encoding parses JSON lines","duration":389.2362999999996,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcReadableStream"],"fullName":"fromRpcReadableStream jsonl encoding produces RpcDecodeError on malformed JSON","status":"passed","title":"jsonl encoding produces RpcDecodeError on malformed JSON","duration":388.7689999999993,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcReadableStream"],"fullName":"fromRpcReadableStream jsonl encoding skips empty lines","status":"passed","title":"jsonl encoding skips empty lines","duration":393.4922000000006,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["fromRpcStreamEnvelope"],"fullName":"fromRpcStreamEnvelope delegates to fromRpcReadableStream","status":"passed","title":"delegates to fromRpcReadableStream","duration":393.4360999999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcValue"],"fullName":"decodeRpcValue passes through plain values","status":"passed","title":"passes through plain values","duration":204.42290000000048,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcValue"],"fullName":"decodeRpcValue converts stream envelope to Effect Stream","status":"passed","title":"converts stream envelope to Effect Stream","duration":205.03660000000036,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcValue"],"fullName":"decodeRpcValue converts bare ReadableStream to bytes Effect Stream","status":"passed","title":"converts bare ReadableStream to bytes Effect Stream","duration":204.9975999999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult succeeds for plain values","status":"passed","title":"succeeds for plain values","duration":204.7523000000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult succeeds for numeric values","status":"passed","title":"succeeds for numeric values","duration":204.6797999999999,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult fails for error envelopes with tagged error","status":"passed","title":"fails for error envelopes with tagged error","duration":204.89050000000043,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult fails with plain Error shape for error envelopes","status":"passed","title":"fails with plain Error shape for error envelopes","duration":204.89990000000034,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["decodeRpcResult"],"fullName":"decodeRpcResult wraps stream envelopes in succeed (stream passthrough)","status":"passed","title":"wraps stream envelopes in succeed (stream passthrough)","duration":205.44959999999992,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream selects jsonl encoding for non-byte data","status":"passed","title":"selects jsonl encoding for non-byte data","duration":197.02109999999993,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream roundtrips a single-element jsonl stream","status":"passed","title":"roundtrips a single-element jsonl stream","duration":215.08439999999973,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream selects bytes encoding for Uint8Array data","status":"passed","title":"selects bytes encoding for Uint8Array data","duration":197.4983999999995,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream roundtrips a single-element bytes stream","status":"passed","title":"roundtrips a single-element bytes stream","duration":218.44670000000042,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["toRpcStream"],"fullName":"toRpcStream handles empty stream as jsonl","status":"passed","title":"handles empty stream as jsonl","duration":218.89459999999963,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["makeRpcStub"],"fullName":"makeRpcStub proxies successful calls","status":"passed","title":"proxies successful calls","duration":322.04199999999946,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["makeRpcStub"],"fullName":"makeRpcStub wraps rejected promises as RpcCallError","status":"passed","title":"wraps rejected promises as RpcCallError","duration":321.96609999999964,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["makeRpcStub"],"fullName":"makeRpcStub decodes error envelopes into Effect.fail","status":"passed","title":"decodes error envelopes into Effect.fail","duration":321.84679999999935,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["makeRpcStub"],"fullName":"makeRpcStub decodes stream envelopes from successful calls","status":"passed","title":"decodes stream envelopes from successful calls","duration":321.8232000000007,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors toRpcStream encodes a stream that fails immediately","status":"passed","title":"toRpcStream encodes a stream that fails immediately","duration":321.7667000000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors toRpcStream encodes a stream that fails after elements","status":"passed","title":"toRpcStream encodes a stream that fails after elements","duration":321.7912000000006,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors fromRpcReadableStream decodes error marker in JSONL","status":"passed","title":"fromRpcReadableStream decodes error marker in JSONL","duration":321.6672000000008,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors fromRpcReadableStream yields elements before error marker","status":"passed","title":"fromRpcReadableStream yields elements before error marker","duration":321.63850000000093,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["stream errors"],"fullName":"stream errors makeRpcStub preserves stream errors (not collapsed to RpcCallError)","status":"passed","title":"makeRpcStub preserves stream errors (not collapsed to RpcCallError)","duration":321.6277999999993,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379721751,"endTime":1782379722860.7913,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Rpc.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"RpcDurableObjectNamespace: Increment / Get round-trip via Worker","status":"passed","title":"RpcDurableObjectNamespace: Increment / Get round-trip via Worker","duration":4557.381399999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcDurableObjectNamespace: separate getByName(id) instances are isolated","status":"passed","title":"RpcDurableObjectNamespace: separate getByName(id) instances are isolated","duration":4222.589200000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcDurableObjectNamespace: streaming RPC via getByName(id).CountUpTo","status":"passed","title":"RpcDurableObjectNamespace: streaming RPC via getByName(id).CountUpTo","duration":1163.8012000000017,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker + RpcDurableObjectNamespace: Worker proxies *DO RPCs through the typed namespace","status":"passed","title":"RpcWorker + RpcDurableObjectNamespace: Worker proxies *DO RPCs through the typed namespace","duration":457.0648000000074,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcDurableObjectNamespace: 100 concurrent Increment calls do not hang","status":"passed","title":"RpcDurableObjectNamespace: 100 concurrent Increment calls do not hang","duration":4383.873000000007,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent unary RPCs do not hang","status":"passed","title":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent unary RPCs do not hang","duration":3488.9967000000033,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent *DO unary RPCs do not hang","status":"passed","title":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent *DO unary RPCs do not hang","duration":3517.5179999999964,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent streaming *DO RPCs do not hang","status":"passed","title":"RpcWorker + RpcDurableObjectNamespace: 100 concurrent streaming *DO RPCs do not hang","duration":1275.082899999994,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379717307,"endTime":1782379722700.083,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/RpcDurableObjectNamespace.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: unary RPC response","status":"skipped","title":"RpcServer.toHttpEffect: unary RPC response","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: streaming RPC response","status":"skipped","title":"RpcServer.toHttpEffect: streaming RPC response","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: array payload streams response items in order","status":"skipped","title":"RpcServer.toHttpEffect: array payload streams response items in order","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: 200 concurrent unary calls do not hang","status":"skipped","title":"RpcServer.toHttpEffect: 200 concurrent unary calls do not hang","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect: concurrent streaming calls do not hang","status":"skipped","title":"RpcServer.toHttpEffect: concurrent streaming calls do not hang","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object unary RPC response","status":"skipped","title":"RpcServer.toHttpEffect Durable Object unary RPC response","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object streaming RPC response","status":"skipped","title":"RpcServer.toHttpEffect Durable Object streaming RPC response","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object array payload streams response items in order","status":"skipped","title":"RpcServer.toHttpEffect Durable Object array payload streams response items in order","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object concurrent unary calls do not hang","status":"skipped","title":"RpcServer.toHttpEffect Durable Object concurrent unary calls do not hang","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcServer.toHttpEffect Durable Object concurrent streaming calls do not hang","status":"skipped","title":"RpcServer.toHttpEffect Durable Object concurrent streaming calls do not hang","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/RpcHttp.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"RpcWorker: target worker exposes Greet","status":"passed","title":"RpcWorker: target worker exposes Greet","duration":204.66960000000108,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker.bind: caller proxies through service binding to target","status":"passed","title":"RpcWorker.bind: caller proxies through service binding to target","duration":252.29200000000128,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"RpcWorker.bind: 100 concurrent ProxyGreet calls do not hang","status":"passed","title":"RpcWorker.bind: 100 concurrent ProxyGreet calls do not hang","duration":2354.1672000000035,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379747798,"endTime":1782379750153.1672,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/RpcWorker.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"adopts the account's existing workers.dev subdomain without mutating it","status":"passed","title":"adopts the account's existing workers.dev subdomain without mutating it","duration":1645.6324999999997,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list returns the account's workers.dev subdomain singleton","status":"passed","title":"list returns the account's workers.dev subdomain singleton","duration":1172.3270000000011,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379733266,"endTime":1782379734911.6326,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Subdomain.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"D1 counter writes from WorkerA are visible from WorkerB (cross-script DO)","status":"skipped","title":"D1 counter writes from WorkerA are visible from WorkerB (cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"DO storage counter writes from WorkerA are visible from WorkerB (cross-script DO)","status":"skipped","title":"DO storage counter writes from WorkerA are visible from WorkerB (cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"WorkerC hosts its own isolated Counter (writes from A/B are not visible from C)","status":"skipped","title":"WorkerC hosts its own isolated Counter (writes from A/B are not visible from C)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Writes from WorkerB are visible from WorkerA (bidirectional cross-script DO)","status":"skipped","title":"Writes from WorkerB are visible from WorkerA (bidirectional cross-script DO)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/TaggedDO.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"RpcWorker WorkerA exposes the same RPC surface as the underlying DO","status":"skipped","title":"RpcWorker WorkerA exposes the same RPC surface as the underlying DO","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"D1 counter writes via WorkerA's RPC are visible from WorkerB (cross-script DO)","status":"skipped","title":"D1 counter writes via WorkerA's RPC are visible from WorkerB (cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"DO storage counter writes via WorkerA's RPC are visible from WorkerB (cross-script DO)","status":"skipped","title":"DO storage counter writes via WorkerA's RPC are visible from WorkerB (cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"Writes from WorkerB are visible from WorkerA's RPC (bidirectional cross-script DO)","status":"skipped","title":"Writes from WorkerB are visible from WorkerA's RPC (bidirectional cross-script DO)","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"WorkerC hosts its own isolated Counter (writes from A/B are not visible from C)","status":"skipped","title":"WorkerC hosts its own isolated Counter (writes from A/B are not visible from C)","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/TaggedRpcDO.test.ts"},{"assertionResults":[{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker create, update, delete worker","status":"failed","title":"create, update, delete worker","duration":26833.2641,"failureMessages":["Unauthorized: Authentication error\n at Object.10000 (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:99:23)\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:429:46)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47"],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker create, update, delete worker with assets","status":"passed","title":"create, update, delete worker with assets","duration":24025.550199999998,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker Worker assets: relocating to a fresh path with identical bytes preserves hash and keeps assets","status":"passed","title":"Worker assets: relocating to a fresh path with identical bytes preserves hash and keeps assets","duration":14199.469800000006,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker Worker assets: editing a file changes the hash and republishes the manifest","status":"passed","title":"Worker assets: editing a file changes the hash and republishes the manifest","duration":14303.685100000002,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker Worker assets: a bundle-only change keeps the asset manifest (hash.assets stable)","status":"passed","title":"Worker assets: a bundle-only change keeps the asset manifest (hash.assets stable)","duration":19757.088100000008,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker create, update, delete internal worker","status":"passed","title":"create, update, delete internal worker","duration":8401.083199999994,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker owned worker (matching alchemy tags) is silently adopted without --adopt","status":"passed","title":"owned worker (matching alchemy tags) is silently adopted without --adopt","duration":8403.675699999993,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker adopt(true) takes over a foreign-tagged worker","status":"passed","title":"adopt(true) takes over a foreign-tagged worker","duration":7628.676800000001,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker url defaults to enabling the workers.dev subdomain on first deploy","status":"passed","title":"url defaults to enabling the workers.dev subdomain on first deploy","duration":3612.116300000009,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker url: false disables the workers.dev subdomain on first deploy","status":"passed","title":"url: false disables the workers.dev subdomain on first deploy","duration":3551.929299999989,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker toggling url between deploys flips the workers.dev subdomain","status":"passed","title":"toggling url between deploys flips the workers.dev subdomain","duration":8176.337400000004,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker redeploy re-enables previewsEnabled when externally disabled","status":"passed","title":"redeploy re-enables previewsEnabled when externally disabled","duration":6457.582300000009,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker domains reflects the workers.dev subdomain and tracks url","status":"passed","title":"domains reflects the workers.dev subdomain and tracks url","duration":5875.4228,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker domains puts custom domains before workers.dev and url is the first","status":"skipped","title":"domains puts custom domains before workers.dev and url is the first","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker list enumerates the deployed worker","status":"passed","title":"list enumerates the deployed worker","duration":4050.0991000000067,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker downstream referencing worker.url is not re-updated when the worker changes","status":"failed","title":"downstream referencing worker.url is not re-updated when the worker changes","duration":722.4421999999904,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker"],"fullName":"Cloudflare.Worker worker.durableObjectNamespaces stability across DO and worker changes","status":"failed","title":"worker.durableObjectNamespaces stability across DO and worker changes","duration":1725.8496000000014,"failureMessages":["Forbidden: You have exceeded the limit of 5 Workers on your account. Please refer to the Workers documentation (https://developers.cloudflare.com/workers/platform/limits/#number-of-workers) for more details.\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379416562,"endTime":1782379449232.5823,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Worker.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"target worker's own fetch handler responds","status":"skipped","title":"target worker's own fetch handler responds","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"async caller can call target's RPC method via service binding","status":"skipped","title":"async caller can call target's RPC method via service binding","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect caller can call target's RPC method via bindWorker","status":"skipped","title":"effect caller can call target's RPC method via bindWorker","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"Please wait and consider throttling your request speed","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/WorkerBinding.test.ts"},{"assertionResults":[{"ancestorTitles":["Cloudflare.Worker env bindings"],"fullName":"Cloudflare.Worker env bindings async worker round-trips every supported binding shape","status":"skipped","title":"async worker round-trips every supported binding shape","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker env bindings"],"fullName":"Cloudflare.Worker env bindings effect worker round-trips env: literals and Redacted via WorkerEnvironment","status":"skipped","title":"effect worker round-trips env: literals and Redacted via WorkerEnvironment","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker env bindings"],"fullName":"Cloudflare.Worker env bindings effect worker resolves the yielded VersionMetadata binding","status":"skipped","title":"effect worker resolves the yielded VersionMetadata binding","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["Cloudflare.Worker env bindings"],"fullName":"Cloudflare.Worker env bindings effect worker round-trips Config.xxx bindings captured in Init","status":"skipped","title":"effect worker round-trips Config.xxx bindings captured in Init","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"Please wait and consider throttling your request speed","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/WorkerEnv.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"async worker loads and proxies to a dynamic worker via env binding","status":"skipped","title":"async worker loads and proxies to a dynamic worker via env binding","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"effect worker loads and proxies to a dynamic worker via yield* loader","status":"skipped","title":"effect worker loads and proxies to a dynamic worker via yield* loader","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"Please wait and consider throttling your request speed","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/WorkerLoader.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"deployed worker can run a workflow to completion","status":"skipped","title":"deployed worker can run a workflow to completion","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed workflow","status":"skipped","title":"list enumerates the deployed workflow","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379347422,"endTime":1782379347422,"status":"failed","message":"Cloudflare State store not found. Run 'alchemy bootstrap cloudflare --profile ' to deploy it first.","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Workers/Workflow.test.ts"},{"assertionResults":[{"ancestorTitles":["ZarazConfig"],"fullName":"ZarazConfig updates and retains a zone-level Zaraz config","status":"skipped","title":"updates and retains a zone-level Zaraz config","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ZarazConfig"],"fullName":"ZarazConfig delete true resets Zaraz config to defaults","status":"skipped","title":"delete true resets Zaraz config to defaults","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ZarazConfig"],"fullName":"ZarazConfig updates and retains Zaraz workflow mode","status":"skipped","title":"updates and retains Zaraz workflow mode","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":["ZarazConfig"],"fullName":"ZarazConfig list enumerates Zaraz config across all zones","status":"failed","title":"list enumerates Zaraz config across all zones","duration":2490.7174000000014,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zaraz/ZarazConfig.test.ts:184:13)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379678687,"endTime":1782379681177.7173,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zaraz/ZarazConfig.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"Zaraz event contracts are type-only","status":"passed","title":"Zaraz event contracts are type-only","duration":8.056600000000344,"failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379727308,"endTime":1782379727316.0566,"status":"passed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zaraz/ZarazEventTypes.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins the custom nameserver toggle and leaves the zone at its baseline on destroy","status":"failed","title":"pins the custom nameserver toggle and leaves the zone at its baseline on destroy","duration":2988.995899999998,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/CustomNameservers.test.ts:33:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"surfaces the typed CustomNameserverSetNotFound error when enabling without an account nameserver set","status":"failed","title":"surfaces the typed CustomNameserverSetNotFound error when enabling without an account nameserver set","duration":2787.225699999999,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/CustomNameservers.test.ts:33:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates zones using account custom nameservers","status":"passed","title":"list enumerates zones using account custom nameservers","duration":2817.4128999999994,"failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list includes a zone with account custom nameservers enabled","status":"skipped","title":"list includes a zone with account custom nameservers enabled","failureMessages":[],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"enables account custom nameservers and restores the baseline on destroy","status":"skipped","title":"enables account custom nameservers and restores the baseline on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379690167,"endTime":1782379693155.9958,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/CustomNameservers.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"surfaces the typed ZoneHoldsRequireEnterprise error on non-Enterprise zones","status":"failed","title":"surfaces the typed ZoneHoldsRequireEnterprise error on non-Enterprise zones","duration":2479.0112999999983,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/Hold.test.ts:34:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the hold state across all zones","status":"failed","title":"list enumerates the hold state across all zones","duration":2194.439699999999,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/Hold.test.ts:34:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"places a hold, updates includeSubdomains in place, and removes it on destroy","status":"skipped","title":"places a hold, updates includeSubdomains in place, and removes it on destroy","failureMessages":[],"meta":{},"tags":[]}],"startTime":1782379695636,"endTime":1782379698115.0112,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/Hold.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"pins a toggle setting and restores the original value on destroy","status":"failed","title":"pins a toggle setting and restores the original value on destroy","duration":2564.7922,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/Setting.test.ts:27:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"updates a numeric setting in place and keeps the captured initial value","status":"failed","title":"updates a numeric setting in place and keeps the captured initial value","duration":2322.628199999999,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/Setting.test.ts:27:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"changing settingId replaces — old setting restored, new setting pinned","status":"failed","title":"changing settingId replaces — old setting restored, new setting pinned","duration":2647.797999999999,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/Setting.test.ts:27:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates the deployed (zone, setting) pair","status":"failed","title":"list enumerates the deployed (zone, setting) pair","duration":2617.4797,"failureMessages":["Error: zone \"alchemy-test-2.us\" not found in account\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/Setting.test.ts:27:7)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379642655,"endTime":1782379645305.798,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/Setting.test.ts"},{"assertionResults":[{"ancestorTitles":[],"fullName":"create zone retains by default — destroy() opts in to deletion","status":"failed","title":"create zone retains by default — destroy() opts in to deletion","duration":934.1730000000002,"failureMessages":["Forbidden: Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"create zone retains by default — survives stack.destroy()","status":"failed","title":"create zone retains by default — survives stack.destroy()","duration":940.8148000000001,"failureMessages":["Forbidden: Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"adoption — existing zone errors without adopt, takes over with adopt(true)","status":"failed","title":"adoption — existing zone errors without adopt, takes over with adopt(true)","duration":371.9204,"failureMessages":["Forbidden: Requires permission \"com.cloudflare.api.account.zone.create\" to create zones for the selected account\n at Object.matchError (D:/code/alchemy/alchemy-effect/distilled/packages/cloudflare/src/client/api.ts:450:9)\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/node_modules/@distilled.cloud/core/src/client.ts:841:34)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]},{"ancestorTitles":[],"fullName":"list enumerates every zone in the account","status":"failed","title":"list enumerates every zone in the account","duration":411.4635000000003,"failureMessages":["AssertionError: expected undefined to be defined\n at Array. (D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/Zone.test.ts:184:22)\n at Generator.next ()\n at Object.~effect/Effect/successCont (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:883:26)\n at Object.~effect/Effect/evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/core.js:296:30)\n at FiberImpl.runLoop (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:444:107)\n at FiberImpl.evaluate (file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:412:23)\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:736:15\n at file:///D:/code/alchemy/alchemy-effect/node_modules/.bun/effect@4.0.0-beta.84/node_modules/effect/dist/internal/effect.js:712:47\n at processTicksAndRejections (node:internal/process/task_queues:104:5)"],"meta":{},"tags":[]}],"startTime":1782379613202,"endTime":1782379614143.8147,"status":"failed","message":"","name":"D:/code/alchemy/alchemy-effect/packages/alchemy/test/Cloudflare/Zone/Zone.test.ts"}]} \ No newline at end of file diff --git a/distilled b/distilled index 651d0e4bd..05730c76a 160000 --- a/distilled +++ b/distilled @@ -1 +1 @@ -Subproject commit 651d0e4bd3b448d0ad78b23627dd63e972ffc1b7 +Subproject commit 05730c76a738d618b02b23a8d10c925814c1471f diff --git a/examples/aws-ec2/alchemy.run.ts b/examples/aws-ec2/alchemy.run.ts index af4cf02dc..d35c8c867 100644 --- a/examples/aws-ec2/alchemy.run.ts +++ b/examples/aws-ec2/alchemy.run.ts @@ -5,11 +5,12 @@ import * as Effect from "effect/Effect"; import { NetworkLive } from "./src/Network.ts"; import Server from "./src/Server.ts"; -const aws = AWS.providers(); - export default Alchemy.Stack( "AwsEc2Example", - { providers: aws, state: Alchemy.localState() }, + { + providers: AWS.providers(), + state: Alchemy.localState(), + }, Effect.gen(function* () { const instance = yield* Server; diff --git a/examples/cloudflare-agent/alchemy.run.ts b/examples/cloudflare-agent/alchemy.run.ts new file mode 100644 index 000000000..082e67ac9 --- /dev/null +++ b/examples/cloudflare-agent/alchemy.run.ts @@ -0,0 +1,22 @@ +import * as Alchemy from "alchemy"; +import * as Cloudflare from "alchemy/Cloudflare"; +import * as GitHub from "alchemy/GitHub"; +import { Layer } from "effect"; +import * as Effect from "effect/Effect"; +import DevBoxLive from "./src/DevBox.ts"; +import ReleaseService from "./src/ReleaseService.ts"; + +export default Alchemy.Stack( + "Stack", + { + providers: Layer.mergeAll(Cloudflare.providers(), GitHub.providers()), + state: Cloudflare.state(), + }, + Effect.gen(function* () { + const releaseService = yield* ReleaseService; + + return { + releaseService: releaseService.url.as(), + }; + }).pipe(Effect.provide(DevBoxLive)), +); diff --git a/examples/cloudflare-agent/package.json b/examples/cloudflare-agent/package.json new file mode 100644 index 000000000..082dcb810 --- /dev/null +++ b/examples/cloudflare-agent/package.json @@ -0,0 +1,26 @@ +{ + "name": "cloudflare-agent", + "version": "0.0.0", + "private": true, + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "git+https://github.com/alchemy-run/alchemy-effect.git", + "directory": "examples/cloudflare-worker" + }, + "type": "module", + "scripts": { + "deploy": "alchemy deploy", + "dev": "alchemy dev", + "destroy": "alchemy destroy", + "logs": "alchemy logs", + "tail": "alchemy tail", + "test": "bun test" + }, + "dependencies": { + "@effect/platform-bun": "catalog:", + "@effect/platform-node": "catalog:", + "alchemy": "workspace:*", + "effect": "catalog:" + } +} \ No newline at end of file diff --git a/examples/cloudflare-agent/src/ClaudeCode.ts b/examples/cloudflare-agent/src/ClaudeCode.ts new file mode 100644 index 000000000..74f46587e --- /dev/null +++ b/examples/cloudflare-agent/src/ClaudeCode.ts @@ -0,0 +1,9 @@ +import * as Cloudflare from "alchemy/Cloudflare"; + +export class ClaudeCode extends Cloudflare.Container("ClaudeCode", { + dockerfile: ` + FROM alpine:latest + RUN curl -fsSL https://claude.ai/install.sh | bash + `, + context: ".", +}) {} diff --git a/examples/cloudflare-agent/src/DevBox.ts b/examples/cloudflare-agent/src/DevBox.ts new file mode 100644 index 000000000..652f9d937 --- /dev/null +++ b/examples/cloudflare-agent/src/DevBox.ts @@ -0,0 +1,49 @@ +import * as Effect from "effect/Effect"; +import * as FileSystem from "effect/FileSystem"; +import * as Stream from "effect/Stream"; +import * as ChildProcess from "effect/unstable/process/ChildProcess"; +import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner"; + +import * as Cloudflare from "alchemy/Cloudflare"; + +export class DevBox extends Cloudflare.Container< + DevBox, + { + readFile: (path: string) => Effect.Effect; + writeFile: (path: string, contents: string) => Effect.Effect; + exec: (command: string) => Effect.Effect<{ + exitCode: number; + stdout: string; + stderr: string; + }>; + } +>()("DevBox") {} + +export default DevBox.make( + { + main: import.meta.filename, + dockerfile: `FROM oven/bun:1.3`, + }, + Effect.gen(function* () { + const fs = yield* FileSystem.FileSystem; + const cp = yield* ChildProcessSpawner; + + return { + readFile: (path: string) => fs.readFileString(path).pipe(Effect.orDie), + writeFile: (path: string, contents: string) => + fs.writeFileString(path, contents).pipe(Effect.orDie), + exec: (command: string) => + cp.spawn(ChildProcess.make(command, { shell: true })).pipe( + Effect.flatMap(({ exitCode, stdout, stderr }) => + Effect.all({ + exitCode, + stdout: stdout.pipe(Stream.decodeText, Stream.mkString), + stderr: stderr.pipe(Stream.decodeText, Stream.mkString), + }), + ), + Effect.scoped, + Effect.orDie, + ), + }; + }), +); diff --git a/examples/cloudflare-agent/src/ReleaseBlogger.ts b/examples/cloudflare-agent/src/ReleaseBlogger.ts new file mode 100644 index 000000000..8989aa5dc --- /dev/null +++ b/examples/cloudflare-agent/src/ReleaseBlogger.ts @@ -0,0 +1,27 @@ +import * as Alchemy from "alchemy"; +import { Bash } from "./tools/Bash.ts"; +import { EditFile, ReadFile, WriteFile } from "./tools/Fs.ts"; +import { Grep } from "./tools/Grep.ts"; +import { Sql } from "./tools/Sql.ts"; + +export class ReleaseBlogger extends Alchemy.Agent()("Blogger")` +You are the Release Blogger. Your job is to turn a merged pull request into a +release blog post under website/src/content/docs/blog/. + +To do your job: + +1. Use the ${Grep} tool to find the most recent post in + website/src/content/docs/blog/ so you can match its format and version number. +2. Use the ${ReadFile} tool to read that post and the PR diff so you understand + the style, frontmatter, and what actually changed. +3. Use the ${Bash} tool to run any commands you need — e.g. inspecting the git + log or listing the blog directory — to gather context about the release. +4. Use the ${WriteFile} tool to create the new post at + website/src/content/docs/blog/YYYY-MM-DD-beta-NN.md, leading with the + headline features and folding the long tail into an "Also in this release" + list. +5. Use the ${EditFile} tool to revise the draft until the prose is lean, + concise, and zero-fluff, citing PRs inline as ([#NNN](…/pull/NNN)). +6. Use the ${Sql} tool to execute the SQL query to read and update the database. + +Always write in the voice of the existing beta posts. Keep it tight.` {} diff --git a/examples/cloudflare-agent/src/ReleaseService.ts b/examples/cloudflare-agent/src/ReleaseService.ts new file mode 100644 index 000000000..5b16691bd --- /dev/null +++ b/examples/cloudflare-agent/src/ReleaseService.ts @@ -0,0 +1,41 @@ +import * as Cloudflare from "alchemy/Cloudflare"; +import * as Github from "alchemy/GitHub"; +import * as Effect from "effect/Effect"; +import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse"; +import { ReleaseVersion } from "./ReleaseVersion.ts"; + +export default Cloudflare.Worker( + "ReleaseService", + { main: import.meta.filename }, + Effect.gen(function* () { + const versions = yield* ReleaseVersion; + + yield* Github.events({ + owner: "alchemy-run", + repository: "alchemy-effect", + events: ["push"], + }).subscribe((event) => { + const title = event.payload.head_commit?.message.split("\n")[0] ?? ""; + const isRelease = + event.payload.ref === "refs/heads/main" && + title.startsWith("chore(release):"); + + return isRelease + ? versions.getByName(event.payload.head_commit!.id).generateBlog({ + input: event.payload, + }) + : Effect.log( + `Skipping commit "${event.payload.head_commit?.message}" (hash: ${event.payload.head_commit!.id})`, + ); + }); + + return { + fetch: Effect.gen(function* () { + yield* versions.getByName("TEST").generateBlog({ + input: "TEST", + }); + return HttpServerResponse.text("Hello, world!"); + }), + }; + }).pipe(Effect.provide(Cloudflare.GitHubRepositoryEventSourceLive)), +); diff --git a/examples/cloudflare-agent/src/ReleaseVersion.ts b/examples/cloudflare-agent/src/ReleaseVersion.ts new file mode 100644 index 000000000..7f2d1f016 --- /dev/null +++ b/examples/cloudflare-agent/src/ReleaseVersion.ts @@ -0,0 +1,50 @@ +import * as Cloudflare from "alchemy/Cloudflare"; +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import { DevBox } from "./DevBox.ts"; +import { ReleaseBlogger } from "./ReleaseBlogger.ts"; +import { EvalLive } from "./tools/Eval.ts"; +import { WriteFileDevBox } from "./tools/Fs.ts"; +import { GrepLive } from "./tools/Grep.ts"; +import { SqlDurableObjectLive } from "./tools/Sql.ts"; + +export class ReleaseVersion extends Cloudflare.DurableObjectNamespace()( + "ReleaseBlogger", + Effect.gen(function* () { + const blogger = yield* ReleaseBlogger; + const state = yield* Cloudflare.DurableObjectState; + + return Effect.gen(function* () { + // RuntimeContext + const sockets = yield* state.getWebSockets(); + + return { + generateBlog: Effect.fn(function* (request: { input: any }) { + const isStarted = yield* state.storage.get("isStarted"); + // request + if (!isStarted) { + yield* blogger.send(request); + yield* state.storage.put("isStarted", true); + for (const socket of sockets) { + yield* socket.send("Blog generated"); + } + } + }), + }; + }); + }).pipe( + Effect.provide( + SqlDurableObjectLive.pipe( + Layer.provideMerge(WriteFileDevBox), + Layer.provideMerge(GrepLive), + Layer.provideMerge(EvalLive), + Layer.provideMerge(Cloudflare.layerChatDurableObject), + Layer.provideMerge( + Cloudflare.layerContainer(DevBox, { + enableInternet: true, + }), + ), + ), + ), + ), +) {} diff --git a/examples/cloudflare-agent/src/tools/Bash.ts b/examples/cloudflare-agent/src/tools/Bash.ts new file mode 100644 index 000000000..3f1c6fcd3 --- /dev/null +++ b/examples/cloudflare-agent/src/tools/Bash.ts @@ -0,0 +1,15 @@ +import * as Effect from "effect/Effect"; +import * as S from "effect/Schema"; + +import * as AI from "alchemy/Ai"; + +export const cmd = AI.Parameter("cmd")( + S.String.pipe(S.optional), +)`The command to run.`; + +export const Bash = AI.Tool("bash")` +Run a shell ${cmd} and return its stdout, stderr, and exit code.`( + Effect.fn(function* ({ cmd }) { + void cmd; + }), +); diff --git a/examples/cloudflare-agent/src/tools/Eval.ts b/examples/cloudflare-agent/src/tools/Eval.ts new file mode 100644 index 000000000..948994178 --- /dev/null +++ b/examples/cloudflare-agent/src/tools/Eval.ts @@ -0,0 +1,55 @@ +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import * as S from "effect/Schema"; +import { HttpClientRequest } from "effect/unstable/http"; + +import * as AI from "alchemy/Ai"; +import * as Cloudflare from "alchemy/Cloudflare"; +import { dedent } from "alchemy/Util"; + +export const code = AI.Parameter("code")(S.String)` +The JavaScript code to evaluate. +Must be a valid module with a default export function accepting no arguments. +Can return any value, including a Promise.`; + +export class Eval extends AI.Tool()("eval")` +Evaluate JavaScript ${code}` {} + +export const EvalLive = Layer.effect( + Eval, + Effect.gen(function* () { + const vm = yield* Cloudflare.WorkerLoader("Eval"); + + return ({ code }) => + vm + .load({ + mainModule: "index.js", + compatibilityDate: "2026-01-28", + modules: { + "code.js": code, + "index.js": dedent` + import util from "node:util"; + import code from "./code.js"; + const lines: string[] = []; + console.log = (...args) => lines.push(util.formatWithOptions({ depth: null }, ...args)); + export default { + fetch: async () => { + const output = await code(); + return Response(lines.join("\\n") + "\\n" + util.inspect(output, { depth: null })); + } + }`, + }, + }) + .pipe( + Effect.flatMap((worker) => + worker.fetch(HttpClientRequest.get("https://worker/")), + ), + Effect.flatMap((response) => response.text), + Effect.catch((e) => + Effect.succeed({ + error: e, + }), + ), + ); + }), +); diff --git a/examples/cloudflare-agent/src/tools/Fs.ts b/examples/cloudflare-agent/src/tools/Fs.ts new file mode 100644 index 000000000..cfe5fa630 --- /dev/null +++ b/examples/cloudflare-agent/src/tools/Fs.ts @@ -0,0 +1,44 @@ +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import * as S from "effect/Schema"; + +import * as Cloudflare from "alchemy/Cloudflare"; + +import * as AI from "alchemy/Ai"; +import { DevBox } from "../DevBox.ts"; + +export const path = AI.Parameter("path", S.String)` +The path to the file to search.`; + +export const contents = AI.Parameter("contents", S.String)` +The contents of the file to write.`; + +export class WriteFile extends AI.Tool()("writeFile")` +Create or overwrite a file at the given ${path} with the provided ${contents}.` {} + +export const Storage = Cloudflare.R2Bucket("Storage"); + +export const WriteFileR2 = Layer.effect( + WriteFile, + Effect.gen(function* () { + const bucket = yield* Cloudflare.R2.ReadWriteBucket(Storage); + + return ({ path, contents }) => + bucket.put(path, contents).pipe(Effect.orDie); + }), +); + +export const WriteFileDevBox = Layer.effect( + WriteFile, + Effect.gen(function* () { + const devBox = yield* DevBox; + + return ({ path, contents }) => devBox.writeFile(path, contents); + }), +); + +export class ReadFile extends AI.Tool("readFile")` +Read the contents of a file at the given ${path}.` {} + +export class EditFile extends AI.Tool("editFile")` +Apply a targeted edit to an existing file by replacing an exact string with a new one.` {} diff --git a/examples/cloudflare-agent/src/tools/Grep.ts b/examples/cloudflare-agent/src/tools/Grep.ts new file mode 100644 index 000000000..8d488370e --- /dev/null +++ b/examples/cloudflare-agent/src/tools/Grep.ts @@ -0,0 +1,23 @@ +import * as Effect from "effect/Effect"; +import * as FileSystem from "effect/FileSystem"; +import * as Layer from "effect/Layer"; +import * as S from "effect/Schema"; + +import * as AI from "alchemy/Ai"; + +export const regex = AI.Parameter("regex")( + S.String, +)`The regex pattern to search for.`; + +export class Grep extends AI.Tool()("grep")` +Search files for a ${regex} pattern and return the matching lines with their file paths and line numbers.` {} + +export const GrepLive = Layer.effect( + Grep, + Effect.gen(function* () { + const _fs = yield* FileSystem.FileSystem; + return Effect.fn(function* ({ regex }) { + void regex; + }); + }), +); diff --git a/examples/cloudflare-agent/src/tools/Sql.ts b/examples/cloudflare-agent/src/tools/Sql.ts new file mode 100644 index 000000000..cb6808d2d --- /dev/null +++ b/examples/cloudflare-agent/src/tools/Sql.ts @@ -0,0 +1,27 @@ +import * as AI from "alchemy/Ai"; +import * as Cloudflare from "alchemy/Cloudflare"; +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import * as S from "effect/Schema"; + +export const sql = AI.Parameter("sql", S.String)`The SQL query to execute.`; + +export class Sql extends AI.Tool()("sql")` +Execute a ${sql} query on the database and return the result.` {} + +export const SqlDurableObjectLive = Layer.effect( + Sql, + Effect.gen(function* () { + const object = yield* Cloudflare.DurableObjectState; + + return Effect.gen(function* () { + // use environment dependencies when an instance of the environment is instantiated + yield* object.storage.sql.exec("CREATE TABLE IF NOT EXIST ..."); + + return ({ sql }) => + object.storage.sql + .exec(sql) + .pipe(Effect.flatMap((result) => result.toArray())); + }); + }), +); diff --git a/examples/cloudflare-agent/tsconfig.json b/examples/cloudflare-agent/tsconfig.json new file mode 100644 index 000000000..fa309f6d5 --- /dev/null +++ b/examples/cloudflare-agent/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "../../tsconfig.base.json", + "include": [ + "alchemy.run.ts", + "src/**/*.ts", + "test/**/*.ts" + ], + "compilerOptions": { + "noEmit": true, + "rootDir": ".", + "module": "Preserve", + "moduleResolution": "Bundler", + "target": "ESNext" + }, + "references": [ + { + "path": "../../packages/alchemy/tsconfig.json" + }, + ] +} \ No newline at end of file diff --git a/examples/cloudflare-dev/src/EffectWorker.ts b/examples/cloudflare-dev/src/EffectWorker.ts index d7b8871ea..8e0eae3d3 100644 --- a/examples/cloudflare-dev/src/EffectWorker.ts +++ b/examples/cloudflare-dev/src/EffectWorker.ts @@ -30,7 +30,7 @@ export default class EffectWorker extends Cloudflare.Worker()( }, }, Effect.gen(function* () { - const kv = yield* Cloudflare.KVNamespace.bind(KV); + const kv = yield* Cloudflare.KV.ReadWriteNamespace(KV); const queue = yield* Cloudflare.Queue("EffectWorkerQueue"); const queueBinding = yield* Cloudflare.Queue.bind(queue); const sandbox = yield* SandboxDO; @@ -101,8 +101,8 @@ export default class EffectWorker extends Cloudflare.Worker()( }; }).pipe( Effect.provide([ - Cloudflare.KVNamespaceBindingLive, - Cloudflare.QueueBindingLive, + Cloudflare.KV.ReadWriteNamespaceBinding, + Cloudflare.Queues.WriteQueueBinding, Cloudflare.QueueEventSourceLive, ]), ), diff --git a/examples/cloudflare-dev/src/NotifyWorkflow.ts b/examples/cloudflare-dev/src/NotifyWorkflow.ts index be3701d86..102699d3c 100644 --- a/examples/cloudflare-dev/src/NotifyWorkflow.ts +++ b/examples/cloudflare-dev/src/NotifyWorkflow.ts @@ -27,7 +27,7 @@ export default class NotifyWorkflow extends Cloudflare.Workflow( // to the outer `Effect.succeed(body)` wrapper (a no-op) instead of // `body` itself in `Workflow.ts`. Exercising `kv.put` / `kv.get` from // inside a `task` keeps the integ test catching any future regression. - const kv = yield* Cloudflare.KVNamespace.bind(KV); + const kv = yield* Cloudflare.KV.ReadWriteNamespace(KV); return Effect.fn(function* (input: { roomId: string; message: string }) { const { roomId, message } = input; diff --git a/examples/cloudflare-dev/test/integ.test.ts b/examples/cloudflare-dev/test/integ.test.ts index d4d3b00a3..fac3e934d 100644 --- a/examples/cloudflare-dev/test/integ.test.ts +++ b/examples/cloudflare-dev/test/integ.test.ts @@ -127,7 +127,7 @@ test( ); /** - * EffectWorker binds a KV namespace via `Cloudflare.KVNamespace.bind(KV)` + * EffectWorker binds a KV namespace via `Cloudflare.KV.ReadWriteNamespace(KV)` * and returns the result of `kv.list()` as JSON. A successful response * proves the Effect-style binding wired the runtime SDK and the * `WorkerEnvironment` service was provisioned for the fetch handler. diff --git a/examples/cloudflare-tanstack/src/backend.ts b/examples/cloudflare-tanstack/src/backend.ts index 79e67c7b8..ff6435b46 100644 --- a/examples/cloudflare-tanstack/src/backend.ts +++ b/examples/cloudflare-tanstack/src/backend.ts @@ -11,7 +11,7 @@ export default class Backend extends Cloudflare.Worker()( main: import.meta.filename, }, Effect.gen(function* () { - const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); + const bucket = yield* Cloudflare.R2.ReadWriteBucket(Bucket); return { // RPC method — read an object from R2 by key, returning the body as @@ -61,5 +61,5 @@ export default class Backend extends Cloudflare.Worker()( ), ), }; - }).pipe(Effect.provide(Cloudflare.R2BucketBindingLive)), + }).pipe(Effect.provide(Cloudflare.R2.ReadWriteBucketBinding)), ) {} diff --git a/examples/cloudflare-worker-async/alchemy.run.ts b/examples/cloudflare-worker-async/alchemy.run.ts index c5164d362..85b3f20bb 100644 --- a/examples/cloudflare-worker-async/alchemy.run.ts +++ b/examples/cloudflare-worker-async/alchemy.run.ts @@ -11,7 +11,7 @@ export const Bucket = Cloudflare.R2Bucket("Bucket"); // Queue producer + consumer wiring (both sides exercised by the same worker). // The Worker sends a message via `env.QUEUE.send(...)` from POST /queue/send, // then receives and persists it via its `queue(batch)` handler — end-to-end -// regression guard for the Queue, QueueBinding, and QueueConsumer resources. +// regression guard for the Queue, QueueWrite, and QueueConsumer resources. export const Queue = Cloudflare.Queue("Queue"); export const Counter = Cloudflare.DurableObjectNamespace( @@ -21,6 +21,14 @@ export const Counter = Cloudflare.DurableObjectNamespace( }, ); +export const ClaudeCode = Cloudflare.Container("ClaudeCode", { + dockerfile: ` + FROM alpine:latest + RUN curl -fsSL https://claude.ai/install.sh | bash + `, + context: ".", +}); + export type WorkerEnv = Cloudflare.InferEnv; export const Worker = Cloudflare.Worker("Worker", { diff --git a/examples/cloudflare-worker-async/src/object.ts b/examples/cloudflare-worker-async/src/object.ts new file mode 100644 index 000000000..2404c7025 --- /dev/null +++ b/examples/cloudflare-worker-async/src/object.ts @@ -0,0 +1,8 @@ +import { DurableObject } from "cloudflare:workers"; +import type { WorkerEnv } from "../alchemy.run.ts"; + +export class ClaudeCode extends DurableObject { + constructor(state: DurableObjectState, env: WorkerEnv) { + super(state, env); + } +} diff --git a/examples/cloudflare-worker-async/src/worker.ts b/examples/cloudflare-worker-async/src/worker.ts index 2b8078417..8d0e299e4 100644 --- a/examples/cloudflare-worker-async/src/worker.ts +++ b/examples/cloudflare-worker-async/src/worker.ts @@ -26,7 +26,7 @@ export default { // Queue producer — POST /queue/send?text=... // - // Exercises Cloudflare.QueueBinding by calling `env.Queue.send(...)`. + // Exercises Cloudflare.Queues.WriteQueue by calling `env.Queue.send(...)`. // The message is persisted by the consumer handler into R2 at /queue/ // so the integ test can read it back and assert the full round-trip. if (request.method === "POST" && path === "/queue/send") { diff --git a/examples/cloudflare-worker/alchemy.run.ts b/examples/cloudflare-worker/alchemy.run.ts index 384038669..15ceb6950 100644 --- a/examples/cloudflare-worker/alchemy.run.ts +++ b/examples/cloudflare-worker/alchemy.run.ts @@ -5,6 +5,7 @@ import * as Effect from "effect/Effect"; import { Gateway } from "./src/AiGateway.ts"; import Api from "./src/Api.ts"; import { Bucket } from "./src/Bucket.ts"; +import SandboxLive from "./src/Sandbox.ts"; import SecondaryApiLive, { SecondaryApi } from "./src/SecondaryApi.ts"; import WorkerTagLive, { WorkerTag } from "./src/WorkerTag.ts"; @@ -52,5 +53,5 @@ export default Alchemy.Stack( secondaryApiUrl: secondaryApi.url.as(), deployedAt: announcement.deployedAt, }; - }).pipe(Effect.provide([WorkerTagLive, SecondaryApiLive])), + }).pipe(Effect.provide([WorkerTagLive, SecondaryApiLive, SandboxLive])), ); diff --git a/examples/cloudflare-worker/src/Agent.ts b/examples/cloudflare-worker/src/Agent.ts index c1f2d93c1..e3411d594 100644 --- a/examples/cloudflare-worker/src/Agent.ts +++ b/examples/cloudflare-worker/src/Agent.ts @@ -6,15 +6,10 @@ import { Sandbox } from "./Sandbox.ts"; export default class Agent extends Cloudflare.DurableObjectNamespace()( "Agents", Effect.gen(function* () { - const sandbox = yield* Cloudflare.Container.bind(Sandbox); + const container = yield* Sandbox; + const state = yield* Cloudflare.DurableObjectState; return Effect.gen(function* () { - const state = yield* Cloudflare.DurableObjectState; - - const container = yield* Cloudflare.start(sandbox, { - enableInternet: true, - }); - const sessions = new Map(); for (const socket of yield* state.getWebSockets()) { @@ -25,7 +20,7 @@ export default class Agent extends Cloudflare.DurableObjectNamespace()( } return { - exec: (command: string) => container.exec(command), + exec: (command: string) => container.exec(command).pipe(Effect.orDie), hello: () => Effect.gen(function* () { const { fetch } = yield* container.getTcpPort(3000); @@ -33,7 +28,7 @@ export default class Agent extends Cloudflare.DurableObjectNamespace()( HttpClientRequest.get("http://container/"), ); return yield* response.text; - }), + }).pipe(Effect.orDie), increment: () => Effect.gen(function* () { const { fetch } = yield* container.getTcpPort(3000); @@ -41,14 +36,14 @@ export default class Agent extends Cloudflare.DurableObjectNamespace()( HttpClientRequest.post("http://container/increment"), ); return yield* response.text; - }), + }).pipe(Effect.orDie), fetch: Effect.gen(function* () { const [response, socket] = yield* Cloudflare.upgrade(); const id = "TODO"; socket.serializeAttachment({ id }); sessions.set(id, socket); return response; - }), + }).pipe(Effect.orDie), webSocketMessage: Effect.fnUntraced(function* ( socket: Cloudflare.DurableWebSocket, message: string | Uint8Array, @@ -77,5 +72,11 @@ export default class Agent extends Cloudflare.DurableObjectNamespace()( }), }; }); - }), + }).pipe( + Effect.provide( + Cloudflare.layerContainer(Sandbox, { + enableInternet: true, + }), + ), + ), ) {} diff --git a/examples/cloudflare-worker/src/Api.ts b/examples/cloudflare-worker/src/Api.ts index 7b27e2289..7fe217c3f 100644 --- a/examples/cloudflare-worker/src/Api.ts +++ b/examples/cloudflare-worker/src/Api.ts @@ -39,10 +39,10 @@ export default class Api extends Cloudflare.Worker()( const rooms = yield* Room; const notifier = yield* NotifyWorkflow; const loader = yield* Cloudflare.WorkerLoader("Loader"); - const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); - const kv = yield* Cloudflare.KVNamespace.bind(KV); + const bucket = yield* Cloudflare.R2.ReadWriteBucket(Bucket); + const kv = yield* Cloudflare.KV.ReadWriteNamespace(KV); const queueResource = yield* Queue; - const queue = yield* Cloudflare.QueueBinding.bind(queueResource); + const queue = yield* Cloudflare.Queues.WriteQueue(queueResource); const repos = yield* Cloudflare.Artifacts.bind(Repos); const aiGateway = yield* Cloudflare.AiGateway.bind(Gateway); @@ -353,7 +353,7 @@ export default class Api extends Cloudflare.Worker()( // GET /queue/result/:id reads the bucket entry the consumer // wrote when it processed that message. // - // Producer side: `Cloudflare.QueueBinding`. Consumer side: + // Producer side: `Cloudflare.Queues.WriteQueue`. Consumer side: // `Cloudflare.messages(Queue).subscribe(...)` registered in // the init phase (above), with `QueueEventSourceLive` on the // worker layer. @@ -446,9 +446,9 @@ export default class Api extends Cloudflare.Worker()( }).pipe( Effect.provide( Layer.mergeAll( - Cloudflare.R2BucketBindingLive, - Cloudflare.KVNamespaceBindingLive, - Cloudflare.QueueBindingLive, + Cloudflare.R2.ReadWriteBucketBinding, + Cloudflare.KV.ReadWriteNamespaceBinding, + Cloudflare.Queues.WriteQueueBinding, Cloudflare.QueueEventSourceLive, Cloudflare.ArtifactsBindingLive, Cloudflare.AiGatewayBindingLive, diff --git a/examples/cloudflare-worker/src/NotifyWorkflow.ts b/examples/cloudflare-worker/src/NotifyWorkflow.ts index 846c3ffc5..a87075cbb 100644 --- a/examples/cloudflare-worker/src/NotifyWorkflow.ts +++ b/examples/cloudflare-worker/src/NotifyWorkflow.ts @@ -28,7 +28,7 @@ export default class NotifyWorkflow extends Cloudflare.Workflow( // to the outer `Effect.succeed(body)` wrapper (a no-op) instead of // `body` itself in `Workflow.ts`. Exercising `kv.put` / `kv.get` from // inside a `task` keeps the integ test catching any future regression. - const kv = yield* Cloudflare.KVNamespace.bind(KV); + const kv = yield* Cloudflare.KV.ReadWriteNamespace(KV); return Effect.fn(function* (input: { roomId: string; message: string }) { const { roomId, message } = input; diff --git a/examples/cloudflare-worker/src/Queue.ts b/examples/cloudflare-worker/src/Queue.ts index 08ad52e24..76d9e9413 100644 --- a/examples/cloudflare-worker/src/Queue.ts +++ b/examples/cloudflare-worker/src/Queue.ts @@ -2,7 +2,7 @@ import * as Cloudflare from "alchemy/Cloudflare"; /** * Queue resource used by the Api worker as a producer (via - * `Cloudflare.QueueBinding.bind(Queue)`). Exercises the Queue + QueueBinding + * `Cloudflare.Queues.WriteQueue(Queue)`). Exercises the Queue + QueueWrite * resources end-to-end in the Effect-based example. * * The async example (`examples/cloudflare-worker-async`) demonstrates the diff --git a/examples/cloudflare-worker/src/Sandbox.ts b/examples/cloudflare-worker/src/Sandbox.ts index 64e46d292..558c8c97f 100644 --- a/examples/cloudflare-worker/src/Sandbox.ts +++ b/examples/cloudflare-worker/src/Sandbox.ts @@ -23,8 +23,9 @@ export class Sandbox extends Cloudflare.Container< PlatformError >; } ->()( - "Sandbox", +>()("Sandbox") {} + +export const SandboxLive = /* @__PURE__ */ Sandbox.make( Stack.useSync((stack) => ({ main: import.meta.filename, instanceType: stack.stage === "prod" ? "standard-1" : "dev", @@ -34,9 +35,6 @@ export class Sandbox extends Cloudflare.Container< }, }, })), -) {} - -export const SandboxLive = /* @__PURE__ */ Sandbox.make( Effect.gen(function* () { // const cp = yield* ChildProcessSpawner; diff --git a/examples/cloudflare-worker/src/SecondaryApi.ts b/examples/cloudflare-worker/src/SecondaryApi.ts index b99e4c663..f45db4fc7 100644 --- a/examples/cloudflare-worker/src/SecondaryApi.ts +++ b/examples/cloudflare-worker/src/SecondaryApi.ts @@ -9,15 +9,15 @@ import Agent from "./Agent.ts"; // ContainerApplication carrying the Agent namespace. With two Workers binding // the same DO, the Sandbox ends up with two bindings that share a single // `namespaceId` — the regression case for the dedupe fix in this PR. -export class SecondaryApi extends Cloudflare.Worker()( +export class SecondaryApi extends Cloudflare.Worker()( "SecondaryApi", +) {} + +export default SecondaryApi.make( { main: import.meta.filename, observability: { enabled: true }, }, -) {} - -export default SecondaryApi.make( Effect.gen(function* () { const agents = yield* Agent; diff --git a/examples/cloudflare-worker/src/WorkerTag.ts b/examples/cloudflare-worker/src/WorkerTag.ts index 7c20ef7e0..770ebab41 100644 --- a/examples/cloudflare-worker/src/WorkerTag.ts +++ b/examples/cloudflare-worker/src/WorkerTag.ts @@ -5,25 +5,26 @@ import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse"; // Tagged Worker DX: declare the class first (lightweight identifier), // then provide the runtime implementation in a second `.make()` call. // This mirrors the pattern in the README/docstring on Cloudflare.Worker. -export class WorkerTag extends Cloudflare.Worker()("WorkerTag", { - main: import.meta.filename, - compatibility: { - flags: ["nodejs_compat"], - date: "2026-04-26", - }, - observability: { - enabled: true, - }, -}) {} +export class WorkerTag extends Cloudflare.Worker()( + "WorkerTag", +) {} export default WorkerTag.make( - Effect.gen(function* () { - return { - fetch: Effect.gen(function* () { - return HttpServerResponse.text("Hello from WorkerTag", { - status: 200, - }); - }), - }; + { + main: import.meta.filename, + compatibility: { + flags: ["nodejs_compat"], + date: "2026-04-26", + }, + observability: { + enabled: true, + }, + }, + Effect.succeed({ + fetch: Effect.gen(function* () { + return HttpServerResponse.text("Hello from WorkerTag", { + status: 200, + }); + }), }), ); diff --git a/package.json b/package.json index 3c1a7e1da..5c2be8172 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "test:canary": "SMOKE_CANARY=1 bun test --only-failures ./test/smoke.test.ts", "test:examples": "bun run --filter ./examples/cloudflare-worker --filter ./examples/cloudflare-worker-auth --filter ./examples/cloudflare-worker-async --filter ./examples/cloudflare-tanstack --filter ./examples/cloudflare-tanstack-start-solid --filter ./examples/aws-lambda test && bun format", "test": "cd ./packages/alchemy && bun vitest run", - "test:fast": "FAST=1 NO_SLOW_TESTS=1 cd ./packages/alchemy && bun vitest run" + "test:fast": "FAST=1 cd ./packages/alchemy && bun vitest run" }, "workspaces": { "packages": [ diff --git a/packages/alchemy/package.json b/packages/alchemy/package.json index 942a971d5..2b8e6710c 100644 --- a/packages/alchemy/package.json +++ b/packages/alchemy/package.json @@ -42,6 +42,12 @@ "worker": "./src/*.ts", "import": "./lib/*.js" }, + "./Ai": { + "types": "./lib/Ai/index.d.ts", + "bun": "./src/Ai/index.ts", + "worker": "./src/Ai/index.ts", + "import": "./lib/Ai/index.js" + }, "./Auth": { "types": "./lib/Auth/index.d.ts", "bun": "./src/Auth/index.ts", @@ -387,4 +393,4 @@ "optional": true } } -} +} \ No newline at end of file diff --git a/packages/alchemy/src/AI/Agent.ts b/packages/alchemy/src/AI/Agent.ts new file mode 100644 index 000000000..89d44d62b --- /dev/null +++ b/packages/alchemy/src/AI/Agent.ts @@ -0,0 +1,78 @@ +import type * as Context from "effect/Context"; +import * as Effect from "effect/Effect"; +import type { RuntimeContext } from "../RuntimeContext.ts"; +import { effectClass } from "../Util/effect.ts"; +import type { ToolImpl } from "./Tool.ts"; + +export type Services = Refs[number] extends infer A + ? A extends + | ToolImpl + | Context.Service + ? Req + : never + : never; + +export interface Agent< + Name extends string = string, + Refs extends any[] = any[], + Service = AgentService, + Req = never, +> { + [Symbol.iterator](): Effect.EffectIterator< + Effect.Effect + >; + "~alchemy/Kind": "Agent"; + name: Name; + refs: Refs; + req: Services; + new (): AgentService; +} + +export interface AgentService { + send(request: { + input: any; + session?: string; + }): Effect.Effect; +} + +export const Agent: { + (): { + ( + id: Name, + ): { + ( + template: TemplateStringsArray, + ...refs: Refs + ): Agent>; + }; + }; + ( + id: Name, + ): { + ( + template: TemplateStringsArray, + ...refs: Refs + ): Agent>; + }; +} = ((name?: string) => + name + ? (template: TemplateStringsArray, ...refs: any[]) => + makeAgent(name, template, refs) + : (name: string) => + (template: TemplateStringsArray, ...refs: any[]) => + makeAgent(name, template, refs)) as any; + +const makeAgent = (name: string, template: TemplateStringsArray, refs: any[]) => + Object.assign( + effectClass( + Effect.gen(function* () { + // TODO(sam): implement the agent + }), + ), + { + "~alchemy/Kind": "Agent", + "~alchemy/Name": name, + refs, + template, + }, + ) as any; diff --git a/packages/alchemy/src/AI/Parameter.ts b/packages/alchemy/src/AI/Parameter.ts new file mode 100644 index 000000000..10b1a6168 --- /dev/null +++ b/packages/alchemy/src/AI/Parameter.ts @@ -0,0 +1,57 @@ +import * as S from "effect/Schema"; + +export type Parameter< + Name extends string = string, + Schema extends S.Top = S.Top, + Refs extends any[] = any[], +> = { + "~alchemy/Kind": "Param"; + "~alchemy/Name": Name; + schema: Schema; + template: TemplateStringsArray; + refs: Refs; +}; + +export const Parameter: { + ( + name: Name, + ): { + ( + schema: Schema, + ): { + ( + template: TemplateStringsArray, + ...refs: Refs + ): Parameter; + }; + }; + ( + name: Name, + schema: Schema, + ): { + ( + template: TemplateStringsArray, + ...refs: Refs + ): Parameter; + }; +} = ((name: string, schema?: S.Top) => + schema + ? (template: TemplateStringsArray, ...refs: any[]) => + makeParameter(name, schema, template, refs) + : (schema: S.Top) => + (template: TemplateStringsArray, ...refs: any[]) => + makeParameter(name, schema, template, refs)) as any; + +const makeParameter = ( + name: string, + schema: S.Top, + template: TemplateStringsArray, + refs: any[], +) => + Object.assign(function () {}, { + "~alchemy/Kind": "Param", + "~alchemy/Name": name, + schema, + template, + refs, + }) as any; diff --git a/packages/alchemy/src/AI/Tool.ts b/packages/alchemy/src/AI/Tool.ts new file mode 100644 index 000000000..57baee2b1 --- /dev/null +++ b/packages/alchemy/src/AI/Tool.ts @@ -0,0 +1,98 @@ +import type * as Context from "effect/Context"; +import * as Effect from "effect/Effect"; +import type { RuntimeContext } from "../RuntimeContext.ts"; +import type { Parameter } from "./Parameter.ts"; + +export type ToolParameters = { + [toolName in Extract["~alchemy/Name"]]: Extract< + Refs, + Parameter + >["schema"]["Type"]; +}; + +export interface Tool< + Name extends string = string, + Refs extends any[] = any[], +> { + "~alchemy/Kind": "Tool"; + "~alchemy/Name": Name; + refs: Refs; + template: TemplateStringsArray; + params: { + [p in keyof ToolParameters]: ToolParameters[p]; + }; + impl: (props: this["params"]) => Effect.Effect; + new (): Tool; + ( + impl: Effect.Effect< + (props: this["params"]) => Effect.Effect, + Err, + Req + >, + ): ToolImpl; + ( + impl: (props: this["params"]) => Effect.Effect, + ): ToolImpl; +} + +export interface ToolImpl< + T extends Tool = any, + Err = any, + Req = any, +> { + "~alchemy/Kind": "ToolImpl"; + tool: T; + impl: (props: T["params"]) => Effect.Effect; + new (): {}; +} + +export const Tool: { + ( + name: Name, + ): { + ( + template: TemplateStringsArray, + ...refs: Refs + ): Tool; + }; + (): { + ( + name: Name, + ): { + ( + template: TemplateStringsArray, + ...refs: Refs + ): Tool & + Context.Service< + Self, + | (( + input: ToolParameters, + ) => Effect.Effect) + | Effect.Effect< + ( + input: ToolParameters, + ) => Effect.Effect, + never, + RuntimeContext + > + >; + }; + }; +} = ((name?: string) => + name + ? (template: TemplateStringsArray, ...refs: any[]) => + makeTool(name, template, refs) + : (name: string) => + (template: TemplateStringsArray, ...refs: any[]) => + makeTool(name, template, refs)) as any; + +const makeTool = (name: string, template: TemplateStringsArray, refs: any[]) => + Object.assign( + function (impl: (props: any) => Effect.Effect) {}, + { + "~alchemy/Kind": "Tool", + "~alchemy/Name": name, + refs, + template, + }, + ) as any; diff --git a/packages/alchemy/src/AI/index.ts b/packages/alchemy/src/AI/index.ts new file mode 100644 index 000000000..c17bb2b64 --- /dev/null +++ b/packages/alchemy/src/AI/index.ts @@ -0,0 +1,3 @@ +export * from "./Agent.ts"; +export * from "./Parameter.ts"; +export * from "./Tool.ts"; diff --git a/packages/alchemy/src/AWS/CloudWatch/DescribeAlarmContributors.ts b/packages/alchemy/src/AWS/CloudWatch/DescribeAlarmContributors.ts index e376f02ee..0562656bf 100644 --- a/packages/alchemy/src/AWS/CloudWatch/DescribeAlarmContributors.ts +++ b/packages/alchemy/src/AWS/CloudWatch/DescribeAlarmContributors.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; +import type { Providers } from "../Providers.ts"; import type { AlarmResource } from "./binding-common.ts"; export interface DescribeAlarmContributorsRequest extends Omit< @@ -53,7 +54,8 @@ export const DescribeAlarmContributorsLive = Layer.effect( export class DescribeAlarmContributorsPolicy extends Binding.Policy< DescribeAlarmContributorsPolicy, - (alarm: AlarmResource) => Effect.Effect + (alarm: AlarmResource) => Effect.Effect, + Providers >()("AWS.CloudWatch.DescribeAlarmContributors") {} export const DescribeAlarmContributorsPolicyLive = diff --git a/packages/alchemy/src/AWS/CloudWatch/DescribeAlarmHistory.ts b/packages/alchemy/src/AWS/CloudWatch/DescribeAlarmHistory.ts index faae096df..d66f693d3 100644 --- a/packages/alchemy/src/AWS/CloudWatch/DescribeAlarmHistory.ts +++ b/packages/alchemy/src/AWS/CloudWatch/DescribeAlarmHistory.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; +import type { Providers } from "../Providers.ts"; export interface DescribeAlarmHistoryRequest extends cloudwatch.DescribeAlarmHistoryInput {} @@ -40,7 +41,8 @@ export const DescribeAlarmHistoryLive = Layer.effect( export class DescribeAlarmHistoryPolicy extends Binding.Policy< DescribeAlarmHistoryPolicy, - () => Effect.Effect + () => Effect.Effect, + Providers >()("AWS.CloudWatch.DescribeAlarmHistory") {} export const DescribeAlarmHistoryPolicyLive = diff --git a/packages/alchemy/src/AWS/CloudWatch/DescribeAlarms.ts b/packages/alchemy/src/AWS/CloudWatch/DescribeAlarms.ts index a2305bfd5..938daeb95 100644 --- a/packages/alchemy/src/AWS/CloudWatch/DescribeAlarms.ts +++ b/packages/alchemy/src/AWS/CloudWatch/DescribeAlarms.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import { type AlarmResource, sortAlarmResources } from "./binding-common.ts"; +import type { Providers } from "../Providers.ts"; export interface DescribeAlarmsRequest extends Omit< cloudwatch.DescribeAlarmsInput, @@ -67,7 +68,8 @@ export const DescribeAlarmsLive = Layer.effect( export class DescribeAlarmsPolicy extends Binding.Policy< DescribeAlarmsPolicy, - (...alarms: AlarmResources) => Effect.Effect + (...alarms: AlarmResources) => Effect.Effect, + Providers >()("AWS.CloudWatch.DescribeAlarms") {} export const DescribeAlarmsPolicyLive = DescribeAlarmsPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/CloudWatch/DescribeAlarmsForMetric.ts b/packages/alchemy/src/AWS/CloudWatch/DescribeAlarmsForMetric.ts index 6183c7a11..6a6b55f06 100644 --- a/packages/alchemy/src/AWS/CloudWatch/DescribeAlarmsForMetric.ts +++ b/packages/alchemy/src/AWS/CloudWatch/DescribeAlarmsForMetric.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; +import type { Providers } from "../Providers.ts"; export interface DescribeAlarmsForMetricRequest extends cloudwatch.DescribeAlarmsForMetricInput {} @@ -40,7 +41,8 @@ export const DescribeAlarmsForMetricLive = Layer.effect( export class DescribeAlarmsForMetricPolicy extends Binding.Policy< DescribeAlarmsForMetricPolicy, - () => Effect.Effect + () => Effect.Effect, + Providers >()("AWS.CloudWatch.DescribeAlarmsForMetric") {} export const DescribeAlarmsForMetricPolicyLive = diff --git a/packages/alchemy/src/AWS/CloudWatch/DescribeAnomalyDetectors.ts b/packages/alchemy/src/AWS/CloudWatch/DescribeAnomalyDetectors.ts index 63ae483b3..8486d567a 100644 --- a/packages/alchemy/src/AWS/CloudWatch/DescribeAnomalyDetectors.ts +++ b/packages/alchemy/src/AWS/CloudWatch/DescribeAnomalyDetectors.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; +import type { Providers } from "../Providers.ts"; export interface DescribeAnomalyDetectorsRequest extends cloudwatch.DescribeAnomalyDetectorsInput {} @@ -42,7 +43,8 @@ export const DescribeAnomalyDetectorsLive = Layer.effect( export class DescribeAnomalyDetectorsPolicy extends Binding.Policy< DescribeAnomalyDetectorsPolicy, - () => Effect.Effect + () => Effect.Effect, + Providers >()("AWS.CloudWatch.DescribeAnomalyDetectors") {} export const DescribeAnomalyDetectorsPolicyLive = diff --git a/packages/alchemy/src/AWS/CloudWatch/DescribeInsightRules.ts b/packages/alchemy/src/AWS/CloudWatch/DescribeInsightRules.ts index 7d4599a4f..f8efb73f7 100644 --- a/packages/alchemy/src/AWS/CloudWatch/DescribeInsightRules.ts +++ b/packages/alchemy/src/AWS/CloudWatch/DescribeInsightRules.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; +import type { Providers } from "../Providers.ts"; export interface DescribeInsightRulesRequest extends cloudwatch.DescribeInsightRulesInput {} @@ -40,7 +41,8 @@ export const DescribeInsightRulesLive = Layer.effect( export class DescribeInsightRulesPolicy extends Binding.Policy< DescribeInsightRulesPolicy, - () => Effect.Effect + () => Effect.Effect, + Providers >()("AWS.CloudWatch.DescribeInsightRules") {} export const DescribeInsightRulesPolicyLive = diff --git a/packages/alchemy/src/AWS/CloudWatch/DisableAlarmActions.ts b/packages/alchemy/src/AWS/CloudWatch/DisableAlarmActions.ts index 35a52533f..b6ce046eb 100644 --- a/packages/alchemy/src/AWS/CloudWatch/DisableAlarmActions.ts +++ b/packages/alchemy/src/AWS/CloudWatch/DisableAlarmActions.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import { type AlarmResource, sortAlarmResources } from "./binding-common.ts"; +import type { Providers } from "../Providers.ts"; type AlarmResources = [AlarmResource, ...AlarmResource[]]; @@ -47,7 +48,8 @@ export const DisableAlarmActionsLive = Layer.effect( export class DisableAlarmActionsPolicy extends Binding.Policy< DisableAlarmActionsPolicy, - (...alarms: AlarmResources) => Effect.Effect + (...alarms: AlarmResources) => Effect.Effect, + Providers >()("AWS.CloudWatch.DisableAlarmActions") {} export const DisableAlarmActionsPolicyLive = diff --git a/packages/alchemy/src/AWS/CloudWatch/DisableInsightRules.ts b/packages/alchemy/src/AWS/CloudWatch/DisableInsightRules.ts index 401de9397..f96b70e0b 100644 --- a/packages/alchemy/src/AWS/CloudWatch/DisableInsightRules.ts +++ b/packages/alchemy/src/AWS/CloudWatch/DisableInsightRules.ts @@ -7,6 +7,7 @@ import { sortInsightRuleResources, type InsightRuleResource, } from "./binding-common.ts"; +import type { Providers } from "../Providers.ts"; type InsightRules = [InsightRuleResource, ...InsightRuleResource[]]; @@ -47,7 +48,8 @@ export const DisableInsightRulesLive = Layer.effect( export class DisableInsightRulesPolicy extends Binding.Policy< DisableInsightRulesPolicy, - (...rules: InsightRules) => Effect.Effect + (...rules: InsightRules) => Effect.Effect, + Providers >()("AWS.CloudWatch.DisableInsightRules") {} export const DisableInsightRulesPolicyLive = diff --git a/packages/alchemy/src/AWS/CloudWatch/EnableAlarmActions.ts b/packages/alchemy/src/AWS/CloudWatch/EnableAlarmActions.ts index 6e54a7d58..122ebb618 100644 --- a/packages/alchemy/src/AWS/CloudWatch/EnableAlarmActions.ts +++ b/packages/alchemy/src/AWS/CloudWatch/EnableAlarmActions.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import { type AlarmResource, sortAlarmResources } from "./binding-common.ts"; +import type { Providers } from "../Providers.ts"; type AlarmResources = [AlarmResource, ...AlarmResource[]]; @@ -47,7 +48,8 @@ export const EnableAlarmActionsLive = Layer.effect( export class EnableAlarmActionsPolicy extends Binding.Policy< EnableAlarmActionsPolicy, - (...alarms: AlarmResources) => Effect.Effect + (...alarms: AlarmResources) => Effect.Effect, + Providers >()("AWS.CloudWatch.EnableAlarmActions") {} export const EnableAlarmActionsPolicyLive = diff --git a/packages/alchemy/src/AWS/CloudWatch/GetAlarmMuteRule.ts b/packages/alchemy/src/AWS/CloudWatch/GetAlarmMuteRule.ts index 584de918a..94e743594 100644 --- a/packages/alchemy/src/AWS/CloudWatch/GetAlarmMuteRule.ts +++ b/packages/alchemy/src/AWS/CloudWatch/GetAlarmMuteRule.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { AlarmMuteRule } from "./AlarmMuteRule.ts"; +import type { Providers } from "../Providers.ts"; export interface GetAlarmMuteRuleRequest extends Omit< cloudwatch.GetAlarmMuteRuleInput, @@ -50,7 +51,8 @@ export const GetAlarmMuteRuleLive = Layer.effect( export class GetAlarmMuteRulePolicy extends Binding.Policy< GetAlarmMuteRulePolicy, - (rule: AlarmMuteRule) => Effect.Effect + (rule: AlarmMuteRule) => Effect.Effect, + Providers >()("AWS.CloudWatch.GetAlarmMuteRule") {} export const GetAlarmMuteRulePolicyLive = GetAlarmMuteRulePolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/CloudWatch/GetDashboard.ts b/packages/alchemy/src/AWS/CloudWatch/GetDashboard.ts index 2cd4600b0..adfb28fdf 100644 --- a/packages/alchemy/src/AWS/CloudWatch/GetDashboard.ts +++ b/packages/alchemy/src/AWS/CloudWatch/GetDashboard.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Dashboard } from "./Dashboard.ts"; +import type { Providers } from "../Providers.ts"; export interface GetDashboardRequest extends Omit< cloudwatch.GetDashboardInput, @@ -50,7 +51,8 @@ export const GetDashboardLive = Layer.effect( export class GetDashboardPolicy extends Binding.Policy< GetDashboardPolicy, - (dashboard: Dashboard) => Effect.Effect + (dashboard: Dashboard) => Effect.Effect, + Providers >()("AWS.CloudWatch.GetDashboard") {} export const GetDashboardPolicyLive = GetDashboardPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/CloudWatch/GetInsightRuleReport.ts b/packages/alchemy/src/AWS/CloudWatch/GetInsightRuleReport.ts index 108cb4f81..185158d54 100644 --- a/packages/alchemy/src/AWS/CloudWatch/GetInsightRuleReport.ts +++ b/packages/alchemy/src/AWS/CloudWatch/GetInsightRuleReport.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { InsightRule } from "./InsightRule.ts"; +import type { Providers } from "../Providers.ts"; export interface GetInsightRuleReportRequest extends Omit< cloudwatch.GetInsightRuleReportInput, @@ -50,7 +51,8 @@ export const GetInsightRuleReportLive = Layer.effect( export class GetInsightRuleReportPolicy extends Binding.Policy< GetInsightRuleReportPolicy, - (rule: InsightRule) => Effect.Effect + (rule: InsightRule) => Effect.Effect, + Providers >()("AWS.CloudWatch.GetInsightRuleReport") {} export const GetInsightRuleReportPolicyLive = diff --git a/packages/alchemy/src/AWS/CloudWatch/GetMetricData.ts b/packages/alchemy/src/AWS/CloudWatch/GetMetricData.ts index f35135d5f..7448de926 100644 --- a/packages/alchemy/src/AWS/CloudWatch/GetMetricData.ts +++ b/packages/alchemy/src/AWS/CloudWatch/GetMetricData.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; +import type { Providers } from "../Providers.ts"; export interface GetMetricDataRequest extends cloudwatch.GetMetricDataInput {} @@ -39,7 +40,8 @@ export const GetMetricDataLive = Layer.effect( export class GetMetricDataPolicy extends Binding.Policy< GetMetricDataPolicy, - () => Effect.Effect + () => Effect.Effect, + Providers >()("AWS.CloudWatch.GetMetricData") {} export const GetMetricDataPolicyLive = GetMetricDataPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/CloudWatch/GetMetricStatistics.ts b/packages/alchemy/src/AWS/CloudWatch/GetMetricStatistics.ts index c3cf101ca..94c85afdd 100644 --- a/packages/alchemy/src/AWS/CloudWatch/GetMetricStatistics.ts +++ b/packages/alchemy/src/AWS/CloudWatch/GetMetricStatistics.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; +import type { Providers } from "../Providers.ts"; export interface GetMetricStatisticsRequest extends cloudwatch.GetMetricStatisticsInput {} @@ -40,7 +41,8 @@ export const GetMetricStatisticsLive = Layer.effect( export class GetMetricStatisticsPolicy extends Binding.Policy< GetMetricStatisticsPolicy, - () => Effect.Effect + () => Effect.Effect, + Providers >()("AWS.CloudWatch.GetMetricStatistics") {} export const GetMetricStatisticsPolicyLive = diff --git a/packages/alchemy/src/AWS/CloudWatch/GetMetricStream.ts b/packages/alchemy/src/AWS/CloudWatch/GetMetricStream.ts index f75e94619..53c7b765c 100644 --- a/packages/alchemy/src/AWS/CloudWatch/GetMetricStream.ts +++ b/packages/alchemy/src/AWS/CloudWatch/GetMetricStream.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { MetricStream } from "./MetricStream.ts"; +import type { Providers } from "../Providers.ts"; export interface GetMetricStreamRequest extends Omit< cloudwatch.GetMetricStreamInput, @@ -50,7 +51,8 @@ export const GetMetricStreamLive = Layer.effect( export class GetMetricStreamPolicy extends Binding.Policy< GetMetricStreamPolicy, - (metricStream: MetricStream) => Effect.Effect + (metricStream: MetricStream) => Effect.Effect, + Providers >()("AWS.CloudWatch.GetMetricStream") {} export const GetMetricStreamPolicyLive = GetMetricStreamPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/CloudWatch/GetMetricWidgetImage.ts b/packages/alchemy/src/AWS/CloudWatch/GetMetricWidgetImage.ts index c92cde892..e56c4c24b 100644 --- a/packages/alchemy/src/AWS/CloudWatch/GetMetricWidgetImage.ts +++ b/packages/alchemy/src/AWS/CloudWatch/GetMetricWidgetImage.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; +import type { Providers } from "../Providers.ts"; export interface GetMetricWidgetImageRequest extends cloudwatch.GetMetricWidgetImageInput {} @@ -40,7 +41,8 @@ export const GetMetricWidgetImageLive = Layer.effect( export class GetMetricWidgetImagePolicy extends Binding.Policy< GetMetricWidgetImagePolicy, - () => Effect.Effect + () => Effect.Effect, + Providers >()("AWS.CloudWatch.GetMetricWidgetImage") {} export const GetMetricWidgetImagePolicyLive = diff --git a/packages/alchemy/src/AWS/CloudWatch/ListAlarmMuteRules.ts b/packages/alchemy/src/AWS/CloudWatch/ListAlarmMuteRules.ts index b7f86ffbb..fd8f668a7 100644 --- a/packages/alchemy/src/AWS/CloudWatch/ListAlarmMuteRules.ts +++ b/packages/alchemy/src/AWS/CloudWatch/ListAlarmMuteRules.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; +import type { Providers } from "../Providers.ts"; export interface ListAlarmMuteRulesRequest extends cloudwatch.ListAlarmMuteRulesInput {} @@ -40,7 +41,8 @@ export const ListAlarmMuteRulesLive = Layer.effect( export class ListAlarmMuteRulesPolicy extends Binding.Policy< ListAlarmMuteRulesPolicy, - () => Effect.Effect + () => Effect.Effect, + Providers >()("AWS.CloudWatch.ListAlarmMuteRules") {} export const ListAlarmMuteRulesPolicyLive = diff --git a/packages/alchemy/src/AWS/CloudWatch/ListDashboards.ts b/packages/alchemy/src/AWS/CloudWatch/ListDashboards.ts index e67777a9a..d979fee2b 100644 --- a/packages/alchemy/src/AWS/CloudWatch/ListDashboards.ts +++ b/packages/alchemy/src/AWS/CloudWatch/ListDashboards.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; +import type { Providers } from "../Providers.ts"; export interface ListDashboardsRequest extends cloudwatch.ListDashboardsInput {} @@ -39,7 +40,8 @@ export const ListDashboardsLive = Layer.effect( export class ListDashboardsPolicy extends Binding.Policy< ListDashboardsPolicy, - () => Effect.Effect + () => Effect.Effect, + Providers >()("AWS.CloudWatch.ListDashboards") {} export const ListDashboardsPolicyLive = ListDashboardsPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/CloudWatch/ListManagedInsightRules.ts b/packages/alchemy/src/AWS/CloudWatch/ListManagedInsightRules.ts index 854bcef1e..e917126ad 100644 --- a/packages/alchemy/src/AWS/CloudWatch/ListManagedInsightRules.ts +++ b/packages/alchemy/src/AWS/CloudWatch/ListManagedInsightRules.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; +import type { Providers } from "../Providers.ts"; export interface ListManagedInsightRulesRequest extends cloudwatch.ListManagedInsightRulesInput {} @@ -42,7 +43,8 @@ export const ListManagedInsightRulesLive = Layer.effect( export class ListManagedInsightRulesPolicy extends Binding.Policy< ListManagedInsightRulesPolicy, - () => Effect.Effect + () => Effect.Effect, + Providers >()("AWS.CloudWatch.ListManagedInsightRules") {} export const ListManagedInsightRulesPolicyLive = diff --git a/packages/alchemy/src/AWS/CloudWatch/ListMetricStreams.ts b/packages/alchemy/src/AWS/CloudWatch/ListMetricStreams.ts index 75172ac4d..45790f6d0 100644 --- a/packages/alchemy/src/AWS/CloudWatch/ListMetricStreams.ts +++ b/packages/alchemy/src/AWS/CloudWatch/ListMetricStreams.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; +import type { Providers } from "../Providers.ts"; export interface ListMetricStreamsRequest extends cloudwatch.ListMetricStreamsInput {} @@ -40,7 +41,8 @@ export const ListMetricStreamsLive = Layer.effect( export class ListMetricStreamsPolicy extends Binding.Policy< ListMetricStreamsPolicy, - () => Effect.Effect + () => Effect.Effect, + Providers >()("AWS.CloudWatch.ListMetricStreams") {} export const ListMetricStreamsPolicyLive = diff --git a/packages/alchemy/src/AWS/CloudWatch/ListMetrics.ts b/packages/alchemy/src/AWS/CloudWatch/ListMetrics.ts index 208da7c3a..dd90d6202 100644 --- a/packages/alchemy/src/AWS/CloudWatch/ListMetrics.ts +++ b/packages/alchemy/src/AWS/CloudWatch/ListMetrics.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; +import type { Providers } from "../Providers.ts"; export interface ListMetricsRequest extends cloudwatch.ListMetricsInput {} @@ -39,7 +40,8 @@ export const ListMetricsLive = Layer.effect( export class ListMetricsPolicy extends Binding.Policy< ListMetricsPolicy, - () => Effect.Effect + () => Effect.Effect, + Providers >()("AWS.CloudWatch.ListMetrics") {} export const ListMetricsPolicyLive = ListMetricsPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/CloudWatch/ListTagsForResource.ts b/packages/alchemy/src/AWS/CloudWatch/ListTagsForResource.ts index e3ba49318..76cd5fc7f 100644 --- a/packages/alchemy/src/AWS/CloudWatch/ListTagsForResource.ts +++ b/packages/alchemy/src/AWS/CloudWatch/ListTagsForResource.ts @@ -7,6 +7,7 @@ import { getTaggableResourceArn, type TaggableResource, } from "./binding-common.ts"; +import type { Providers } from "../Providers.ts"; export interface ListTagsForResourceRequest extends Omit< cloudwatch.ListTagsForResourceInput, @@ -53,7 +54,8 @@ export const ListTagsForResourceLive = Layer.effect( export class ListTagsForResourcePolicy extends Binding.Policy< ListTagsForResourcePolicy, - (resource: TaggableResource) => Effect.Effect + (resource: TaggableResource) => Effect.Effect, + Providers >()("AWS.CloudWatch.ListTagsForResource") {} export const ListTagsForResourcePolicyLive = diff --git a/packages/alchemy/src/AWS/CloudWatch/PutMetricData.ts b/packages/alchemy/src/AWS/CloudWatch/PutMetricData.ts index 2dc5fdacb..77146fe05 100644 --- a/packages/alchemy/src/AWS/CloudWatch/PutMetricData.ts +++ b/packages/alchemy/src/AWS/CloudWatch/PutMetricData.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; +import type { Providers } from "../Providers.ts"; export interface PutMetricDataRequest extends cloudwatch.PutMetricDataInput {} @@ -39,7 +40,8 @@ export const PutMetricDataLive = Layer.effect( export class PutMetricDataPolicy extends Binding.Policy< PutMetricDataPolicy, - () => Effect.Effect + () => Effect.Effect, + Providers >()("AWS.CloudWatch.PutMetricData") {} export const PutMetricDataPolicyLive = PutMetricDataPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/CloudWatch/SetAlarmState.ts b/packages/alchemy/src/AWS/CloudWatch/SetAlarmState.ts index 18f0a87f3..fc4dfbac1 100644 --- a/packages/alchemy/src/AWS/CloudWatch/SetAlarmState.ts +++ b/packages/alchemy/src/AWS/CloudWatch/SetAlarmState.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { AlarmResource } from "./binding-common.ts"; +import type { Providers } from "../Providers.ts"; export interface SetAlarmStateRequest extends Omit< cloudwatch.SetAlarmStateInput, @@ -50,7 +51,8 @@ export const SetAlarmStateLive = Layer.effect( export class SetAlarmStatePolicy extends Binding.Policy< SetAlarmStatePolicy, - (alarm: AlarmResource) => Effect.Effect + (alarm: AlarmResource) => Effect.Effect, + Providers >()("AWS.CloudWatch.SetAlarmState") {} export const SetAlarmStatePolicyLive = SetAlarmStatePolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/Credentials.ts b/packages/alchemy/src/AWS/Credentials.ts index 5390c09db..0fe134d86 100644 --- a/packages/alchemy/src/AWS/Credentials.ts +++ b/packages/alchemy/src/AWS/Credentials.ts @@ -5,6 +5,12 @@ import { AWSEnvironment } from "./Environment.ts"; export { Credentials } from "@distilled.cloud/aws/Credentials"; +declare module "@distilled.cloud/aws/Credentials" { + interface Credentials { + readonly kind: "Credentials"; + } +} + /** * Lazy `Credentials` layer derived from the surrounding {@link AWSEnvironment}. * Credentials are resolved on first access (not during layer construction), diff --git a/packages/alchemy/src/AWS/DynamoDB/BatchExecuteStatement.ts b/packages/alchemy/src/AWS/DynamoDB/BatchExecuteStatement.ts index 0979b42b3..fc96ec1dd 100644 --- a/packages/alchemy/src/AWS/DynamoDB/BatchExecuteStatement.ts +++ b/packages/alchemy/src/AWS/DynamoDB/BatchExecuteStatement.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import * as Output from "../../Output.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Table } from "./Table.ts"; +import type { Providers } from "../Providers.ts"; type BatchExecuteStatementTables = [Table, ...Table[]]; @@ -76,7 +77,8 @@ export const BatchExecuteStatementLive = Layer.effect( export class BatchExecuteStatementPolicy extends Binding.Policy< BatchExecuteStatementPolicy, - (...tables: BatchExecuteStatementTables) => Effect.Effect + (...tables: BatchExecuteStatementTables) => Effect.Effect, + Providers >()("AWS.DynamoDB.BatchExecuteStatement") {} export const BatchExecuteStatementPolicyLive = diff --git a/packages/alchemy/src/AWS/DynamoDB/BatchGetItem.ts b/packages/alchemy/src/AWS/DynamoDB/BatchGetItem.ts index c84bcdb9a..20532a3f9 100644 --- a/packages/alchemy/src/AWS/DynamoDB/BatchGetItem.ts +++ b/packages/alchemy/src/AWS/DynamoDB/BatchGetItem.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Table } from "./Table.ts"; +import type { Providers } from "../Providers.ts"; type BatchGetItemTables = [Table, ...Table[]]; @@ -110,7 +111,8 @@ export const BatchGetItemLive = Layer.effect( export class BatchGetItemPolicy extends Binding.Policy< BatchGetItemPolicy, - (...tables: BatchGetItemTables) => Effect.Effect + (...tables: BatchGetItemTables) => Effect.Effect, + Providers >()("AWS.DynamoDB.BatchGetItem") {} export const BatchGetItemPolicyLive = BatchGetItemPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/DynamoDB/BatchWriteItem.ts b/packages/alchemy/src/AWS/DynamoDB/BatchWriteItem.ts index 0f6d3a575..c97c33547 100644 --- a/packages/alchemy/src/AWS/DynamoDB/BatchWriteItem.ts +++ b/packages/alchemy/src/AWS/DynamoDB/BatchWriteItem.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Table } from "./Table.ts"; +import type { Providers } from "../Providers.ts"; type BatchWriteItemTables = [Table, ...Table[]]; @@ -116,7 +117,8 @@ export const BatchWriteItemLive = Layer.effect( export class BatchWriteItemPolicy extends Binding.Policy< BatchWriteItemPolicy, - (...tables: BatchWriteItemTables) => Effect.Effect + (...tables: BatchWriteItemTables) => Effect.Effect, + Providers >()("AWS.DynamoDB.BatchWriteItem") {} export const BatchWriteItemPolicyLive = BatchWriteItemPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/DynamoDB/DeleteItem.ts b/packages/alchemy/src/AWS/DynamoDB/DeleteItem.ts index d65e217d3..a0ce442c0 100644 --- a/packages/alchemy/src/AWS/DynamoDB/DeleteItem.ts +++ b/packages/alchemy/src/AWS/DynamoDB/DeleteItem.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Table } from "./Table.ts"; +import type { Providers } from "../Providers.ts"; export interface DeleteItemRequest extends Omit< DynamoDB.DeleteItemInput, @@ -44,7 +45,8 @@ export const DeleteItemLive = Layer.effect( export class DeleteItemPolicy extends Binding.Policy< DeleteItemPolicy, - (table: T) => Effect.Effect + (table: T) => Effect.Effect, + Providers >()("AWS.DynamoDB.DeleteItem") {} export const DeleteItemPolicyLive = DeleteItemPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/DynamoDB/DescribeTable.ts b/packages/alchemy/src/AWS/DynamoDB/DescribeTable.ts index e668c97f0..2da988dfa 100644 --- a/packages/alchemy/src/AWS/DynamoDB/DescribeTable.ts +++ b/packages/alchemy/src/AWS/DynamoDB/DescribeTable.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Table } from "./Table.ts"; +import type { Providers } from "../Providers.ts"; export interface DescribeTableRequest extends Omit< DynamoDB.DescribeTableInput, @@ -46,7 +47,8 @@ export const DescribeTableLive = Layer.effect( export class DescribeTablePolicy extends Binding.Policy< DescribeTablePolicy, - (table: T) => Effect.Effect + (table: T) => Effect.Effect, + Providers >()("AWS.DynamoDB.DescribeTable") {} export const DescribeTablePolicyLive = DescribeTablePolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/DynamoDB/DescribeTimeToLive.ts b/packages/alchemy/src/AWS/DynamoDB/DescribeTimeToLive.ts index 4e299dc83..abe490f5a 100644 --- a/packages/alchemy/src/AWS/DynamoDB/DescribeTimeToLive.ts +++ b/packages/alchemy/src/AWS/DynamoDB/DescribeTimeToLive.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Table } from "./Table.ts"; +import type { Providers } from "../Providers.ts"; export interface DescribeTimeToLiveRequest extends Omit< DynamoDB.DescribeTimeToLiveInput, @@ -46,7 +47,8 @@ export const DescribeTimeToLiveLive = Layer.effect( export class DescribeTimeToLivePolicy extends Binding.Policy< DescribeTimeToLivePolicy, - (table: T) => Effect.Effect + (table: T) => Effect.Effect, + Providers >()("AWS.DynamoDB.DescribeTimeToLive") {} export const DescribeTimeToLivePolicyLive = diff --git a/packages/alchemy/src/AWS/DynamoDB/ExecuteStatement.ts b/packages/alchemy/src/AWS/DynamoDB/ExecuteStatement.ts index 0209bf63f..561c60e35 100644 --- a/packages/alchemy/src/AWS/DynamoDB/ExecuteStatement.ts +++ b/packages/alchemy/src/AWS/DynamoDB/ExecuteStatement.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import * as Output from "../../Output.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Table } from "./Table.ts"; +import type { Providers } from "../Providers.ts"; export interface ExecuteStatementRequest extends DynamoDB.ExecuteStatementInput {} @@ -57,7 +58,8 @@ export const ExecuteStatementLive = Layer.effect( export class ExecuteStatementPolicy extends Binding.Policy< ExecuteStatementPolicy, - (table: T) => Effect.Effect + (table: T) => Effect.Effect, + Providers >()("AWS.DynamoDB.ExecuteStatement") {} export const ExecuteStatementPolicyLive = ExecuteStatementPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/DynamoDB/ExecuteTransaction.ts b/packages/alchemy/src/AWS/DynamoDB/ExecuteTransaction.ts index 8b809f511..472b67681 100644 --- a/packages/alchemy/src/AWS/DynamoDB/ExecuteTransaction.ts +++ b/packages/alchemy/src/AWS/DynamoDB/ExecuteTransaction.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Table } from "./Table.ts"; +import type { Providers } from "../Providers.ts"; export interface ExecuteTransactionRequest extends DynamoDB.ExecuteTransactionInput {} @@ -42,7 +43,8 @@ export const ExecuteTransactionLive = Layer.effect( export class ExecuteTransactionPolicy extends Binding.Policy< ExecuteTransactionPolicy, - (...tables: ExecuteTransactionTables) => Effect.Effect + (...tables: ExecuteTransactionTables) => Effect.Effect, + Providers >()("AWS.DynamoDB.ExecuteTransaction") {} export const ExecuteTransactionPolicyLive = diff --git a/packages/alchemy/src/AWS/DynamoDB/GetItem.ts b/packages/alchemy/src/AWS/DynamoDB/GetItem.ts index c5be5ea1c..0f5a9bfd5 100644 --- a/packages/alchemy/src/AWS/DynamoDB/GetItem.ts +++ b/packages/alchemy/src/AWS/DynamoDB/GetItem.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Table } from "./Table.ts"; +import type { Providers } from "../Providers.ts"; export interface GetItemRequest extends Omit< DynamoDB.GetItemInput, @@ -61,7 +62,8 @@ export const GetItemLive = Layer.effect( export class GetItemPolicy extends Binding.Policy< GetItemPolicy, - (table: T) => Effect.Effect + (table: T) => Effect.Effect, + Providers >()("AWS.DynamoDB.GetItem") {} export const GetItemPolicyLive = GetItemPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/DynamoDB/ListTables.ts b/packages/alchemy/src/AWS/DynamoDB/ListTables.ts index ffdd548b5..0dff7f150 100644 --- a/packages/alchemy/src/AWS/DynamoDB/ListTables.ts +++ b/packages/alchemy/src/AWS/DynamoDB/ListTables.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; +import type { Providers } from "../Providers.ts"; export interface ListTablesRequest extends DynamoDB.ListTablesInput {} @@ -33,7 +34,8 @@ export const ListTablesLive = Layer.effect( export class ListTablesPolicy extends Binding.Policy< ListTablesPolicy, - () => Effect.Effect + () => Effect.Effect, + Providers >()("AWS.DynamoDB.ListTables") {} export const ListTablesPolicyLive = ListTablesPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/DynamoDB/ListTagsOfResource.ts b/packages/alchemy/src/AWS/DynamoDB/ListTagsOfResource.ts index ea81cd62b..f5b07fa6a 100644 --- a/packages/alchemy/src/AWS/DynamoDB/ListTagsOfResource.ts +++ b/packages/alchemy/src/AWS/DynamoDB/ListTagsOfResource.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Table } from "./Table.ts"; +import type { Providers } from "../Providers.ts"; export interface ListTagsOfResourceRequest extends Omit< DynamoDB.ListTagsOfResourceInput, @@ -46,7 +47,8 @@ export const ListTagsOfResourceLive = Layer.effect( export class ListTagsOfResourcePolicy extends Binding.Policy< ListTagsOfResourcePolicy, - (table: T) => Effect.Effect + (table: T) => Effect.Effect, + Providers >()("AWS.DynamoDB.ListTagsOfResource") {} export const ListTagsOfResourcePolicyLive = diff --git a/packages/alchemy/src/AWS/DynamoDB/PutItem.ts b/packages/alchemy/src/AWS/DynamoDB/PutItem.ts index 1f6122f60..eec10377c 100644 --- a/packages/alchemy/src/AWS/DynamoDB/PutItem.ts +++ b/packages/alchemy/src/AWS/DynamoDB/PutItem.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Table } from "./Table.ts"; +import type { Providers } from "../Providers.ts"; export interface PutItemRequest extends Omit< DynamoDB.PutItemInput, @@ -44,7 +45,8 @@ export const PutItemLive = Layer.effect( export class PutItemPolicy extends Binding.Policy< PutItemPolicy, - (table: T) => Effect.Effect + (table: T) => Effect.Effect, + Providers >()("AWS.DynamoDB.PutItem") {} export const PutItemPolicyLive = PutItemPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/DynamoDB/Query.ts b/packages/alchemy/src/AWS/DynamoDB/Query.ts index df5c3aa94..70471b0aa 100644 --- a/packages/alchemy/src/AWS/DynamoDB/Query.ts +++ b/packages/alchemy/src/AWS/DynamoDB/Query.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import * as Output from "../../Output.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Table } from "./Table.ts"; +import type { Providers } from "../Providers.ts"; export interface QueryRequest extends Omit {} @@ -42,7 +43,8 @@ export const QueryLive = Layer.effect( export class QueryPolicy extends Binding.Policy< QueryPolicy, - (table: T) => Effect.Effect + (table: T) => Effect.Effect, + Providers >()("AWS.DynamoDB.Query") {} export const QueryPolicyLive = QueryPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/DynamoDB/RestoreTableToPointInTime.ts b/packages/alchemy/src/AWS/DynamoDB/RestoreTableToPointInTime.ts index 5daf26c4e..18f297619 100644 --- a/packages/alchemy/src/AWS/DynamoDB/RestoreTableToPointInTime.ts +++ b/packages/alchemy/src/AWS/DynamoDB/RestoreTableToPointInTime.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Table } from "./Table.ts"; +import type { Providers } from "../Providers.ts"; export interface RestoreTableToPointInTimeRequest extends Omit< DynamoDB.RestoreTableToPointInTimeInput, @@ -55,7 +56,8 @@ export class RestoreTableToPointInTimePolicy extends Binding.Policy< ( from: From, to: To, - ) => Effect.Effect + ) => Effect.Effect, + Providers >()("AWS.DynamoDB.RestoreTableToPointInTime") {} export const RestoreTableToPointInTimePolicyLive = diff --git a/packages/alchemy/src/AWS/DynamoDB/Scan.ts b/packages/alchemy/src/AWS/DynamoDB/Scan.ts index 92c4eed00..755020d12 100644 --- a/packages/alchemy/src/AWS/DynamoDB/Scan.ts +++ b/packages/alchemy/src/AWS/DynamoDB/Scan.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import * as Output from "../../Output.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Table } from "./Table.ts"; +import type { Providers } from "../Providers.ts"; export interface ScanRequest extends Omit {} @@ -42,7 +43,8 @@ export const ScanLive = Layer.effect( export class ScanPolicy extends Binding.Policy< ScanPolicy, - (table: T) => Effect.Effect + (table: T) => Effect.Effect, + Providers >()("AWS.DynamoDB.Scan") {} export const ScanPolicyLive = ScanPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/DynamoDB/TransactGetItems.ts b/packages/alchemy/src/AWS/DynamoDB/TransactGetItems.ts index ad30701b3..fa04cfdcb 100644 --- a/packages/alchemy/src/AWS/DynamoDB/TransactGetItems.ts +++ b/packages/alchemy/src/AWS/DynamoDB/TransactGetItems.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Table } from "./Table.ts"; +import type { Providers } from "../Providers.ts"; type TransactGetItemsTables = [Table, ...Table[]]; @@ -126,7 +127,8 @@ export const TransactGetItemsLive = Layer.effect( export class TransactGetItemsPolicy extends Binding.Policy< TransactGetItemsPolicy, - (...tables: TransactGetItemsTables) => Effect.Effect + (...tables: TransactGetItemsTables) => Effect.Effect, + Providers >()("AWS.DynamoDB.TransactGetItems") {} export const TransactGetItemsPolicyLive = TransactGetItemsPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/DynamoDB/TransactWriteItems.ts b/packages/alchemy/src/AWS/DynamoDB/TransactWriteItems.ts index 4403cc66e..10d769ce8 100644 --- a/packages/alchemy/src/AWS/DynamoDB/TransactWriteItems.ts +++ b/packages/alchemy/src/AWS/DynamoDB/TransactWriteItems.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Table } from "./Table.ts"; +import type { Providers } from "../Providers.ts"; type TransactWriteItemsTables = [Table, ...Table[]]; @@ -182,7 +183,8 @@ export const TransactWriteItemsLive = Layer.effect( export class TransactWriteItemsPolicy extends Binding.Policy< TransactWriteItemsPolicy, - (...tables: TransactWriteItemsTables) => Effect.Effect + (...tables: TransactWriteItemsTables) => Effect.Effect, + Providers >()("AWS.DynamoDB.TransactWriteItems") {} export const TransactWriteItemsPolicyLive = diff --git a/packages/alchemy/src/AWS/DynamoDB/UpdateItem.ts b/packages/alchemy/src/AWS/DynamoDB/UpdateItem.ts index 8b23ef641..ff3eaf717 100644 --- a/packages/alchemy/src/AWS/DynamoDB/UpdateItem.ts +++ b/packages/alchemy/src/AWS/DynamoDB/UpdateItem.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Table } from "./Table.ts"; +import type { Providers } from "../Providers.ts"; export interface UpdateItemRequest extends Omit< DynamoDB.UpdateItemInput, @@ -44,7 +45,8 @@ export const UpdateItemLive = Layer.effect( export class UpdateItemPolicy extends Binding.Policy< UpdateItemPolicy, - (table: T) => Effect.Effect + (table: T) => Effect.Effect, + Providers >()("AWS.DynamoDB.UpdateItem") {} export const UpdateItemPolicyLive = UpdateItemPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/DynamoDB/UpdateTimeToLive.ts b/packages/alchemy/src/AWS/DynamoDB/UpdateTimeToLive.ts index 4947ca612..2bc4697c3 100644 --- a/packages/alchemy/src/AWS/DynamoDB/UpdateTimeToLive.ts +++ b/packages/alchemy/src/AWS/DynamoDB/UpdateTimeToLive.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Table } from "./Table.ts"; +import type { Providers } from "../Providers.ts"; export interface UpdateTimeToLiveRequest extends Omit< DynamoDB.UpdateTimeToLiveInput, @@ -46,7 +47,8 @@ export const UpdateTimeToLiveLive = Layer.effect( export class UpdateTimeToLivePolicy extends Binding.Policy< UpdateTimeToLivePolicy, - (table: T) => Effect.Effect + (table: T) => Effect.Effect, + Providers >()("AWS.DynamoDB.UpdateTimeToLive") {} export const UpdateTimeToLivePolicyLive = UpdateTimeToLivePolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/ECS/DescribeTasks.ts b/packages/alchemy/src/AWS/ECS/DescribeTasks.ts index e0c2c9160..8e2b9c86a 100644 --- a/packages/alchemy/src/AWS/ECS/DescribeTasks.ts +++ b/packages/alchemy/src/AWS/ECS/DescribeTasks.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import { isTask } from "./Task.ts"; import type { Cluster } from "./Cluster.ts"; +import type { Providers } from "../Providers.ts"; export interface DescribeTasksRequest extends Omit< ECS.DescribeTasksRequest, @@ -44,7 +45,8 @@ export const DescribeTasksLive = Layer.effect( export class DescribeTasksPolicy extends Binding.Policy< DescribeTasksPolicy, - (cluster: Cluster) => Effect.Effect + (cluster: Cluster) => Effect.Effect, + Providers >()("AWS.ECS.DescribeTasks") {} export const DescribeTasksPolicyLive = DescribeTasksPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/ECS/ListTasks.ts b/packages/alchemy/src/AWS/ECS/ListTasks.ts index acc550690..26a74246e 100644 --- a/packages/alchemy/src/AWS/ECS/ListTasks.ts +++ b/packages/alchemy/src/AWS/ECS/ListTasks.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import { isTask } from "./Task.ts"; import type { Cluster } from "./Cluster.ts"; +import type { Providers } from "../Providers.ts"; export interface ListTasksRequest extends Omit< ECS.ListTasksRequest, @@ -44,7 +45,8 @@ export const ListTasksLive = Layer.effect( export class ListTasksPolicy extends Binding.Policy< ListTasksPolicy, - (cluster: Cluster) => Effect.Effect + (cluster: Cluster) => Effect.Effect, + Providers >()("AWS.ECS.ListTasks") {} export const ListTasksPolicyLive = ListTasksPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/ECS/RunTask.ts b/packages/alchemy/src/AWS/ECS/RunTask.ts index 47811c59c..90fb9540b 100644 --- a/packages/alchemy/src/AWS/ECS/RunTask.ts +++ b/packages/alchemy/src/AWS/ECS/RunTask.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import { Task, isTask } from "./Task.ts"; import type { Cluster } from "./Cluster.ts"; +import type { Providers } from "../Providers.ts"; export interface RunTaskRequest extends Omit< ECS.RunTaskRequest, @@ -49,7 +50,8 @@ export const RunTaskLive = Layer.effect( export class RunTaskPolicy extends Binding.Policy< RunTaskPolicy, - (cluster: Cluster, task: Task) => Effect.Effect + (cluster: Cluster, task: Task) => Effect.Effect, + Providers >()("AWS.ECS.RunTask") {} export const RunTaskPolicyLive = RunTaskPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/ECS/StopTask.ts b/packages/alchemy/src/AWS/ECS/StopTask.ts index bf732b649..6df19de90 100644 --- a/packages/alchemy/src/AWS/ECS/StopTask.ts +++ b/packages/alchemy/src/AWS/ECS/StopTask.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import { isTask } from "./Task.ts"; import type { Cluster } from "./Cluster.ts"; +import type { Providers } from "../Providers.ts"; export interface StopTaskRequest extends Omit {} @@ -41,7 +42,8 @@ export const StopTaskLive = Layer.effect( export class StopTaskPolicy extends Binding.Policy< StopTaskPolicy, - (cluster: Cluster) => Effect.Effect + (cluster: Cluster) => Effect.Effect, + Providers >()("AWS.ECS.StopTask") {} export const StopTaskPolicyLive = StopTaskPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/Environment.ts b/packages/alchemy/src/AWS/Environment.ts index 1b07bd8d0..565a568ca 100644 --- a/packages/alchemy/src/AWS/Environment.ts +++ b/packages/alchemy/src/AWS/Environment.ts @@ -1,6 +1,6 @@ -import { - type CredentialsError, - type ResolvedCredentials, +import type { + CredentialsError, + ResolvedCredentials, } from "@distilled.cloud/aws/Credentials"; import * as Config from "effect/Config"; import * as Context from "effect/Context"; @@ -58,6 +58,7 @@ export class AWSEnvironment extends Context.Service< Effect.Effect >()("AWS::Environment") { static current = AWSEnvironment.use((env) => env); + readonly kind = "Environment" as const; } export const Default = Layer.effect( diff --git a/packages/alchemy/src/AWS/EventBridge/DescribeEventBus.ts b/packages/alchemy/src/AWS/EventBridge/DescribeEventBus.ts index 7560d3169..5c266beda 100644 --- a/packages/alchemy/src/AWS/EventBridge/DescribeEventBus.ts +++ b/packages/alchemy/src/AWS/EventBridge/DescribeEventBus.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { EventBus } from "./EventBus.ts"; +import type { Providers } from "../Providers.ts"; export interface DescribeEventBusRequest extends Omit< eventbridge.DescribeEventBusRequest, @@ -46,7 +47,8 @@ export const DescribeEventBusLive = Layer.effect( export class DescribeEventBusPolicy extends Binding.Policy< DescribeEventBusPolicy, - (bus: EventBus) => Effect.Effect + (bus: EventBus) => Effect.Effect, + Providers >()("AWS.EventBridge.DescribeEventBus") {} export const DescribeEventBusPolicyLive = DescribeEventBusPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/EventBridge/DescribeRule.ts b/packages/alchemy/src/AWS/EventBridge/DescribeRule.ts index 562899352..48056567c 100644 --- a/packages/alchemy/src/AWS/EventBridge/DescribeRule.ts +++ b/packages/alchemy/src/AWS/EventBridge/DescribeRule.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Rule } from "./Rule.ts"; +import type { Providers } from "../Providers.ts"; export interface DescribeRuleRequest extends Omit< eventbridge.DescribeRuleRequest, @@ -50,7 +51,8 @@ export const DescribeRuleLive = Layer.effect( export class DescribeRulePolicy extends Binding.Policy< DescribeRulePolicy, - (rule: Rule) => Effect.Effect + (rule: Rule) => Effect.Effect, + Providers >()("AWS.EventBridge.DescribeRule") {} export const DescribeRulePolicyLive = DescribeRulePolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/EventBridge/ListEventBuses.ts b/packages/alchemy/src/AWS/EventBridge/ListEventBuses.ts index 71d4b4f0a..b10d72263 100644 --- a/packages/alchemy/src/AWS/EventBridge/ListEventBuses.ts +++ b/packages/alchemy/src/AWS/EventBridge/ListEventBuses.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; +import type { Providers } from "../Providers.ts"; export interface ListEventBusesRequest extends eventbridge.ListEventBusesRequest {} @@ -37,7 +38,8 @@ export const ListEventBusesLive = Layer.effect( export class ListEventBusesPolicy extends Binding.Policy< ListEventBusesPolicy, - () => Effect.Effect + () => Effect.Effect, + Providers >()("AWS.EventBridge.ListEventBuses") {} export const ListEventBusesPolicyLive = ListEventBusesPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/EventBridge/ListRules.ts b/packages/alchemy/src/AWS/EventBridge/ListRules.ts index 6104961fd..0669e244d 100644 --- a/packages/alchemy/src/AWS/EventBridge/ListRules.ts +++ b/packages/alchemy/src/AWS/EventBridge/ListRules.ts @@ -6,6 +6,7 @@ import * as Output from "../../Output.ts"; import { AWSEnvironment } from "../Environment.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { EventBus } from "./EventBus.ts"; +import type { Providers } from "../Providers.ts"; export interface ListRulesRequest extends Omit< eventbridge.ListRulesRequest, @@ -51,7 +52,8 @@ export const ListRulesLive = Layer.effect( export class ListRulesPolicy extends Binding.Policy< ListRulesPolicy, - (bus?: EventBus) => Effect.Effect + (bus?: EventBus) => Effect.Effect, + Providers >()("AWS.EventBridge.ListRules") {} export const ListRulesPolicyLive = ListRulesPolicy.layer.effect( diff --git a/packages/alchemy/src/AWS/EventBridge/ListTargetsByRule.ts b/packages/alchemy/src/AWS/EventBridge/ListTargetsByRule.ts index 8f18e04c9..5dff18fc3 100644 --- a/packages/alchemy/src/AWS/EventBridge/ListTargetsByRule.ts +++ b/packages/alchemy/src/AWS/EventBridge/ListTargetsByRule.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Rule } from "./Rule.ts"; +import type { Providers } from "../Providers.ts"; export interface ListTargetsByRuleRequest extends Omit< eventbridge.ListTargetsByRuleRequest, @@ -50,7 +51,8 @@ export const ListTargetsByRuleLive = Layer.effect( export class ListTargetsByRulePolicy extends Binding.Policy< ListTargetsByRulePolicy, - (rule: Rule) => Effect.Effect + (rule: Rule) => Effect.Effect, + Providers >()("AWS.EventBridge.ListTargetsByRule") {} export const ListTargetsByRulePolicyLive = diff --git a/packages/alchemy/src/AWS/EventBridge/PutEvents.ts b/packages/alchemy/src/AWS/EventBridge/PutEvents.ts index 310b4f6f8..64813a26d 100644 --- a/packages/alchemy/src/AWS/EventBridge/PutEvents.ts +++ b/packages/alchemy/src/AWS/EventBridge/PutEvents.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import { AWSEnvironment } from "../Environment.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { EventBus } from "./EventBus.ts"; +import type { Providers } from "../Providers.ts"; export interface PutEventsRequest extends Omit< eventbridge.PutEventsRequest, @@ -56,7 +57,8 @@ export const PutEventsLive = Layer.effect( export class PutEventsPolicy extends Binding.Policy< PutEventsPolicy, - (bus?: EventBus) => Effect.Effect + (bus?: EventBus) => Effect.Effect, + Providers >()("AWS.EventBridge.PutEvents") {} export const PutEventsPolicyLive = PutEventsPolicy.layer.effect( diff --git a/packages/alchemy/src/AWS/EventBridge/TestEventPattern.ts b/packages/alchemy/src/AWS/EventBridge/TestEventPattern.ts index fa1fc76cb..5bf54290a 100644 --- a/packages/alchemy/src/AWS/EventBridge/TestEventPattern.ts +++ b/packages/alchemy/src/AWS/EventBridge/TestEventPattern.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; +import type { Providers } from "../Providers.ts"; export interface TestEventPatternRequest extends eventbridge.TestEventPatternRequest {} @@ -37,7 +38,8 @@ export const TestEventPatternLive = Layer.effect( export class TestEventPatternPolicy extends Binding.Policy< TestEventPatternPolicy, - () => Effect.Effect + () => Effect.Effect, + Providers >()("AWS.EventBridge.TestEventPattern") {} export const TestEventPatternPolicyLive = TestEventPatternPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/EventBridge/ToLambda.ts b/packages/alchemy/src/AWS/EventBridge/ToLambda.ts index e35c473f9..19cc7d57f 100644 --- a/packages/alchemy/src/AWS/EventBridge/ToLambda.ts +++ b/packages/alchemy/src/AWS/EventBridge/ToLambda.ts @@ -5,6 +5,7 @@ import type { Function as LambdaFunction } from "../Lambda/Function.ts"; import { Permission as LambdaPermission } from "../Lambda/Permission.ts"; import type { EventBus } from "./EventBus.ts"; import { Rule, type RuleProps, type RuleTarget } from "./Rule.ts"; +import type { Providers } from "../Providers.ts"; interface EventDescriptor { id?: string; @@ -28,7 +29,8 @@ export class ToLambdaPolicy extends Binding.Policy< routeId: string, rule: { ruleArn: unknown }, fn: LambdaFunction, - ) => Effect.Effect + ) => Effect.Effect, + Providers >()("AWS.EventBridge.ToLambda") {} export const ToLambdaPolicyLive = ToLambdaPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/EventBridge/ToQueue.ts b/packages/alchemy/src/AWS/EventBridge/ToQueue.ts index 9f09df750..38ee6f85f 100644 --- a/packages/alchemy/src/AWS/EventBridge/ToQueue.ts +++ b/packages/alchemy/src/AWS/EventBridge/ToQueue.ts @@ -5,6 +5,7 @@ import type { PolicyStatement } from "../IAM/Policy.ts"; import type { Queue } from "../SQS/Queue.ts"; import type { EventBus } from "./EventBus.ts"; import { Rule, type RuleProps, type RuleTarget } from "./Rule.ts"; +import type { Providers } from "../Providers.ts"; interface EventDescriptor { id?: string; @@ -26,7 +27,8 @@ export interface QueueRouteTargetProps extends Pick< export class ToQueuePolicy extends Binding.Policy< ToQueuePolicy, - (rule: { ruleArn: unknown }, queue: Queue) => Effect.Effect + (rule: { ruleArn: unknown }, queue: Queue) => Effect.Effect, + Providers >()("AWS.EventBridge.ToQueue") {} export const ToQueuePolicyLive = ToQueuePolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/Kinesis/DescribeAccountSettings.ts b/packages/alchemy/src/AWS/Kinesis/DescribeAccountSettings.ts index 795a3e3af..7f131e6c0 100644 --- a/packages/alchemy/src/AWS/Kinesis/DescribeAccountSettings.ts +++ b/packages/alchemy/src/AWS/Kinesis/DescribeAccountSettings.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; +import type { Providers } from "../Providers.ts"; export interface DescribeAccountSettingsRequest extends Kinesis.DescribeAccountSettingsInput {} @@ -37,7 +38,8 @@ export const DescribeAccountSettingsLive = Layer.effect( export class DescribeAccountSettingsPolicy extends Binding.Policy< DescribeAccountSettingsPolicy, - () => Effect.Effect + () => Effect.Effect, + Providers >()("AWS.Kinesis.DescribeAccountSettings") {} export const DescribeAccountSettingsPolicyLive = diff --git a/packages/alchemy/src/AWS/Kinesis/DescribeLimits.ts b/packages/alchemy/src/AWS/Kinesis/DescribeLimits.ts index 34843bcd5..88c150bea 100644 --- a/packages/alchemy/src/AWS/Kinesis/DescribeLimits.ts +++ b/packages/alchemy/src/AWS/Kinesis/DescribeLimits.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; +import type { Providers } from "../Providers.ts"; export interface DescribeLimitsRequest extends Kinesis.DescribeLimitsInput {} @@ -36,7 +37,8 @@ export const DescribeLimitsLive = Layer.effect( export class DescribeLimitsPolicy extends Binding.Policy< DescribeLimitsPolicy, - () => Effect.Effect + () => Effect.Effect, + Providers >()("AWS.Kinesis.DescribeLimits") {} export const DescribeLimitsPolicyLive = DescribeLimitsPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/Kinesis/DescribeStream.ts b/packages/alchemy/src/AWS/Kinesis/DescribeStream.ts index 3ebe9d988..7bf2ccd22 100644 --- a/packages/alchemy/src/AWS/Kinesis/DescribeStream.ts +++ b/packages/alchemy/src/AWS/Kinesis/DescribeStream.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Stream } from "./Stream.ts"; +import type { Providers } from "../Providers.ts"; export interface DescribeStreamRequest extends Omit< Kinesis.DescribeStreamInput, @@ -46,7 +47,8 @@ export const DescribeStreamLive = Layer.effect( export class DescribeStreamPolicy extends Binding.Policy< DescribeStreamPolicy, - (stream: Stream) => Effect.Effect + (stream: Stream) => Effect.Effect, + Providers >()("AWS.Kinesis.DescribeStream") {} export const DescribeStreamPolicyLive = DescribeStreamPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/Kinesis/DescribeStreamConsumer.ts b/packages/alchemy/src/AWS/Kinesis/DescribeStreamConsumer.ts index 071399b38..9e3b3daee 100644 --- a/packages/alchemy/src/AWS/Kinesis/DescribeStreamConsumer.ts +++ b/packages/alchemy/src/AWS/Kinesis/DescribeStreamConsumer.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { StreamConsumer } from "./StreamConsumer.ts"; +import type { Providers } from "../Providers.ts"; export interface DescribeStreamConsumerRequest extends Omit< Kinesis.DescribeStreamConsumerInput, @@ -46,7 +47,8 @@ export const DescribeStreamConsumerLive = Layer.effect( export class DescribeStreamConsumerPolicy extends Binding.Policy< DescribeStreamConsumerPolicy, - (consumer: StreamConsumer) => Effect.Effect + (consumer: StreamConsumer) => Effect.Effect, + Providers >()("AWS.Kinesis.DescribeStreamConsumer") {} export const DescribeStreamConsumerPolicyLive = diff --git a/packages/alchemy/src/AWS/Kinesis/DescribeStreamSummary.ts b/packages/alchemy/src/AWS/Kinesis/DescribeStreamSummary.ts index 37085513c..3366a4f98 100644 --- a/packages/alchemy/src/AWS/Kinesis/DescribeStreamSummary.ts +++ b/packages/alchemy/src/AWS/Kinesis/DescribeStreamSummary.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Stream } from "./Stream.ts"; +import type { Providers } from "../Providers.ts"; export interface DescribeStreamSummaryRequest extends Omit< Kinesis.DescribeStreamSummaryInput, @@ -46,7 +47,8 @@ export const DescribeStreamSummaryLive = Layer.effect( export class DescribeStreamSummaryPolicy extends Binding.Policy< DescribeStreamSummaryPolicy, - (stream: Stream) => Effect.Effect + (stream: Stream) => Effect.Effect, + Providers >()("AWS.Kinesis.DescribeStreamSummary") {} export const DescribeStreamSummaryPolicyLive = diff --git a/packages/alchemy/src/AWS/Kinesis/GetRecords.ts b/packages/alchemy/src/AWS/Kinesis/GetRecords.ts index 96ddd2458..bc9013597 100644 --- a/packages/alchemy/src/AWS/Kinesis/GetRecords.ts +++ b/packages/alchemy/src/AWS/Kinesis/GetRecords.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Stream } from "./Stream.ts"; +import type { Providers } from "../Providers.ts"; export interface GetRecordsRequest extends Kinesis.GetRecordsInput {} @@ -36,7 +37,8 @@ export const GetRecordsLive = Layer.effect( export class GetRecordsPolicy extends Binding.Policy< GetRecordsPolicy, - (stream: Stream) => Effect.Effect + (stream: Stream) => Effect.Effect, + Providers >()("AWS.Kinesis.GetRecords") {} export const GetRecordsPolicyLive = GetRecordsPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/Kinesis/GetResourcePolicy.ts b/packages/alchemy/src/AWS/Kinesis/GetResourcePolicy.ts index b5b4ef7a0..46047e08b 100644 --- a/packages/alchemy/src/AWS/Kinesis/GetResourcePolicy.ts +++ b/packages/alchemy/src/AWS/Kinesis/GetResourcePolicy.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Stream } from "./Stream.ts"; +import type { Providers } from "../Providers.ts"; export interface GetResourcePolicyRequest extends Omit< Kinesis.GetResourcePolicyInput, @@ -46,7 +47,8 @@ export const GetResourcePolicyLive = Layer.effect( export class GetResourcePolicyPolicy extends Binding.Policy< GetResourcePolicyPolicy, - (stream: Stream) => Effect.Effect + (stream: Stream) => Effect.Effect, + Providers >()("AWS.Kinesis.GetResourcePolicy") {} export const GetResourcePolicyPolicyLive = diff --git a/packages/alchemy/src/AWS/Kinesis/GetShardIterator.ts b/packages/alchemy/src/AWS/Kinesis/GetShardIterator.ts index 95fe9333a..1ae78901e 100644 --- a/packages/alchemy/src/AWS/Kinesis/GetShardIterator.ts +++ b/packages/alchemy/src/AWS/Kinesis/GetShardIterator.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Stream } from "./Stream.ts"; +import type { Providers } from "../Providers.ts"; export interface GetShardIteratorRequest extends Omit< Kinesis.GetShardIteratorInput, @@ -46,7 +47,8 @@ export const GetShardIteratorLive = Layer.effect( export class GetShardIteratorPolicy extends Binding.Policy< GetShardIteratorPolicy, - (stream: Stream) => Effect.Effect + (stream: Stream) => Effect.Effect, + Providers >()("AWS.Kinesis.GetShardIterator") {} export const GetShardIteratorPolicyLive = GetShardIteratorPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/Kinesis/ListShards.ts b/packages/alchemy/src/AWS/Kinesis/ListShards.ts index 613f50006..9a89b127f 100644 --- a/packages/alchemy/src/AWS/Kinesis/ListShards.ts +++ b/packages/alchemy/src/AWS/Kinesis/ListShards.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Stream } from "./Stream.ts"; +import type { Providers } from "../Providers.ts"; export interface ListShardsRequest extends Omit< Kinesis.ListShardsInput, @@ -43,7 +44,8 @@ export const ListShardsLive = Layer.effect( export class ListShardsPolicy extends Binding.Policy< ListShardsPolicy, - (stream: Stream) => Effect.Effect + (stream: Stream) => Effect.Effect, + Providers >()("AWS.Kinesis.ListShards") {} export const ListShardsPolicyLive = ListShardsPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/Kinesis/ListStreamConsumers.ts b/packages/alchemy/src/AWS/Kinesis/ListStreamConsumers.ts index b3fbe17b1..cf4c27fe2 100644 --- a/packages/alchemy/src/AWS/Kinesis/ListStreamConsumers.ts +++ b/packages/alchemy/src/AWS/Kinesis/ListStreamConsumers.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Stream } from "./Stream.ts"; +import type { Providers } from "../Providers.ts"; export interface ListStreamConsumersRequest extends Omit< Kinesis.ListStreamConsumersInput, @@ -46,7 +47,8 @@ export const ListStreamConsumersLive = Layer.effect( export class ListStreamConsumersPolicy extends Binding.Policy< ListStreamConsumersPolicy, - (stream: Stream) => Effect.Effect + (stream: Stream) => Effect.Effect, + Providers >()("AWS.Kinesis.ListStreamConsumers") {} export const ListStreamConsumersPolicyLive = diff --git a/packages/alchemy/src/AWS/Kinesis/ListStreams.ts b/packages/alchemy/src/AWS/Kinesis/ListStreams.ts index 0cc4e94bc..876697193 100644 --- a/packages/alchemy/src/AWS/Kinesis/ListStreams.ts +++ b/packages/alchemy/src/AWS/Kinesis/ListStreams.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; +import type { Providers } from "../Providers.ts"; export interface ListStreamsRequest extends Kinesis.ListStreamsInput {} @@ -33,7 +34,8 @@ export const ListStreamsLive = Layer.effect( export class ListStreamsPolicy extends Binding.Policy< ListStreamsPolicy, - () => Effect.Effect + () => Effect.Effect, + Providers >()("AWS.Kinesis.ListStreams") {} export const ListStreamsPolicyLive = ListStreamsPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/Kinesis/ListTagsForResource.ts b/packages/alchemy/src/AWS/Kinesis/ListTagsForResource.ts index 6b8b2f334..fdc4de186 100644 --- a/packages/alchemy/src/AWS/Kinesis/ListTagsForResource.ts +++ b/packages/alchemy/src/AWS/Kinesis/ListTagsForResource.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Stream } from "./Stream.ts"; import type { StreamConsumer } from "./StreamConsumer.ts"; +import type { Providers } from "../Providers.ts"; type TaggableResource = Stream | StreamConsumer; @@ -52,7 +53,8 @@ export const ListTagsForResourceLive = Layer.effect( export class ListTagsForResourcePolicy extends Binding.Policy< ListTagsForResourcePolicy, - (resource: TaggableResource) => Effect.Effect + (resource: TaggableResource) => Effect.Effect, + Providers >()("AWS.Kinesis.ListTagsForResource") {} export const ListTagsForResourcePolicyLive = diff --git a/packages/alchemy/src/AWS/Kinesis/PutRecord.ts b/packages/alchemy/src/AWS/Kinesis/PutRecord.ts index e248e0920..72ef8fbee 100644 --- a/packages/alchemy/src/AWS/Kinesis/PutRecord.ts +++ b/packages/alchemy/src/AWS/Kinesis/PutRecord.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import * as Output from "../../Output.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Stream } from "./Stream.ts"; +import type { Providers } from "../Providers.ts"; export interface PutRecordRequest extends Omit< Kinesis.PutRecordInput, @@ -44,7 +45,8 @@ export const PutRecordLive = Layer.effect( export class PutRecordPolicy extends Binding.Policy< PutRecordPolicy, - (stream: Stream) => Effect.Effect + (stream: Stream) => Effect.Effect, + Providers >()("AWS.Kinesis.PutRecord") {} export const PutRecordPolicyLive = PutRecordPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/Kinesis/PutRecords.ts b/packages/alchemy/src/AWS/Kinesis/PutRecords.ts index b584b88ab..e1a9fc8fa 100644 --- a/packages/alchemy/src/AWS/Kinesis/PutRecords.ts +++ b/packages/alchemy/src/AWS/Kinesis/PutRecords.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import * as Output from "../../Output.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Stream } from "./Stream.ts"; +import type { Providers } from "../Providers.ts"; export interface PutRecordsRequest extends Omit< Kinesis.PutRecordsInput, @@ -44,7 +45,8 @@ export const PutRecordsLive = Layer.effect( export class PutRecordsPolicy extends Binding.Policy< PutRecordsPolicy, - (stream: Stream) => Effect.Effect + (stream: Stream) => Effect.Effect, + Providers >()("AWS.Kinesis.PutRecords") {} export const PutRecordsPolicyLive = PutRecordsPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/Kinesis/StreamSink.ts b/packages/alchemy/src/AWS/Kinesis/StreamSink.ts index ee930d7de..09a79c49c 100644 --- a/packages/alchemy/src/AWS/Kinesis/StreamSink.ts +++ b/packages/alchemy/src/AWS/Kinesis/StreamSink.ts @@ -6,6 +6,7 @@ import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import { PutRecords } from "./PutRecords.ts"; import type { Stream } from "./Stream.ts"; +import type { Providers } from "../Providers.ts"; export type StreamSinkRecord = Kinesis.PutRecordsRequestEntry; @@ -45,7 +46,8 @@ export const StreamSinkLive = Layer.effect( export class StreamSinkPolicy extends Binding.Policy< StreamSinkPolicy, - (stream: Stream) => Effect.Effect + (stream: Stream) => Effect.Effect, + Providers >()("AWS.Kinesis.StreamSink") {} export const StreamSinkPolicyLive = StreamSinkPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/Kinesis/SubscribeToShard.ts b/packages/alchemy/src/AWS/Kinesis/SubscribeToShard.ts index 3bcdc1bc4..13e4ccfa0 100644 --- a/packages/alchemy/src/AWS/Kinesis/SubscribeToShard.ts +++ b/packages/alchemy/src/AWS/Kinesis/SubscribeToShard.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { StreamConsumer } from "./StreamConsumer.ts"; +import type { Providers } from "../Providers.ts"; export interface SubscribeToShardRequest extends Omit< Kinesis.SubscribeToShardInput, @@ -46,7 +47,8 @@ export const SubscribeToShardLive = Layer.effect( export class SubscribeToShardPolicy extends Binding.Policy< SubscribeToShardPolicy, - (consumer: StreamConsumer) => Effect.Effect + (consumer: StreamConsumer) => Effect.Effect, + Providers >()("AWS.Kinesis.SubscribeToShard") {} export const SubscribeToShardPolicyLive = SubscribeToShardPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/Lambda/BucketEventSource.ts b/packages/alchemy/src/AWS/Lambda/BucketEventSource.ts index 01965d7be..6496f6f8a 100644 --- a/packages/alchemy/src/AWS/Lambda/BucketEventSource.ts +++ b/packages/alchemy/src/AWS/Lambda/BucketEventSource.ts @@ -16,6 +16,7 @@ import type { import type { S3EventType } from "../S3/S3Event.ts"; import * as Lambda from "./Function.ts"; import { Permission as LambdaPermission } from "./Permission.ts"; +import type { Providers } from "../Providers.ts"; /** * Connects an S3 bucket notification stream to the current Lambda function. @@ -101,7 +102,8 @@ export class BucketEventSourcePolicy extends Binding.Policy< ( bucket: Bucket, props?: NotificationsProps, - ) => Effect.Effect + ) => Effect.Effect, + Providers >()("AWS.S3.BucketEventSource") {} export const BucketEventSourcePolicyLive = BucketEventSourcePolicy.layer.effect( diff --git a/packages/alchemy/src/AWS/Lambda/EventBridgeEventSource.ts b/packages/alchemy/src/AWS/Lambda/EventBridgeEventSource.ts index b5066b56e..b229226ee 100644 --- a/packages/alchemy/src/AWS/Lambda/EventBridgeEventSource.ts +++ b/packages/alchemy/src/AWS/Lambda/EventBridgeEventSource.ts @@ -13,6 +13,7 @@ import { } from "../EventBridge/EventSource.ts"; import { toLambda as createLambdaRoute } from "../EventBridge/ToLambda.ts"; import * as Lambda from "./Function.ts"; +import type { Providers } from "../Providers.ts"; /** * Narrow an arbitrary Lambda invocation payload to an EventBridge event. @@ -158,7 +159,8 @@ export class EventSourcePolicy extends Binding.Policy< EventSourcePolicy, ( descriptor: Parameters[0], - ) => Effect.Effect + ) => Effect.Effect, + Providers >()("AWS.EventBridge.EventSource") {} /** diff --git a/packages/alchemy/src/AWS/Lambda/InvokeFunction.ts b/packages/alchemy/src/AWS/Lambda/InvokeFunction.ts index d378b0297..c9f53679e 100644 --- a/packages/alchemy/src/AWS/Lambda/InvokeFunction.ts +++ b/packages/alchemy/src/AWS/Lambda/InvokeFunction.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import * as Output from "../../Output.ts"; import type { Function } from "./Function.ts"; import { isFunction } from "./Function.ts"; +import type { Providers } from "../Providers.ts"; export interface InvokeFunctionRequest extends Omit< Lambda.InvocationRequest, @@ -44,7 +45,8 @@ export const InvokeFunctionLive = Layer.effect( export class InvokeFunctionPolicy extends Binding.Policy< InvokeFunctionPolicy, - (func: Function) => Effect.Effect + (func: Function) => Effect.Effect, + Providers >()("AWS.Lambda.InvokeFunction") {} export const InvokeFunctionPolicyLive = InvokeFunctionPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/Lambda/QueueEventSource.ts b/packages/alchemy/src/AWS/Lambda/QueueEventSource.ts index 79d0f132d..0fb616098 100644 --- a/packages/alchemy/src/AWS/Lambda/QueueEventSource.ts +++ b/packages/alchemy/src/AWS/Lambda/QueueEventSource.ts @@ -12,6 +12,7 @@ import { } from "../SQS/QueueEventSource.ts"; import { EventSourceMapping } from "./EventSourceMapping.ts"; import * as Lambda from "./Function.ts"; +import type { Providers } from "../Providers.ts"; export const isSQSEvent = (event: any): event is lambda.SQSEvent => Array.isArray(event?.Records) && @@ -53,7 +54,8 @@ export const QueueEventSource = Layer.effect( export class QueueEventSourcePolicy extends Binding.Policy< QueueEventSourcePolicy, - (queue: Queue, props: QueueEventSourceProps) => Effect.Effect + (queue: Queue, props: QueueEventSourceProps) => Effect.Effect, + Providers >()("AWS.SQS.QueueEventSourcePolicy") {} export const QueueEventSourcePolicyLive = QueueEventSourcePolicy.layer.effect( diff --git a/packages/alchemy/src/AWS/Lambda/StreamEventSource.ts b/packages/alchemy/src/AWS/Lambda/StreamEventSource.ts index 63961b1e0..ae847c174 100644 --- a/packages/alchemy/src/AWS/Lambda/StreamEventSource.ts +++ b/packages/alchemy/src/AWS/Lambda/StreamEventSource.ts @@ -12,6 +12,7 @@ import { } from "../Kinesis/StreamEventSource.ts"; import { EventSourceMapping } from "./EventSourceMapping.ts"; import * as Lambda from "./Function.ts"; +import type { Providers } from "../Providers.ts"; export const isKinesisStreamEvent = ( event: any, @@ -63,7 +64,8 @@ export const StreamEventSource = Layer.effect( export class StreamEventSourcePolicy extends Binding.Policy< StreamEventSourcePolicy, - (stream: KinesisStream, props: StreamEventSourceProps) => Effect.Effect + (stream: KinesisStream, props: StreamEventSourceProps) => Effect.Effect, + Providers >()("AWS.Kinesis.StreamEventSource") {} export const StreamEventSourcePolicyLive = StreamEventSourcePolicy.layer.effect( diff --git a/packages/alchemy/src/AWS/Lambda/TableEventSource.ts b/packages/alchemy/src/AWS/Lambda/TableEventSource.ts index 3e4138ace..039d4fa9c 100644 --- a/packages/alchemy/src/AWS/Lambda/TableEventSource.ts +++ b/packages/alchemy/src/AWS/Lambda/TableEventSource.ts @@ -14,6 +14,7 @@ import { import type { Table } from "../DynamoDB/Table.ts"; import { EventSourceMapping } from "./EventSourceMapping.ts"; import * as Lambda from "./Function.ts"; +import type { Providers } from "../Providers.ts"; export const isDynamoDBStreamEvent = ( event: any, @@ -66,7 +67,8 @@ export const TableEventSource = Layer.effect( export class TableEventSourcePolicy extends Binding.Policy< TableEventSourcePolicy, - (table: Table, props: StreamsProps) => Effect.Effect + (table: Table, props: StreamsProps) => Effect.Effect, + Providers >()("AWS.DynamoDB.TableEventSource") {} export const TableEventSourcePolicyLive = TableEventSourcePolicy.layer.effect( diff --git a/packages/alchemy/src/AWS/Lambda/TopicEventSource.ts b/packages/alchemy/src/AWS/Lambda/TopicEventSource.ts index 2c9a975c5..0fefed31a 100644 --- a/packages/alchemy/src/AWS/Lambda/TopicEventSource.ts +++ b/packages/alchemy/src/AWS/Lambda/TopicEventSource.ts @@ -13,6 +13,7 @@ import { } from "../SNS/TopicEventSource.ts"; import * as Lambda from "./Function.ts"; import { Permission as LambdaPermission } from "./Permission.ts"; +import type { Providers } from "../Providers.ts"; export const isSNSEvent = (event: any): event is lambda.SNSEvent => Array.isArray(event?.Records) && @@ -63,7 +64,8 @@ export const TopicEventSource = Layer.effect( export class TopicEventSourcePolicy extends Binding.Policy< TopicEventSourcePolicy, - (topic: Topic, props?: TopicEventSourceProps) => Effect.Effect + (topic: Topic, props?: TopicEventSourceProps) => Effect.Effect, + Providers >()("AWS.SNS.TopicEventSource") {} export const TopicEventSourcePolicyLive = TopicEventSourcePolicy.layer.effect( diff --git a/packages/alchemy/src/AWS/Providers.ts b/packages/alchemy/src/AWS/Providers.ts index 2eea06485..b3b15c56c 100644 --- a/packages/alchemy/src/AWS/Providers.ts +++ b/packages/alchemy/src/AWS/Providers.ts @@ -40,8 +40,8 @@ import { Default as DefaultEnvironment } from "./Environment.ts"; import * as EventBridge from "./EventBridge/index.ts"; import * as IAM from "./IAM/index.ts"; import * as IdentityCenter from "./IdentityCenter/index.ts"; -import * as KMS from "./KMS/index.ts"; import * as Kinesis from "./Kinesis/index.ts"; +import * as KMS from "./KMS/index.ts"; import * as Lambda from "./Lambda/index.ts"; import * as Logs from "./Logs/index.ts"; import * as Organizations from "./Organizations/index.ts"; diff --git a/packages/alchemy/src/AWS/RDS/Connect.ts b/packages/alchemy/src/AWS/RDS/Connect.ts index 37d7a581c..84ee41ff0 100644 --- a/packages/alchemy/src/AWS/RDS/Connect.ts +++ b/packages/alchemy/src/AWS/RDS/Connect.ts @@ -11,6 +11,7 @@ import type { Secret } from "../SecretsManager/Secret.ts"; import type { DBCluster } from "./DBCluster.ts"; import type { DBProxy } from "./DBProxy.ts"; import type { DBProxyEndpoint } from "./DBProxyEndpoint.ts"; +import type { Providers } from "../Providers.ts"; type ConnectResource = DBCluster | DBProxy | DBProxyEndpoint; @@ -101,7 +102,8 @@ export const ConnectLive = Layer.effect( export class ConnectPolicy extends Binding.Policy< ConnectPolicy, - (resource: ConnectResource, options: ConnectOptions) => Effect.Effect + (resource: ConnectResource, options: ConnectOptions) => Effect.Effect, + Providers >()("AWS.RDS.Connect") {} export const ConnectPolicyLive = ConnectPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/RDSData/BatchExecuteStatement.ts b/packages/alchemy/src/AWS/RDSData/BatchExecuteStatement.ts index eecd26a02..70d694ff5 100644 --- a/packages/alchemy/src/AWS/RDSData/BatchExecuteStatement.ts +++ b/packages/alchemy/src/AWS/RDSData/BatchExecuteStatement.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { DBCluster } from "../RDS/DBCluster.ts"; import type { Secret } from "../SecretsManager/Secret.ts"; +import type { Providers } from "../Providers.ts"; export interface BatchExecuteStatementOptions { secret: Secret; @@ -69,7 +70,8 @@ export class BatchExecuteStatementPolicy extends Binding.Policy< ( cluster: DBCluster, options: BatchExecuteStatementOptions, - ) => Effect.Effect + ) => Effect.Effect, + Providers >()("AWS.RDSData.BatchExecuteStatement") {} export const BatchExecuteStatementPolicyLive = diff --git a/packages/alchemy/src/AWS/RDSData/BeginTransaction.ts b/packages/alchemy/src/AWS/RDSData/BeginTransaction.ts index 000bd1e66..5cc20a571 100644 --- a/packages/alchemy/src/AWS/RDSData/BeginTransaction.ts +++ b/packages/alchemy/src/AWS/RDSData/BeginTransaction.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { DBCluster } from "../RDS/DBCluster.ts"; import type { Secret } from "../SecretsManager/Secret.ts"; +import type { Providers } from "../Providers.ts"; export interface BeginTransactionOptions { secret: Secret; @@ -58,7 +59,8 @@ export const BeginTransactionLive = Layer.effect( export class BeginTransactionPolicy extends Binding.Policy< BeginTransactionPolicy, - (cluster: DBCluster, options: BeginTransactionOptions) => Effect.Effect + (cluster: DBCluster, options: BeginTransactionOptions) => Effect.Effect, + Providers >()("AWS.RDSData.BeginTransaction") {} export const BeginTransactionPolicyLive = BeginTransactionPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/RDSData/CommitTransaction.ts b/packages/alchemy/src/AWS/RDSData/CommitTransaction.ts index 9e2de8557..47036c584 100644 --- a/packages/alchemy/src/AWS/RDSData/CommitTransaction.ts +++ b/packages/alchemy/src/AWS/RDSData/CommitTransaction.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { DBCluster } from "../RDS/DBCluster.ts"; import type { Secret } from "../SecretsManager/Secret.ts"; +import type { Providers } from "../Providers.ts"; export interface CommitTransactionOptions { secret: Secret; @@ -62,7 +63,11 @@ export const CommitTransactionLive = Layer.effect( export class CommitTransactionPolicy extends Binding.Policy< CommitTransactionPolicy, - (cluster: DBCluster, options: CommitTransactionOptions) => Effect.Effect + ( + cluster: DBCluster, + options: CommitTransactionOptions, + ) => Effect.Effect, + Providers >()("AWS.RDSData.CommitTransaction") {} export const CommitTransactionPolicyLive = diff --git a/packages/alchemy/src/AWS/RDSData/ExecuteSql.ts b/packages/alchemy/src/AWS/RDSData/ExecuteSql.ts index 49096a03e..312a0095a 100644 --- a/packages/alchemy/src/AWS/RDSData/ExecuteSql.ts +++ b/packages/alchemy/src/AWS/RDSData/ExecuteSql.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { DBCluster } from "../RDS/DBCluster.ts"; import type { Secret } from "../SecretsManager/Secret.ts"; +import type { Providers } from "../Providers.ts"; export interface ExecuteSqlOptions { secret: Secret; @@ -61,7 +62,8 @@ export const ExecuteSqlLive = Layer.effect( export class ExecuteSqlPolicy extends Binding.Policy< ExecuteSqlPolicy, - (cluster: DBCluster, options: ExecuteSqlOptions) => Effect.Effect + (cluster: DBCluster, options: ExecuteSqlOptions) => Effect.Effect, + Providers >()("AWS.RDSData.ExecuteSql") {} export const ExecuteSqlPolicyLive = ExecuteSqlPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/RDSData/ExecuteStatement.ts b/packages/alchemy/src/AWS/RDSData/ExecuteStatement.ts index 79ec41bdc..4a10d63a5 100644 --- a/packages/alchemy/src/AWS/RDSData/ExecuteStatement.ts +++ b/packages/alchemy/src/AWS/RDSData/ExecuteStatement.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { DBCluster } from "../RDS/DBCluster.ts"; import type { Secret } from "../SecretsManager/Secret.ts"; +import type { Providers } from "../Providers.ts"; export interface ExecuteStatementOptions { secret: Secret; @@ -66,7 +67,8 @@ export const ExecuteStatementLive = Layer.effect( export class ExecuteStatementPolicy extends Binding.Policy< ExecuteStatementPolicy, - (cluster: DBCluster, options: ExecuteStatementOptions) => Effect.Effect + (cluster: DBCluster, options: ExecuteStatementOptions) => Effect.Effect, + Providers >()("AWS.RDSData.ExecuteStatement") {} export const ExecuteStatementPolicyLive = ExecuteStatementPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/RDSData/RollbackTransaction.ts b/packages/alchemy/src/AWS/RDSData/RollbackTransaction.ts index 6810b6816..5637f3875 100644 --- a/packages/alchemy/src/AWS/RDSData/RollbackTransaction.ts +++ b/packages/alchemy/src/AWS/RDSData/RollbackTransaction.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { DBCluster } from "../RDS/DBCluster.ts"; import type { Secret } from "../SecretsManager/Secret.ts"; +import type { Providers } from "../Providers.ts"; export interface RollbackTransactionOptions { secret: Secret; @@ -65,7 +66,8 @@ export class RollbackTransactionPolicy extends Binding.Policy< ( cluster: DBCluster, options: RollbackTransactionOptions, - ) => Effect.Effect + ) => Effect.Effect, + Providers >()("AWS.RDSData.RollbackTransaction") {} export const RollbackTransactionPolicyLive = diff --git a/packages/alchemy/src/AWS/Region.ts b/packages/alchemy/src/AWS/Region.ts index 957a9467c..f69ce2362 100644 --- a/packages/alchemy/src/AWS/Region.ts +++ b/packages/alchemy/src/AWS/Region.ts @@ -6,6 +6,12 @@ import { AWSEnvironment } from "./Environment.ts"; export { AWS_REGION, type RegionID } from "./Environment.ts"; export { Region } from "@distilled.cloud/aws/Region"; +declare module "@distilled.cloud/aws/Region" { + interface Region { + readonly kind: "Environment"; + } +} + export const of = (region: string) => Layer.succeed(Region.Region, Effect.succeed(region)); diff --git a/packages/alchemy/src/AWS/S3/AbortMultipartUpload.ts b/packages/alchemy/src/AWS/S3/AbortMultipartUpload.ts index da8afeeb0..d3386a04f 100644 --- a/packages/alchemy/src/AWS/S3/AbortMultipartUpload.ts +++ b/packages/alchemy/src/AWS/S3/AbortMultipartUpload.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import * as Output from "../../Output.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Bucket } from "./Bucket.ts"; +import type { Providers } from "../Providers.ts"; export interface AbortMultipartUploadRequest extends Omit< S3.AbortMultipartUploadRequest, @@ -47,7 +48,8 @@ export const AbortMultipartUploadLive = Layer.effect( export class AbortMultipartUploadPolicy extends Binding.Policy< AbortMultipartUploadPolicy, - (bucket: Bucket) => Effect.Effect + (bucket: Bucket) => Effect.Effect, + Providers >()("AWS.S3.AbortMultipartUpload") {} export const AbortMultipartUploadPolicyLive = diff --git a/packages/alchemy/src/AWS/S3/CompleteMultipartUpload.ts b/packages/alchemy/src/AWS/S3/CompleteMultipartUpload.ts index f2f6f5dbe..7b087a8ab 100644 --- a/packages/alchemy/src/AWS/S3/CompleteMultipartUpload.ts +++ b/packages/alchemy/src/AWS/S3/CompleteMultipartUpload.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import * as Output from "../../Output.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Bucket } from "./Bucket.ts"; +import type { Providers } from "../Providers.ts"; export interface CompleteMultipartUploadRequest extends Omit< S3.CompleteMultipartUploadRequest, @@ -47,7 +48,8 @@ export const CompleteMultipartUploadLive = Layer.effect( export class CompleteMultipartUploadPolicy extends Binding.Policy< CompleteMultipartUploadPolicy, - (bucket: Bucket) => Effect.Effect + (bucket: Bucket) => Effect.Effect, + Providers >()("AWS.S3.CompleteMultipartUpload") {} export const CompleteMultipartUploadPolicyLive = diff --git a/packages/alchemy/src/AWS/S3/CopyObject.ts b/packages/alchemy/src/AWS/S3/CopyObject.ts index 1d5fd70cf..91a9a0908 100644 --- a/packages/alchemy/src/AWS/S3/CopyObject.ts +++ b/packages/alchemy/src/AWS/S3/CopyObject.ts @@ -6,6 +6,7 @@ import * as Binding from "../../Binding.ts"; import * as Output from "../../Output.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Bucket } from "./Bucket.ts"; +import type { Providers } from "../Providers.ts"; export interface CopyObjectRequest extends Omit< S3.CopyObjectRequest, @@ -45,7 +46,8 @@ export const CopyObjectLive = Layer.effect( export class CopyObjectPolicy extends Binding.Policy< CopyObjectPolicy, - (bucket: Bucket) => Effect.Effect + (bucket: Bucket) => Effect.Effect, + Providers >()("AWS.S3.CopyObject") {} export const CopyObjectPolicyLive = CopyObjectPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/S3/CreateMultipartUpload.ts b/packages/alchemy/src/AWS/S3/CreateMultipartUpload.ts index d637519d8..3e341fa60 100644 --- a/packages/alchemy/src/AWS/S3/CreateMultipartUpload.ts +++ b/packages/alchemy/src/AWS/S3/CreateMultipartUpload.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import * as Output from "../../Output.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Bucket } from "./Bucket.ts"; +import type { Providers } from "../Providers.ts"; export interface CreateMultipartUploadRequest extends Omit< S3.CreateMultipartUploadRequest, @@ -47,7 +48,8 @@ export const CreateMultipartUploadLive = Layer.effect( export class CreateMultipartUploadPolicy extends Binding.Policy< CreateMultipartUploadPolicy, - (bucket: Bucket) => Effect.Effect + (bucket: Bucket) => Effect.Effect, + Providers >()("AWS.S3.CreateMultipartUpload") {} export const CreateMultipartUploadPolicyLive = diff --git a/packages/alchemy/src/AWS/S3/DeleteObject.ts b/packages/alchemy/src/AWS/S3/DeleteObject.ts index 458dc01ad..b53e6ab9e 100644 --- a/packages/alchemy/src/AWS/S3/DeleteObject.ts +++ b/packages/alchemy/src/AWS/S3/DeleteObject.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import * as Output from "../../Output.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Bucket } from "./Bucket.ts"; +import type { Providers } from "../Providers.ts"; export interface DeleteObjectRequest extends Omit< S3.DeleteObjectRequest, @@ -44,7 +45,8 @@ export const DeleteObjectLive = Layer.effect( export class DeleteObjectPolicy extends Binding.Policy< DeleteObjectPolicy, - (bucket: Bucket) => Effect.Effect + (bucket: Bucket) => Effect.Effect, + Providers >()("AWS.S3.DeleteObject") {} export const DeleteObjectPolicyLive = DeleteObjectPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/S3/GetObject.ts b/packages/alchemy/src/AWS/S3/GetObject.ts index 7466837ff..9934923a8 100644 --- a/packages/alchemy/src/AWS/S3/GetObject.ts +++ b/packages/alchemy/src/AWS/S3/GetObject.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import * as Output from "../../Output.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Bucket } from "./Bucket.ts"; +import type { Providers } from "../Providers.ts"; export interface GetObjectRequest extends Omit {} @@ -42,7 +43,8 @@ export const GetObjectLive = Layer.effect( export class GetObjectPolicy extends Binding.Policy< GetObjectPolicy, - (bucket: Bucket) => Effect.Effect + (bucket: Bucket) => Effect.Effect, + Providers >()("AWS.S3.GetObject") {} export const GetObjectPolicyLive = GetObjectPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/S3/HeadObject.ts b/packages/alchemy/src/AWS/S3/HeadObject.ts index 44ac3c38a..5a36c3701 100644 --- a/packages/alchemy/src/AWS/S3/HeadObject.ts +++ b/packages/alchemy/src/AWS/S3/HeadObject.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import * as Output from "../../Output.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Bucket } from "./Bucket.ts"; +import type { Providers } from "../Providers.ts"; export interface HeadObjectRequest extends Omit< S3.HeadObjectRequest, @@ -44,7 +45,8 @@ export const HeadObjectLive = Layer.effect( export class HeadObjectPolicy extends Binding.Policy< HeadObjectPolicy, - (bucket: Bucket) => Effect.Effect + (bucket: Bucket) => Effect.Effect, + Providers >()("AWS.S3.HeadObject") {} export const HeadObjectPolicyLive = HeadObjectPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/S3/ListObjectsV2.ts b/packages/alchemy/src/AWS/S3/ListObjectsV2.ts index 4c5978154..6cb358aaf 100644 --- a/packages/alchemy/src/AWS/S3/ListObjectsV2.ts +++ b/packages/alchemy/src/AWS/S3/ListObjectsV2.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import * as Output from "../../Output.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Bucket } from "./Bucket.ts"; +import type { Providers } from "../Providers.ts"; export interface ListObjectsV2Request extends Omit< S3.ListObjectsV2Request, @@ -44,7 +45,8 @@ export const ListObjectsV2Live = Layer.effect( export class ListObjectsV2Policy extends Binding.Policy< ListObjectsV2Policy, - (bucket: Bucket) => Effect.Effect + (bucket: Bucket) => Effect.Effect, + Providers >()("AWS.S3.ListObjectsV2") {} export const ListObjectsV2PolicyLive = ListObjectsV2Policy.layer.succeed( diff --git a/packages/alchemy/src/AWS/S3/PutObject.ts b/packages/alchemy/src/AWS/S3/PutObject.ts index 5ae3a5f8b..681a46756 100644 --- a/packages/alchemy/src/AWS/S3/PutObject.ts +++ b/packages/alchemy/src/AWS/S3/PutObject.ts @@ -6,6 +6,7 @@ import * as Binding from "../../Binding.ts"; import * as Output from "../../Output.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Bucket } from "./Bucket.ts"; +import type { Providers } from "../Providers.ts"; export interface PutObjectRequest extends Omit {} @@ -59,7 +60,8 @@ export const PutObjectLive = Layer.effect( export class PutObjectPolicy extends Binding.Policy< PutObjectPolicy, - (bucket: Bucket) => Effect.Effect + (bucket: Bucket) => Effect.Effect, + Providers >()("AWS.S3.PutObject") {} export const PutObjectPolicyLive = PutObjectPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/S3/UploadPart.ts b/packages/alchemy/src/AWS/S3/UploadPart.ts index 45eebd835..f726a9078 100644 --- a/packages/alchemy/src/AWS/S3/UploadPart.ts +++ b/packages/alchemy/src/AWS/S3/UploadPart.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import * as Output from "../../Output.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Bucket } from "./Bucket.ts"; +import type { Providers } from "../Providers.ts"; export interface UploadPartRequest extends Omit< S3.UploadPartRequest, @@ -44,7 +45,8 @@ export const UploadPartLive = Layer.effect( export class UploadPartPolicy extends Binding.Policy< UploadPartPolicy, - (bucket: Bucket) => Effect.Effect + (bucket: Bucket) => Effect.Effect, + Providers >()("AWS.S3.UploadPart") {} export const UploadPartPolicyLive = UploadPartPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/SNS/AddPermission.ts b/packages/alchemy/src/AWS/SNS/AddPermission.ts index da2e29cc8..df1e1ae50 100644 --- a/packages/alchemy/src/AWS/SNS/AddPermission.ts +++ b/packages/alchemy/src/AWS/SNS/AddPermission.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Topic } from "./Topic.ts"; +import type { Providers } from "../Providers.ts"; export interface AddPermissionRequest extends Omit< sns.AddPermissionInput, @@ -43,7 +44,8 @@ export const AddPermissionLive = Layer.effect( export class AddPermissionPolicy extends Binding.Policy< AddPermissionPolicy, - (topic: Topic) => Effect.Effect + (topic: Topic) => Effect.Effect, + Providers >()("AWS.SNS.AddPermission") {} export const AddPermissionPolicyLive = AddPermissionPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/SNS/ConfirmSubscription.ts b/packages/alchemy/src/AWS/SNS/ConfirmSubscription.ts index 529552e93..d452eae44 100644 --- a/packages/alchemy/src/AWS/SNS/ConfirmSubscription.ts +++ b/packages/alchemy/src/AWS/SNS/ConfirmSubscription.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Subscription } from "./Subscription.ts"; +import type { Providers } from "../Providers.ts"; export interface ConfirmSubscriptionRequest extends Omit< sns.ConfirmSubscriptionInput, @@ -46,7 +47,8 @@ export const ConfirmSubscriptionLive = Layer.effect( export class ConfirmSubscriptionPolicy extends Binding.Policy< ConfirmSubscriptionPolicy, - (subscription: Subscription) => Effect.Effect + (subscription: Subscription) => Effect.Effect, + Providers >()("AWS.SNS.ConfirmSubscription") {} export const ConfirmSubscriptionPolicyLive = diff --git a/packages/alchemy/src/AWS/SNS/GetDataProtectionPolicy.ts b/packages/alchemy/src/AWS/SNS/GetDataProtectionPolicy.ts index 2e2d1f2d5..d2e908327 100644 --- a/packages/alchemy/src/AWS/SNS/GetDataProtectionPolicy.ts +++ b/packages/alchemy/src/AWS/SNS/GetDataProtectionPolicy.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Topic } from "./Topic.ts"; +import type { Providers } from "../Providers.ts"; export interface GetDataProtectionPolicyRequest extends Omit< sns.GetDataProtectionPolicyInput, @@ -46,7 +47,8 @@ export const GetDataProtectionPolicyLive = Layer.effect( export class GetDataProtectionPolicyPolicy extends Binding.Policy< GetDataProtectionPolicyPolicy, - (topic: Topic) => Effect.Effect + (topic: Topic) => Effect.Effect, + Providers >()("AWS.SNS.GetDataProtectionPolicy") {} export const GetDataProtectionPolicyPolicyLive = diff --git a/packages/alchemy/src/AWS/SNS/GetSubscriptionAttributes.ts b/packages/alchemy/src/AWS/SNS/GetSubscriptionAttributes.ts index 25f8925e9..922b9cc6e 100644 --- a/packages/alchemy/src/AWS/SNS/GetSubscriptionAttributes.ts +++ b/packages/alchemy/src/AWS/SNS/GetSubscriptionAttributes.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Subscription } from "./Subscription.ts"; +import type { Providers } from "../Providers.ts"; export interface GetSubscriptionAttributesRequest extends Omit< sns.GetSubscriptionAttributesInput, @@ -46,7 +47,8 @@ export const GetSubscriptionAttributesLive = Layer.effect( export class GetSubscriptionAttributesPolicy extends Binding.Policy< GetSubscriptionAttributesPolicy, - (subscription: Subscription) => Effect.Effect + (subscription: Subscription) => Effect.Effect, + Providers >()("AWS.SNS.GetSubscriptionAttributes") {} export const GetSubscriptionAttributesPolicyLive = diff --git a/packages/alchemy/src/AWS/SNS/GetTopicAttributes.ts b/packages/alchemy/src/AWS/SNS/GetTopicAttributes.ts index 48c877e80..e3a230662 100644 --- a/packages/alchemy/src/AWS/SNS/GetTopicAttributes.ts +++ b/packages/alchemy/src/AWS/SNS/GetTopicAttributes.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Topic } from "./Topic.ts"; +import type { Providers } from "../Providers.ts"; export interface GetTopicAttributesRequest extends Omit< sns.GetTopicAttributesInput, @@ -46,7 +47,8 @@ export const GetTopicAttributesLive = Layer.effect( export class GetTopicAttributesPolicy extends Binding.Policy< GetTopicAttributesPolicy, - (topic: Topic) => Effect.Effect + (topic: Topic) => Effect.Effect, + Providers >()("AWS.SNS.GetTopicAttributes") {} export const GetTopicAttributesPolicyLive = diff --git a/packages/alchemy/src/AWS/SNS/ListSubscriptions.ts b/packages/alchemy/src/AWS/SNS/ListSubscriptions.ts index b7e80fd90..5c497aab6 100644 --- a/packages/alchemy/src/AWS/SNS/ListSubscriptions.ts +++ b/packages/alchemy/src/AWS/SNS/ListSubscriptions.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; +import type { Providers } from "../Providers.ts"; export interface ListSubscriptionsRequest extends sns.ListSubscriptionsInput {} @@ -36,7 +37,8 @@ export const ListSubscriptionsLive = Layer.effect( export class ListSubscriptionsPolicy extends Binding.Policy< ListSubscriptionsPolicy, - () => Effect.Effect + () => Effect.Effect, + Providers >()("AWS.SNS.ListSubscriptions") {} export const ListSubscriptionsPolicyLive = diff --git a/packages/alchemy/src/AWS/SNS/ListSubscriptionsByTopic.ts b/packages/alchemy/src/AWS/SNS/ListSubscriptionsByTopic.ts index 524f4c191..d72d61cbb 100644 --- a/packages/alchemy/src/AWS/SNS/ListSubscriptionsByTopic.ts +++ b/packages/alchemy/src/AWS/SNS/ListSubscriptionsByTopic.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Topic } from "./Topic.ts"; +import type { Providers } from "../Providers.ts"; export interface ListSubscriptionsByTopicRequest extends Omit< sns.ListSubscriptionsByTopicInput, @@ -46,7 +47,8 @@ export const ListSubscriptionsByTopicLive = Layer.effect( export class ListSubscriptionsByTopicPolicy extends Binding.Policy< ListSubscriptionsByTopicPolicy, - (topic: Topic) => Effect.Effect + (topic: Topic) => Effect.Effect, + Providers >()("AWS.SNS.ListSubscriptionsByTopic") {} export const ListSubscriptionsByTopicPolicyLive = diff --git a/packages/alchemy/src/AWS/SNS/ListTagsForResource.ts b/packages/alchemy/src/AWS/SNS/ListTagsForResource.ts index 03134bb8b..38bc5d507 100644 --- a/packages/alchemy/src/AWS/SNS/ListTagsForResource.ts +++ b/packages/alchemy/src/AWS/SNS/ListTagsForResource.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Topic } from "./Topic.ts"; +import type { Providers } from "../Providers.ts"; export interface ListTagsForResourceRequest extends Omit< sns.ListTagsForResourceRequest, @@ -46,7 +47,8 @@ export const ListTagsForResourceLive = Layer.effect( export class ListTagsForResourcePolicy extends Binding.Policy< ListTagsForResourcePolicy, - (topic: Topic) => Effect.Effect + (topic: Topic) => Effect.Effect, + Providers >()("AWS.SNS.ListTagsForResource") {} export const ListTagsForResourcePolicyLive = diff --git a/packages/alchemy/src/AWS/SNS/ListTopics.ts b/packages/alchemy/src/AWS/SNS/ListTopics.ts index 3a177dbde..391a4ccba 100644 --- a/packages/alchemy/src/AWS/SNS/ListTopics.ts +++ b/packages/alchemy/src/AWS/SNS/ListTopics.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; +import type { Providers } from "../Providers.ts"; export interface ListTopicsRequest extends sns.ListTopicsInput {} @@ -33,7 +34,8 @@ export const ListTopicsLive = Layer.effect( export class ListTopicsPolicy extends Binding.Policy< ListTopicsPolicy, - () => Effect.Effect + () => Effect.Effect, + Providers >()("AWS.SNS.ListTopics") {} export const ListTopicsPolicyLive = ListTopicsPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/SNS/Publish.ts b/packages/alchemy/src/AWS/SNS/Publish.ts index b1d307d57..a2d9d43a0 100644 --- a/packages/alchemy/src/AWS/SNS/Publish.ts +++ b/packages/alchemy/src/AWS/SNS/Publish.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Topic } from "./Topic.ts"; +import type { Providers } from "../Providers.ts"; export interface PublishRequest extends Omit< sns.PublishInput, @@ -43,7 +44,8 @@ export const PublishLive = Layer.effect( export class PublishPolicy extends Binding.Policy< PublishPolicy, - (topic: Topic) => Effect.Effect + (topic: Topic) => Effect.Effect, + Providers >()("AWS.SNS.Publish") {} export const PublishPolicyLive = PublishPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/SNS/PublishBatch.ts b/packages/alchemy/src/AWS/SNS/PublishBatch.ts index 810b4d1cf..680734fa7 100644 --- a/packages/alchemy/src/AWS/SNS/PublishBatch.ts +++ b/packages/alchemy/src/AWS/SNS/PublishBatch.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Topic } from "./Topic.ts"; +import type { Providers } from "../Providers.ts"; export interface PublishBatchRequest extends Omit< sns.PublishBatchInput, @@ -43,7 +44,8 @@ export const PublishBatchLive = Layer.effect( export class PublishBatchPolicy extends Binding.Policy< PublishBatchPolicy, - (topic: Topic) => Effect.Effect + (topic: Topic) => Effect.Effect, + Providers >()("AWS.SNS.PublishBatch") {} export const PublishBatchPolicyLive = PublishBatchPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/SNS/PutDataProtectionPolicy.ts b/packages/alchemy/src/AWS/SNS/PutDataProtectionPolicy.ts index 2d4575f01..ff41ab479 100644 --- a/packages/alchemy/src/AWS/SNS/PutDataProtectionPolicy.ts +++ b/packages/alchemy/src/AWS/SNS/PutDataProtectionPolicy.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Topic } from "./Topic.ts"; +import type { Providers } from "../Providers.ts"; export interface PutDataProtectionPolicyRequest extends Omit< sns.PutDataProtectionPolicyInput, @@ -46,7 +47,8 @@ export const PutDataProtectionPolicyLive = Layer.effect( export class PutDataProtectionPolicyPolicy extends Binding.Policy< PutDataProtectionPolicyPolicy, - (topic: Topic) => Effect.Effect + (topic: Topic) => Effect.Effect, + Providers >()("AWS.SNS.PutDataProtectionPolicy") {} export const PutDataProtectionPolicyPolicyLive = diff --git a/packages/alchemy/src/AWS/SNS/RemovePermission.ts b/packages/alchemy/src/AWS/SNS/RemovePermission.ts index 03f72c95b..b3717905e 100644 --- a/packages/alchemy/src/AWS/SNS/RemovePermission.ts +++ b/packages/alchemy/src/AWS/SNS/RemovePermission.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Topic } from "./Topic.ts"; +import type { Providers } from "../Providers.ts"; export interface RemovePermissionRequest extends Omit< sns.RemovePermissionInput, @@ -43,7 +44,8 @@ export const RemovePermissionLive = Layer.effect( export class RemovePermissionPolicy extends Binding.Policy< RemovePermissionPolicy, - (topic: Topic) => Effect.Effect + (topic: Topic) => Effect.Effect, + Providers >()("AWS.SNS.RemovePermission") {} export const RemovePermissionPolicyLive = RemovePermissionPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/SNS/SetSubscriptionAttributes.ts b/packages/alchemy/src/AWS/SNS/SetSubscriptionAttributes.ts index bc667d22d..b1f7dc1b8 100644 --- a/packages/alchemy/src/AWS/SNS/SetSubscriptionAttributes.ts +++ b/packages/alchemy/src/AWS/SNS/SetSubscriptionAttributes.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Subscription } from "./Subscription.ts"; +import type { Providers } from "../Providers.ts"; export interface SetSubscriptionAttributesRequest extends Omit< sns.SetSubscriptionAttributesInput, @@ -46,7 +47,8 @@ export const SetSubscriptionAttributesLive = Layer.effect( export class SetSubscriptionAttributesPolicy extends Binding.Policy< SetSubscriptionAttributesPolicy, - (subscription: Subscription) => Effect.Effect + (subscription: Subscription) => Effect.Effect, + Providers >()("AWS.SNS.SetSubscriptionAttributes") {} export const SetSubscriptionAttributesPolicyLive = diff --git a/packages/alchemy/src/AWS/SNS/SetTopicAttributes.ts b/packages/alchemy/src/AWS/SNS/SetTopicAttributes.ts index 5c371af6d..b5364eeaf 100644 --- a/packages/alchemy/src/AWS/SNS/SetTopicAttributes.ts +++ b/packages/alchemy/src/AWS/SNS/SetTopicAttributes.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Topic } from "./Topic.ts"; +import type { Providers } from "../Providers.ts"; export interface SetTopicAttributesRequest extends Omit< sns.SetTopicAttributesInput, @@ -46,7 +47,8 @@ export const SetTopicAttributesLive = Layer.effect( export class SetTopicAttributesPolicy extends Binding.Policy< SetTopicAttributesPolicy, - (topic: Topic) => Effect.Effect + (topic: Topic) => Effect.Effect, + Providers >()("AWS.SNS.SetTopicAttributes") {} export const SetTopicAttributesPolicyLive = diff --git a/packages/alchemy/src/AWS/SNS/TagResource.ts b/packages/alchemy/src/AWS/SNS/TagResource.ts index d08a230c6..0f4ef1827 100644 --- a/packages/alchemy/src/AWS/SNS/TagResource.ts +++ b/packages/alchemy/src/AWS/SNS/TagResource.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Topic } from "./Topic.ts"; +import type { Providers } from "../Providers.ts"; export interface TagResourceRequest extends Omit< sns.TagResourceRequest, @@ -43,7 +44,8 @@ export const TagResourceLive = Layer.effect( export class TagResourcePolicy extends Binding.Policy< TagResourcePolicy, - (topic: Topic) => Effect.Effect + (topic: Topic) => Effect.Effect, + Providers >()("AWS.SNS.TagResource") {} export const TagResourcePolicyLive = TagResourcePolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/SNS/TopicSink.ts b/packages/alchemy/src/AWS/SNS/TopicSink.ts index e5a028510..f3f0efb9d 100644 --- a/packages/alchemy/src/AWS/SNS/TopicSink.ts +++ b/packages/alchemy/src/AWS/SNS/TopicSink.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import { PublishBatch } from "./PublishBatch.ts"; import type { Topic } from "./Topic.ts"; +import type { Providers } from "../Providers.ts"; /** @binding */ export class TopicSink extends Binding.Service< @@ -38,7 +39,8 @@ export const TopicSinkLive = Layer.effect( export class TopicSinkPolicy extends Binding.Policy< TopicSinkPolicy, - (topic: Topic) => Effect.Effect + (topic: Topic) => Effect.Effect, + Providers >()("AWS.SNS.TopicSink") {} export const TopicSinkPolicyLive = TopicSinkPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/SNS/UntagResource.ts b/packages/alchemy/src/AWS/SNS/UntagResource.ts index 28975e08a..6e8cc2aef 100644 --- a/packages/alchemy/src/AWS/SNS/UntagResource.ts +++ b/packages/alchemy/src/AWS/SNS/UntagResource.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Topic } from "./Topic.ts"; +import type { Providers } from "../Providers.ts"; export interface UntagResourceRequest extends Omit< sns.UntagResourceRequest, @@ -43,7 +44,8 @@ export const UntagResourceLive = Layer.effect( export class UntagResourcePolicy extends Binding.Policy< UntagResourcePolicy, - (topic: Topic) => Effect.Effect + (topic: Topic) => Effect.Effect, + Providers >()("AWS.SNS.UntagResource") {} export const UntagResourcePolicyLive = UntagResourcePolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/SQS/DeleteMessage.ts b/packages/alchemy/src/AWS/SQS/DeleteMessage.ts index 54ebfdc2f..9d5e79538 100644 --- a/packages/alchemy/src/AWS/SQS/DeleteMessage.ts +++ b/packages/alchemy/src/AWS/SQS/DeleteMessage.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import * as Output from "../../Output.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Queue } from "./Queue.ts"; +import type { Providers } from "../Providers.ts"; export interface DeleteMessageRequest extends Omit< sqs.DeleteMessageRequest, @@ -44,7 +45,8 @@ export const DeleteMessageLive = Layer.effect( export class DeleteMessagePolicy extends Binding.Policy< DeleteMessagePolicy, - (queue: Queue) => Effect.Effect + (queue: Queue) => Effect.Effect, + Providers >()("AWS.SQS.DeleteMessage") {} export const DeleteMessagePolicyLive = DeleteMessagePolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/SQS/DeleteMessageBatch.ts b/packages/alchemy/src/AWS/SQS/DeleteMessageBatch.ts index aaf9a6bd0..cb041265b 100644 --- a/packages/alchemy/src/AWS/SQS/DeleteMessageBatch.ts +++ b/packages/alchemy/src/AWS/SQS/DeleteMessageBatch.ts @@ -6,6 +6,7 @@ import * as Output from "../../Output.ts"; import { isInstance } from "../EC2/Instance.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Queue } from "./Queue.ts"; +import type { Providers } from "../Providers.ts"; export interface DeleteMessageBatchRequest extends Omit< sqs.DeleteMessageBatchRequest, @@ -48,7 +49,8 @@ export const DeleteMessageBatchLive = Layer.effect( export class DeleteMessageBatchPolicy extends Binding.Policy< DeleteMessageBatchPolicy, - (queue: Queue) => Effect.Effect + (queue: Queue) => Effect.Effect, + Providers >()("AWS.SQS.DeleteMessageBatch") {} export const DeleteMessageBatchPolicyLive = diff --git a/packages/alchemy/src/AWS/SQS/QueueSink.ts b/packages/alchemy/src/AWS/SQS/QueueSink.ts index d02926953..40e79d536 100644 --- a/packages/alchemy/src/AWS/SQS/QueueSink.ts +++ b/packages/alchemy/src/AWS/SQS/QueueSink.ts @@ -6,6 +6,7 @@ import * as Output from "../../Output.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Queue } from "./Queue.ts"; import { SendMessageBatch } from "./SendMessageBatch.ts"; +import type { Providers } from "../Providers.ts"; /** @binding */ export class QueueSink extends Binding.Service< @@ -38,7 +39,8 @@ export const QueueSinkLive = Layer.effect( export class QueueSinkPolicy extends Binding.Policy< QueueSinkPolicy, - (queue: Queue) => Effect.Effect + (queue: Queue) => Effect.Effect, + Providers >()("AWS.SQS.QueueSinkPolicy") {} export const QueueSinkPolicyLive = QueueSinkPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/SQS/ReceiveMessage.ts b/packages/alchemy/src/AWS/SQS/ReceiveMessage.ts index 6d7ee2912..c1732ebd5 100644 --- a/packages/alchemy/src/AWS/SQS/ReceiveMessage.ts +++ b/packages/alchemy/src/AWS/SQS/ReceiveMessage.ts @@ -6,6 +6,7 @@ import * as Output from "../../Output.ts"; import { isInstance } from "../EC2/Instance.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Queue } from "./Queue.ts"; +import type { Providers } from "../Providers.ts"; export interface ReceiveMessageRequest extends Omit< sqs.ReceiveMessageRequest, @@ -45,7 +46,8 @@ export const ReceiveMessageLive = Layer.effect( export class ReceiveMessagePolicy extends Binding.Policy< ReceiveMessagePolicy, - (queue: Queue) => Effect.Effect + (queue: Queue) => Effect.Effect, + Providers >()("AWS.SQS.ReceiveMessage") {} export const ReceiveMessagePolicyLive = ReceiveMessagePolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/SQS/SendMessage.ts b/packages/alchemy/src/AWS/SQS/SendMessage.ts index 02f7d99ac..f6d72ea6a 100644 --- a/packages/alchemy/src/AWS/SQS/SendMessage.ts +++ b/packages/alchemy/src/AWS/SQS/SendMessage.ts @@ -6,6 +6,7 @@ import * as Output from "../../Output.ts"; import { isInstance } from "../EC2/Instance.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Queue } from "./Queue.ts"; +import type { Providers } from "../Providers.ts"; export interface SendMessageRequest extends Omit< sqs.SendMessageRequest, @@ -46,7 +47,8 @@ export const SendMessageLive = Layer.effect( export class SendMessagePolicy extends Binding.Policy< SendMessagePolicy, - (queue: Queue) => Effect.Effect + (queue: Queue) => Effect.Effect, + Providers >()("AWS.SQS.SendMessage") {} export const SendMessagePolicyLive = SendMessagePolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/SQS/SendMessageBatch.ts b/packages/alchemy/src/AWS/SQS/SendMessageBatch.ts index fcf893e6d..6431da2cd 100644 --- a/packages/alchemy/src/AWS/SQS/SendMessageBatch.ts +++ b/packages/alchemy/src/AWS/SQS/SendMessageBatch.ts @@ -5,6 +5,7 @@ import * as Binding from "../../Binding.ts"; import * as Output from "../../Output.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Queue } from "./Queue.ts"; +import type { Providers } from "../Providers.ts"; export interface SendMessageBatchRequest extends Omit< sqs.SendMessageBatchRequest, @@ -44,7 +45,8 @@ export const SendMessageBatchLive = Layer.effect( export class SendMessageBatchPolicy extends Binding.Policy< SendMessageBatchPolicy, - (queue: Queue) => Effect.Effect + (queue: Queue) => Effect.Effect, + Providers >()("AWS.SQS.SendMessageBatch") {} export const SendMessageBatchPolicyLive = SendMessageBatchPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/SecretsManager/DescribeSecret.ts b/packages/alchemy/src/AWS/SecretsManager/DescribeSecret.ts index 7dc61c9ed..3cb78c47b 100644 --- a/packages/alchemy/src/AWS/SecretsManager/DescribeSecret.ts +++ b/packages/alchemy/src/AWS/SecretsManager/DescribeSecret.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Secret } from "./Secret.ts"; +import type { Providers } from "../Providers.ts"; /** * Runtime binding for `secretsmanager:DescribeSecret`. @@ -41,7 +42,8 @@ export const DescribeSecretLive = Layer.effect( export class DescribeSecretPolicy extends Binding.Policy< DescribeSecretPolicy, - (secret: Secret) => Effect.Effect + (secret: Secret) => Effect.Effect, + Providers >()("AWS.SecretsManager.DescribeSecret") {} export const DescribeSecretPolicyLive = DescribeSecretPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/SecretsManager/GetRandomPassword.ts b/packages/alchemy/src/AWS/SecretsManager/GetRandomPassword.ts index 8155794b4..dbe954279 100644 --- a/packages/alchemy/src/AWS/SecretsManager/GetRandomPassword.ts +++ b/packages/alchemy/src/AWS/SecretsManager/GetRandomPassword.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; +import type { Providers } from "../Providers.ts"; /** * Runtime binding for `secretsmanager:GetRandomPassword`. @@ -39,7 +40,8 @@ export const GetRandomPasswordLive = Layer.effect( export class GetRandomPasswordPolicy extends Binding.Policy< GetRandomPasswordPolicy, - () => Effect.Effect + () => Effect.Effect, + Providers >()("AWS.SecretsManager.GetRandomPassword") {} export const GetRandomPasswordPolicyLive = diff --git a/packages/alchemy/src/AWS/SecretsManager/GetSecretValue.ts b/packages/alchemy/src/AWS/SecretsManager/GetSecretValue.ts index 914804ae5..b631240b0 100644 --- a/packages/alchemy/src/AWS/SecretsManager/GetSecretValue.ts +++ b/packages/alchemy/src/AWS/SecretsManager/GetSecretValue.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Secret } from "./Secret.ts"; +import type { Providers } from "../Providers.ts"; export interface GetSecretValueRequest extends Omit< secretsmanager.GetSecretValueRequest, @@ -50,7 +51,8 @@ export const GetSecretValueLive = Layer.effect( export class GetSecretValuePolicy extends Binding.Policy< GetSecretValuePolicy, - (secret: Secret) => Effect.Effect + (secret: Secret) => Effect.Effect, + Providers >()("AWS.SecretsManager.GetSecretValue") {} export const GetSecretValuePolicyLive = GetSecretValuePolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/SecretsManager/ListSecrets.ts b/packages/alchemy/src/AWS/SecretsManager/ListSecrets.ts index f853afb24..db85b0312 100644 --- a/packages/alchemy/src/AWS/SecretsManager/ListSecrets.ts +++ b/packages/alchemy/src/AWS/SecretsManager/ListSecrets.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; +import type { Providers } from "../Providers.ts"; /** * Runtime binding for `secretsmanager:ListSecrets`. @@ -39,7 +40,8 @@ export const ListSecretsLive = Layer.effect( export class ListSecretsPolicy extends Binding.Policy< ListSecretsPolicy, - () => Effect.Effect + () => Effect.Effect, + Providers >()("AWS.SecretsManager.ListSecrets") {} export const ListSecretsPolicyLive = ListSecretsPolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/SecretsManager/PutSecretValue.ts b/packages/alchemy/src/AWS/SecretsManager/PutSecretValue.ts index b0acab9ad..aacae2702 100644 --- a/packages/alchemy/src/AWS/SecretsManager/PutSecretValue.ts +++ b/packages/alchemy/src/AWS/SecretsManager/PutSecretValue.ts @@ -4,6 +4,7 @@ import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import { isFunction } from "../Lambda/Function.ts"; import type { Secret } from "./Secret.ts"; +import type { Providers } from "../Providers.ts"; export interface PutSecretValueRequest extends Omit< secretsmanager.PutSecretValueRequest, @@ -50,7 +51,8 @@ export const PutSecretValueLive = Layer.effect( export class PutSecretValuePolicy extends Binding.Policy< PutSecretValuePolicy, - (secret: Secret) => Effect.Effect + (secret: Secret) => Effect.Effect, + Providers >()("AWS.SecretsManager.PutSecretValue") {} export const PutSecretValuePolicyLive = PutSecretValuePolicy.layer.succeed( diff --git a/packages/alchemy/src/AWS/Website/Router.ts b/packages/alchemy/src/AWS/Website/Router.ts index 527e94743..44caa174a 100644 --- a/packages/alchemy/src/AWS/Website/Router.ts +++ b/packages/alchemy/src/AWS/Website/Router.ts @@ -1,6 +1,5 @@ import * as Effect from "effect/Effect"; import { createHash } from "node:crypto"; -import * as Construct from "../../Construct.ts"; import { toPath } from "../../FQN.ts"; import type { Input } from "../../Input.ts"; import * as Namespace from "../../Namespace.ts"; @@ -56,232 +55,237 @@ import type { RouterProps } from "./shared.ts"; * }); * ``` */ -export const Router = Construct.fn(function* (id: string, props: RouterProps) { - const domain = props.domain; +export const Router = (id: string, props: RouterProps) => + Effect.gen(function* () { + const domain = props.domain; - if (domain && domain.dns === false && !domain.cert) { - return yield* Effect.die( - "Router domain configuration with `dns: false` requires `cert`.", - ); - } + if (domain && domain.dns === false && !domain.cert) { + return yield* Effect.die( + "Router domain configuration with `dns: false` requires `cert`.", + ); + } - const certificate = - !domain || domain.cert - ? domain?.cert - ? { certificateArn: domain.cert } - : undefined - : yield* Certificate("Certificate", { - domainName: domain.name, - subjectAlternativeNames: [ - ...(domain.aliases ?? []), - ...(domain.redirects ?? []), - ], - hostedZoneId: domain.hostedZoneId, - tags: props.tags, - }); + const certificate = + !domain || domain.cert + ? domain?.cert + ? { certificateArn: domain.cert } + : undefined + : yield* Certificate("Certificate", { + domainName: domain.name, + subjectAlternativeNames: [ + ...(domain.aliases ?? []), + ...(domain.redirects ?? []), + ], + hostedZoneId: domain.hostedZoneId, + tags: props.tags, + }); - const stack = yield* Stack; - const stage = yield* Stage; - const ns = yield* Namespace.CurrentNamespace; - const fqn = ns ? toPath(ns).join("/") : id; - const kvNamespace = createHash("md5") - .update(`${stack.name}-${stage}-${fqn}`) - .digest("hex") - .substring(0, 4); + const stack = yield* Stack; + const stage = yield* Stage; + const ns = yield* Namespace.CurrentNamespace; + const fqn = ns ? toPath(ns).join("/") : id; + const kvNamespace = createHash("md5") + .update(`${stack.name}-${stage}-${fqn}`) + .digest("hex") + .substring(0, 4); - const kvStore = yield* KeyValueStore("KvStore", {}); + const kvStore = yield* KeyValueStore("KvStore", {}); - const viewerRequest = yield* CloudFrontFunction("ViewerRequest", { - comment: `${id} viewer request`, - code: buildRouterRequestFunctionCode({ - kvNamespace, - userInjection: props.edge?.viewerRequest?.injection, - blockCloudfrontUrl: !!domain, - }), - keyValueStoreArns: [kvStore.keyValueStoreArn], - }); + const viewerRequest = yield* CloudFrontFunction("ViewerRequest", { + comment: `${id} viewer request`, + code: buildRouterRequestFunctionCode({ + kvNamespace, + userInjection: props.edge?.viewerRequest?.injection, + blockCloudfrontUrl: !!domain, + }), + keyValueStoreArns: [kvStore.keyValueStoreArn], + }); - const viewerResponse = props.edge?.viewerResponse - ? yield* CloudFrontFunction("ViewerResponse", { - comment: `${id} viewer response`, - code: buildRouterResponseFunctionCode( - props.edge.viewerResponse.injection, - ), - keyValueStoreArns: props.edge.viewerResponse.keyValueStoreArn - ? [props.edge.viewerResponse.keyValueStoreArn as any] - : undefined, - }) - : undefined; + const viewerResponse = props.edge?.viewerResponse + ? yield* CloudFrontFunction("ViewerResponse", { + comment: `${id} viewer response`, + code: buildRouterResponseFunctionCode( + props.edge.viewerResponse.injection, + ), + keyValueStoreArns: props.edge.viewerResponse.keyValueStoreArn + ? [props.edge.viewerResponse.keyValueStoreArn as any] + : undefined, + }) + : undefined; - const functionAssociations: DistributionBehavior["functionAssociations"] = [ - { - eventType: "viewer-request" as const, - functionArn: viewerRequest.functionArn as any, - }, - ...(viewerResponse - ? [ - { - eventType: "viewer-response" as const, - functionArn: viewerResponse.functionArn as any, - }, - ] - : []), - ]; + const functionAssociations: DistributionBehavior["functionAssociations"] = [ + { + eventType: "viewer-request" as const, + functionArn: viewerRequest.functionArn as any, + }, + ...(viewerResponse + ? [ + { + eventType: "viewer-response" as const, + functionArn: viewerResponse.functionArn as any, + }, + ] + : []), + ]; - const inlineRouteEntries: Record> = {}; + const inlineRouteEntries: Record> = {}; - if (props.routes) { - let routeIndex = 0; - for (const [pattern, route] of Object.entries(props.routes)) { - routeIndex++; - const routeNs = createHash("md5") - .update(`${stack.name}-${stage}-${fqn}:route:${routeIndex}`) - .digest("hex") - .substring(0, 4); + if (props.routes) { + let routeIndex = 0; + for (const [pattern, route] of Object.entries(props.routes)) { + routeIndex++; + const routeNs = createHash("md5") + .update(`${stack.name}-${stage}-${fqn}:route:${routeIndex}`) + .digest("hex") + .substring(0, 4); - if (typeof route === "string" || "url" in (route as any)) { - const url = typeof route === "string" ? route : (route as any).url; - const host = typeof url === "string" ? new URL(url).host : url; - inlineRouteEntries[`${routeNs}:metadata`] = stringifyResolvedString( - host, - (resolvedHost) => - JSON.stringify({ - host: resolvedHost, - origin: (route as any).origin, - rewrite: (route as any).rewrite, - }), - ); - yield* KvRoutesUpdate(`Route${routeIndex}`, { - store: kvStore.keyValueStoreArn as any, - namespace: kvNamespace, - key: "routes", - entry: `url,${routeNs},,${normalizePattern(pattern)}`, - }); - } else { - const bucketRoute = route as any; - const bucketDomain = - typeof bucketRoute.bucket === "string" - ? bucketRoute.bucket - : bucketRoute.bucket.bucketRegionalDomainName; - inlineRouteEntries[`${routeNs}:metadata`] = stringifyResolvedString( - bucketDomain, - (resolvedDomain) => - JSON.stringify({ - domain: resolvedDomain, - origin: bucketRoute.origin, - rewrite: bucketRoute.rewrite, - }), - ); - yield* KvRoutesUpdate(`Route${routeIndex}`, { - store: kvStore.keyValueStoreArn as any, - namespace: kvNamespace, - key: "routes", - entry: `bucket,${routeNs},,${normalizePattern(pattern)}`, - }); + if (typeof route === "string" || "url" in (route as any)) { + const url = typeof route === "string" ? route : (route as any).url; + const host = typeof url === "string" ? new URL(url).host : url; + inlineRouteEntries[`${routeNs}:metadata`] = stringifyResolvedString( + host, + (resolvedHost) => + JSON.stringify({ + host: resolvedHost, + origin: (route as any).origin, + rewrite: (route as any).rewrite, + }), + ); + yield* KvRoutesUpdate(`Route${routeIndex}`, { + store: kvStore.keyValueStoreArn as any, + namespace: kvNamespace, + key: "routes", + entry: `url,${routeNs},,${normalizePattern(pattern)}`, + }); + } else { + const bucketRoute = route as any; + const bucketDomain = + typeof bucketRoute.bucket === "string" + ? bucketRoute.bucket + : bucketRoute.bucket.bucketRegionalDomainName; + inlineRouteEntries[`${routeNs}:metadata`] = stringifyResolvedString( + bucketDomain, + (resolvedDomain) => + JSON.stringify({ + domain: resolvedDomain, + origin: bucketRoute.origin, + rewrite: bucketRoute.rewrite, + }), + ); + yield* KvRoutesUpdate(`Route${routeIndex}`, { + store: kvStore.keyValueStoreArn as any, + namespace: kvNamespace, + key: "routes", + entry: `bucket,${routeNs},,${normalizePattern(pattern)}`, + }); + } } } - } - if (Object.keys(inlineRouteEntries).length > 0) { - yield* KvEntries("InlineRouteEntries", { - store: kvStore.keyValueStoreArn as any, - namespace: kvNamespace, - entries: inlineRouteEntries, - }); - } + if (Object.keys(inlineRouteEntries).length > 0) { + yield* KvEntries("InlineRouteEntries", { + store: kvStore.keyValueStoreArn as any, + namespace: kvNamespace, + entries: inlineRouteEntries, + }); + } - const distribution = yield* Distribution("Distribution", { - aliases: domain - ? [domain.name, ...(domain.aliases ?? []), ...(domain.redirects ?? [])] - : undefined, - origins: [ - { - id: "default", - domainName: "placeholder.sst.dev", - customOriginConfig: { - httpPort: 80, - httpsPort: 443, - originProtocolPolicy: "https-only", - originReadTimeout: 20, - originSslProtocols: ["TLSv1.2"], + const distribution = yield* Distribution("Distribution", { + aliases: domain + ? [domain.name, ...(domain.aliases ?? []), ...(domain.redirects ?? [])] + : undefined, + origins: [ + { + id: "default", + domainName: "placeholder.sst.dev", + customOriginConfig: { + httpPort: 80, + httpsPort: 443, + originProtocolPolicy: "https-only", + originReadTimeout: 20, + originSslProtocols: ["TLSv1.2"], + }, }, - }, - ], - defaultCacheBehavior: { - targetOriginId: "default", - viewerProtocolPolicy: "redirect-to-https", - allowedMethods: [ - "DELETE", - "GET", - "HEAD", - "OPTIONS", - "PATCH", - "POST", - "PUT", ], - cachedMethods: ["GET", "HEAD"], - compress: true, - cachePolicyId: MANAGED_CACHING_OPTIMIZED_POLICY_ID, - functionAssociations, - }, - viewerCertificate: certificate - ? { - acmCertificateArn: (certificate as any).certificateArn, - sslSupportMethod: "sni-only", - minimumProtocolVersion: "TLSv1.2_2021", - } - : undefined, - tags: props.tags, - }); + defaultCacheBehavior: { + targetOriginId: "default", + viewerProtocolPolicy: "redirect-to-https", + allowedMethods: [ + "DELETE", + "GET", + "HEAD", + "OPTIONS", + "PATCH", + "POST", + "PUT", + ], + cachedMethods: ["GET", "HEAD"], + compress: true, + cachePolicyId: MANAGED_CACHING_OPTIMIZED_POLICY_ID, + functionAssociations, + }, + viewerCertificate: certificate + ? { + acmCertificateArn: (certificate as any).certificateArn, + sslSupportMethod: "sni-only", + minimumProtocolVersion: "TLSv1.2_2021", + } + : undefined, + tags: props.tags, + }); - const records = - domain?.hostedZoneId && domain.dns !== false - ? yield* Effect.forEach( - [domain.name, ...(domain.aliases ?? []), ...(domain.redirects ?? [])], - (name, index) => - Route53Record(`AliasRecord${index + 1}`, { - hostedZoneId: domain.hostedZoneId!, - name, - type: "A", - aliasTarget: { - hostedZoneId: distribution.hostedZoneId, - dnsName: distribution.domainName, - }, - }), - { concurrency: "unbounded" }, - ) - : []; + const records = + domain?.hostedZoneId && domain.dns !== false + ? yield* Effect.forEach( + [ + domain.name, + ...(domain.aliases ?? []), + ...(domain.redirects ?? []), + ], + (name, index) => + Route53Record(`AliasRecord${index + 1}`, { + hostedZoneId: domain.hostedZoneId!, + name, + type: "A", + aliasTarget: { + hostedZoneId: distribution.hostedZoneId, + dnsName: distribution.domainName, + }, + }), + { concurrency: "unbounded" }, + ) + : []; - const invalidation = - props.invalidation === false || !props.invalidation - ? undefined - : yield* Invalidation("Invalidation", { - distributionId: distribution.distributionId, - version: createHash("sha256") - .update(JSON.stringify(inlineRouteEntries)) - .digest("hex"), - wait: props.invalidation.wait, - paths: - props.invalidation.paths === "all" || !props.invalidation.paths - ? ["/*"] - : Array.isArray(props.invalidation.paths) - ? props.invalidation.paths - : ["/*"], - }); + const invalidation = + props.invalidation === false || !props.invalidation + ? undefined + : yield* Invalidation("Invalidation", { + distributionId: distribution.distributionId, + version: createHash("sha256") + .update(JSON.stringify(inlineRouteEntries)) + .digest("hex"), + wait: props.invalidation.wait, + paths: + props.invalidation.paths === "all" || !props.invalidation.paths + ? ["/*"] + : Array.isArray(props.invalidation.paths) + ? props.invalidation.paths + : ["/*"], + }); - return { - certificate, - distribution, - records, - invalidation, - kvStoreArn: kvStore.keyValueStoreArn as Input, - kvNamespace, - distributionId: distribution.distributionId as Input, - url: domain - ? Output.interpolate`https://${domain.name}` - : Output.interpolate`https://${distribution.domainName}`, - }; -}); + return { + certificate, + distribution, + records, + invalidation, + kvStoreArn: kvStore.keyValueStoreArn as Input, + kvNamespace, + distributionId: distribution.distributionId as Input, + url: domain + ? Output.interpolate`https://${domain.name}` + : Output.interpolate`https://${distribution.domainName}`, + }; + }).pipe(Namespace.push(id)); const buildRouterRequestFunctionCode = ({ kvNamespace, diff --git a/packages/alchemy/src/AWS/Website/SsrSite.ts b/packages/alchemy/src/AWS/Website/SsrSite.ts index 1d4dd6b26..cb4c6aa0f 100644 --- a/packages/alchemy/src/AWS/Website/SsrSite.ts +++ b/packages/alchemy/src/AWS/Website/SsrSite.ts @@ -1,7 +1,7 @@ import type * as cloudfront from "@distilled.cloud/aws/cloudfront"; import * as Effect from "effect/Effect"; -import * as Construct from "../../Construct.ts"; import type { Input } from "../../Input.ts"; +import * as Namespace from "../../Namespace.ts"; import * as Output from "../../Output.ts"; import { Certificate } from "../ACM/Certificate.ts"; import { Distribution } from "../CloudFront/Distribution.ts"; @@ -164,230 +164,229 @@ const serverOriginOf = (server: SsrSiteServerOrigin): Input => * }); * ``` */ -export const SsrSite = Construct.fn(function* ( - id: string, - props: SsrSiteProps, -) { - const assetPattern = props.assets?.pathPattern ?? "/_assets/*"; - const serverUrl = serverUrlOf(props.server); - const serverOriginHost = serverOriginOf(props.server); +export const SsrSite = (id: string, props: SsrSiteProps) => + Effect.gen(function* () { + const assetPattern = props.assets?.pathPattern ?? "/_assets/*"; + const serverUrl = serverUrlOf(props.server); + const serverOriginHost = serverOriginOf(props.server); - const assetBucket = props.assets - ? yield* Bucket("AssetsBucket", { - bucketName: props.assets.bucketName, - tags: props.tags, - }) - : undefined; - - const assetFiles = - props.assets && assetBucket - ? yield* AssetDeployment("AssetsFiles", { - bucket: assetBucket, - sourcePath: props.assets.sourcePath, - prefix: props.assets.prefix, - purge: props.assets.purge ?? false, - fileOptions: props.assets.fileOptions, + const assetBucket = props.assets + ? yield* Bucket("AssetsBucket", { + bucketName: props.assets.bucketName, + tags: props.tags, }) : undefined; - const assetOac = - props.assets && assetBucket - ? yield* OriginAccessControl("AssetsOriginAccessControl", { - originType: "s3", - description: `${id} SSR asset origin access control`, - }) - : undefined; + const assetFiles = + props.assets && assetBucket + ? yield* AssetDeployment("AssetsFiles", { + bucket: assetBucket, + sourcePath: props.assets.sourcePath, + prefix: props.assets.prefix, + purge: props.assets.purge ?? false, + fileOptions: props.assets.fileOptions, + }) + : undefined; - const routeTargets: SsrSiteRouteTargets = { - server: { - url: serverUrl, - originProtocolPolicy: props.server.originProtocolPolicy ?? "https-only", - }, - assets: - assetBucket && assetOac - ? { - pattern: assetPattern, - route: { - bucket: assetBucket, - originAccessControlId: assetOac.originAccessControlId, - originPath: props.assets?.prefix, - version: assetFiles?.version, - }, - } - : undefined, - }; + const assetOac = + props.assets && assetBucket + ? yield* OriginAccessControl("AssetsOriginAccessControl", { + originType: "s3", + description: `${id} SSR asset origin access control`, + }) + : undefined; - if (props.cdn === false) { - return { - assetBucket, - assetFiles, - assetOriginAccessControl: assetOac, - certificate: undefined, - distribution: undefined, - records: [], - invalidation: undefined, - routeTargets, - url: undefined, + const routeTargets: SsrSiteRouteTargets = { + server: { + url: serverUrl, + originProtocolPolicy: props.server.originProtocolPolicy ?? "https-only", + }, + assets: + assetBucket && assetOac + ? { + pattern: assetPattern, + route: { + bucket: assetBucket, + originAccessControlId: assetOac.originAccessControlId, + originPath: props.assets?.prefix, + version: assetFiles?.version, + }, + } + : undefined, }; - } - if (props.domain && props.domain.dns === false && !props.domain.cert) { - return yield* Effect.fail( - new Error( - "SsrSite domain configuration with `dns: false` requires `cert`.", - ), - ); - } + if (props.cdn === false) { + return { + assetBucket, + assetFiles, + assetOriginAccessControl: assetOac, + certificate: undefined, + distribution: undefined, + records: [], + invalidation: undefined, + routeTargets, + url: undefined, + }; + } - const certificate = - !props.domain || props.domain.cert - ? props.domain?.cert - ? { certificateArn: props.domain.cert } - : undefined - : yield* Certificate("Certificate", { - domainName: props.domain.name, - subjectAlternativeNames: [ - ...(props.domain.aliases ?? []), - ...(props.domain.redirects ?? []), - ], - hostedZoneId: props.domain.hostedZoneId, - tags: props.tags, - }); + if (props.domain && props.domain.dns === false && !props.domain.cert) { + return yield* Effect.fail( + new Error( + "SsrSite domain configuration with `dns: false` requires `cert`.", + ), + ); + } + + const certificate = + !props.domain || props.domain.cert + ? props.domain?.cert + ? { certificateArn: props.domain.cert } + : undefined + : yield* Certificate("Certificate", { + domainName: props.domain.name, + subjectAlternativeNames: [ + ...(props.domain.aliases ?? []), + ...(props.domain.redirects ?? []), + ], + hostedZoneId: props.domain.hostedZoneId, + tags: props.tags, + }); - const distribution = yield* Distribution("Distribution", { - aliases: props.domain - ? [props.domain.name, ...(props.domain.aliases ?? [])] - : undefined, - origins: [ - { - id: "server", - domainName: serverOriginHost, - customOriginConfig: { - originProtocolPolicy: - props.server.originProtocolPolicy ?? "https-only", + const distribution = yield* Distribution("Distribution", { + aliases: props.domain + ? [props.domain.name, ...(props.domain.aliases ?? [])] + : undefined, + origins: [ + { + id: "server", + domainName: serverOriginHost, + customOriginConfig: { + originProtocolPolicy: + props.server.originProtocolPolicy ?? "https-only", + }, }, - }, - ...(assetBucket && assetOac - ? [ - { - id: "assets", - domainName: assetBucket.bucketRegionalDomainName, - originPath: props.assets?.prefix, - s3Origin: true, - originAccessControlId: assetOac.originAccessControlId, - }, - ] - : []), - ], - defaultCacheBehavior: { - targetOriginId: "server", - viewerProtocolPolicy: "redirect-to-https", - compress: true, - allowedMethods: [ - "DELETE", - "GET", - "HEAD", - "OPTIONS", - "PATCH", - "POST", - "PUT", + ...(assetBucket && assetOac + ? [ + { + id: "assets", + domainName: assetBucket.bucketRegionalDomainName, + originPath: props.assets?.prefix, + s3Origin: true, + originAccessControlId: assetOac.originAccessControlId, + }, + ] + : []), ], - cachedMethods: ["GET", "HEAD"], - cachePolicyId: props.cachePolicyId ?? MANAGED_CACHING_DISABLED_POLICY_ID, - originRequestPolicyId: MANAGED_ALL_VIEWER_EXCEPT_HOST_HEADER_POLICY_ID, - }, - orderedCacheBehaviors: - assetBucket && assetOac - ? [ - { - pathPattern: assetPattern, - targetOriginId: "assets", - viewerProtocolPolicy: "redirect-to-https", - compress: true, - allowedMethods: ["GET", "HEAD", "OPTIONS"], - cachedMethods: ["GET", "HEAD"], - cachePolicyId: MANAGED_CACHING_OPTIMIZED_POLICY_ID, - }, - ] + defaultCacheBehavior: { + targetOriginId: "server", + viewerProtocolPolicy: "redirect-to-https", + compress: true, + allowedMethods: [ + "DELETE", + "GET", + "HEAD", + "OPTIONS", + "PATCH", + "POST", + "PUT", + ], + cachedMethods: ["GET", "HEAD"], + cachePolicyId: + props.cachePolicyId ?? MANAGED_CACHING_DISABLED_POLICY_ID, + originRequestPolicyId: MANAGED_ALL_VIEWER_EXCEPT_HOST_HEADER_POLICY_ID, + }, + orderedCacheBehaviors: + assetBucket && assetOac + ? [ + { + pathPattern: assetPattern, + targetOriginId: "assets", + viewerProtocolPolicy: "redirect-to-https", + compress: true, + allowedMethods: ["GET", "HEAD", "OPTIONS"], + cachedMethods: ["GET", "HEAD"], + cachePolicyId: MANAGED_CACHING_OPTIMIZED_POLICY_ID, + }, + ] + : undefined, + viewerCertificate: certificate + ? { + acmCertificateArn: (certificate as any).certificateArn, + sslSupportMethod: "sni-only", + minimumProtocolVersion: "TLSv1.2_2021", + } : undefined, - viewerCertificate: certificate - ? { - acmCertificateArn: (certificate as any).certificateArn, - sslSupportMethod: "sni-only", - minimumProtocolVersion: "TLSv1.2_2021", - } - : undefined, - tags: props.tags, - }); + tags: props.tags, + }); - if (assetBucket && assetOac) { - const bucketPolicy: PolicyStatement = { - Effect: "Allow", - Principal: { - Service: "cloudfront.amazonaws.com", - }, - Action: ["s3:GetObject"], - Resource: [Output.interpolate`${assetBucket.bucketArn}/*` as any], - Condition: { - StringEquals: { - "AWS:SourceArn": distribution.distributionArn as any, + if (assetBucket && assetOac) { + const bucketPolicy: PolicyStatement = { + Effect: "Allow", + Principal: { + Service: "cloudfront.amazonaws.com", }, - }, - }; + Action: ["s3:GetObject"], + Resource: [Output.interpolate`${assetBucket.bucketArn}/*` as any], + Condition: { + StringEquals: { + "AWS:SourceArn": distribution.distributionArn as any, + }, + }, + }; - yield* assetBucket.bind`AWS.S3.Policy(${distribution}, ${assetBucket})`({ - policyStatements: [bucketPolicy], - }); - } + yield* assetBucket.bind`AWS.S3.Policy(${distribution}, ${assetBucket})`({ + policyStatements: [bucketPolicy], + }); + } - const records = - props.domain?.hostedZoneId && props.domain.dns !== false - ? yield* Effect.forEach( - [ - props.domain.name, - ...(props.domain.aliases ?? []), - ...(props.domain.redirects ?? []), - ], - (name, index) => - Route53Record(`AliasRecord${index + 1}`, { - hostedZoneId: props.domain!.hostedZoneId!, - name, - type: "A", - aliasTarget: { - hostedZoneId: distribution.hostedZoneId, - dnsName: distribution.domainName, - }, - }), - { concurrency: "unbounded" }, - ) - : []; + const records = + props.domain?.hostedZoneId && props.domain.dns !== false + ? yield* Effect.forEach( + [ + props.domain.name, + ...(props.domain.aliases ?? []), + ...(props.domain.redirects ?? []), + ], + (name, index) => + Route53Record(`AliasRecord${index + 1}`, { + hostedZoneId: props.domain!.hostedZoneId!, + name, + type: "A", + aliasTarget: { + hostedZoneId: distribution.hostedZoneId, + dnsName: distribution.domainName, + }, + }), + { concurrency: "unbounded" }, + ) + : []; - const invalidation = - props.invalidate === false || !assetFiles - ? undefined - : yield* Invalidation("Invalidation", { - distributionId: distribution.distributionId, - version: assetFiles.version, - wait: props.invalidate?.wait, - paths: - props.invalidate?.paths === "versioned" - ? [assetPattern] - : props.invalidate?.paths === "all" || !props.invalidate?.paths - ? ["/*"] - : props.invalidate.paths, - }); + const invalidation = + props.invalidate === false || !assetFiles + ? undefined + : yield* Invalidation("Invalidation", { + distributionId: distribution.distributionId, + version: assetFiles.version, + wait: props.invalidate?.wait, + paths: + props.invalidate?.paths === "versioned" + ? [assetPattern] + : props.invalidate?.paths === "all" || !props.invalidate?.paths + ? ["/*"] + : props.invalidate.paths, + }); - return { - assetBucket, - assetFiles, - assetOriginAccessControl: assetOac, - certificate, - distribution, - records, - invalidation, - routeTargets, - url: props.domain - ? Output.interpolate`https://${props.domain.name}` - : Output.interpolate`https://${distribution.domainName}`, - }; -}); + return { + assetBucket, + assetFiles, + assetOriginAccessControl: assetOac, + certificate, + distribution, + records, + invalidation, + routeTargets, + url: props.domain + ? Output.interpolate`https://${props.domain.name}` + : Output.interpolate`https://${distribution.domainName}`, + }; + }).pipe(Namespace.push(id)); diff --git a/packages/alchemy/src/AWS/Website/StaticSite.ts b/packages/alchemy/src/AWS/Website/StaticSite.ts index 7c2ede767..00955d09b 100644 --- a/packages/alchemy/src/AWS/Website/StaticSite.ts +++ b/packages/alchemy/src/AWS/Website/StaticSite.ts @@ -3,7 +3,6 @@ import { createHash } from "node:crypto"; import { readdirSync } from "node:fs"; import path from "node:path"; import { Command } from "../../Build/Command.ts"; -import * as Construct from "../../Construct.ts"; import { toPath } from "../../FQN.ts"; import type { Input } from "../../Input.ts"; import * as Namespace from "../../Namespace.ts"; @@ -153,304 +152,308 @@ export interface StaticSiteProps { * }); * ``` */ -export const StaticSite = Construct.fn(function* ( - id: string, - props: StaticSiteProps, -) { - const domain = normalizeDomain(props.domain); - const sitePath = (props.path ?? ".") as string; - const indexPage = props.indexPage ?? "index.html"; - const assetPrefix = normalizePrefix(props.assets?.path); - const assetRoutes = [...(props.assets?.routes ?? [])] - .map((value) => value.trim()) - .filter(Boolean) - .map(normalizeRoutePath); - const invalidationProps = - props.invalidation !== undefined - ? props.invalidation - : { paths: "all" as const, wait: false }; - - if (props.router && props.domain) { - return yield* Effect.die( - `Cannot provide both "domain" and "router". Use the "domain" prop on the Router component.`, - ); - } - if (props.router && props.edge) { - return yield* Effect.die( - `Cannot provide both "edge" and "router". Use the "edge" prop on the Router component.`, - ); - } - - const build = props.build - ? yield* Command("Build", { - command: props.build.command, - cwd: sitePath, - memo: { - include: props.build.include, - exclude: props.build.exclude, - lockfile: props.build.lockfile, - }, - outdir: props.build.output, - env: props.environment, - }) - : undefined; - - const uploadSourcePath = (build?.outdir ?? sitePath) as string; - - const providedBucket = props.assets?.bucket; - const bucket = - providedBucket ?? - (yield* Bucket("Bucket", { - bucketName: props.bucketName, - forceDestroy: props.forceDestroy, - tags: props.tags, - })); - - const routerAttachment = props.router; - const routerPathPrefix = routerAttachment?.path - ? "/" + routerAttachment.path.replace(/^\//, "").replace(/\/$/, "") - : undefined; - - const files = yield* AssetDeployment("Files", { - bucket: bucket, - sourcePath: uploadSourcePath, - prefix: normalizeUploadPrefix(assetPrefix, routerPathPrefix), - purge: props.assets?.purge ?? true, - fileOptions: props.assets?.fileOptions, - textEncoding: props.assets?.textEncoding, - }); - - const stack = yield* Stack; - const stage = yield* Stage; - const ns = yield* Namespace.CurrentNamespace; - const fqn = ns ? toPath(ns).join("/") : id; - const kvNamespace = createHash("md5") - .update(`${stack.name}-${stage}-${fqn}`) - .digest("hex") - .substring(0, 4); - - const kvEntries = buildKvEntries( - uploadSourcePath, - bucket, - assetPrefix, - assetRoutes, - indexPage, - props.errorPage, - routerPathPrefix, - ); - - let distributionId: Input; - let kvStoreArn: Input; - let distribution: Distribution | undefined; - let prodUrl: Input | undefined; - - if (routerAttachment) { - kvStoreArn = routerAttachment.instance.kvStoreArn; - distributionId = routerAttachment.instance.distributionId; - const hostPattern = routerAttachment.domain - ? routerAttachment.domain - .replace(/[.+?^${}()|[\]\\]/g, "\\$&") - .replace(/\*/g, ".*") - : undefined; - yield* KvRoutesUpdate("RoutesUpdate", { - store: kvStoreArn, - namespace: routerAttachment.instance.kvNamespace as any, - key: "routes", - entry: [ - "site", - kvNamespace, - hostPattern ?? "", - routerPathPrefix ?? "/", - ].join(","), - }); - prodUrl = routerAttachment.domain - ? `https://${routerAttachment.domain}${routerPathPrefix ?? ""}` - : Output.interpolate`${routerAttachment.instance.url}${routerPathPrefix ?? ""}`; - } else { - if ( - domain && - !domain.cert && - !domain.hostedZoneId && - domain.dns === false - ) { +export const StaticSite = (id: string, props: StaticSiteProps) => + Effect.gen(function* () { + const domain = normalizeDomain(props.domain); + const sitePath = (props.path ?? ".") as string; + const indexPage = props.indexPage ?? "index.html"; + const assetPrefix = normalizePrefix(props.assets?.path); + const assetRoutes = [...(props.assets?.routes ?? [])] + .map((value) => value.trim()) + .filter(Boolean) + .map(normalizeRoutePath); + const invalidationProps = + props.invalidation !== undefined + ? props.invalidation + : { paths: "all" as const, wait: false }; + + if (props.router && props.domain) { + return yield* Effect.die( + `Cannot provide both "domain" and "router". Use the "domain" prop on the Router component.`, + ); + } + if (props.router && props.edge) { return yield* Effect.die( - "StaticSite domain configuration with `dns: false` requires `cert`.", + `Cannot provide both "edge" and "router". Use the "edge" prop on the Router component.`, ); } - const certificate = - !domain || domain.cert - ? domain?.cert - ? { certificateArn: domain.cert } - : undefined - : yield* Certificate("Certificate", { - domainName: domain.name, - subjectAlternativeNames: [ - ...(domain.aliases ?? []), - ...(domain.redirects ?? []), - ], - hostedZoneId: domain.hostedZoneId, - tags: props.tags, - }); + const build = props.build + ? yield* Command("Build", { + command: props.build.command, + cwd: sitePath, + memo: { + include: props.build.include, + exclude: props.build.exclude, + lockfile: props.build.lockfile, + }, + outdir: props.build.output, + env: props.environment, + }) + : undefined; - const kvStore = yield* KeyValueStore("KvStore", {}); - kvStoreArn = kvStore.keyValueStoreArn; - - const viewerRequest = yield* CloudFrontFunction("ViewerRequest", { - comment: `${id} viewer request`, - code: buildRequestFunctionCode({ - kvNamespace, - userInjection: props.edge?.viewerRequest?.injection, - blockCloudfrontUrl: !!domain, - }), - keyValueStoreArns: [kvStore.keyValueStoreArn], - }); + const uploadSourcePath = (build?.outdir ?? sitePath) as string; - const viewerResponse = props.edge?.viewerResponse - ? yield* CloudFrontFunction("ViewerResponse", { - comment: `${id} viewer response`, - code: buildResponseFunctionCode(props.edge.viewerResponse.injection), - keyValueStoreArns: props.edge.viewerResponse.keyValueStoreArn - ? [props.edge.viewerResponse.keyValueStoreArn] - : undefined, - }) + const providedBucket = props.assets?.bucket; + const bucket = + providedBucket ?? + (yield* Bucket("Bucket", { + bucketName: props.bucketName, + forceDestroy: props.forceDestroy, + tags: props.tags, + })); + + const routerAttachment = props.router; + const routerPathPrefix = routerAttachment?.path + ? "/" + routerAttachment.path.replace(/^\//, "").replace(/\/$/, "") : undefined; - const functionAssociations = [ - { - eventType: "viewer-request" as const, - functionArn: viewerRequest.functionArn, - }, - ...(viewerResponse + const files = yield* AssetDeployment("Files", { + bucket: bucket, + sourcePath: uploadSourcePath, + prefix: normalizeUploadPrefix(assetPrefix, routerPathPrefix), + purge: props.assets?.purge ?? true, + fileOptions: props.assets?.fileOptions, + textEncoding: props.assets?.textEncoding, + }); + + const stack = yield* Stack; + const stage = yield* Stage; + const ns = yield* Namespace.CurrentNamespace; + const fqn = ns ? toPath(ns).join("/") : id; + const kvNamespace = createHash("md5") + .update(`${stack.name}-${stage}-${fqn}`) + .digest("hex") + .substring(0, 4); + + const kvEntries = buildKvEntries( + uploadSourcePath, + bucket, + assetPrefix, + assetRoutes, + indexPage, + props.errorPage, + routerPathPrefix, + ); + + let distributionId: Input; + let kvStoreArn: Input; + let distribution: Distribution | undefined; + let prodUrl: Input | undefined; + + if (routerAttachment) { + kvStoreArn = routerAttachment.instance.kvStoreArn; + distributionId = routerAttachment.instance.distributionId; + const hostPattern = routerAttachment.domain + ? routerAttachment.domain + .replace(/[.+?^${}()|[\]\\]/g, "\\$&") + .replace(/\*/g, ".*") + : undefined; + yield* KvRoutesUpdate("RoutesUpdate", { + store: kvStoreArn, + namespace: routerAttachment.instance.kvNamespace as any, + key: "routes", + entry: [ + "site", + kvNamespace, + hostPattern ?? "", + routerPathPrefix ?? "/", + ].join(","), + }); + prodUrl = routerAttachment.domain + ? `https://${routerAttachment.domain}${routerPathPrefix ?? ""}` + : Output.interpolate`${routerAttachment.instance.url}${routerPathPrefix ?? ""}`; + } else { + if ( + domain && + !domain.cert && + !domain.hostedZoneId && + domain.dns === false + ) { + return yield* Effect.die( + "StaticSite domain configuration with `dns: false` requires `cert`.", + ); + } + + const certificate = + !domain || domain.cert + ? domain?.cert + ? { certificateArn: domain.cert } + : undefined + : yield* Certificate("Certificate", { + domainName: domain.name, + subjectAlternativeNames: [ + ...(domain.aliases ?? []), + ...(domain.redirects ?? []), + ], + hostedZoneId: domain.hostedZoneId, + tags: props.tags, + }); + + const kvStore = yield* KeyValueStore("KvStore", {}); + kvStoreArn = kvStore.keyValueStoreArn; + + const viewerRequest = yield* CloudFrontFunction("ViewerRequest", { + comment: `${id} viewer request`, + code: buildRequestFunctionCode({ + kvNamespace, + userInjection: props.edge?.viewerRequest?.injection, + blockCloudfrontUrl: !!domain, + }), + keyValueStoreArns: [kvStore.keyValueStoreArn], + }); + + const viewerResponse = props.edge?.viewerResponse + ? yield* CloudFrontFunction("ViewerResponse", { + comment: `${id} viewer response`, + code: buildResponseFunctionCode( + props.edge.viewerResponse.injection, + ), + keyValueStoreArns: props.edge.viewerResponse.keyValueStoreArn + ? [props.edge.viewerResponse.keyValueStoreArn] + : undefined, + }) + : undefined; + + const functionAssociations = [ + { + eventType: "viewer-request" as const, + functionArn: viewerRequest.functionArn, + }, + ...(viewerResponse + ? [ + { + eventType: "viewer-response" as const, + functionArn: viewerResponse.functionArn, + }, + ] + : []), + ]; + + const errorPage = "/" + (props.errorPage ?? indexPage).replace(/^\//, ""); + const customErrorResponses = props.errorPage ? [ { - eventType: "viewer-response" as const, - functionArn: viewerResponse.functionArn, + ErrorCode: 403, + ResponseCode: "404", + ResponsePagePath: errorPage, + ErrorCachingMinTTL: 0, + }, + { + ErrorCode: 404, + ResponseCode: "404", + ResponsePagePath: errorPage, + ErrorCachingMinTTL: 0, }, ] - : []), - ]; + : undefined; - const errorPage = "/" + (props.errorPage ?? indexPage).replace(/^\//, ""); - const customErrorResponses = props.errorPage - ? [ - { - ErrorCode: 403, - ResponseCode: "404", - ResponsePagePath: errorPage, - ErrorCachingMinTTL: 0, - }, + distribution = yield* Distribution("Distribution", { + aliases: domain + ? [ + domain.name, + ...(domain.aliases ?? []), + ...(domain.redirects ?? []), + ] + : undefined, + origins: [ { - ErrorCode: 404, - ResponseCode: "404", - ResponsePagePath: errorPage, - ErrorCachingMinTTL: 0, - }, - ] - : undefined; - - distribution = yield* Distribution("Distribution", { - aliases: domain - ? [domain.name, ...(domain.aliases ?? []), ...(domain.redirects ?? [])] - : undefined, - origins: [ - { - id: "default", - domainName: "placeholder.alchemy.run", - customOriginConfig: { - httpPort: 80, - httpsPort: 443, - originProtocolPolicy: "https-only", - originReadTimeout: 20, - originSslProtocols: ["TLSv1.2"], + id: "default", + domainName: "placeholder.alchemy.run", + customOriginConfig: { + httpPort: 80, + httpsPort: 443, + originProtocolPolicy: "https-only", + originReadTimeout: 20, + originSslProtocols: ["TLSv1.2"], + }, }, - }, - ], - defaultCacheBehavior: { - targetOriginId: "default", - viewerProtocolPolicy: "redirect-to-https", - allowedMethods: [ - "DELETE", - "GET", - "HEAD", - "OPTIONS", - "PATCH", - "POST", - "PUT", ], - cachedMethods: ["GET", "HEAD"], - compress: true, - cachePolicyId: MANAGED_CACHING_OPTIMIZED_POLICY_ID, - functionAssociations, - }, - customErrorResponses, - viewerCertificate: certificate - ? { - acmCertificateArn: certificate.certificateArn, - sslSupportMethod: "sni-only", - minimumProtocolVersion: "TLSv1.2_2021", - } - : undefined, - tags: props.tags, - }); + defaultCacheBehavior: { + targetOriginId: "default", + viewerProtocolPolicy: "redirect-to-https", + allowedMethods: [ + "DELETE", + "GET", + "HEAD", + "OPTIONS", + "PATCH", + "POST", + "PUT", + ], + cachedMethods: ["GET", "HEAD"], + compress: true, + cachePolicyId: MANAGED_CACHING_OPTIMIZED_POLICY_ID, + functionAssociations, + }, + customErrorResponses, + viewerCertificate: certificate + ? { + acmCertificateArn: certificate.certificateArn, + sslSupportMethod: "sni-only", + minimumProtocolVersion: "TLSv1.2_2021", + } + : undefined, + tags: props.tags, + }); + + const dist = distribution; + distributionId = dist.distributionId; + + if (domain?.hostedZoneId && domain.dns !== false) { + yield* Effect.forEach( + [domain.name, ...(domain.aliases ?? []), ...(domain.redirects ?? [])], + (name, index) => + Route53Record(`AliasRecord${index + 1}`, { + hostedZoneId: domain.hostedZoneId!, + name, + type: "A", + aliasTarget: { + hostedZoneId: dist.hostedZoneId, + dnsName: dist.domainName, + }, + }), + { concurrency: "unbounded" }, + ); + } - const dist = distribution; - distributionId = dist.distributionId; - - if (domain?.hostedZoneId && domain.dns !== false) { - yield* Effect.forEach( - [domain.name, ...(domain.aliases ?? []), ...(domain.redirects ?? [])], - (name, index) => - Route53Record(`AliasRecord${index + 1}`, { - hostedZoneId: domain.hostedZoneId!, - name, - type: "A", - aliasTarget: { - hostedZoneId: dist.hostedZoneId, - dnsName: dist.domainName, - }, - }), - { concurrency: "unbounded" }, - ); + prodUrl = domain + ? Output.interpolate`https://${domain.name}` + : Output.interpolate`https://${dist.domainName}`; } - prodUrl = domain - ? Output.interpolate`https://${domain.name}` - : Output.interpolate`https://${dist.domainName}`; - } - - yield* KvEntries("KvEntries", { - store: kvStoreArn, - namespace: kvNamespace, - entries: kvEntries, - purge: props.assets?.purge ?? true, - }); - - const invalidation = - invalidationProps === false - ? undefined - : yield* Invalidation("Invalidation", { - distributionId: distributionId, - version: files.version, - wait: invalidationProps?.wait, - paths: - invalidationProps?.paths === "all" || !invalidationProps?.paths - ? ["/*"] - : invalidationProps.paths === "versioned" - ? [`/${indexPage.replace(/^\/+/, "")}`] - : invalidationProps.paths, - }); - - return { - bucket: bucket, - build, - files, - distribution, - invalidation, - kvNamespace, - url: prodUrl, - }; -}); + yield* KvEntries("KvEntries", { + store: kvStoreArn, + namespace: kvNamespace, + entries: kvEntries, + purge: props.assets?.purge ?? true, + }); + + const invalidation = + invalidationProps === false + ? undefined + : yield* Invalidation("Invalidation", { + distributionId: distributionId, + version: files.version, + wait: invalidationProps?.wait, + paths: + invalidationProps?.paths === "all" || !invalidationProps?.paths + ? ["/*"] + : invalidationProps.paths === "versioned" + ? [`/${indexPage.replace(/^\/+/, "")}`] + : invalidationProps.paths, + }); + + return { + bucket: bucket, + build, + files, + distribution, + invalidation, + kvNamespace, + url: prodUrl, + }; + }).pipe(Namespace.push(id)); const buildKvEntries = ( outputPath: string, diff --git a/packages/alchemy/src/Binding.ts b/packages/alchemy/src/Binding.ts index 2101bf565..fc15fb3ee 100644 --- a/packages/alchemy/src/Binding.ts +++ b/packages/alchemy/src/Binding.ts @@ -127,10 +127,14 @@ export interface Policy< * absent, so the policy gracefully becomes a no-op. */ export const Policy = - Effect.Effect>() => + < + Self, + Shape extends (...args: any[]) => Effect.Effect, + Provider = Self, + >() => ( Identifier: Identifier, - ): Policy`, Shape> => { + ): Policy`, Shape> => { const self = Context.Service(`Policy<${Identifier}>`); // we use a service option because at runtime (e.g. in a Lambda Function or Cloudflare Worker) diff --git a/packages/alchemy/src/Bundle/Bundle.ts b/packages/alchemy/src/Bundle/Bundle.ts index cf4839a93..5c10e2d27 100644 --- a/packages/alchemy/src/Bundle/Bundle.ts +++ b/packages/alchemy/src/Bundle/Bundle.ts @@ -86,6 +86,55 @@ export declare namespace BundleWatchEvent { } } +/** + * Compile-time constants substituted into every bundle via rolldown's + * `transform.define`. + * + * `globalThis.__ALCHEMY_RUNTIME__` is the runtime-phase flag: it is folded to + * `true` in all bundled (runtime) artifacts — deployed Workers, Lambdas, + * Containers, etc. — so any plan-only code guarded by + * `if (!globalThis.__ALCHEMY_RUNTIME__)` becomes `if (!true)` and is + * dead-code-eliminated from what ships. + * + * When the same source runs WITHOUT the bundler (the plan process executing + * `.ts` directly with bun/node), `globalThis.__ALCHEMY_RUNTIME__` reads as + * `undefined` (falsy) rather than throwing, so plan-only branches run. + * See `ALCHEMY_PHASE` in `Phase.ts`. + */ +const ALCHEMY_DEFINE: Record = { + "globalThis.__ALCHEMY_RUNTIME__": "true", +}; + +/** + * Merge {@link ALCHEMY_DEFINE} into the caller's `transform.define`, letting + * the framework flags win over any caller-provided keys. + */ +const withAlchemyDefine = ( + inputOptions: rolldown.InputOptions, +): rolldown.InputOptions => ({ + ...inputOptions, + transform: { + ...inputOptions.transform, + define: { + ...inputOptions.transform?.define, + ...ALCHEMY_DEFINE, + }, + }, +}); + +/** + * Default `minify` to `"dce-only"` when the caller hasn't chosen a mode, so the + * `if (false) { … }` branches produced by {@link ALCHEMY_DEFINE} are physically + * removed from every bundle (define alone only folds the condition; removal + * needs DCE). Callers that opt into full minification (e.g. Workers) keep it. + */ +const withDceDefault = ( + outputOptions?: rolldown.OutputOptions, +): rolldown.OutputOptions => ({ + ...outputOptions, + minify: outputOptions?.minify ?? "dce-only", +}); + /** * Build a bundle using rolldown from the given input options and output options. * @param inputOptions - The input options for the bundle. @@ -100,7 +149,7 @@ export const build = ( Effect.tryPromise({ try: async () => { const bundle = await rolldown.rolldown({ - ...inputOptions, + ...withAlchemyDefine(inputOptions), plugins: [inputOptions.plugins, builtInPlugins(extra)], optimization: inputOptions.optimization ?? { inlineConst: { @@ -109,7 +158,7 @@ export const build = ( }, }, }); - const result = await bundle.write(outputOptions); + const result = await bundle.write(withDceDefault(outputOptions)); await bundle.close(); return result.output; }, @@ -141,7 +190,7 @@ export const watch = ( Effect.acquireRelease( Effect.sync(() => { const watcher = rolldown.watch({ - ...inputOptions, + ...withAlchemyDefine(inputOptions), plugins: [ inputOptions.plugins, builtInPlugins(extra), @@ -171,7 +220,7 @@ export const watch = ( // they stay watched and local HMR is unaffected. exclude: ["**/node_modules/**"], }, - output: outputOptions, + output: withDceDefault(outputOptions), }); watcher.on("event", (event) => { if (event.code === "ERROR") { diff --git a/packages/alchemy/src/Cloudflare/AiGateway/AiGatewayBinding.ts b/packages/alchemy/src/Cloudflare/AiGateway/AiGatewayBinding.ts index 5de5209a2..77d0b92b6 100644 --- a/packages/alchemy/src/Cloudflare/AiGateway/AiGatewayBinding.ts +++ b/packages/alchemy/src/Cloudflare/AiGateway/AiGatewayBinding.ts @@ -7,6 +7,7 @@ import type { LanguageModel } from "effect/unstable/ai/LanguageModel"; import * as Binding from "../../Binding.ts"; import type { ResourceLike } from "../../Resource.ts"; import type { RuntimeContext } from "../../RuntimeContext.ts"; +import type { Providers } from "../Providers.ts"; import { isWorker, WorkerEnvironment } from "../Workers/Worker.ts"; import type { AiGateway as AiGatewayResource } from "./AiGateway.ts"; import { @@ -193,7 +194,8 @@ export const AiGatewayBindingLive = Layer.effect( */ export class AiGatewayBindingPolicy extends Binding.Policy< AiGatewayBindingPolicy, - (gateway: AiGatewayResource) => Effect.Effect + (gateway: AiGatewayResource) => Effect.Effect, + Providers >()("Cloudflare.AiGateway.Binding") {} /** diff --git a/packages/alchemy/src/Cloudflare/AiGateway/DurableObjectChatPersistence.ts b/packages/alchemy/src/Cloudflare/AiGateway/DurableObjectChatPersistence.ts new file mode 100644 index 000000000..20a4f5f17 --- /dev/null +++ b/packages/alchemy/src/Cloudflare/AiGateway/DurableObjectChatPersistence.ts @@ -0,0 +1,88 @@ +import * as Arr from "effect/Array"; +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import * as Chat from "effect/unstable/ai/Chat"; +import { + BackingPersistence, + PersistenceError, + type BackingPersistenceStore, +} from "effect/unstable/persistence/Persistence"; +import { RuntimeContext } from "../../RuntimeContext.ts"; +import { DurableObjectState } from "../Workers/DurableObjectState.ts"; + +export const DurableObjectChatPersistence = Layer.effect(BackingPersistence)( + Effect.gen(function* () { + const state = yield* DurableObjectState; + const storage = state.storage; + + const wrapErr = (op: string, key?: string) => (cause: unknown) => + new PersistenceError({ + message: `Failed to ${op}${key !== undefined ? ` key ${key}` : ""} in DurableObject storage`, + cause, + }); + + return BackingPersistence.of({ + make: (storeId) => + Effect.sync(() => { + const prefixed = (k: string) => `${storeId}:${k}`; + return { + get: (key) => + storage + .get(prefixed(key)) + .pipe( + Effect.mapError(wrapErr("get", key)), + Effect.provide(RuntimeContext.phantom), + ), + getMany: (keys) => + storage.get(keys.map(prefixed)).pipe( + Effect.mapError(wrapErr("getMany")), + Effect.map( + (map) => + keys.map((k) => map.get(prefixed(k))) as Arr.NonEmptyArray< + object | undefined + >, + ), + Effect.provide(RuntimeContext.phantom), + ), + set: (key, value, _ttl) => + storage + .put(prefixed(key), value) + .pipe( + Effect.mapError(wrapErr("set", key)), + Effect.provide(RuntimeContext.phantom), + ), + setMany: (entries) => + storage + .put( + Object.fromEntries(entries.map(([k, v]) => [prefixed(k), v])), + ) + .pipe( + Effect.mapError(wrapErr("setMany")), + Effect.provide(RuntimeContext.phantom), + ), + remove: (key) => + storage + .delete(prefixed(key)) + .pipe( + Effect.asVoid, + Effect.mapError(wrapErr("remove", key)), + Effect.provide(RuntimeContext.phantom), + ), + clear: storage.list({ prefix: `${storeId}:` }).pipe( + Effect.flatMap((map) => { + const ks = [...map.keys()]; + if (ks.length === 0) return Effect.void; + return Effect.asVoid(storage.delete(ks)); + }), + Effect.mapError(wrapErr("clear")), + Effect.provide(RuntimeContext.phantom), + ), + } satisfies BackingPersistenceStore; + }), + }); + }), +); + +export const layerChatDurableObject = Chat.layerPersisted({ + storeId: "alchemy.chat", +}).pipe(Layer.provide(DurableObjectChatPersistence)); diff --git a/packages/alchemy/src/Cloudflare/AiGateway/index.ts b/packages/alchemy/src/Cloudflare/AiGateway/index.ts index db5a7ccc1..11a95cf03 100644 --- a/packages/alchemy/src/Cloudflare/AiGateway/index.ts +++ b/packages/alchemy/src/Cloudflare/AiGateway/index.ts @@ -1,5 +1,6 @@ export * from "./AiGateway.ts"; export * from "./AiGatewayBinding.ts"; +export * from "./DurableObjectChatPersistence.ts"; export * from "./LanguageModel.ts"; export * from "./AiGatewaySpendingLimit.ts"; export * from "./Dataset.ts"; diff --git a/packages/alchemy/src/Cloudflare/AiSearch/AiSearch.ts b/packages/alchemy/src/Cloudflare/AiSearch/AiSearch.ts index b22c9ddb3..2628c101a 100644 --- a/packages/alchemy/src/Cloudflare/AiSearch/AiSearch.ts +++ b/packages/alchemy/src/Cloudflare/AiSearch/AiSearch.ts @@ -1,9 +1,10 @@ -import * as Construct from "../../Construct.ts"; +import * as Effect from "effect/Effect"; import type { Input, InputProps } from "../../Input.ts"; +import * as Namespace from "../../Namespace.ts"; import { isResource } from "../../Resource.ts"; import { AccountApiToken } from "../ApiToken/AccountApiToken.ts"; import { CloudflareEnvironment } from "../CloudflareEnvironment.ts"; -import type { R2Bucket } from "../R2/R2Bucket.ts"; +import type { R2Bucket } from "../R2/Bucket.ts"; import { AiSearchInstance, type AiSearchInstanceProps, @@ -293,103 +294,101 @@ export type AiSearch = AiSearchInstance & { * * @see https://developers.cloudflare.com/ai-search/ */ -export const AiSearch = Construct.fn(function* ( - id: string, - props: AiSearchProps, -) { - const { - source, - prefix, - include, - exclude, - jurisdiction, - parse, - crawl, - store, - namespace, - ...shared - } = props; +export const AiSearch = (id: string, props: AiSearchProps) => + Effect.gen(function* () { + const { + source, + prefix, + include, + exclude, + jurisdiction, + parse, + crawl, + store, + namespace, + ...shared + } = props; - let tokenId = shared.tokenId; - let serviceToken: AiSearchToken | undefined; - let type: "r2" | "web-crawler"; - let instanceSource: Input; - let sourceParams: Input | undefined; + let tokenId = shared.tokenId; + let serviceToken: AiSearchToken | undefined; + let type: "r2" | "web-crawler"; + let instanceSource: Input; + let sourceParams: Input | undefined; - // Discriminate the data source on what `source` is: an R2Bucket (a resource) - // indexes a bucket and needs a service token to read it; a URL crawls a seed - // and doesn't. - if (isResource(source)) { - const bucket = source as R2Bucket; - type = "r2"; - instanceSource = bucket.bucketName; - sourceParams = clean({ - prefix, - includeItems: include, - excludeItems: exclude, - r2Jurisdiction: jurisdiction, - }) as Input | undefined; + // Discriminate the data source on what `source` is: an R2Bucket (a resource) + // indexes a bucket and needs a service token to read it; a URL crawls a seed + // and doesn't. + if (isResource(source)) { + const bucket = source as R2Bucket; + type = "r2"; + instanceSource = bucket.bucketName; + sourceParams = clean({ + prefix, + includeItems: include, + excludeItems: exclude, + r2Jurisdiction: jurisdiction, + }) as Input | undefined; - // Cloudflare requires a service token to read an R2 source and only - // auto-creates one via the dashboard/Wrangler — not on a programmatic - // API create. Mint one ourselves unless the caller passed a `tokenId`. - if (tokenId === undefined) { - const { accountId } = yield* yield* CloudflareEnvironment; - const apiToken = yield* AccountApiToken("ApiToken", { - policies: [ - { - effect: "allow", - permissionGroups: ["AI Search Index Engine"], - resources: { - [`com.cloudflare.api.account.${accountId}`]: "*", + // Cloudflare requires a service token to read an R2 source and only + // auto-creates one via the dashboard/Wrangler — not on a programmatic + // API create. Mint one ourselves unless the caller passed a `tokenId`. + if (tokenId === undefined) { + const { accountId } = yield* yield* CloudflareEnvironment; + const apiToken = yield* AccountApiToken("ApiToken", { + policies: [ + { + effect: "allow", + permissionGroups: ["AI Search Index Engine"], + resources: { + [`com.cloudflare.api.account.${accountId}`]: "*", + }, }, - }, - ], - }); - serviceToken = yield* AiSearchToken("Token", { - cfApiId: apiToken.tokenId, - cfApiKey: apiToken.value, + ], + }); + serviceToken = yield* AiSearchToken("Token", { + cfApiId: apiToken.tokenId, + cfApiKey: apiToken.value, + }); + tokenId = serviceToken.id; + } + } else { + type = "web-crawler"; + instanceSource = source as Input; + + const { type: parseType, ...parseOptions } = parse ?? {}; + const webCrawler = clean({ + parseType, + parseOptions: clean(parseOptions), + crawlOptions: crawl ? clean(crawl) : undefined, + storeOptions: store + ? clean({ + storageId: store.bucket.bucketName, + storageType: "r2" as const, + r2Jurisdiction: store.jurisdiction, + }) + : undefined, }); - tokenId = serviceToken.id; + sourceParams = webCrawler + ? ({ webCrawler } as Input) + : undefined; } - } else { - type = "web-crawler"; - instanceSource = source as Input; - const { type: parseType, ...parseOptions } = parse ?? {}; - const webCrawler = clean({ - parseType, - parseOptions: clean(parseOptions), - crawlOptions: crawl ? clean(crawl) : undefined, - storeOptions: store - ? clean({ - storageId: store.bucket.bucketName, - storageType: "r2" as const, - r2Jurisdiction: store.jurisdiction, - }) - : undefined, + const instance = yield* AiSearchInstance("Instance", { + ...shared, + // The instance is keyed by namespace name; pass the namespace's `name` + // output so the engine orders instance-after-namespace. + namespace: namespace?.name, + type, + source: instanceSource, + tokenId, + sourceParams, }); - sourceParams = webCrawler - ? ({ webCrawler } as Input) - : undefined; - } - - const instance = yield* AiSearchInstance("Instance", { - ...shared, - // The instance is keyed by namespace name; pass the namespace's `name` - // output so the engine orders instance-after-namespace. - namespace: namespace?.name, - type, - source: instanceSource, - tokenId, - sourceParams, - }); - // Return the instance itself (augmented with the managed `serviceToken`) - // so an `AiSearch` is usable anywhere an `AiSearchInstance` is expected — - // `AiSearchInstance.bind(search)`, `env: { SEARCH: search }`, etc. - return Object.assign(instance, { serviceToken }) as AiSearch; -}); + // Return the instance itself (augmented with the managed `serviceToken`) + // so an `AiSearch` is usable anywhere an `AiSearchInstance` is expected — + // `AiSearchInstance.bind(search)`, `env: { SEARCH: search }`, etc. + return Object.assign(instance, { serviceToken }) as AiSearch; + }).pipe(Namespace.push(id)); /** Drop `undefined` entries; return `undefined` when nothing is left. */ const clean = >( diff --git a/packages/alchemy/src/Cloudflare/AiSearch/AiSearchBinding.ts b/packages/alchemy/src/Cloudflare/AiSearch/AiSearchBinding.ts index 763f625cf..0903d142b 100644 --- a/packages/alchemy/src/Cloudflare/AiSearch/AiSearchBinding.ts +++ b/packages/alchemy/src/Cloudflare/AiSearch/AiSearchBinding.ts @@ -9,6 +9,7 @@ import type { RuntimeContext } from "../../RuntimeContext.ts"; import { isWorker, WorkerEnvironment } from "../Workers/Worker.ts"; import type { AiSearchInstance } from "./AiSearchInstance.ts"; import type { AiSearchNamespace } from "./AiSearchNamespace.ts"; +import type { Providers } from "../Providers.ts"; /** * Error raised by AI Search runtime binding operations. @@ -182,7 +183,8 @@ export const AiSearchInstanceBindingLive = Layer.effect( */ export class AiSearchInstanceBindingPolicy extends Binding.Policy< AiSearchInstanceBindingPolicy, - (instance: AiSearchInstance) => Effect.Effect + (instance: AiSearchInstance) => Effect.Effect, + Providers >()("Cloudflare.AiSearch.InstanceBinding") {} /** @@ -309,7 +311,8 @@ export const AiSearchNamespaceBindingLive = Layer.effect( */ export class AiSearchNamespaceBindingPolicy extends Binding.Policy< AiSearchNamespaceBindingPolicy, - (namespace: AiSearchNamespace) => Effect.Effect + (namespace: AiSearchNamespace) => Effect.Effect, + Providers >()("Cloudflare.AiSearch.NamespaceBinding") {} /** diff --git a/packages/alchemy/src/Cloudflare/AnalyticsEngine/AnalyticsEngineDatasetBinding.ts b/packages/alchemy/src/Cloudflare/AnalyticsEngine/AnalyticsEngineDatasetBinding.ts index f0cf6743d..8f9e7ac72 100644 --- a/packages/alchemy/src/Cloudflare/AnalyticsEngine/AnalyticsEngineDatasetBinding.ts +++ b/packages/alchemy/src/Cloudflare/AnalyticsEngine/AnalyticsEngineDatasetBinding.ts @@ -6,6 +6,7 @@ import type { ResourceLike } from "../../Resource.ts"; import type { RuntimeContext } from "../../RuntimeContext.ts"; import { isWorker, WorkerEnvironment } from "../Workers/Worker.ts"; import type { AnalyticsEngineDataset as AnalyticsEngineDatasetLike } from "./AnalyticsEngineDataset.ts"; +import type { Providers } from "../Providers.ts"; export interface AnalyticsEngineDataPoint { indexes?: string[]; @@ -79,7 +80,8 @@ export const AnalyticsEngineDatasetBindingLive = Layer.effect( export class AnalyticsEngineDatasetBindingPolicy extends Binding.Policy< AnalyticsEngineDatasetBindingPolicy, - (dataset: AnalyticsEngineDatasetLike) => Effect.Effect + (dataset: AnalyticsEngineDatasetLike) => Effect.Effect, + Providers >()("Cloudflare.AnalyticsEngineDataset.Binding") {} export const AnalyticsEngineDatasetBindingPolicyLive = diff --git a/packages/alchemy/src/Cloudflare/Artifacts/ArtifactsBinding.ts b/packages/alchemy/src/Cloudflare/Artifacts/ArtifactsBinding.ts index 36d48cc67..a5d8d017e 100644 --- a/packages/alchemy/src/Cloudflare/Artifacts/ArtifactsBinding.ts +++ b/packages/alchemy/src/Cloudflare/Artifacts/ArtifactsBinding.ts @@ -6,6 +6,7 @@ import type { ResourceLike } from "../../Resource.ts"; import type { RuntimeContext } from "../../RuntimeContext.ts"; import { isWorker, WorkerEnvironment } from "../Workers/Worker.ts"; import { type Artifacts as ArtifactsLike } from "./Artifacts.ts"; +import type { Providers } from "../Providers.ts"; export class ArtifactsError extends Data.TaggedError("ArtifactsError")<{ message: string; @@ -138,7 +139,8 @@ export const ArtifactsBindingLive = Layer.effect( export class ArtifactsBindingPolicy extends Binding.Policy< ArtifactsBindingPolicy, - (artifacts: ArtifactsLike) => Effect.Effect + (artifacts: ArtifactsLike) => Effect.Effect, + Providers >()("Cloudflare.Artifacts.Binding") {} export const ArtifactsBindingPolicyLive = ArtifactsBindingPolicy.layer.succeed( diff --git a/packages/alchemy/src/Cloudflare/Browser/BrowserBinding.ts b/packages/alchemy/src/Cloudflare/Browser/BrowserBinding.ts index cc622136b..7d72520da 100644 --- a/packages/alchemy/src/Cloudflare/Browser/BrowserBinding.ts +++ b/packages/alchemy/src/Cloudflare/Browser/BrowserBinding.ts @@ -8,6 +8,7 @@ import type { ResourceLike } from "../../Resource.ts"; import type { RuntimeContext } from "../../RuntimeContext.ts"; import { isWorker, WorkerEnvironment } from "../Workers/Worker.ts"; import type { Browser as BrowserLike } from "./Browser.ts"; +import type { Providers } from "../Providers.ts"; export class BrowserError extends Data.TaggedError("BrowserError")<{ message: string; @@ -182,7 +183,8 @@ export const BrowserBindingLive = Layer.effect( export class BrowserBindingPolicy extends Binding.Policy< BrowserBindingPolicy, - (browser: BrowserLike) => Effect.Effect + (browser: BrowserLike) => Effect.Effect, + Providers >()("Cloudflare.Browser.Binding") {} export const BrowserBindingPolicyLive = BrowserBindingPolicy.layer.succeed( diff --git a/packages/alchemy/src/Cloudflare/CloudflareEnvironment.ts b/packages/alchemy/src/Cloudflare/CloudflareEnvironment.ts index eebea3f48..363c7973e 100644 --- a/packages/alchemy/src/Cloudflare/CloudflareEnvironment.ts +++ b/packages/alchemy/src/Cloudflare/CloudflareEnvironment.ts @@ -14,7 +14,9 @@ import { export class CloudflareEnvironment extends Context.Service< CloudflareEnvironment, Effect.Effect ->()("Cloudflare::CloudflareEnvironment") {} +>()("Cloudflare::CloudflareEnvironment") { + readonly kind = "Environment" as const; +} const CLOUDFLARE_ACCOUNT_ID = Config.string("CLOUDFLARE_ACCOUNT_ID"); diff --git a/packages/alchemy/src/Cloudflare/Container/Container.ts b/packages/alchemy/src/Cloudflare/Container/Container.ts index e5cce1670..afce5c8a0 100644 --- a/packages/alchemy/src/Cloudflare/Container/Container.ts +++ b/packages/alchemy/src/Cloudflare/Container/Container.ts @@ -2,19 +2,26 @@ import type * as cf from "@cloudflare/workers-types"; import * as Config from "effect/Config"; import * as Data from "effect/Data"; import * as Effect from "effect/Effect"; -import * as Option from "effect/Option"; -import { HttpServer, type HttpEffect } from "../../Http.ts"; -import * as Output from "../../Output.ts"; -import { Platform } from "../../Platform.ts"; -import * as Server from "../../Server/index.ts"; +import * as Layer from "effect/Layer"; +import type * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"; +import type * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"; +import type * as HttpServerRequest from "effect/unstable/http/HttpServerRequest"; +import type * as HttpServerResponse from "effect/unstable/http/HttpServerResponse"; +import type { InputProps } from "../../Input.ts"; +import type { Named } from "../../Named.ts"; +import type { ResourceClassLike } from "../../Resource.ts"; +import type { Rpc } from "../../Rpc.ts"; +import type { RuntimeContext } from "../../RuntimeContext.ts"; +import type { Props } from "../../State/ResourceState.ts"; +import { effectClass } from "../../Util/effect.ts"; import type { Fetcher } from "../Fetcher.ts"; +import type { Providers } from "../Providers.ts"; +import { type WorkerShape } from "../Workers/Worker.ts"; import type { ContainerApplication, ContainerApplicationProps, - ContainerServices, - ContainerShape, } from "./ContainerApplication.ts"; -import { bindContainer } from "./ContainerBinding.ts"; +import { ContainerPlatform } from "./ContainerPlatform.ts"; export const ContainerTypeId = "Cloudflare.Container"; export type ContainerTypeId = typeof ContainerTypeId; @@ -32,20 +39,65 @@ export class ContainerError extends Data.TaggedError("ContainerError")<{ export interface ContainerStartupOptions extends cf.ContainerStartupOptions {} -export interface ContainerProps extends ContainerApplicationProps { +/** + * Bundle an Effect-native program into a generated image. Alchemy bundles + * {@link main} and bakes it in as the container's entrypoint. + */ +export interface EffectfulContainerProps extends ContainerApplicationProps { + /** Entrypoint file for the Effect program, typically `import.meta.filename`. */ main: string; } +/** + * Build the container image from your own Dockerfile and build context — no + * Effect program is bundled. The image is shipped as-is. + */ +export interface ExternalContainerProps extends ContainerApplicationProps { + /** + * The build context directory containing the Dockerfile and any files it + * copies. + * + * @default `./` + */ + context?: string; + /** + * The Dockerfile to build, resolved relative to {@link context}. + * + * @default `/Dockerfile` + */ + dockerfile?: string; +} +/** + * Deploy a pre-built remote image — Alchemy pulls it and re-pushes it to + * Cloudflare's managed registry without building anything. + */ +export interface RemoteContainerProps extends ContainerApplicationProps { + /** + * The pre-built image to pull and re-push. + * + * E.g. `ghcr.io/alpine/alpine:latest` + */ + image: string; +} -export type Container = { - get running(): Effect.Effect; - start(options?: ContainerStartupOptions): Effect.Effect; - monitor(): Effect.Effect; - destroy(error?: any): Effect.Effect; - signal(signo: number): Effect.Effect; - getTcpPort(port: number): Effect.Effect; - setInactivityTimeout(durationMs: number | bigint): Effect.Effect; - interceptOutboundHttp(addr: string, binding: Fetcher): Effect.Effect; - interceptAllOutboundHttp(binding: Fetcher): Effect.Effect; +export type Container = Named & { + get running(): Effect.Effect; + start( + options?: ContainerStartupOptions, + ): Effect.Effect; + monitor(): Effect.Effect; + destroy(error?: any): Effect.Effect; + signal(signo: number): Effect.Effect; + getTcpPort(port: number): Effect.Effect; + setInactivityTimeout( + durationMs: number | bigint, + ): Effect.Effect; + interceptOutboundHttp( + addr: string, + binding: Fetcher, + ): Effect.Effect; + interceptAllOutboundHttp( + binding: Fetcher, + ): Effect.Effect; }; /** @@ -102,11 +154,15 @@ export type Container = { * const cp = yield* ChildProcessSpawner; * * return Sandbox.of({ - * exec: (cmd) => - * cp.spawn(ChildProcess.make(cmd, { shell: true })).pipe( - * Effect.map(({ exitCode, stdout, stderr }) => ({ - * exitCode, stdout, stderr, - * })), + * exec: (command) => + * cp.spawn(ChildProcess.make(command, { shell: true })).pipe( + * Effect.flatMap(({ exitCode, stdout, stderr }) => + * Effect.all({ + * exitCode, + * stdout: stdout.pipe(Stream.decodeText, Stream.mkString), + * stderr: stderr.pipe(Stream.decodeText, Stream.mkString), + * }), + * ), * Effect.scoped, * ), * fetch: Effect.succeed( @@ -117,6 +173,83 @@ export type Container = { * ); * ``` * + * @section Image Sources + * A container's image comes from one of three sources, picked by which + * prop you set: + * + * - `main` — bundle your Effect program into a generated image. + * - `context` (+ optional `dockerfile`) — build your own Dockerfile. + * - `image` — pull a pre-built remote image and re-push it. + * + * Only the `main` source bundles and injects an Effect runtime. The other + * two ship an arbitrary image as-is, so `.make()` just registers the + * container's identity (it has no Effect implementation to run). + * + * @example Effect-native image (`main`) + * ```typescript + * // Alchemy bundles this file's Effect program and bakes it into a + * // generated image as the entrypoint. + * export class Sandbox extends Cloudflare.Container< + * Sandbox, + * { ping: () => Effect.Effect } + * >()("Sandbox", { main: import.meta.filename }) {} + * + * export default Sandbox.make( + * Effect.gen(function* () { + * return Sandbox.of({ + * ping: () => Effect.succeed("pong"), + * fetch: Effect.succeed(HttpServerResponse.text("hello")), + * }); + * }), + * ); + * ``` + * + * @example Build your own Dockerfile (`context` / `dockerfile`) + * ```typescript + * // Alchemy builds the Dockerfile against the context directory — no + * // Effect bundling. `dockerfile` defaults to `/Dockerfile`. + * export class Web extends Cloudflare.Container()("Web", { + * context: `${import.meta.dirname}/context`, + * }) {} + * + * // No Effect runtime to provide — `.make()` only registers identity. + * export default Web.make(Effect.succeed(undefined)); + * ``` + * + * @example Remote image (`image`) + * ```typescript + * // Alchemy pulls the public image and re-pushes it to Cloudflare's + * // registry — no build, no bundling. + * export class Echo extends Cloudflare.Container()("Echo", { + * image: "mendhak/http-https-echo:latest", + * }) {} + * + * export default Echo.make(Effect.succeed(undefined)); + * ``` + * + * @example Reaching an arbitrary image's port from a Durable Object + * ```typescript + * // `external` and `remote` images expose no RPC methods, so the DO + * // talks to them purely over their TCP port via `getTcpPort`. + * export class WebObject extends Cloudflare.DurableObjectNamespace()( + * "WebObject", + * Effect.gen(function* () { + * const bound = yield* Cloudflare.Container.bind(Web); + * return Effect.gen(function* () { + * const container = yield* Cloudflare.start(bound); + * return { + * hello: () => + * Effect.gen(function* () { + * const { fetch } = yield* container.getTcpPort(8080); + * const res = yield* fetch(HttpClientRequest.get("http://container/")); + * return yield* res.text; + * }), + * }; + * }); + * }), + * ) {} + * ``` + * * @section Configuration * The props object accepts `main` (entrypoint file), `instanceType` * (compute size), `runtime` (`"bun"` or `"node"`), and @@ -225,96 +358,95 @@ export type Container = { * ); * ``` */ -export const Container: Platform< - ContainerApplication, - ContainerServices, - ContainerShape, - Server.ProcessContext, - Container -> & { - bind: typeof bindContainer; -} = Platform( - "Cloudflare.Container", - { - createRuntimeContext: (id: string): Server.ProcessContext => { - const runners: Effect.Effect[] = []; - const env: Record = {}; - - const serve = (handler: HttpEffect) => - Effect.sync(() => { - runners.push( - Effect.gen(function* () { - const httpServer = yield* Effect.serviceOption(HttpServer).pipe( - Effect.map(Option.getOrUndefined), - ); - if (httpServer) { - yield* httpServer.serve(handler); - yield* Effect.never; - } else { - // this should only happen at plantime, validate? - } - }).pipe(Effect.orDie), - ); +export const Container: ResourceClassLike & { + ( + id: Id, + props: InputProps, + ): Container.Decl, {}, Id>; + (): { + < + const Id extends string, + Props extends InputProps, + >( + id: Id, + props: Props, + ): Container.Decl; + }; + (): { + ( + id: Id, + ): Container.Decl>; + }; +} = ((...args: any[]) => { + if (args.length === 0) { + return (...args: any[]) => { + if (args.length === 1) { + const [id] = args as [string]; + const tag = ContainerPlatform()(id); + // for containers, we want the `yield* ContainerTag` to act as the Binding + const eff = ContainerPlatform.bind(tag); + return Object.assign(effectClass(eff), { + "~alchemy/Id": id, + make: (props: any, impl: any) => tag.make(props, impl), + // yield* MyContainer.Application to get the ContainerApplication Resource Outputs + Application: tag, }); + } else { + return Container(...(args as [string, any])); + } + }; + } else { + const [id, props] = args as [string, any]; + const resource = ContainerPlatform(id, props); + // for containers, we want the `yield* ContainerTag` to act as the Binding + const eff = effectClass(ContainerPlatform.bind(resource)); + return Object.assign(eff, { + "~alchemy/Id": id, + // yield* MyContainer.Application to get the ContainerApplication Resource Outputs + Application: resource, + }); + } +}) as any; - return { - Type: ContainerTypeId, - LogicalId: id, - id, - env, - set: (bindingId: string, output: Output.Output) => - Effect.sync(() => { - const key = bindingId.replaceAll(/[^a-zA-Z0-9]/g, "_"); - env[key] = output.pipe( - Output.map((value) => JSON.stringify(value)), - ); - return key; - }), - get: (key: string) => - Config.string(key) +export declare namespace Container { + export interface Decl< + Self = any, + Shape = any, + Id extends string = string, + Req = never, + > + extends Effect.Effect, Rpc, Named { + new (): Container & Shape; + make: ( + props: Props, + impl: Effect.Effect< + Shape & WorkerShape, + Config.ConfigError, + InitReq + >, + ) => Layer.Layer, never, Providers>; + of(shape: Shape & WorkerShape): Shape; + } + export namespace Decl { + export type Any = Decl; + } - .pipe( - Effect.flatMap((value) => - Effect.try({ - try: () => JSON.parse(value) as T, - catch: (error) => error as Error, - }), - ), - Effect.catch((cause) => - Effect.die( - new Error(`Failed to get environment variable: ${key}`, { - cause, - }), - ), - ), - ), - run: ((effect: Effect.Effect) => - Effect.sync(() => { - runners.push(effect); - })) as unknown as Server.ProcessContext["run"], - serve, - exports: Effect.sync(() => ({ - default: Effect.all( - runners.map((eff) => - Effect.forever( - eff.pipe( - // Log and ignore errors (daemon mode, it should just re-run) - Effect.tapError((err) => Effect.logError(err)), - Effect.ignore, - // TODO(sam): ignore cause? for now, let that actually kill the server - // Effect.ignoreCause - ), - ), - ), - { - concurrency: "unbounded", - }, - ), - })), - } as Server.ProcessContext; - }, - }, - { - bind: bindContainer, - }, -); + export interface Application { + "~alchemy/Kind": "ContainerApplication"; + "~alchemy/Self": Self; + } + + export type Instance = Container & + Shape & { + getTcpPort: (portNumber: number) => Effect.Effect<{ + fetch: { + ( + request: HttpClientRequest.HttpClientRequest, + ): Effect.Effect; + ( + request: HttpServerRequest.HttpServerRequest, + ): Effect.Effect; + }; + }>; + }; +} diff --git a/packages/alchemy/src/Cloudflare/Container/ContainerApplication.ts b/packages/alchemy/src/Cloudflare/Container/ContainerApplication.ts index 20187abef..87eb937fe 100644 --- a/packages/alchemy/src/Cloudflare/Container/ContainerApplication.ts +++ b/packages/alchemy/src/Cloudflare/Container/ContainerApplication.ts @@ -1,38 +1,16 @@ import type { ContainerImage } from "@distilled.cloud/cloudflare-runtime/Docker"; import * as Containers from "@distilled.cloud/cloudflare/containers"; -import * as Effect from "effect/Effect"; -import * as Schedule from "effect/Schedule"; -import { Unowned } from "../../AdoptPolicy.ts"; -import { AlchemyContext } from "../../AlchemyContext.ts"; -import { - dockerBuild, - materializeDockerfile, - pushImage, - writeContextFiles, -} from "../../Bundle/Docker.ts"; -import { getStableContextDir } from "../../Bundle/TempRoot.ts"; -import { deepEqual, isResolved } from "../../Diff.ts"; import * as ProviderLayer from "../../Local/ProviderLayer.ts"; import { type Main, type PlatformProps, type PlatformServices, } from "../../Platform.ts"; -import * as Provider from "../../Provider.ts"; -import { Resource, type ResourceBinding } from "../../Resource.ts"; +import { Resource } from "../../Resource.ts"; import * as Server from "../../Server/index.ts"; -import { sha256Object } from "../../Util/sha256.ts"; -import { normalizeNulls } from "../../Util/stable.ts"; -import { CloudflareEnvironment } from "../CloudflareEnvironment.ts"; -import { isLiveId } from "../LocalRuntime.ts"; -import { CloudflareLogs, type TelemetryFilter } from "../Logs.ts"; import type { Providers } from "../Providers.ts"; -import { Container, ContainerTypeId } from "./Container.ts"; -import { - buildFinalDockerfile, - bundleContainerProgram, - createContainerApplicationName, -} from "./ContainerBundle.ts"; +import { ContainerTypeId } from "./Container.ts"; +import { LiveContainerProvider } from "./ContainerProvider.ts"; import { LocalContainerProvider } from "./LocalContainerProvider.ts"; export { Credentials } from "@distilled.cloud/cloudflare/Credentials"; @@ -88,10 +66,33 @@ export namespace ContainerApplication { export interface ContainerApplicationProps extends PlatformProps { /** - * Main entrypoint for the container program. This file is bundled and - * added to the Docker image as the container's entrypoint. + * Main entrypoint for an Effect-native container program. This file is + * bundled and added to a generated Docker image as the container's + * entrypoint. + * + * The image source is selected by which of these props are set, in order: + * + * 1. {@link main} — bundle the Effect program and build a generated image. + * 2. {@link image} — pull the given remote image and re-push it to + * Cloudflare's registry (no build). + * 3. {@link context} / {@link dockerfile} — build the user's own Dockerfile + * against a build context directory. */ main?: string; + /** + * A pre-built remote image to deploy, e.g. `ghcr.io/alpine/alpine:latest`. + * + * When set (and {@link main} is not), Alchemy pulls this image and re-pushes + * it to Cloudflare's managed registry instead of building anything. + */ + image?: string; + /** + * Build context directory used when building the container from a user + * Dockerfile (i.e. when neither {@link main} nor {@link image} is set). + * + * @default `.` + */ + context?: string; /** * Exported handler symbol inside the bundled module. * @default "default" @@ -135,9 +136,16 @@ export interface ContainerApplicationProps extends PlatformProps { */ name?: string; /** - * Inline Dockerfile used as the base for building the container image. - * Alchemy appends statements to copy the bundled program and set the - * entrypoint. If omitted, a default base image matching the runtime is used. + * Dockerfile used to build the container image. Its meaning depends on the + * selected variant: + * + * - With {@link main}: an inline Dockerfile string used as the base image. + * Alchemy appends statements to copy the bundled program and set the + * entrypoint. If omitted, a default base image matching the runtime is used. + * - Without {@link main} (and without {@link image}): a path to the + * Dockerfile to build, resolved relative to {@link context}. + * + * @default `/Dockerfile` for the user-Dockerfile variant. */ dockerfile?: string; /** @@ -299,6 +307,35 @@ export type ContainerShape = Main; * useful for adopting an existing application, while `handler` runs the named * `runWorker` export rather than the module's default. * + * @section Image Sources + * The image is resolved from exactly one of three props, checked in order: + * `main` (bundle an Effect program into a generated image), then `image` + * (pull and re-push a remote image), then `context` / `dockerfile` (build + * your own Dockerfile). Only `main` injects an Effect runtime; the other two + * ship an arbitrary image unchanged. + * + * @example Build your own Dockerfile (`context` / `dockerfile`) + * ```typescript + * export class Web extends Cloudflare.Container()("Web", { + * context: `${import.meta.dirname}/context`, + * dockerfile: "Dockerfile", + * }) {} + * ``` + * + * Alchemy builds `dockerfile` against the `context` directory with no `main` + * bundling. `dockerfile` is resolved relative to `context` and defaults to + * `/Dockerfile`. + * + * @example Remote image (`image`) + * ```typescript + * export class Echo extends Cloudflare.Container()("Echo", { + * image: "mendhak/http-https-echo:latest", + * }) {} + * ``` + * + * Alchemy pulls the pre-built public image and re-pushes it to Cloudflare's + * managed registry instead of building anything. + * * @section Bundling & Dependencies * By default the entrypoint is bundled for the `bun` runtime. Use `runtime` to * switch to Node, `external` to keep native/precompiled packages out of the @@ -563,873 +600,14 @@ export interface ContainerApplication extends Resource< Shape: Shape; } +// `DevContainerImage` stays here (consumed by the Attrs `dev` field above and +// imported by Worker.ts). The provider helpers that previously lived here +// (resolveDurableObjectApplicationRecovery, the readiness schedule/retry, etc.) +// were extracted to ContainerProvider.ts on this branch. export type DevContainerImage = ContainerImage; -const resolveDurableObjectApplicationRecovery = ({ - namespaceId, - expectedName, - existingName, -}: { - namespaceId: string; - expectedName: string; - existingName: string | undefined; -}) => { - if (!existingName) { - return { - canAdopt: false as const, - message: `Container application for Durable Object namespace "${namespaceId}" already exists but could not be found for adoption.`, - }; - } - if (existingName !== expectedName) { - return { - canAdopt: false as const, - message: `Existing container application "${existingName}" is already attached to Durable Object namespace "${namespaceId}". Use that application name to adopt it.`, - }; - } - return { - canAdopt: true as const, - }; -}; - -const containerApplicationReadinessSchedule = Schedule.exponential(150).pipe( - Schedule.both(Schedule.recurs(10)), -); - -const isContainerApplicationNotFound = ( - error: unknown, -): error is Containers.ContainerApplicationNotFound => - typeof error === "object" && - error !== null && - "_tag" in error && - error._tag === "ContainerApplicationNotFound"; - -export const retryForContainerApplicationReadiness = ( - operation: string, - applicationId: string, - effect: Effect.Effect, -) => - effect.pipe( - Effect.tapError((error) => - isContainerApplicationNotFound(error) - ? Effect.logDebug( - `Cloudflare Container ${operation}: application ${applicationId} not found yet, retrying`, - ) - : Effect.void, - ), - Effect.retry({ - while: isContainerApplicationNotFound, - schedule: containerApplicationReadinessSchedule, - }), - ); - export const ContainerProvider = () => ProviderLayer.select({ live: () => LiveContainerProvider(), local: () => LocalContainerProvider(), }); - -export const LiveContainerProvider = () => - Provider.effect( - Container, - Effect.gen(function* () { - const { dotAlchemy } = yield* AlchemyContext; - - const telemetry = yield* CloudflareLogs; - - const createApplicationName = createContainerApplicationName; - - const findApplicationByName = Effect.fnUntraced(function* (name: string) { - const { accountId } = yield* yield* CloudflareEnvironment; - - return yield* Containers.listContainerApplications({ accountId }).pipe( - Effect.map((apps) => apps.find((app) => app.name === name)), - ); - }); - - const findApplicationByNamespace = Effect.fnUntraced(function* ( - namespaceId: string, - ) { - const { accountId } = yield* yield* CloudflareEnvironment; - - return yield* Containers.listContainerApplications({ accountId }).pipe( - Effect.map((apps) => - apps.find((app) => app.durableObjects?.namespaceId === namespaceId), - ), - ); - }); - - const desiredConfiguration = ( - props: ContainerApplicationProps, - imageRef: string, - ) => - normalizeNulls({ - image: imageRef, - instanceType: props.instanceType, - observability: props.observability, - sshPublicKeyIds: props.sshPublicKeyIds, - secrets: props.secrets, - vcpu: props.vcpu, - memory: props.memory, - disk: props.disk, - environmentVariables: props.environmentVariables, - labels: props.labels, - network: props.network, - command: props.command, - entrypoint: props.entrypoint, - dns: props.dns, - ports: props.ports, - checks: props.checks, - }) as ContainerApplication.Configuration; - - const computeImageHash = Effect.fnUntraced(function* ( - id: string, - props: ContainerApplicationProps, - ) { - const main = props.main; - if (!main) { - return yield* Effect.fail( - new Error("Container requires a `main` entrypoint."), - ); - } - const { accountId } = yield* yield* CloudflareEnvironment; - - const runtime = props.runtime ?? "bun"; - const { files, hash: bundleHash } = yield* bundleContainerProgram({ - id, - main, - runtime, - handler: props.handler, - isExternal: props.isExternal, - external: props.external, - }); - - const finalDockerfile = buildFinalDockerfile( - props.dockerfile, - runtime, - props.external, - props.autoInstallExternals, - ); - const imageHash = (yield* sha256Object({ - bundleHash, - dockerfile: finalDockerfile, - })).slice(0, 16); - - const name = yield* createApplicationName(id, props.name); - const registryId = props.registryId ?? "registry.cloudflare.com"; - const repositoryName = name.toLowerCase(); - const imageRef = `${registryId}/${accountId}/${repositoryName}:${imageHash}`; - - return { files, imageRef, imageHash }; - }); - - const buildAndPushImage = Effect.fnUntraced(function* ( - id: string, - props: ContainerApplicationProps, - files: ReadonlyArray<{ path: string; content: Uint8Array }>, - imageRef: string, - session?: { note: (message: string) => Effect.Effect }, - ) { - const { accountId } = yield* yield* CloudflareEnvironment; - - const runtime = props.runtime ?? "bun"; - - yield* Effect.logInfo( - `Cloudflare Container image: building ${imageRef}`, - ); - if (session) { - yield* session.note(`Building container image ${imageRef}...`); - } - - const contextDir = yield* getStableContextDir( - process.cwd(), - dotAlchemy, - `${id}-container`, - ); - const finalDockerfile = buildFinalDockerfile( - props.dockerfile, - runtime, - props.external, - props.autoInstallExternals, - ); - yield* materializeDockerfile(finalDockerfile, contextDir); - yield* writeContextFiles( - contextDir, - files.map((f, i) => ({ - // Keep the entry rename to `index.mjs` so the Dockerfile - // ENTRYPOINT (`ENTRYPOINT ["bun", "/app/index.mjs"]`) stays - // valid; preserve rolldown-assigned fileNames for every other - // chunk so intra-bundle relative imports resolve at runtime. - path: i === 0 ? "index.mjs" : f.path, - content: f.content, - })), - ); - yield* dockerBuild({ - tag: imageRef, - context: contextDir, - platform: "linux/amd64", - }); - - yield* Effect.logInfo( - `Cloudflare Container image: pushing ${imageRef}`, - ); - if (session) { - yield* session.note(`Pushing container image ${imageRef}...`); - } - - const registryId = props.registryId ?? "registry.cloudflare.com"; - const credentials = - yield* Containers.createContainerRegistryCredentials({ - accountId, - registryId, - permissions: ["pull", "push"], - expirationMinutes: 60, - }); - const username = credentials.username ?? (credentials as any).user; - if (!username) { - return yield* Effect.fail( - new Error( - "Cloudflare registry credentials did not include a username.", - ), - ); - } - - yield* pushImage(imageRef, { - username, - password: credentials.password, - server: registryId, - }); - }); - - const maybeCreateRollout = Effect.fnUntraced(function* ({ - applicationId, - configuration, - rollout, - }: { - applicationId: string; - configuration: ContainerApplication.Configuration; - rollout: ContainerApplication.Rollout | undefined; - }) { - const { accountId } = yield* yield* CloudflareEnvironment; - - const strategy = rollout?.strategy ?? "immediate"; - const stepPercentage = - strategy === "immediate" ? 100 : (rollout?.stepPercentage ?? 25); - - yield* retryForContainerApplicationReadiness( - "rollout", - applicationId, - Containers.createContainerApplicationRollout({ - accountId, - applicationId, - description: - strategy === "immediate" - ? "Immediate update" - : "Progressive update", - strategy: "rolling", - kind: rollout?.kind ?? "full_auto", - stepPercentage, - targetConfiguration: configuration, - }), - ); - }); - - const createApplication = Effect.fnUntraced(function* ({ - id, - news, - name, - configuration, - durableObjects, - session, - }: { - id: string; - news: ContainerApplicationProps; - name: string; - configuration: ContainerApplication.Configuration; - durableObjects: - | { - namespaceId: string; - } - | undefined; - session: { note: (message: string) => Effect.Effect }; - }) { - const { accountId } = yield* yield* CloudflareEnvironment; - - const describeError = (error: unknown) => { - if (error instanceof Error) { - return JSON.stringify( - Object.fromEntries( - Object.getOwnPropertyNames(error).map((key) => [ - key, - (error as unknown as Record)[key], - ]), - ), - null, - 2, - ); - } - return String(error); - }; - - // Engine has cleared us via `read` (foreign-named applications are - // surfaced as `Unowned`). Re-fetch the existing application to fold - // it into the upsert path. - const existingByName = yield* findApplicationByName(name); - - if (existingByName) { - yield* Effect.logInfo( - `Cloudflare Container create: adopting existing application ${name}`, - ); - return yield* upsertApplication({ - id, - news, - existing: toAttributes(existingByName), - session, - }); - } - - yield* Effect.logInfo( - `Cloudflare Container create: creating application ${name}`, - ); - yield* session.note(`Creating container application ${name}...`); - const adoptExistingByName = Effect.gen(function* () { - yield* Effect.logInfo( - `Cloudflare Container create: application ${name} already exists, adopting`, - ); - const existing = yield* findApplicationByName(name); - if (!existing) { - return yield* Effect.fail( - new Error( - `Container application "${name}" already exists but could not be found for adoption.`, - ), - ); - } - return yield* upsertApplication({ - id, - news, - existing: toAttributes(existing), - session, - }); - }); - - const application = yield* Containers.createContainerApplication({ - accountId, - name, - instances: news.instances ?? 1, - maxInstances: news.maxInstances ?? 1, - schedulingPolicy: news.schedulingPolicy ?? "default", - constraints: news.constraints ?? {}, - affinities: news.affinities, - configuration, - durableObjects, - }).pipe( - Effect.catchTag("DurableObjectAlreadyHasApplication", () => - durableObjects - ? Effect.gen(function* () { - const existing = yield* findApplicationByNamespace( - durableObjects.namespaceId, - ); - const recovery = resolveDurableObjectApplicationRecovery({ - namespaceId: durableObjects.namespaceId, - expectedName: name, - existingName: existing?.name, - }); - if (!recovery.canAdopt) { - return yield* Effect.fail(new Error(recovery.message)); - } - if (!existing) { - return yield* Effect.fail( - new Error( - `Container application for Durable Object namespace "${durableObjects.namespaceId}" already exists but could not be found for adoption.`, - ), - ); - } - return yield* upsertApplication({ - id, - news, - existing: toAttributes(existing), - session, - }); - }) - : Effect.fail( - new Error( - "Durable Object namespace already has a container application. Set AdoptPolicy to adopt it.", - ), - ), - ), - Effect.catchIf( - (e) => - "message" in (e as any) && - String((e as any).message).includes("already exists"), - () => adoptExistingByName, - ), - Effect.tapError((error) => - Effect.logError( - `Cloudflare Container create error: ${describeError(error)}`, - ), - ), - ); - - return "applicationId" in application - ? application - : toAttributes(application); - }); - - const upsertApplication = Effect.fnUntraced(function* ({ - id, - news, - existing, - session, - }: { - id: string; - news: ContainerApplicationProps; - existing: ContainerApplication["Attributes"]; - session: { note: (message: string) => Effect.Effect }; - }) { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* Effect.logInfo( - `Cloudflare Container update: preparing ${existing.applicationName}`, - ); - const { files, imageRef, imageHash } = yield* computeImageHash( - id, - news, - ); - const configuration = desiredConfiguration(news, imageRef); - - if (imageHash !== existing.hash?.image) { - yield* buildAndPushImage(id, news, files, imageRef, session); - } - - yield* session.note( - `Updating container application ${existing.applicationName}...`, - ); - const application = yield* retryForContainerApplicationReadiness( - "update", - existing.applicationId, - Containers.updateContainerApplication({ - accountId, - applicationId: existing.applicationId, - instances: news.instances ?? 1, - maxInstances: news.maxInstances ?? 1, - schedulingPolicy: news.schedulingPolicy ?? "default", - constraints: news.constraints ?? {}, - affinities: news.affinities, - configuration, - }), - ); - const updated = toAttributes(application); - if (!deepEqual(existing.configuration, configuration)) { - yield* Effect.logInfo( - `Cloudflare Container update: creating rollout for ${updated.applicationName}`, - ); - yield* maybeCreateRollout({ - applicationId: updated.applicationId, - configuration, - rollout: news.rollout, - }); - } - return { ...updated, configuration, hash: { image: imageHash } }; - }); - - const getDurableObjects = ( - bindings: ResourceBinding[], - ) => { - const dos = bindings.flatMap((b) => - b.data.durableObjects ? [b.data.durableObjects] : [], - ); - // A single DO namespace may appear in multiple bindings (e.g. when - // a Container is referenced by several resources). Dedupe by namespaceId. - const uniqueDos = dos.filter( - (d, i, arr) => - arr.findIndex((other) => other.namespaceId === d.namespaceId) === i, - ); - if (uniqueDos.length === 0) { - return Effect.succeed(undefined); - } - if (uniqueDos.length === 1) { - return Effect.succeed(uniqueDos[0]); - } - return Effect.die( - new Error( - `A Container can only be bound to one Durable Object namespace. Found ${uniqueDos.length} unique namespaces in bindings: ${uniqueDos.map((d) => d.namespaceId).join(", ")}`, - ), - ); - }; - - return Container.Provider.of({ - stables: ["accountId", "applicationId"], - diff: Effect.fnUntraced(function* ({ - id, - olds = {}, - news = {}, - output, - newBindings, - oldBindings, - }) { - if (!isResolved(news) || !isResolved(newBindings)) { - return undefined; - } - const { accountId } = yield* yield* CloudflareEnvironment; - - const name = yield* createApplicationName(id, news.name); - const oldName = output?.applicationName - ? output.applicationName - : yield* createApplicationName(id, olds.name); - - if ( - (output?.accountId ?? accountId) !== accountId || - name !== oldName - ) { - return { action: "replace" } as const; - } - - const hasDurableObjects = - (yield* getDurableObjects(newBindings)) !== undefined; - const hadDurableObjects = - (yield* getDurableObjects(oldBindings)) !== undefined; - if (hasDurableObjects !== hadDurableObjects) { - return { action: "replace" } as const; - } - - if (!output) { - return undefined; - } - - // A `dev:` applicationId means the resource only exists locally and - // the real application has never been created. Promote it by forcing - // an update so reconcile creates the live application. - if (!isLiveId(output.applicationId)) { - // Override stables to only include the accountId because the applicationId is going to change. - return { action: "update", stables: ["accountId"] } as const; - } - - const { imageHash } = yield* computeImageHash(id, news); - if (imageHash !== output.hash?.image) { - return { action: "update" } as const; - } - }), - precreate: Effect.fnUntraced(function* ({ id, news = {}, session }) { - const name = yield* createApplicationName(id, news.name); - yield* Effect.logInfo( - `Cloudflare Container precreate: starting ${name}`, - ); - - const { files, imageRef, imageHash } = yield* computeImageHash( - id, - news, - ); - const configuration = desiredConfiguration(news, imageRef); - yield* buildAndPushImage(id, news, files, imageRef, session); - - // Precreate intentionally omits the Durable Object attachment so the - // worker can bind to this application id and break the circular - // dependency. The final create step recreates the application with the - // resolved namespace when needed. - const result = yield* createApplication({ - id, - news, - name, - configuration, - durableObjects: undefined, - session: { - ...session, - note: (message) => - session.note(message.replace("Creating", "Pre-creating")), - }, - }); - return { - ...("applicationId" in result ? result : toAttributes(result)), - hash: { image: imageHash }, - }; - }), - reconcile: Effect.fnUntraced(function* ({ - id, - news = {}, - bindings, - output, - session, - }) { - const name = yield* createApplicationName(id, news.name); - yield* Effect.logInfo( - `Cloudflare Container reconcile: starting ${name}`, - ); - const durableObjects = yield* getDurableObjects(bindings); - const { files, imageRef, imageHash } = yield* computeImageHash( - id, - news, - ); - const configuration = desiredConfiguration(news, imageRef); - - // Observe — re-fetch the cached application to confirm it still - // exists. Cloudflare reports a deleted container application as - // `ContainerApplicationNotFound`; we fall back to a name lookup - // so we can recover from out-of-band deletes or partial state - // persistence failures. - let existing: ContainerApplication["Attributes"] | undefined; - // A `dev:` applicationId never exists on Cloudflare — skip the - // cached-id fetch and fall through to the name lookup / create path - // so we promote the local resource to a real application. - if (output?.applicationId && isLiveId(output.applicationId)) { - existing = yield* Containers.getContainerApplication({ - accountId: output.accountId, - applicationId: output.applicationId, - }).pipe( - Effect.map((app) => ({ - ...toAttributes(app), - hash: output.hash, - })), - Effect.catchTag("ContainerApplicationNotFound", () => - Effect.succeed(undefined), - ), - ); - } - if (!existing) { - const found = yield* findApplicationByName(name); - if (found) { - existing = { - ...toAttributes(found), - hash: output?.hash, - }; - } - } - - // Special case: precreate produced an application without the - // durable object attachment, but the real reconcile now has one - // (or vice versa). The DO attachment is immutable, so we delete - // and recreate. Adoption-by-namespace is preferred when an app - // already owns the namespace. - if (existing && !deepEqual(existing.durableObjects, durableObjects)) { - if (durableObjects) { - const owner = yield* findApplicationByNamespace( - durableObjects.namespaceId, - ); - const recovery = resolveDurableObjectApplicationRecovery({ - namespaceId: durableObjects.namespaceId, - expectedName: name, - existingName: owner?.name, - }); - if (recovery.canAdopt) { - if (!owner) { - return yield* Effect.fail( - new Error( - `Container application for Durable Object namespace "${durableObjects.namespaceId}" already exists but could not be found for adoption.`, - ), - ); - } - return yield* upsertApplication({ - id, - news, - existing: toAttributes(owner), - session, - }); - } - } - yield* Effect.logInfo( - `Cloudflare Container reconcile: recreating ${name} to attach durable object binding`, - ); - yield* session.note( - `Recreating container application ${name} with durable object binding...`, - ); - yield* Containers.deleteContainerApplication({ - accountId: existing.accountId, - applicationId: existing.applicationId, - }).pipe( - Effect.catchTag( - "ContainerApplicationNotFound", - () => Effect.void, - ), - ); - if (imageHash !== existing.hash?.image) { - yield* buildAndPushImage(id, news, files, imageRef, session); - } - const result = yield* createApplication({ - id, - news, - name, - configuration, - durableObjects, - session, - }); - return { - ...("applicationId" in result ? result : toAttributes(result)), - hash: { image: imageHash }, - }; - } - - // Sync — application exists with correct DO attachment. Apply - // the desired configuration (image + scheduling + secrets, etc.) - // through the upsert path, which builds and pushes the image - // only when the hash changed and creates a rollout if the - // configuration drifted. - if (existing) { - return yield* upsertApplication({ - id, - news, - existing, - session, - }); - } - - // Ensure — no application exists. Build and push the image, - // then create. `createApplication` itself tolerates concurrent - // creates by adopting an existing application with the same - // name or namespace. - yield* buildAndPushImage(id, news, files, imageRef, session); - const result = yield* createApplication({ - id, - news, - name, - configuration, - durableObjects, - session, - }); - return { - ...("applicationId" in result ? result : toAttributes(result)), - hash: { image: imageHash }, - }; - }), - delete: Effect.fnUntraced(function* ({ output }) { - // A `dev:` applicationId only exists locally — there is no live - // application to delete on Cloudflare. - if (!isLiveId(output.applicationId)) return; - yield* Effect.logInfo( - `Cloudflare Container delete: deleting ${output.applicationName}`, - ); - yield* Containers.deleteContainerApplication({ - accountId: output.accountId, - applicationId: output.applicationId, - }).pipe( - Effect.catchTag("ContainerApplicationNotFound", () => Effect.void), - ); - }), - read: Effect.fnUntraced(function* ({ id, olds, output }) { - const readByName = (name: string) => - Effect.gen(function* () { - yield* Effect.logInfo( - `Cloudflare Container read: looking up ${name}`, - ); - const existing = yield* findApplicationByName(name); - if (!existing) { - yield* Effect.logInfo( - `Cloudflare Container read: ${name} not found`, - ); - return undefined; - } - return { - ...toAttributes(existing), - hash: output?.hash, - }; - }); - - let attrs: ContainerApplication["Attributes"] | undefined; - // A `dev:` applicationId never exists on Cloudflare — look the - // application up by its (deterministic) name instead of hitting the - // API with a fake id. - if (output?.applicationId && !isLiveId(output.applicationId)) { - return yield* readByName(output.applicationName); - } - if (output?.applicationId) { - yield* Effect.logInfo( - `Cloudflare Container read: checking ${output.applicationName}`, - ); - attrs = yield* Containers.getContainerApplication({ - accountId: output.accountId, - applicationId: output.applicationId, - }).pipe( - Effect.map((app) => ({ - ...toAttributes(app), - hash: output.hash, - })), - Effect.catchTag("ContainerApplicationNotFound", () => - readByName(output.applicationName), - ), - ); - // If we matched by id from prior state, treat as owned. - return attrs; - } - - const name = yield* createApplicationName(id, olds?.name); - attrs = yield* readByName(name); - if (!attrs) return undefined; - // Cloudflare container applications carry no ownership signal that - // we can read back from the API, so a name match is not proof of - // ownership. Brand it `Unowned` so the engine surfaces - // `OwnedBySomeoneElse` unless the caller opted in via `--adopt`. - return Unowned(attrs); - }), - list: () => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - // Account-scoped collection. `listContainerApplications` returns - // the full application objects in one (non-paginated) response, so - // each item already carries the complete `read` attributes shape — - // no per-item hydration is required. - return yield* Containers.listContainerApplications({ - accountId, - }).pipe( - Effect.map((apps) => apps.map((app) => toAttributes(app))), - // Accounts without the containers product reject the route; treat - // a non-entitled account as an empty collection rather than an - // error. - Effect.catchTag("InvalidRoute", () => Effect.succeed([])), - ); - }), - tail: ({ output }) => - telemetry.tailStream({ - accountId: output.accountId, - filters: containerFilters(output.applicationId), - }), - logs: ({ output, options }) => - telemetry.queryLogs({ - accountId: output.accountId, - filters: containerFilters(output.applicationId), - options, - }), - }); - }), - ); - -const containerFilters = (applicationId: string): TelemetryFilter[] => [ - { - key: "$metadata.type", - operation: "eq", - type: "string", - value: "cf-container", - }, - { - key: "$metadata.service", - operation: "eq", - type: "string", - value: applicationId, - }, -]; - -const toAttributes = ( - application: - | Containers.CreateContainerApplicationResponse - | Containers.UpdateContainerApplicationResponse - | Containers.GetContainerApplicationResponse - | Containers.ListContainerApplicationsResponse[number], -): ContainerApplication["Attributes"] => ({ - applicationId: application.id, - applicationName: application.name, - accountId: application.accountId, - schedulingPolicy: application.schedulingPolicy, - instances: application.instances, - maxInstances: application.maxInstances, - constraints: normalizeNulls( - application.constraints as ContainerApplication.Constraints | undefined, - ), - affinities: normalizeNulls( - application.affinities as ContainerApplication.Affinities | undefined, - ), - configuration: normalizeNulls( - application.configuration as ContainerApplication.Configuration, - ), - durableObjects: normalizeNulls(application.durableObjects) as - | { namespaceId: string } - | undefined, - createdAt: application.createdAt, - version: application.version, - dev: undefined, -}); diff --git a/packages/alchemy/src/Cloudflare/Container/ContainerBinding.ts b/packages/alchemy/src/Cloudflare/Container/ContainerBinding.ts deleted file mode 100644 index a1e46b30a..000000000 --- a/packages/alchemy/src/Cloudflare/Container/ContainerBinding.ts +++ /dev/null @@ -1,74 +0,0 @@ -import * as Effect from "effect/Effect"; -import { type Rpc } from "../../Rpc.ts"; -import { - fromCloudflareFetcher, - toCloudflareFetcher, - type Fetcher, -} from "../Fetcher.ts"; -import { DurableObjectNamespace } from "../Workers/DurableObjectNamespace.ts"; -import { DurableObjectState } from "../Workers/DurableObjectState.ts"; -import { Worker } from "../Workers/Worker.ts"; -import type { Container } from "./Container.ts"; -import type { ContainerApplication } from "./ContainerApplication.ts"; - -export const bindContainer = Effect.fnUntraced(function* ( - containerEff: - | (ContainerApplication & Rpc) - | Effect.Effect, never, Req>, -) { - const namespace = yield* DurableObjectNamespace; - - const container = Effect.isEffect(containerEff) - ? yield* containerEff as unknown as Effect.Effect< - ContainerApplication & Rpc - > - : containerEff; - - yield* container.bind`${namespace}`({ - durableObjects: { - namespaceId: namespace.namespaceId, - }, - }); - - const worker = yield* Worker; - const className = namespace.name; - - yield* worker.bind`${container.LogicalId}`({ - containers: [{ className, dev: container.dev }], - }); - - // TODO(sam): register this in the Container Execution Context - // const _httpEffect = yield* init; - return Effect.gen(function* () { - const state = yield* DurableObjectState; - return { - running: Effect.sync(() => state.container!.running ?? false), - destroy: (error?: any) => - Effect.promise(() => state.container!.destroy(error)), - signal: (signo: number) => - Effect.sync(() => state.container!.signal(signo)), - getTcpPort: (port: number) => - Effect.sync(() => - fromCloudflareFetcher(state.container!.getTcpPort(port)), - ), - setInactivityTimeout: (durationMs: number | bigint) => - Effect.sync(() => state.container!.setInactivityTimeout(durationMs)), - interceptOutboundHttp: (addr: string, binding: Fetcher) => - toCloudflareFetcher(binding).pipe( - Effect.map((binding) => - state.container!.interceptOutboundHttp(addr, binding), - ), - ), - interceptAllOutboundHttp: (binding: Fetcher) => - toCloudflareFetcher(binding).pipe( - Effect.map((binding) => - state.container!.interceptAllOutboundHttp(binding), - ), - ), - monitor: () => - Effect.promise(() => state.container?.monitor() ?? Promise.resolve()), - start: (options?: ContainerStartupOptions) => - Effect.sync(() => state.container!.start(options)), - } satisfies Container as Shape; - }); -}); diff --git a/packages/alchemy/src/Cloudflare/Container/ContainerPlatform.ts b/packages/alchemy/src/Cloudflare/Container/ContainerPlatform.ts new file mode 100644 index 000000000..616026e0d --- /dev/null +++ b/packages/alchemy/src/Cloudflare/Container/ContainerPlatform.ts @@ -0,0 +1,176 @@ +import * as Config from "effect/Config"; +import * as Effect from "effect/Effect"; +import * as Option from "effect/Option"; +import { HttpServer, type HttpEffect } from "../../Http.ts"; +import * as Output from "../../Output.ts"; +import { Platform } from "../../Platform.ts"; +import type { Rpc } from "../../Rpc.ts"; +import * as Server from "../../Server/index.ts"; +import type { Fetcher } from "../Fetcher.ts"; +import { fromCloudflareFetcher, toCloudflareFetcher } from "../Fetcher.ts"; +import { DurableObjectNamespace } from "../Workers/DurableObjectNamespace.ts"; +import { DurableObjectState } from "../Workers/DurableObjectState.ts"; +import { Worker } from "../Workers/Worker.ts"; +import { ContainerTypeId } from "./Container.ts"; +import type { + ContainerApplication, + ContainerServices, + ContainerShape, +} from "./ContainerApplication.ts"; + +export const ContainerPlatform: Platform< + ContainerApplication, + ContainerServices, + ContainerShape, + Server.ProcessContext, + Container +> = Platform( + "Cloudflare.Container", + { + createRuntimeContext: (id: string): Server.ProcessContext => { + const runners: Effect.Effect[] = []; + const env: Record = {}; + + const serve = (handler: HttpEffect) => + Effect.sync(() => { + runners.push( + Effect.gen(function* () { + const httpServer = yield* Effect.serviceOption(HttpServer).pipe( + Effect.map(Option.getOrUndefined), + ); + if (httpServer) { + yield* httpServer.serve(handler); + yield* Effect.never; + } else { + // this should only happen at plantime, validate? + } + }).pipe(Effect.orDie), + ); + }); + + return { + Type: ContainerTypeId, + LogicalId: id, + id, + env, + set: (bindingId: string, output: Output.Output) => + Effect.sync(() => { + const key = bindingId.replaceAll(/[^a-zA-Z0-9]/g, "_"); + env[key] = output.pipe( + Output.map((value) => JSON.stringify(value)), + ); + return key; + }), + get: (key: string) => + Config.string(key) + + .pipe( + Effect.flatMap((value) => + Effect.try({ + try: () => JSON.parse(value) as T, + catch: (error) => error as Error, + }), + ), + Effect.catch((cause) => + Effect.die( + new Error(`Failed to get environment variable: ${key}`, { + cause, + }), + ), + ), + ), + run: ((effect: Effect.Effect) => + Effect.sync(() => { + runners.push(effect); + })) as unknown as Server.ProcessContext["run"], + serve, + exports: Effect.sync(() => ({ + default: Effect.all( + runners.map((eff) => + Effect.forever( + eff.pipe( + // Log and ignore errors (daemon mode, it should just re-run) + Effect.tapError((err) => Effect.logError(err)), + Effect.ignore, + // TODO(sam): ignore cause? for now, let that actually kill the server + // Effect.ignoreCause + ), + ), + ), + { + concurrency: "unbounded", + }, + ), + })), + } as Server.ProcessContext; + }, + }, + { + bind: Effect.fnUntraced(function* ( + containerEff: + | (ContainerApplication & Rpc) + | Effect.Effect, never, Req>, + ) { + const namespace = yield* DurableObjectNamespace; + + const container = Effect.isEffect(containerEff) + ? yield* containerEff as unknown as Effect.Effect< + ContainerApplication & Rpc + > + : containerEff; + + yield* container.bind`${namespace}`({ + durableObjects: { + namespaceId: namespace.namespaceId, + }, + }); + + const worker = yield* Worker; + const className = namespace.name; + + yield* worker.bind`${container.LogicalId}`({ + containers: [{ className, dev: container.dev }], + }); + + // TODO(sam): register this in the Container Execution Context + // const _httpEffect = yield* init; + return Effect.gen(function* () { + const state = yield* DurableObjectState; + return { + id: container.LogicalId, + running: Effect.sync(() => state.container!.running ?? false), + destroy: (error?: any) => + Effect.promise(() => state.container!.destroy(error)), + signal: (signo: number) => + Effect.sync(() => state.container!.signal(signo)), + getTcpPort: (port: number) => + Effect.sync(() => + fromCloudflareFetcher(state.container!.getTcpPort(port)), + ), + setInactivityTimeout: (durationMs: number | bigint) => + Effect.sync(() => + state.container!.setInactivityTimeout(durationMs), + ), + interceptOutboundHttp: (addr: string, binding: Fetcher) => + toCloudflareFetcher(binding).pipe( + Effect.map((binding) => + state.container!.interceptOutboundHttp(addr, binding), + ), + ), + interceptAllOutboundHttp: (binding: Fetcher) => + toCloudflareFetcher(binding).pipe( + Effect.map((binding) => + state.container!.interceptAllOutboundHttp(binding), + ), + ), + monitor: () => + Effect.promise( + () => state.container?.monitor() ?? Promise.resolve(), + ), + start: (options?: ContainerStartupOptions) => + Effect.sync(() => state.container!.start(options)), + } as unknown; + }); + }), + }, +); diff --git a/packages/alchemy/src/Cloudflare/Container/ContainerProvider.ts b/packages/alchemy/src/Cloudflare/Container/ContainerProvider.ts new file mode 100644 index 000000000..385618a37 --- /dev/null +++ b/packages/alchemy/src/Cloudflare/Container/ContainerProvider.ts @@ -0,0 +1,978 @@ +import * as Containers from "@distilled.cloud/cloudflare/containers"; +import * as Effect from "effect/Effect"; +import * as FileSystem from "effect/FileSystem"; +import * as Path from "effect/Path"; +import * as Schedule from "effect/Schedule"; +import { Unowned } from "../../AdoptPolicy.ts"; +import { AlchemyContext } from "../../AlchemyContext.ts"; +import { hashDirectory } from "../../Build/Memo.ts"; +import { + dockerBuild, + dockerTag, + materializeDockerfile, + pushImage, + runDockerCommand, + writeContextFiles, +} from "../../Bundle/Docker.ts"; +import { getStableContextDir } from "../../Bundle/TempRoot.ts"; +import { deepEqual, isResolved } from "../../Diff.ts"; +import * as Provider from "../../Provider.ts"; +import { type ResourceBinding } from "../../Resource.ts"; +import { sha256Object } from "../../Util/sha256.ts"; +import { normalizeNulls } from "../../Util/stable.ts"; +import { CloudflareEnvironment } from "../CloudflareEnvironment.ts"; +import { isLiveId } from "../LocalRuntime.ts"; +import { CloudflareLogs, type TelemetryFilter } from "../Logs.ts"; +import type { + ContainerApplication, + ContainerApplicationProps, +} from "./ContainerApplication.ts"; +import { + buildFinalDockerfile, + bundleContainerProgram, + createContainerApplicationName, +} from "./ContainerBundle.ts"; +import { ContainerPlatform } from "./ContainerPlatform.ts"; + +/** + * The image source resolved from a {@link ContainerApplicationProps}. Selects + * one of three strategies used by `buildAndPushImage`: + * + * - `effectful` — bundle an Effect-native `main` and build a generated image. + * - `external` — build a user-supplied Dockerfile against a context directory. + * - `remote` — pull a pre-built remote image and re-push it to Cloudflare. + */ +type ImageBuild = + | { + readonly kind: "effectful"; + readonly files: ReadonlyArray<{ path: string; content: Uint8Array }>; + } + | { + readonly kind: "external"; + readonly context: string; + readonly dockerfile: string; + } + | { + readonly kind: "remote"; + readonly image: string; + }; + +export const LiveContainerProvider = () => + Provider.effect( + ContainerPlatform, + Effect.gen(function* () { + const { dotAlchemy } = yield* AlchemyContext; + const fs = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + + const telemetry = yield* CloudflareLogs; + + const createApplicationName = createContainerApplicationName; + + const findApplicationByName = Effect.fn(function* (name: string) { + const { accountId } = yield* yield* CloudflareEnvironment; + + return yield* Containers.listContainerApplications({ accountId }).pipe( + Effect.map((apps) => apps.find((app) => app.name === name)), + ); + }); + + const findApplicationByNamespace = Effect.fn(function* ( + namespaceId: string, + ) { + const { accountId } = yield* yield* CloudflareEnvironment; + + return yield* Containers.listContainerApplications({ accountId }).pipe( + Effect.map((apps) => + apps.find((app) => app.durableObjects?.namespaceId === namespaceId), + ), + ); + }); + + const desiredConfiguration = ( + props: ContainerApplicationProps, + imageRef: string, + ) => + normalizeNulls({ + image: imageRef, + instanceType: props.instanceType, + observability: props.observability, + sshPublicKeyIds: props.sshPublicKeyIds, + secrets: props.secrets, + vcpu: props.vcpu, + memory: props.memory, + disk: props.disk, + environmentVariables: props.environmentVariables, + labels: props.labels, + network: props.network, + command: props.command, + entrypoint: props.entrypoint, + dns: props.dns, + ports: props.ports, + checks: props.checks, + }) as ContainerApplication.Configuration; + + const computeImage = Effect.fn(function* ( + id: string, + props: ContainerApplicationProps, + ) { + const { accountId } = yield* yield* CloudflareEnvironment; + const name = yield* createApplicationName(id, props.name); + const registryId = props.registryId ?? "registry.cloudflare.com"; + const repositoryName = name.toLowerCase(); + const makeRef = (imageHash: string) => + `${registryId}/${accountId}/${repositoryName}:${imageHash}`; + + // Variant 1 — Effect-native program. Bundle `main` and build a + // generated Dockerfile around it. + if (props.main) { + const runtime = props.runtime ?? "bun"; + const { files, hash: bundleHash } = yield* bundleContainerProgram({ + id, + main: props.main, + runtime, + handler: props.handler, + isExternal: props.isExternal, + external: props.external, + }); + const finalDockerfile = buildFinalDockerfile( + props.dockerfile, + runtime, + props.external, + props.autoInstallExternals, + ); + const imageHash = (yield* sha256Object({ + bundleHash, + dockerfile: finalDockerfile, + })).slice(0, 16); + return { + build: { kind: "effectful" as const, files }, + imageRef: makeRef(imageHash), + imageHash, + }; + } + + // Variant 2 — pre-built remote image. The image reference is the + // identity; we pull and re-push it without building anything. + if (props.image) { + const imageHash = (yield* sha256Object({ + image: props.image, + })).slice(0, 16); + return { + build: { kind: "remote" as const, image: props.image }, + imageRef: makeRef(imageHash), + imageHash, + }; + } + + // Variant 3 — user-supplied Dockerfile + build context directory. + const context = yield* fs.realPath(props.context ?? "."); + const dockerfile = props.dockerfile + ? yield* fs.realPath(props.dockerfile) + : path.join(context, "Dockerfile"); + const contextHash = yield* hashDirectory({ cwd: context }); + const dockerfileContent = yield* fs.readFileString(dockerfile); + const imageHash = (yield* sha256Object({ + contextHash, + dockerfile: dockerfileContent, + })).slice(0, 16); + return { + build: { kind: "external" as const, context, dockerfile }, + imageRef: makeRef(imageHash), + imageHash, + }; + }); + + const buildAndPushImage = Effect.fn(function* ( + id: string, + props: ContainerApplicationProps, + build: ImageBuild, + imageRef: string, + session?: { note: (message: string) => Effect.Effect }, + ) { + const { accountId } = yield* yield* CloudflareEnvironment; + const platform = "linux/amd64"; + + if (build.kind === "remote") { + // Pull the pre-built image and re-tag it to the Cloudflare registry + // reference; nothing is built locally. + yield* Effect.logInfo( + `Cloudflare Container image: pulling ${build.image}`, + ); + if (session) { + yield* session.note(`Pulling container image ${build.image}...`); + } + yield* runDockerCommand([ + "pull", + "--platform", + platform, + build.image, + ]); + yield* dockerTag(build.image, imageRef); + } else if (build.kind === "external") { + // Build the user's Dockerfile directly against their context dir so + // relative `COPY`/`ADD` paths resolve as the author intended. + yield* Effect.logInfo( + `Cloudflare Container image: building ${imageRef}`, + ); + if (session) { + yield* session.note(`Building container image ${imageRef}...`); + } + yield* dockerBuild({ + tag: imageRef, + context: build.context, + platform, + extraArgs: ["-f", build.dockerfile], + }); + } else { + // Effect-native program: materialize the generated Dockerfile and + // bundled chunks into a stable staging dir, then build. + yield* Effect.logInfo( + `Cloudflare Container image: building ${imageRef}`, + ); + if (session) { + yield* session.note(`Building container image ${imageRef}...`); + } + const runtime = props.runtime ?? "bun"; + const contextDir = yield* getStableContextDir( + process.cwd(), + dotAlchemy, + `${id}-container`, + ); + const finalDockerfile = buildFinalDockerfile( + props.dockerfile, + runtime, + props.external, + props.autoInstallExternals, + ); + yield* materializeDockerfile(finalDockerfile, contextDir); + yield* writeContextFiles( + contextDir, + build.files.map((f, i) => ({ + // Keep the entry rename to `index.mjs` so the Dockerfile + // ENTRYPOINT (`ENTRYPOINT ["bun", "/app/index.mjs"]`) stays + // valid; preserve rolldown-assigned fileNames for every other + // chunk so intra-bundle relative imports resolve at runtime. + path: i === 0 ? "index.mjs" : f.path, + content: f.content, + })), + ); + yield* dockerBuild({ + tag: imageRef, + context: contextDir, + platform, + }); + } + + yield* Effect.logInfo( + `Cloudflare Container image: pushing ${imageRef}`, + ); + if (session) { + yield* session.note(`Pushing container image ${imageRef}...`); + } + + const registryId = props.registryId ?? "registry.cloudflare.com"; + const credentials = + yield* Containers.createContainerRegistryCredentials({ + accountId, + registryId, + permissions: ["pull", "push"], + expirationMinutes: 60, + }); + const username = credentials.username ?? (credentials as any).user; + if (!username) { + return yield* Effect.fail( + new Error( + "Cloudflare registry credentials did not include a username.", + ), + ); + } + + yield* pushImage(imageRef, { + username, + password: credentials.password, + server: registryId, + }); + }); + + const maybeCreateRollout = Effect.fn(function* ({ + applicationId, + configuration, + rollout, + }: { + applicationId: string; + configuration: ContainerApplication.Configuration; + rollout: ContainerApplication.Rollout | undefined; + }) { + const { accountId } = yield* yield* CloudflareEnvironment; + + const strategy = rollout?.strategy ?? "immediate"; + const stepPercentage = + strategy === "immediate" ? 100 : (rollout?.stepPercentage ?? 25); + + yield* retryForContainerApplicationReadiness( + "rollout", + applicationId, + Containers.createContainerApplicationRollout({ + accountId, + applicationId, + description: + strategy === "immediate" + ? "Immediate update" + : "Progressive update", + strategy: "rolling", + kind: rollout?.kind ?? "full_auto", + stepPercentage, + targetConfiguration: configuration, + }), + ); + }); + + const createApplication = Effect.fn(function* ({ + id, + news, + name, + configuration, + durableObjects, + session, + }: { + id: string; + news: ContainerApplicationProps; + name: string; + configuration: ContainerApplication.Configuration; + durableObjects: + | { + namespaceId: string; + } + | undefined; + session: { note: (message: string) => Effect.Effect }; + }) { + const { accountId } = yield* yield* CloudflareEnvironment; + + const describeError = (error: unknown) => { + if (error instanceof Error) { + return JSON.stringify( + Object.fromEntries( + Object.getOwnPropertyNames(error).map((key) => [ + key, + (error as unknown as Record)[key], + ]), + ), + null, + 2, + ); + } + return String(error); + }; + + // Engine has cleared us via `read` (foreign-named applications are + // surfaced as `Unowned`). Re-fetch the existing application to fold + // it into the upsert path. + const existingByName = yield* findApplicationByName(name); + + if (existingByName) { + yield* Effect.logInfo( + `Cloudflare Container create: adopting existing application ${name}`, + ); + return yield* upsertApplication({ + id, + news, + existing: toAttributes(existingByName), + session, + }); + } + + yield* Effect.logInfo( + `Cloudflare Container create: creating application ${name}`, + ); + yield* session.note(`Creating container application ${name}...`); + const adoptExistingByName = Effect.gen(function* () { + yield* Effect.logInfo( + `Cloudflare Container create: application ${name} already exists, adopting`, + ); + const existing = yield* findApplicationByName(name); + if (!existing) { + return yield* Effect.fail( + new Error( + `Container application "${name}" already exists but could not be found for adoption.`, + ), + ); + } + return yield* upsertApplication({ + id, + news, + existing: toAttributes(existing), + session, + }); + }); + + const application = yield* Containers.createContainerApplication({ + accountId, + name, + instances: news.instances ?? 1, + maxInstances: news.maxInstances ?? 1, + schedulingPolicy: news.schedulingPolicy ?? "default", + constraints: news.constraints ?? {}, + affinities: news.affinities, + configuration, + durableObjects, + }).pipe( + Effect.catchTag("DurableObjectAlreadyHasApplication", () => + durableObjects + ? Effect.gen(function* () { + const existing = yield* findApplicationByNamespace( + durableObjects.namespaceId, + ); + const recovery = resolveDurableObjectApplicationRecovery({ + namespaceId: durableObjects.namespaceId, + expectedName: name, + existingName: existing?.name, + }); + if (!recovery.canAdopt) { + return yield* Effect.fail(new Error(recovery.message)); + } + if (!existing) { + return yield* Effect.fail( + new Error( + `Container application for Durable Object namespace "${durableObjects.namespaceId}" already exists but could not be found for adoption.`, + ), + ); + } + return yield* upsertApplication({ + id, + news, + existing: toAttributes(existing), + session, + }); + }) + : Effect.fail( + new Error( + "Durable Object namespace already has a container application. Set AdoptPolicy to adopt it.", + ), + ), + ), + Effect.catchIf( + (e) => + "message" in (e as any) && + String((e as any).message).includes("already exists"), + () => adoptExistingByName, + ), + Effect.tapError((error) => + Effect.logError( + `Cloudflare Container create error: ${describeError(error)}`, + ), + ), + ); + + return "applicationId" in application + ? application + : toAttributes(application); + }); + + const upsertApplication = Effect.fn(function* ({ + id, + news, + existing, + session, + }: { + id: string; + news: ContainerApplicationProps; + existing: ContainerApplication["Attributes"]; + session: { note: (message: string) => Effect.Effect }; + }) { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* Effect.logInfo( + `Cloudflare Container update: preparing ${existing.applicationName}`, + ); + const { build, imageRef, imageHash } = yield* computeImage(id, news); + const configuration = desiredConfiguration(news, imageRef); + + if (imageHash !== existing.hash?.image) { + yield* buildAndPushImage(id, news, build, imageRef, session); + } + + yield* session.note( + `Updating container application ${existing.applicationName}...`, + ); + const application = yield* retryForContainerApplicationReadiness( + "update", + existing.applicationId, + Containers.updateContainerApplication({ + accountId, + applicationId: existing.applicationId, + instances: news.instances ?? 1, + maxInstances: news.maxInstances ?? 1, + schedulingPolicy: news.schedulingPolicy ?? "default", + constraints: news.constraints ?? {}, + affinities: news.affinities, + configuration, + }), + ); + const updated = toAttributes(application); + if (!deepEqual(existing.configuration, configuration)) { + yield* Effect.logInfo( + `Cloudflare Container update: creating rollout for ${updated.applicationName}`, + ); + yield* maybeCreateRollout({ + applicationId: updated.applicationId, + configuration, + rollout: news.rollout, + }); + } + return { ...updated, configuration, hash: { image: imageHash } }; + }); + + const getDurableObjects = ( + bindings: ResourceBinding[], + ) => { + const dos = bindings.flatMap((b) => + b.data.durableObjects ? [b.data.durableObjects] : [], + ); + // A single DO namespace may appear in multiple bindings (e.g. when + // a Container is referenced by several resources). Dedupe by namespaceId. + const uniqueDos = dos.filter( + (d, i, arr) => + arr.findIndex((other) => other.namespaceId === d.namespaceId) === i, + ); + if (uniqueDos.length === 0) { + return Effect.succeed(undefined); + } + if (uniqueDos.length === 1) { + return Effect.succeed(uniqueDos[0]); + } + return Effect.die( + new Error( + `A Container can only be bound to one Durable Object namespace. Found ${uniqueDos.length} unique namespaces in bindings: ${uniqueDos.map((d) => d.namespaceId).join(", ")}`, + ), + ); + }; + + return ContainerPlatform.Provider.of({ + stables: ["accountId", "applicationId"], + diff: Effect.fn(function* ({ + id, + olds = {}, + news = {}, + output, + newBindings, + oldBindings, + }) { + if (!isResolved(news) || !isResolved(newBindings)) { + return undefined; + } + const { accountId } = yield* yield* CloudflareEnvironment; + + const name = yield* createApplicationName(id, news.name); + const oldName = output?.applicationName + ? output.applicationName + : yield* createApplicationName(id, olds.name); + + if ( + (output?.accountId ?? accountId) !== accountId || + name !== oldName + ) { + return { action: "replace" } as const; + } + + const hasDurableObjects = + (yield* getDurableObjects(newBindings)) !== undefined; + const hadDurableObjects = + (yield* getDurableObjects(oldBindings)) !== undefined; + if (hasDurableObjects !== hadDurableObjects) { + return { action: "replace" } as const; + } + + if (!output) { + return undefined; + } + + // A `dev:` applicationId means the resource only exists locally and + // the real application has never been created. Promote it by forcing + // an update so reconcile creates the live application. + if (!isLiveId(output.applicationId)) { + // Override stables to only include the accountId because the applicationId is going to change. + return { action: "update", stables: ["accountId"] } as const; + } + + const { imageHash } = yield* computeImage(id, news); + if (imageHash !== output.hash?.image) { + return { action: "update" } as const; + } + }), + precreate: Effect.fn(function* ({ id, news = {}, session }) { + const name = yield* createApplicationName(id, news.name); + yield* Effect.logInfo( + `Cloudflare Container precreate: starting ${name}`, + ); + + const { build, imageRef, imageHash } = yield* computeImage(id, news); + const configuration = desiredConfiguration(news, imageRef); + yield* buildAndPushImage(id, news, build, imageRef, session); + + // Precreate intentionally omits the Durable Object attachment so the + // worker can bind to this application id and break the circular + // dependency. The final create step recreates the application with the + // resolved namespace when needed. + const result = yield* createApplication({ + id, + news, + name, + configuration, + durableObjects: undefined, + session: { + ...session, + note: (message) => + session.note(message.replace("Creating", "Pre-creating")), + }, + }); + return { + ...("applicationId" in result ? result : toAttributes(result)), + hash: { image: imageHash }, + }; + }), + reconcile: Effect.fn(function* ({ + id, + news = {}, + bindings, + output, + session, + }) { + const name = yield* createApplicationName(id, news.name); + yield* Effect.logInfo( + `Cloudflare Container reconcile: starting ${name}`, + ); + const durableObjects = yield* getDurableObjects(bindings); + const { build, imageRef, imageHash } = yield* computeImage(id, news); + const configuration = desiredConfiguration(news, imageRef); + + // Observe — re-fetch the cached application to confirm it still + // exists. Cloudflare reports a deleted container application as + // `ContainerApplicationNotFound`; we fall back to a name lookup + // so we can recover from out-of-band deletes or partial state + // persistence failures. + let existing: ContainerApplication["Attributes"] | undefined; + // A `dev:` applicationId never exists on Cloudflare — skip the + // cached-id fetch and fall through to the name lookup / create path + // so we promote the local resource to a real application. + if (output?.applicationId && isLiveId(output.applicationId)) { + existing = yield* Containers.getContainerApplication({ + accountId: output.accountId, + applicationId: output.applicationId, + }).pipe( + Effect.map((app) => ({ + ...toAttributes(app), + hash: output.hash, + })), + Effect.catchTag("ContainerApplicationNotFound", () => + Effect.succeed(undefined), + ), + ); + } + if (!existing) { + const found = yield* findApplicationByName(name); + if (found) { + existing = { + ...toAttributes(found), + hash: output?.hash, + }; + } + } + + // Special case: precreate produced an application without the + // durable object attachment, but the real reconcile now has one + // (or vice versa). The DO attachment is immutable, so we delete + // and recreate. Adoption-by-namespace is preferred when an app + // already owns the namespace. + if (existing && !deepEqual(existing.durableObjects, durableObjects)) { + if (durableObjects) { + const owner = yield* findApplicationByNamespace( + durableObjects.namespaceId, + ); + const recovery = resolveDurableObjectApplicationRecovery({ + namespaceId: durableObjects.namespaceId, + expectedName: name, + existingName: owner?.name, + }); + if (recovery.canAdopt) { + if (!owner) { + return yield* Effect.fail( + new Error( + `Container application for Durable Object namespace "${durableObjects.namespaceId}" already exists but could not be found for adoption.`, + ), + ); + } + return yield* upsertApplication({ + id, + news, + existing: toAttributes(owner), + session, + }); + } + } + yield* Effect.logInfo( + `Cloudflare Container reconcile: recreating ${name} to attach durable object binding`, + ); + yield* session.note( + `Recreating container application ${name} with durable object binding...`, + ); + yield* Containers.deleteContainerApplication({ + accountId: existing.accountId, + applicationId: existing.applicationId, + }).pipe( + Effect.catchTag( + "ContainerApplicationNotFound", + () => Effect.void, + ), + ); + if (imageHash !== existing.hash?.image) { + yield* buildAndPushImage(id, news, build, imageRef, session); + } + const result = yield* createApplication({ + id, + news, + name, + configuration, + durableObjects, + session, + }); + return { + ...("applicationId" in result ? result : toAttributes(result)), + hash: { image: imageHash }, + }; + } + + // Sync — application exists with correct DO attachment. Apply + // the desired configuration (image + scheduling + secrets, etc.) + // through the upsert path, which builds and pushes the image + // only when the hash changed and creates a rollout if the + // configuration drifted. + if (existing) { + return yield* upsertApplication({ + id, + news, + existing, + session, + }); + } + + // Ensure — no application exists. Build and push the image, + // then create. `createApplication` itself tolerates concurrent + // creates by adopting an existing application with the same + // name or namespace. + yield* buildAndPushImage(id, news, build, imageRef, session); + const result = yield* createApplication({ + id, + news, + name, + configuration, + durableObjects, + session, + }); + return { + ...("applicationId" in result ? result : toAttributes(result)), + hash: { image: imageHash }, + }; + }), + delete: Effect.fn(function* ({ output }) { + // A `dev:` applicationId only exists locally — there is no live + // application to delete on Cloudflare. + if (!isLiveId(output.applicationId)) return; + yield* Effect.logInfo( + `Cloudflare Container delete: deleting ${output.applicationName}`, + ); + yield* Containers.deleteContainerApplication({ + accountId: output.accountId, + applicationId: output.applicationId, + }).pipe( + Effect.catchTag("ContainerApplicationNotFound", () => Effect.void), + ); + }), + read: Effect.fn(function* ({ id, olds, output }) { + const readByName = (name: string) => + Effect.gen(function* () { + yield* Effect.logInfo( + `Cloudflare Container read: looking up ${name}`, + ); + const existing = yield* findApplicationByName(name); + if (!existing) { + yield* Effect.logInfo( + `Cloudflare Container read: ${name} not found`, + ); + return undefined; + } + return { + ...toAttributes(existing), + hash: output?.hash, + }; + }); + + let attrs: ContainerApplication["Attributes"] | undefined; + // A `dev:` applicationId never exists on Cloudflare — look the + // application up by its (deterministic) name instead of hitting the + // API with a fake id. + if (output?.applicationId && !isLiveId(output.applicationId)) { + return yield* readByName(output.applicationName); + } + if (output?.applicationId) { + yield* Effect.logInfo( + `Cloudflare Container read: checking ${output.applicationName}`, + ); + attrs = yield* Containers.getContainerApplication({ + accountId: output.accountId, + applicationId: output.applicationId, + }).pipe( + Effect.map((app) => ({ + ...toAttributes(app), + hash: output.hash, + })), + Effect.catchTag("ContainerApplicationNotFound", () => + readByName(output.applicationName), + ), + ); + // If we matched by id from prior state, treat as owned. + return attrs; + } + + const name = yield* createApplicationName(id, olds?.name); + attrs = yield* readByName(name); + if (!attrs) return undefined; + // Cloudflare container applications carry no ownership signal that + // we can read back from the API, so a name match is not proof of + // ownership. Brand it `Unowned` so the engine surfaces + // `OwnedBySomeoneElse` unless the caller opted in via `--adopt`. + return Unowned(attrs); + }), + list: () => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + // Account-scoped collection. `listContainerApplications` returns + // the full application objects in one (non-paginated) response, so + // each item already carries the complete `read` attributes shape — + // no per-item hydration is required. + return yield* Containers.listContainerApplications({ + accountId, + }).pipe( + Effect.map((apps) => apps.map((app) => toAttributes(app))), + // Accounts without the containers product reject the route; treat + // a non-entitled account as an empty collection rather than an + // error. + Effect.catchTag("InvalidRoute", () => Effect.succeed([])), + ); + }), + tail: ({ output }) => + telemetry.tailStream({ + accountId: output.accountId, + filters: containerFilters(output.applicationId), + }), + logs: ({ output, options }) => + telemetry.queryLogs({ + accountId: output.accountId, + filters: containerFilters(output.applicationId), + options, + }), + }); + }), + ); + +const containerFilters = (applicationId: string): TelemetryFilter[] => [ + { + key: "$metadata.type", + operation: "eq", + type: "string", + value: "cf-container", + }, + { + key: "$metadata.service", + operation: "eq", + type: "string", + value: applicationId, + }, +]; + +const resolveDurableObjectApplicationRecovery = ({ + namespaceId, + expectedName, + existingName, +}: { + namespaceId: string; + expectedName: string; + existingName: string | undefined; +}) => { + if (!existingName) { + return { + canAdopt: false as const, + message: `Container application for Durable Object namespace "${namespaceId}" already exists but could not be found for adoption.`, + }; + } + if (existingName !== expectedName) { + return { + canAdopt: false as const, + message: `Existing container application "${existingName}" is already attached to Durable Object namespace "${namespaceId}". Use that application name to adopt it.`, + }; + } + return { + canAdopt: true as const, + }; +}; + +const containerApplicationReadinessSchedule = Schedule.exponential(150).pipe( + Schedule.both(Schedule.recurs(10)), +); + +const isContainerApplicationNotFound = ( + error: unknown, +): error is Containers.ContainerApplicationNotFound => + typeof error === "object" && + error !== null && + "_tag" in error && + error._tag === "ContainerApplicationNotFound"; + +export const retryForContainerApplicationReadiness = ( + operation: string, + applicationId: string, + effect: Effect.Effect, +) => + effect.pipe( + Effect.tapError((error) => + isContainerApplicationNotFound(error) + ? Effect.logDebug( + `Cloudflare Container ${operation}: application ${applicationId} not found yet, retrying`, + ) + : Effect.void, + ), + Effect.retry({ + while: isContainerApplicationNotFound, + schedule: containerApplicationReadinessSchedule, + }), + ); + +const toAttributes = ( + application: + | Containers.CreateContainerApplicationResponse + | Containers.UpdateContainerApplicationResponse + | Containers.GetContainerApplicationResponse + | Containers.ListContainerApplicationsResponse[number], +): ContainerApplication["Attributes"] => ({ + applicationId: application.id, + applicationName: application.name, + accountId: application.accountId, + schedulingPolicy: application.schedulingPolicy, + instances: application.instances, + maxInstances: application.maxInstances, + constraints: normalizeNulls( + application.constraints as ContainerApplication.Constraints | undefined, + ), + affinities: normalizeNulls( + application.affinities as ContainerApplication.Affinities | undefined, + ), + configuration: normalizeNulls( + application.configuration as ContainerApplication.Configuration, + ), + durableObjects: normalizeNulls(application.durableObjects) as + | { namespaceId: string } + | undefined, + createdAt: application.createdAt, + version: application.version, + dev: undefined, +}); diff --git a/packages/alchemy/src/Cloudflare/Container/LocalContainerProvider.ts b/packages/alchemy/src/Cloudflare/Container/LocalContainerProvider.ts index 7c548a535..76af1e7be 100644 --- a/packages/alchemy/src/Cloudflare/Container/LocalContainerProvider.ts +++ b/packages/alchemy/src/Cloudflare/Container/LocalContainerProvider.ts @@ -9,7 +9,6 @@ import { sha256Object } from "../../Util/sha256.ts"; import { normalizeNulls } from "../../Util/stable.ts"; import { CloudflareEnvironment } from "../CloudflareEnvironment.ts"; import { generateLocalId, LOCAL_ENTRY_URL } from "../LocalRuntime.ts"; -import { Container } from "./Container.ts"; import type { ContainerApplication, ContainerApplicationProps, @@ -19,6 +18,7 @@ import { bundleContainerProgram, createContainerApplicationName, } from "./ContainerBundle.ts"; +import { ContainerPlatform } from "./ContainerPlatform.ts"; /** * Local (dev) provider for Cloudflare Container applications. @@ -36,7 +36,7 @@ import { */ export const LocalContainerProvider = () => RpcProvider.effect( - Container, + ContainerPlatform, LOCAL_ENTRY_URL, Effect.gen(function* () { const { dotAlchemy } = yield* AlchemyContext; diff --git a/packages/alchemy/src/Cloudflare/Container/StartContainer.ts b/packages/alchemy/src/Cloudflare/Container/StartContainer.ts index fc3488d03..e55e16760 100644 --- a/packages/alchemy/src/Cloudflare/Container/StartContainer.ts +++ b/packages/alchemy/src/Cloudflare/Container/StartContainer.ts @@ -1,28 +1,44 @@ +import * as Context from "effect/Context"; import * as Duration from "effect/Duration"; import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; import * as Schedule from "effect/Schedule"; import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"; import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"; import * as HttpServerRequest from "effect/unstable/http/HttpServerRequest"; import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse"; +import { ALCHEMY_PHASE } from "../../Phase.ts"; import { type Fetcher } from "../Fetcher.ts"; -import { DurableObjectState } from "../Workers/DurableObjectState.ts"; import { type Container, ContainerError, type ContainerStartupOptions, } from "./Container.ts"; + +export const layerContainer = ( + container: Image, + options?: ContainerStartupOptions, +): Layer.Layer> => { + // + const id = container["~alchemy/Id"]; + class Tag extends Context.Service< + InstanceType, + Container.Instance> + >()(`Container<${id}>`) {} + return Layer.effect(Tag, startContainer(container, options)); +}; + /** * Runs the Container in a Durable Object and monitors it, providing a durable fetch and RPC interface to it. */ -export const start = Effect.fnUntraced(function* < - Shape extends Container, - Req = never, ->( - containerEff: Effect.Effect, - options?: ContainerStartupOptions, -) { - const container = yield* containerEff; +export const startContainer = Effect.fn(function* < + Image extends Container.Decl.Any, +>(containerEff: Image, options?: ContainerStartupOptions) { + const container: Container = yield* containerEff as any as Effect.Effect< + any, + never, + never + >; const ensureRunning = Effect.gen(function* () { if (yield* container.running) return; @@ -39,12 +55,22 @@ export const start = Effect.fnUntraced(function* < ); }); - yield* ensureRunning; - + // Poll the container roughly every 2–3s while it cold-starts, but bound the + // total wait (~3 min) so an unreachable container surfaces a `ContainerError` + // instead of hanging the Durable Object request forever. Without this cap a + // container that never accepts connections on the requested port (e.g. it + // crash-loops, or the process never binds the port) would retry indefinitely + // and the worker request would never return. const startupBackoff = Schedule.exponential(100, 1.5).pipe( Schedule.modifyDelay((_, delay) => - Effect.succeed(Duration.max(delay, Duration.seconds(2))), + Effect.succeed( + Duration.min( + Duration.max(delay, Duration.seconds(1)), + Duration.seconds(3), + ), + ), ), + Schedule.both(Schedule.recurs(75)), ); const getTcpPort = (portNumber: number) => @@ -78,9 +104,17 @@ export const start = Effect.fnUntraced(function* < }, }); + const phase = yield* ALCHEMY_PHASE; + + // eagerly start the container when in runtime, no-op during planning + if (phase === "runtime") { + // erase the RuntimeContext color (we are applying it eagerly as an optimization only during runtime) + yield* ensureRunning as Effect.Effect; + } + return { ...container, getTcpPort, fetch: getTcpPort(3000), - }; + } as Container.Instance>; }); diff --git a/packages/alchemy/src/Cloudflare/Container/index.ts b/packages/alchemy/src/Cloudflare/Container/index.ts index 8b8153eba..526e0d59b 100644 --- a/packages/alchemy/src/Cloudflare/Container/index.ts +++ b/packages/alchemy/src/Cloudflare/Container/index.ts @@ -1,6 +1,7 @@ export * from "./Container.ts"; export * from "./ContainerApplication.ts"; -export * from "./ContainerBinding.ts"; export * from "./ContainerBundle.ts"; +export * from "./ContainerPlatform.ts"; +export * from "./ContainerProvider.ts"; export * from "./LocalContainerProvider.ts"; export * from "./StartContainer.ts"; diff --git a/packages/alchemy/src/Cloudflare/Credentials.ts b/packages/alchemy/src/Cloudflare/Credentials.ts index d8a4ef67f..2a853f3d6 100644 --- a/packages/alchemy/src/Cloudflare/Credentials.ts +++ b/packages/alchemy/src/Cloudflare/Credentials.ts @@ -20,6 +20,12 @@ import { export { Credentials, fromEnv } from "@distilled.cloud/cloudflare/Credentials"; +declare module "@distilled.cloud/cloudflare/Credentials" { + interface Credentials { + readonly kind: "Credentials"; + } +} + /** * Build a `Credentials` layer that resolves Cloudflare credentials via the * Alchemy AuthProvider using the configured profile (defaults to "default", diff --git a/packages/alchemy/src/Cloudflare/D1/D1Connection.ts b/packages/alchemy/src/Cloudflare/D1/D1Connection.ts index 4be4dc151..1660f84cf 100644 --- a/packages/alchemy/src/Cloudflare/D1/D1Connection.ts +++ b/packages/alchemy/src/Cloudflare/D1/D1Connection.ts @@ -3,6 +3,7 @@ import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; import type { RuntimeContext } from "../../RuntimeContext.ts"; +import type { Providers } from "../Providers.ts"; import { WorkerEnvironment } from "../Workers/Worker.ts"; import type { D1Database } from "./D1Database.ts"; import { DatabaseBinding } from "./D1DatabaseBinding.ts"; @@ -168,7 +169,8 @@ export const D1ConnectionLive = Layer.effect( export class D1ConnectionPolicy extends Binding.Policy< D1ConnectionPolicy, - (database: D1Database) => Effect.Effect + (database: D1Database) => Effect.Effect, + Providers >()("Cloudflare.D1.Connection") {} export const D1ConnectionPolicyLive = diff --git a/packages/alchemy/src/Cloudflare/Dns/DnsBinding.ts b/packages/alchemy/src/Cloudflare/Dns/DnsBinding.ts index 685f977f2..221126f7a 100644 --- a/packages/alchemy/src/Cloudflare/Dns/DnsBinding.ts +++ b/packages/alchemy/src/Cloudflare/Dns/DnsBinding.ts @@ -1,15 +1,7 @@ -import { - Credentials, - fromApiToken, -} from "@distilled.cloud/cloudflare/Credentials"; import * as Effect from "effect/Effect"; -import * as Layer from "effect/Layer"; -import * as Redacted from "effect/Redacted"; -import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient"; -import { type HttpClient } from "effect/unstable/http/HttpClient"; +import type * as Redacted from "effect/Redacted"; import type * as Binding from "../../Binding.ts"; import * as Output from "../../Output.ts"; -import { RuntimeContext } from "../../RuntimeContext.ts"; import { AccountApiToken } from "../ApiToken/AccountApiToken.ts"; import type { ApiTokenPermissionGroupRef } from "../ApiToken/Common.ts"; import type { Zone } from "../Zone/Zone.ts"; @@ -36,27 +28,6 @@ export const bindDnsToken = (token: AccountApiToken) => return { value } satisfies DnsToken; }); -/** - * Resolve credentials from a bound token and provide them (plus the - * fetch-based HTTP client) to a raw SDK operation. - */ -export const authorizeDns = - (token: DnsToken) => - ( - eff: Effect.Effect, - ): Effect.Effect => - token.value.pipe( - Effect.flatMap((value) => - eff.pipe( - Effect.provide( - fromApiToken({ apiToken: Redacted.value(value) }).pipe( - Layer.provideMerge(FetchHttpClient.layer), - ), - ), - ), - ), - ); - /** * Shared runtime body for a DNS binding: create a token scoped to the requested * zone, attach the (narrow) policy, bind the token's value and the zone's id diff --git a/packages/alchemy/src/Cloudflare/Dns/DnsRead.ts b/packages/alchemy/src/Cloudflare/Dns/DnsRead.ts index 96cc406c6..940d8f503 100644 --- a/packages/alchemy/src/Cloudflare/Dns/DnsRead.ts +++ b/packages/alchemy/src/Cloudflare/Dns/DnsRead.ts @@ -13,11 +13,12 @@ import type { RuntimeContext } from "../../RuntimeContext.ts"; import type { AccountApiToken } from "../ApiToken/AccountApiToken.ts"; import type { Zone } from "../Zone/Zone.ts"; import { - authorizeDns, type DnsToken, makeDnsClient, makeDnsPolicyLive, } from "./DnsBinding.ts"; +import { authorizeWith } from "../HttpClientUtils.ts"; +import type { Providers } from "../Providers.ts"; /** List-records request, minus the zone id (bound at `.bind(zone)` time). */ export type ListRecordsRequestInput = Omit; @@ -42,7 +43,7 @@ export const dnsReadClient = ( token: DnsToken, zoneId: Effect.Effect, ): DnsReadClient => { - const authorize = authorizeDns(token); + const authorize = authorizeWith(token); return { getDnsRecord: Effect.fn("Cloudflare.Dns.getDnsRecord")( function* (dnsRecordId) { @@ -114,7 +115,8 @@ export class DnsRead extends Binding.Service< */ export class DnsReadPolicy extends Binding.Policy< DnsReadPolicy, - (token: AccountApiToken, zone: Zone) => Effect.Effect + (token: AccountApiToken, zone: Zone) => Effect.Effect, + Providers >()("Cloudflare.DnsRead") {} /** Runtime layer for {@link DnsRead}. */ diff --git a/packages/alchemy/src/Cloudflare/Dns/DnsReadWrite.ts b/packages/alchemy/src/Cloudflare/Dns/DnsReadWrite.ts index 1ca2baaea..9dd27adb5 100644 --- a/packages/alchemy/src/Cloudflare/Dns/DnsReadWrite.ts +++ b/packages/alchemy/src/Cloudflare/Dns/DnsReadWrite.ts @@ -10,6 +10,7 @@ import { } from "./DnsBinding.ts"; import { dnsReadClient, type DnsReadClient } from "./DnsRead.ts"; import { dnsWriteClient, type DnsWriteClient } from "./DnsWrite.ts"; +import type { Providers } from "../Providers.ts"; /** Combined read + write DNS record operations. */ export interface DnsReadWriteClient extends DnsReadClient, DnsWriteClient {} @@ -84,7 +85,8 @@ export class DnsReadWrite extends Binding.Service< */ export class DnsReadWritePolicy extends Binding.Policy< DnsReadWritePolicy, - (token: AccountApiToken, zone: Zone) => Effect.Effect + (token: AccountApiToken, zone: Zone) => Effect.Effect, + Providers >()("Cloudflare.DnsReadWrite") {} /** Runtime layer for {@link DnsReadWrite}. */ diff --git a/packages/alchemy/src/Cloudflare/Dns/DnsWrite.ts b/packages/alchemy/src/Cloudflare/Dns/DnsWrite.ts index 9e523de58..fe3d482af 100644 --- a/packages/alchemy/src/Cloudflare/Dns/DnsWrite.ts +++ b/packages/alchemy/src/Cloudflare/Dns/DnsWrite.ts @@ -22,11 +22,12 @@ import type { RuntimeContext } from "../../RuntimeContext.ts"; import type { AccountApiToken } from "../ApiToken/AccountApiToken.ts"; import type { Zone } from "../Zone/Zone.ts"; import { - authorizeDns, type DnsToken, makeDnsClient, makeDnsPolicyLive, } from "./DnsBinding.ts"; +import { authorizeWith } from "../HttpClientUtils.ts"; +import type { Providers } from "../Providers.ts"; /** Create-record request, minus the zone id (bound at `.bind(zone)` time). */ export type CreateRecordRequestInput = Omit; @@ -80,7 +81,7 @@ export const dnsWriteClient = ( token: DnsToken, zoneId: Effect.Effect, ): DnsWriteClient => { - const authorize = authorizeDns(token); + const authorize = authorizeWith(token); return { createDnsRecord: Effect.fn("Cloudflare.Dns.createDnsRecord")( function* (request) { @@ -194,7 +195,8 @@ export class DnsWrite extends Binding.Service< */ export class DnsWritePolicy extends Binding.Policy< DnsWritePolicy, - (token: AccountApiToken, zone: Zone) => Effect.Effect + (token: AccountApiToken, zone: Zone) => Effect.Effect, + Providers >()("Cloudflare.DnsWrite") {} /** Runtime layer for {@link DnsWrite}. */ diff --git a/packages/alchemy/src/Cloudflare/Email/SendEmailBinding.ts b/packages/alchemy/src/Cloudflare/Email/SendEmailBinding.ts index c5a7e41b5..e2f0ab33a 100644 --- a/packages/alchemy/src/Cloudflare/Email/SendEmailBinding.ts +++ b/packages/alchemy/src/Cloudflare/Email/SendEmailBinding.ts @@ -7,6 +7,7 @@ import type { ResourceLike } from "../../Resource.ts"; import type { RuntimeContext } from "../../RuntimeContext.ts"; import { isWorker, WorkerEnvironment } from "../Workers/Worker.ts"; import type { SendEmail } from "./SendEmail.ts"; +import type { Providers } from "../Providers.ts"; /** * Email body shape for the builder-form `send` call. Either `text`, `html`, @@ -102,7 +103,8 @@ export const SendEmailBindingLive = Layer.effect( export class SendEmailBindingPolicy extends Binding.Policy< SendEmailBindingPolicy, - (sender: SendEmail) => Effect.Effect + (sender: SendEmail) => Effect.Effect, + Providers >()("Cloudflare.SendEmail.Binding") {} export const SendEmailBindingPolicyLive = SendEmailBindingPolicy.layer.succeed( diff --git a/packages/alchemy/src/Cloudflare/Flagship/FlagshipBinding.ts b/packages/alchemy/src/Cloudflare/Flagship/FlagshipBinding.ts index a9d8adec9..68145a5ef 100644 --- a/packages/alchemy/src/Cloudflare/Flagship/FlagshipBinding.ts +++ b/packages/alchemy/src/Cloudflare/Flagship/FlagshipBinding.ts @@ -9,6 +9,7 @@ import type { ResourceLike } from "../../Resource.ts"; import type { RuntimeContext } from "../../RuntimeContext.ts"; import { isWorker, WorkerEnvironment } from "../Workers/Worker.ts"; import type { FlagshipApp } from "./App.ts"; +import type { Providers } from "../Providers.ts"; export class FlagshipError extends Data.TaggedError("FlagshipError")<{ message: string; @@ -151,7 +152,8 @@ export const FlagshipBindingLive = Layer.effect( export class FlagshipBindingPolicy extends Binding.Policy< FlagshipBindingPolicy, - (app: FlagshipApp) => Effect.Effect + (app: FlagshipApp) => Effect.Effect, + Providers >()("Cloudflare.Flagship.Binding") {} export const FlagshipBindingPolicyLive = FlagshipBindingPolicy.layer.succeed( diff --git a/packages/alchemy/src/Cloudflare/HttpClientUtils.ts b/packages/alchemy/src/Cloudflare/HttpClientUtils.ts new file mode 100644 index 000000000..6541d8dc7 --- /dev/null +++ b/packages/alchemy/src/Cloudflare/HttpClientUtils.ts @@ -0,0 +1,31 @@ +import { + Credentials, + fromApiToken, +} from "@distilled.cloud/cloudflare/Credentials"; +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import * as Redacted from "effect/Redacted"; +import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient"; +import type { HttpClient } from "effect/unstable/http/HttpClient"; +import type { RuntimeContext } from "../RuntimeContext.ts"; + +/** + * Resolve credentials from a bound token's value and provide them (plus the + * fetch-based HTTP client) to a raw SDK operation. + */ +export const authorizeWith = + (token: { value: Effect.Effect> }) => + ( + eff: Effect.Effect, + ): Effect.Effect => + token.value.pipe( + Effect.flatMap((value) => + eff.pipe( + Effect.provide( + fromApiToken({ apiToken: Redacted.value(value) }).pipe( + Layer.provideMerge(FetchHttpClient.layer), + ), + ), + ), + ), + ); diff --git a/packages/alchemy/src/Cloudflare/Hyperdrive/HyperdriveBinding.ts b/packages/alchemy/src/Cloudflare/Hyperdrive/HyperdriveBinding.ts index b31de3477..41f8768d7 100644 --- a/packages/alchemy/src/Cloudflare/Hyperdrive/HyperdriveBinding.ts +++ b/packages/alchemy/src/Cloudflare/Hyperdrive/HyperdriveBinding.ts @@ -9,6 +9,7 @@ import type { RuntimeContext } from "../../RuntimeContext.ts"; import { isWorker, WorkerEnvironment } from "../Workers/Worker.ts"; import type { Hyperdrive } from "./Hyperdrive.ts"; import { defaultPort, type HyperdriveDevOrigin } from "./Hyperdrive.ts"; +import type { Providers } from "../Providers.ts"; export interface HyperdriveBindingClient { /** @@ -98,7 +99,8 @@ export const HyperdriveBindingLive = Layer.effect( export class HyperdriveBindingPolicy extends Binding.Policy< HyperdriveBindingPolicy, - (hyperdrive: Hyperdrive) => Effect.Effect + (hyperdrive: Hyperdrive) => Effect.Effect, + Providers >()("Cloudflare.Hyperdrive.Binding") {} export const HyperdriveBindingPolicyLive = HyperdriveBindingPolicy.layer.effect( diff --git a/packages/alchemy/src/Cloudflare/Images/ImagesBinding.ts b/packages/alchemy/src/Cloudflare/Images/ImagesBinding.ts index d849e6eae..de92c42af 100644 --- a/packages/alchemy/src/Cloudflare/Images/ImagesBinding.ts +++ b/packages/alchemy/src/Cloudflare/Images/ImagesBinding.ts @@ -8,6 +8,7 @@ import type { ResourceLike } from "../../Resource.ts"; import type { RuntimeContext } from "../../RuntimeContext.ts"; import { isWorker, WorkerEnvironment } from "../Workers/Worker.ts"; import { type Images as ImagesLike } from "./Images.ts"; +import type { Providers } from "../Providers.ts"; export class ImagesError extends Data.TaggedError("ImagesError")<{ message: string; @@ -120,7 +121,8 @@ export const ImagesBindingLive = Layer.effect( export class ImagesBindingPolicy extends Binding.Policy< ImagesBindingPolicy, - (images: ImagesLike) => Effect.Effect + (images: ImagesLike) => Effect.Effect, + Providers >()("Cloudflare.Images.Binding") {} export const ImagesBindingPolicyLive = ImagesBindingPolicy.layer.succeed( diff --git a/packages/alchemy/src/Cloudflare/KV/KVNamespace.ts b/packages/alchemy/src/Cloudflare/KV/Namespace.ts similarity index 95% rename from packages/alchemy/src/Cloudflare/KV/KVNamespace.ts rename to packages/alchemy/src/Cloudflare/KV/Namespace.ts index edc0d13ba..7df2d3841 100644 --- a/packages/alchemy/src/Cloudflare/KV/KVNamespace.ts +++ b/packages/alchemy/src/Cloudflare/KV/Namespace.ts @@ -8,7 +8,6 @@ import * as Provider from "../../Provider.ts"; import { Resource } from "../../Resource.ts"; import { CloudflareEnvironment } from "../CloudflareEnvironment.ts"; import type { Providers } from "../Providers.ts"; -import { KVNamespaceBinding } from "./KVNamespaceBinding.ts"; export const isKVNamespace = (value: unknown): value is KVNamespace => typeof value === "object" && @@ -54,7 +53,7 @@ export type KVNamespace = Resource< * @section Binding to a Worker * @example Using KV inside a Worker * ```typescript - * const kv = yield* Cloudflare.KVNamespace.bind(MyKV); + * const kv = yield* Cloudflare.KV.ReadWriteNamespace(MyKV); * * // Read a value * const value = yield* kv.get("my-key"); @@ -62,10 +61,14 @@ export type KVNamespace = Resource< * // Write a value * yield* kv.put("my-key", "hello world"); * ``` + * + * Provide `Cloudflare.KV.ReadWriteNamespaceBinding` (native Worker + * binding) or `Cloudflare.KV.ReadWriteNamespaceHttp` (scoped HTTP + * token) in the worker's runtime layer. Use `Cloudflare.KV.ReadNamespace` + * / `Cloudflare.KV.WriteNamespace` for least-privilege read- or + * write-only access. */ -export const KVNamespace = Resource("Cloudflare.KVNamespace")({ - bind: KVNamespaceBinding.bind, -}); +export const KVNamespace = Resource("Cloudflare.KVNamespace"); export const KVNamespaceProvider = () => Provider.succeed(KVNamespace, { diff --git a/packages/alchemy/src/Cloudflare/KV/NamespaceBinding.ts b/packages/alchemy/src/Cloudflare/KV/NamespaceBinding.ts new file mode 100644 index 000000000..4474ecbcf --- /dev/null +++ b/packages/alchemy/src/Cloudflare/KV/NamespaceBinding.ts @@ -0,0 +1,68 @@ +import type * as runtime from "@cloudflare/workers-types"; +import * as Effect from "effect/Effect"; +import { Worker, WorkerEnvironment } from "../Workers/Worker.ts"; +import type { KVNamespace } from "./Namespace.ts"; +import { KVNamespaceError } from "./NamespaceTypes.ts"; + +/** + * Shared scaffolding for the Worker-binding implementations of the KV + * services. + * + * Resolves the {@link WorkerEnvironment} and host {@link Worker}, registers + * the `kv_namespace` binding at deploy time, then delegates to `makeClient` + * with the shared {@link makeKVNamespaceHelpers} to build the + * read/write/read-write client. + */ +export const makeKVNamespaceBinding = (options: { + makeClient: (helpers: ReturnType) => Client; +}) => + Effect.gen(function* () { + const env = yield* WorkerEnvironment; + const host = yield* Worker; + + return Effect.fn(function* (namespace: KVNamespace) { + if (!globalThis.__ALCHEMY_RUNTIME__) { + yield* host.bind`${namespace}`({ + bindings: [ + { + type: "kv_namespace", + name: namespace.LogicalId, + namespaceId: namespace.namespaceId, + }, + ], + }); + } + + return options.makeClient(makeKVNamespaceHelpers(env, namespace)); + }); + }); + +/** Primitives shared by the read and write halves of the binding client. */ +export const makeKVNamespaceHelpers = ( + env: Record, + namespace: KVNamespace, +) => { + const raw = Effect.sync( + // Lazy — the WorkerEnvironment binding is not populated until runtime. + () => (env as Record)[namespace.LogicalId]!, + ); + + const tryPromise = ( + fn: () => Promise, + ): Effect.Effect => + Effect.tryPromise({ + try: fn, + catch: (error: any) => + new KVNamespaceError({ + message: error?.message ?? "Unknown error", + cause: error, + }), + }); + + const use = ( + fn: (raw: runtime.KVNamespace) => Promise, + ): Effect.Effect => + raw.pipe(Effect.flatMap((raw) => tryPromise(() => fn(raw)))); + + return { raw, use, tryPromise }; +}; diff --git a/packages/alchemy/src/Cloudflare/KV/NamespaceHttp.ts b/packages/alchemy/src/Cloudflare/KV/NamespaceHttp.ts new file mode 100644 index 000000000..b2c594234 --- /dev/null +++ b/packages/alchemy/src/Cloudflare/KV/NamespaceHttp.ts @@ -0,0 +1,89 @@ +import * as Effect from "effect/Effect"; +import type * as Redacted from "effect/Redacted"; +import { Self } from "../../Self.ts"; +import { AccountApiToken } from "../ApiToken/AccountApiToken.ts"; +import type { ApiTokenPermissionGroupRef } from "../ApiToken/Common.ts"; +import { CloudflareEnvironment } from "../CloudflareEnvironment.ts"; +import type { KVNamespace } from "./Namespace.ts"; +import { KVNamespaceError } from "./NamespaceTypes.ts"; + +export interface KVHttpToken { + value: Effect.Effect>; + accountId: Effect.Effect; +} + +export interface KVHttpScope { + accountId: string; + namespaceId: string; +} + +const KV_HTTP_PERMISSION_GROUPS: ApiTokenPermissionGroupRef[] = [ + "Workers KV Storage Read", + "Workers KV Storage Write", +]; + +type PermissionGroup = (typeof KV_HTTP_PERMISSION_GROUPS)[number]; + +/** + * Shared scaffolding for the HTTP-backed KV services. + * + * Creates a scoped {@link AccountApiToken}, binds its `value` / `accountId` + * into the host Worker at deploy time, then delegates to `makeClient` with + * the bound token and the namespace's `namespaceId`. + */ +export const makeHttpKVNamespaceBinding = (options: { + permissionGroups: PermissionGroup[]; + makeClient: ( + token: KVHttpToken, + namespaceId: Effect.Effect, + ) => Client; +}) => + Effect.gen(function* () { + const Token = yield* AccountApiToken; + const self = yield* Self; + const env = yield* CloudflareEnvironment; + + return Effect.fn(function* (namespace: KVNamespace) { + const { accountId } = yield* env; + const token = yield* Token(`${self.LogicalId}Token`); + if (!globalThis.__ALCHEMY_RUNTIME__) { + yield* token.bind`${namespace.LogicalId}`({ + policies: [ + { + effect: "allow", + permissionGroups: options.permissionGroups, + resources: { + [`com.cloudflare.api.account.${accountId}`]: "*", + }, + }, + ], + }); + } + const bound = { + value: yield* token.value, + accountId: yield* token.accountId, + } satisfies KVHttpToken; + const namespaceId = yield* namespace.namespaceId; + return options.makeClient(bound, namespaceId); + }); + }); + +/** Resolve the account and namespace id once per operation. */ +export const makeKVHttpScope = ( + token: KVHttpToken, + namespaceId: Effect.Effect, +): Effect.Effect => + Effect.gen(function* () { + const accountId = yield* token.accountId; + const id = yield* namespaceId; + return { accountId, namespaceId: id }; + }); + +export const toKVNamespaceError = (error: unknown): KVNamespaceError => + new KVNamespaceError({ + message: + typeof error === "object" && error !== null && "message" in error + ? String((error as { message: unknown }).message) + : "Unknown KV error", + cause: error instanceof Error ? error : new Error(String(error)), + }); diff --git a/packages/alchemy/src/Cloudflare/KV/KVNamespaceBinding.ts b/packages/alchemy/src/Cloudflare/KV/NamespaceRead.ts similarity index 62% rename from packages/alchemy/src/Cloudflare/KV/KVNamespaceBinding.ts rename to packages/alchemy/src/Cloudflare/KV/NamespaceRead.ts index 48776b6a9..f1be232f0 100644 --- a/packages/alchemy/src/Cloudflare/KV/KVNamespaceBinding.ts +++ b/packages/alchemy/src/Cloudflare/KV/NamespaceRead.ts @@ -1,20 +1,24 @@ import type * as runtime from "@cloudflare/workers-types"; -import * as Data from "effect/Data"; import * as Effect from "effect/Effect"; -import * as Layer from "effect/Layer"; import * as Binding from "../../Binding.ts"; -import type { ResourceLike } from "../../Resource.ts"; import type { RuntimeContext } from "../../RuntimeContext.ts"; -import { isWorker, WorkerEnvironment } from "../Workers/Worker.ts"; -import type { KVNamespace } from "./KVNamespace.ts"; +import type { KVNamespace } from "./Namespace.ts"; +import type { KVNamespaceError } from "./NamespaceTypes.ts"; -export class KVNamespaceError extends Data.TaggedError("KVNamespaceError")<{ - message: string; - cause: Error; -}> {} +/** + * @binding + * @product KV + * @category Storage & Databases + */ +export class KVNamespaceRead extends Binding.Service< + KVNamespaceRead, + (namespace: KVNamespace) => Effect.Effect +>()("Cloudflare.KVNamespace.Read") {} -export interface KVNamespaceClient { - raw: Effect.Effect; +export const ReadNamespace = KVNamespaceRead.bind; + +export interface ReadKVNamespaceClient { + raw: Effect.Effect; get( key: Key, options?: Partial>, @@ -98,11 +102,6 @@ export interface KVNamespaceClient { KVNamespaceError, RuntimeContext >; - put( - key: Key, - value: string | ArrayBuffer | ArrayBufferView | ReadableStream, - options?: KVNamespacePutOptions, - ): Effect.Effect; getWithMetadata( key: Key, options?: Partial>, @@ -215,92 +214,4 @@ export interface KVNamespaceClient { KVNamespaceError, RuntimeContext >; - delete(key: Key): Effect.Effect; } - -/** - * @binding - * @product KV - * @category Storage & Databases - */ -export class KVNamespaceBinding extends Binding.Service< - KVNamespaceBinding, - (bucket: KVNamespace) => Effect.Effect ->()("Cloudflare.KVNamespace") {} - -export const KVNamespaceBindingLive = Layer.effect( - KVNamespaceBinding, - Effect.gen(function* () { - const bind = yield* KVNamespaceBindingPolicy; - const env = yield* WorkerEnvironment; - - return Effect.fn(function* (bucket: KVNamespace) { - yield* bind(bucket); - const raw = Effect.sync( - // this must be lazy because the WorkerEnvironment is not available yet - () => (env as Record)[bucket.LogicalId], - ); - const tryPromise = ( - fn: () => Promise, - ): Effect.Effect => - Effect.tryPromise({ - try: fn, - catch: (error: any) => - new KVNamespaceError({ - message: error.message ?? "Unknown error", - cause: error, - }), - }); - - const use = ( - fn: (raw: runtime.KVNamespace) => Promise, - ): Effect.Effect => - raw.pipe(Effect.flatMap((raw) => tryPromise(() => fn(raw)))); - - // @ts-expect-error - return { - raw: raw, - // @ts-expect-error - get: (...args: Parameters) => - use((raw) => raw.get(...args)), - // @ts-expect-error - getWithMetadata: ( - ...args: Parameters - ) => use((raw) => raw.getWithMetadata(...args)), - // @ts-expect-error - put: (...args: Parameters) => - use((raw) => raw.put(...args)), - list: (...args: Parameters) => - use((raw) => raw.list(...args)), - delete: (...args: Parameters) => - use((raw) => raw.delete(...args)), - } satisfies KVNamespaceClient as KVNamespaceClient; - }); - }), -); - -export class KVNamespaceBindingPolicy extends Binding.Policy< - KVNamespaceBindingPolicy, - (bucket: KVNamespace) => Effect.Effect ->()("Cloudflare.KVNamespace") {} - -export const KVNamespaceBindingPolicyLive = - KVNamespaceBindingPolicy.layer.succeed( - Effect.fn(function* (host: ResourceLike, namespace: KVNamespace) { - if (isWorker(host)) { - yield* host.bind`${namespace}`({ - bindings: [ - { - type: "kv_namespace", - name: namespace.LogicalId, - namespaceId: namespace.namespaceId, - }, - ], - }); - } else { - return yield* Effect.die( - new Error(`BucketBinding does not support runtime '${host.Type}'`), - ); - } - }), - ); diff --git a/packages/alchemy/src/Cloudflare/KV/NamespaceReadBinding.ts b/packages/alchemy/src/Cloudflare/KV/NamespaceReadBinding.ts new file mode 100644 index 000000000..b31133dad --- /dev/null +++ b/packages/alchemy/src/Cloudflare/KV/NamespaceReadBinding.ts @@ -0,0 +1,39 @@ +import type * as runtime from "@cloudflare/workers-types"; +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import { + makeKVNamespaceBinding, + makeKVNamespaceHelpers, +} from "./NamespaceBinding.ts"; +import { + KVNamespaceRead, + type ReadKVNamespaceClient, +} from "./NamespaceRead.ts"; + +/** + * Implementation of the {@link KVNamespaceRead} service that uses a Worker + * binding. + */ +export const ReadNamespaceBinding = Layer.effect( + KVNamespaceRead, + Effect.suspend(() => + makeKVNamespaceBinding({ makeClient: makeReadKVClient }), + ), +); + +/** Build the read half of the binding client. */ +export const makeReadKVClient = ({ + raw, + use, +}: ReturnType): ReadKVNamespaceClient => { + return { + raw, + get: ((...args: Parameters) => + use((raw) => raw.get(...(args as [any, any])))) as any, + getWithMetadata: (( + ...args: Parameters + ) => use((raw) => raw.getWithMetadata(...(args as [any, any])))) as any, + list: ((...args: Parameters) => + use((raw) => raw.list(...args))) as any, + }; +}; diff --git a/packages/alchemy/src/Cloudflare/KV/NamespaceReadHttp.ts b/packages/alchemy/src/Cloudflare/KV/NamespaceReadHttp.ts new file mode 100644 index 000000000..e7b352b6e --- /dev/null +++ b/packages/alchemy/src/Cloudflare/KV/NamespaceReadHttp.ts @@ -0,0 +1,171 @@ +import * as kv from "@distilled.cloud/cloudflare/kv"; +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import { authorizeWith } from "../HttpClientUtils.ts"; +import { + makeHttpKVNamespaceBinding, + makeKVHttpScope, + toKVNamespaceError, + type KVHttpToken, +} from "./NamespaceHttp.ts"; +import { + KVNamespaceRead, + type ReadKVNamespaceClient, +} from "./NamespaceRead.ts"; + +/** + * HTTP-backed implementation of the {@link KVNamespaceRead} service. + * + * It creates a scoped {@link AccountApiToken} with the `Workers KV Storage + * Read` permission and reads values via the Cloudflare KV HTTP API. + */ +export const ReadNamespaceHttp = Layer.effect( + KVNamespaceRead, + Effect.suspend(() => + makeHttpKVNamespaceBinding({ + permissionGroups: ["Workers KV Storage Read"], + makeClient: makeReadKVHttpClient, + }), + ), +); + +export const makeReadKVHttpClient = ( + token: KVHttpToken, + namespaceId: Effect.Effect, +): ReadKVNamespaceClient => { + const authorize = authorizeWith(token); + const scope = makeKVHttpScope(token, namespaceId); + + const getOne = (key: string, type: string) => + scope.pipe( + Effect.flatMap(({ accountId, namespaceId }) => + authorize( + kv.getNamespaceValue({ accountId, namespaceId, keyName: key }), + ).pipe( + Effect.map((value) => decodeValue(value, type)), + Effect.catchTag("KeyNotFound", () => Effect.succeed(null)), + ), + ), + Effect.mapError(toKVNamespaceError), + ); + + const getMany = (keys: string[], type: string) => + scope.pipe( + Effect.flatMap(({ accountId, namespaceId }) => + authorize( + kv.bulkGetNamespaceKeys({ + accountId, + namespaceId, + keys, + type: type === "json" ? "json" : "text", + }), + ), + ), + Effect.mapError(toKVNamespaceError), + Effect.map((res) => { + const values = (res.values ?? {}) as Record; + const map = new Map(); + for (const key of keys) { + map.set(key, key in values ? decodeValue(values[key], type) : null); + } + return map; + }), + ); + + const getWithMetadataOne = (key: string, type: string) => + scope.pipe( + Effect.flatMap(({ accountId, namespaceId }) => + Effect.all({ + value: authorize( + kv.getNamespaceValue({ accountId, namespaceId, keyName: key }), + ).pipe( + Effect.map((value) => decodeValue(value, type)), + Effect.catchTag("KeyNotFound", () => Effect.succeed(null)), + ), + metadata: authorize( + kv.getNamespaceMetadata({ accountId, namespaceId, keyName: key }), + ).pipe( + Effect.catchTag(["KeyNotFound", "NamespaceNotFound"], () => + Effect.succeed(null), + ), + ), + }), + ), + Effect.mapError(toKVNamespaceError), + Effect.map(({ value, metadata }) => ({ + value, + metadata: metadata ?? null, + cacheStatus: null, + })), + ); + + return { + raw: Effect.die( + new (class extends Error {})( + "KV HTTP client does not expose a native KVNamespace binding; use get/list/getWithMetadata.", + ), + ) as any, + get: ((key: string | string[], typeOrOptions?: unknown) => + Array.isArray(key) + ? getMany(key, readType(typeOrOptions)) + : getOne(key, readType(typeOrOptions))) as any, + getWithMetadata: ((key: string | string[], typeOrOptions?: unknown) => { + const type = readType(typeOrOptions); + if (Array.isArray(key)) { + return getMany(key, type).pipe( + Effect.map((values) => { + const map = new Map(); + for (const [k, value] of values) { + map.set(k, { value, metadata: null, cacheStatus: null }); + } + return map; + }), + ); + } + return getWithMetadataOne(key, type); + }) as any, + list: ((options?: KVNamespaceListOptions) => + scope.pipe( + Effect.flatMap(({ accountId, namespaceId }) => + authorize( + kv.listNamespaceKeys({ + accountId, + namespaceId, + prefix: options?.prefix ?? undefined, + limit: options?.limit ?? undefined, + cursor: options?.cursor ?? undefined, + }), + ), + ), + Effect.mapError(toKVNamespaceError), + Effect.map((res) => { + const keys = res.result.map((k) => ({ + name: k.name, + expiration: k.expiration ?? undefined, + metadata: k.metadata ?? undefined, + })); + const cursor = res.resultInfo?.cursor ?? undefined; + return ( + cursor + ? { keys, list_complete: false, cursor, cacheStatus: null } + : { keys, list_complete: true, cacheStatus: null } + ) as unknown; + }), + )) as any, + }; +}; + +/** Resolve the requested decode type from the overloaded second argument. */ +const readType = (typeOrOptions: unknown): string => + typeof typeOrOptions === "string" + ? typeOrOptions + : ((typeOrOptions as { type?: string } | undefined)?.type ?? "text"); + +/** Decode a raw KV value according to the requested type. */ +const decodeValue = (value: unknown, type: string): unknown => { + if (value === null || value === undefined) return null; + if (type === "json") { + return typeof value === "string" ? JSON.parse(value) : value; + } + return typeof value === "string" ? value : String(value); +}; diff --git a/packages/alchemy/src/Cloudflare/KV/NamespaceReadWrite.ts b/packages/alchemy/src/Cloudflare/KV/NamespaceReadWrite.ts new file mode 100644 index 000000000..265032e54 --- /dev/null +++ b/packages/alchemy/src/Cloudflare/KV/NamespaceReadWrite.ts @@ -0,0 +1,20 @@ +import * as Effect from "effect/Effect"; +import * as Binding from "../../Binding.ts"; +import type { KVNamespace } from "./Namespace.ts"; +import type { ReadKVNamespaceClient } from "./NamespaceRead.ts"; +import type { WriteKVNamespaceClient } from "./NamespaceWrite.ts"; + +/** + * @binding + * @product KV + * @category Storage & Databases + */ +export class KVNamespaceReadWrite extends Binding.Service< + KVNamespaceReadWrite, + (namespace: KVNamespace) => Effect.Effect +>()("Cloudflare.KVNamespace.ReadWrite") {} + +export const ReadWriteNamespace = KVNamespaceReadWrite.bind; + +export interface ReadWriteKVNamespaceClient + extends ReadKVNamespaceClient, WriteKVNamespaceClient {} diff --git a/packages/alchemy/src/Cloudflare/KV/NamespaceReadWriteBinding.ts b/packages/alchemy/src/Cloudflare/KV/NamespaceReadWriteBinding.ts new file mode 100644 index 000000000..9e31b8474 --- /dev/null +++ b/packages/alchemy/src/Cloudflare/KV/NamespaceReadWriteBinding.ts @@ -0,0 +1,32 @@ +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import { + makeKVNamespaceBinding, + type makeKVNamespaceHelpers, +} from "./NamespaceBinding.ts"; +import { makeReadKVClient } from "./NamespaceReadBinding.ts"; +import { + KVNamespaceReadWrite, + type ReadWriteKVNamespaceClient, +} from "./NamespaceReadWrite.ts"; +import { makeWriteKVClient } from "./NamespaceWriteBinding.ts"; + +/** + * Implementation of the {@link KVNamespaceReadWrite} service that uses a + * Worker binding. + */ +export const ReadWriteNamespaceBinding = Layer.effect( + KVNamespaceReadWrite, + Effect.suspend(() => + makeKVNamespaceBinding({ makeClient: makeReadWriteKVClient }), + ), +); + +/** Build the read-write binding client from its read and write halves. */ +export const makeReadWriteKVClient = ( + helpers: ReturnType, +): ReadWriteKVNamespaceClient => + ({ + ...makeReadKVClient(helpers), + ...makeWriteKVClient(helpers), + }) as ReadWriteKVNamespaceClient; diff --git a/packages/alchemy/src/Cloudflare/KV/NamespaceReadWriteHttp.ts b/packages/alchemy/src/Cloudflare/KV/NamespaceReadWriteHttp.ts new file mode 100644 index 000000000..ce40c281e --- /dev/null +++ b/packages/alchemy/src/Cloudflare/KV/NamespaceReadWriteHttp.ts @@ -0,0 +1,38 @@ +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import { + makeHttpKVNamespaceBinding, + type KVHttpToken, +} from "./NamespaceHttp.ts"; +import { makeReadKVHttpClient } from "./NamespaceReadHttp.ts"; +import { + KVNamespaceReadWrite, + type ReadWriteKVNamespaceClient, +} from "./NamespaceReadWrite.ts"; +import { makeWriteKVHttpClient } from "./NamespaceWriteHttp.ts"; + +/** + * HTTP-backed implementation of the {@link KVNamespaceReadWrite} service. + * + * It creates a scoped {@link AccountApiToken} with the `Workers KV Storage + * Read` and `Workers KV Storage Write` permissions. + */ +export const ReadWriteNamespaceHttp = Layer.effect( + KVNamespaceReadWrite, + Effect.suspend(() => + makeHttpKVNamespaceBinding({ + permissionGroups: ["Workers KV Storage Read", "Workers KV Storage Write"], + makeClient: makeReadWriteKVHttpClient, + }), + ), +); + +/** Build the HTTP-backed read-write client over a bound token + namespace. */ +export const makeReadWriteKVHttpClient = ( + token: KVHttpToken, + namespaceId: Effect.Effect, +): ReadWriteKVNamespaceClient => + ({ + ...makeReadKVHttpClient(token, namespaceId), + ...makeWriteKVHttpClient(token, namespaceId), + }) as ReadWriteKVNamespaceClient; diff --git a/packages/alchemy/src/Cloudflare/KV/NamespaceTypes.ts b/packages/alchemy/src/Cloudflare/KV/NamespaceTypes.ts new file mode 100644 index 000000000..faae6358c --- /dev/null +++ b/packages/alchemy/src/Cloudflare/KV/NamespaceTypes.ts @@ -0,0 +1,6 @@ +import * as Data from "effect/Data"; + +export class KVNamespaceError extends Data.TaggedError("KVNamespaceError")<{ + message: string; + cause: Error; +}> {} diff --git a/packages/alchemy/src/Cloudflare/KV/NamespaceWrite.ts b/packages/alchemy/src/Cloudflare/KV/NamespaceWrite.ts new file mode 100644 index 000000000..01017ee70 --- /dev/null +++ b/packages/alchemy/src/Cloudflare/KV/NamespaceWrite.ts @@ -0,0 +1,26 @@ +import * as Effect from "effect/Effect"; +import * as Binding from "../../Binding.ts"; +import type { RuntimeContext } from "../../RuntimeContext.ts"; +import type { KVNamespace } from "./Namespace.ts"; +import type { KVNamespaceError } from "./NamespaceTypes.ts"; + +/** + * @binding + * @product KV + * @category Storage & Databases + */ +export class KVNamespaceWrite extends Binding.Service< + KVNamespaceWrite, + (namespace: KVNamespace) => Effect.Effect +>()("Cloudflare.KVNamespace.Write") {} + +export const WriteNamespace = KVNamespaceWrite.bind; + +export interface WriteKVNamespaceClient { + put( + key: Key, + value: string | ArrayBuffer | ArrayBufferView | ReadableStream, + options?: KVNamespacePutOptions, + ): Effect.Effect; + delete(key: Key): Effect.Effect; +} diff --git a/packages/alchemy/src/Cloudflare/KV/NamespaceWriteBinding.ts b/packages/alchemy/src/Cloudflare/KV/NamespaceWriteBinding.ts new file mode 100644 index 000000000..1b23249ff --- /dev/null +++ b/packages/alchemy/src/Cloudflare/KV/NamespaceWriteBinding.ts @@ -0,0 +1,34 @@ +import type * as runtime from "@cloudflare/workers-types"; +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import { + makeKVNamespaceBinding, + makeKVNamespaceHelpers, +} from "./NamespaceBinding.ts"; +import { + KVNamespaceWrite, + type WriteKVNamespaceClient, +} from "./NamespaceWrite.ts"; + +/** + * Implementation of the {@link KVNamespaceWrite} service that uses a Worker + * binding. + */ +export const WriteNamespaceBinding = Layer.effect( + KVNamespaceWrite, + Effect.suspend(() => + makeKVNamespaceBinding({ makeClient: makeWriteKVClient }), + ), +); + +/** Build the write half of the binding client. */ +export const makeWriteKVClient = ({ + use, +}: ReturnType): WriteKVNamespaceClient => { + return { + put: ((...args: Parameters) => + use((raw) => raw.put(...args))) as any, + delete: ((...args: Parameters) => + use((raw) => raw.delete(...args))) as any, + }; +}; diff --git a/packages/alchemy/src/Cloudflare/KV/NamespaceWriteHttp.ts b/packages/alchemy/src/Cloudflare/KV/NamespaceWriteHttp.ts new file mode 100644 index 000000000..be4ba7735 --- /dev/null +++ b/packages/alchemy/src/Cloudflare/KV/NamespaceWriteHttp.ts @@ -0,0 +1,105 @@ +import * as kv from "@distilled.cloud/cloudflare/kv"; +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import { authorizeWith } from "../HttpClientUtils.ts"; +import { + makeHttpKVNamespaceBinding, + makeKVHttpScope, + toKVNamespaceError, + type KVHttpToken, +} from "./NamespaceHttp.ts"; +import { KVNamespaceError } from "./NamespaceTypes.ts"; +import { + KVNamespaceWrite, + type WriteKVNamespaceClient, +} from "./NamespaceWrite.ts"; + +/** + * HTTP-backed implementation of the {@link KVNamespaceWrite} service. + * + * It creates a scoped {@link AccountApiToken} with the `Workers KV Storage + * Write` permission and writes values via the Cloudflare KV HTTP API. + */ +export const WriteNamespaceHttp = Layer.effect( + KVNamespaceWrite, + Effect.suspend(() => + makeHttpKVNamespaceBinding({ + permissionGroups: ["Workers KV Storage Write"], + makeClient: makeWriteKVHttpClient, + }), + ), +); + +export const makeWriteKVHttpClient = ( + token: KVHttpToken, + namespaceId: Effect.Effect, +): WriteKVNamespaceClient => { + const authorize = authorizeWith(token); + const scope = makeKVHttpScope(token, namespaceId); + + return { + put: (( + key: string, + value: string | ArrayBuffer | ArrayBufferView | ReadableStream, + options?: KVNamespacePutOptions, + ) => + scope.pipe( + Effect.flatMap(({ accountId, namespaceId }) => + toKVBody(value).pipe( + Effect.flatMap((body) => + authorize( + kv.putNamespaceValue({ + accountId, + namespaceId, + keyName: key, + value: body, + expiration: options?.expiration, + expirationTtl: options?.expirationTtl, + metadata: options?.metadata, + }), + ), + ), + ), + ), + Effect.mapError(toKVNamespaceError), + Effect.asVoid, + )) as any, + delete: ((key: string) => + scope.pipe( + Effect.flatMap(({ accountId, namespaceId }) => + authorize( + kv.deleteNamespaceValue({ accountId, namespaceId, keyName: key }), + ), + ), + Effect.mapError(toKVNamespaceError), + Effect.asVoid, + )) as any, + }; +}; + +/** Collect a put value into a body accepted by the KV HTTP API. */ +const toKVBody = ( + value: string | ArrayBuffer | ArrayBufferView | ReadableStream, +): Effect.Effect => + Effect.gen(function* () { + if (typeof value === "string") return value; + if (value instanceof Blob) return value; + if (value instanceof ArrayBuffer) return new Blob([value]); + if (value instanceof ReadableStream) { + const buffer = yield* Effect.tryPromise({ + try: () => new Response(value as any).arrayBuffer(), + catch: toKVNamespaceError, + }); + return new Blob([buffer]); + } + const view = value as ArrayBufferView; + const bytes = new Uint8Array(view.byteLength); + bytes.set( + new Uint8Array( + view.buffer as ArrayBuffer, + view.byteOffset, + view.byteLength, + ), + ); + return new Blob([bytes]); + }); diff --git a/packages/alchemy/src/Cloudflare/KV/index.ts b/packages/alchemy/src/Cloudflare/KV/index.ts index 08f1364f0..37954d8af 100644 --- a/packages/alchemy/src/Cloudflare/KV/index.ts +++ b/packages/alchemy/src/Cloudflare/KV/index.ts @@ -1,2 +1,11 @@ -export * from "./KVNamespace.ts"; -export * from "./KVNamespaceBinding.ts"; +export * from "./Namespace.ts"; +export * from "./NamespaceRead.ts"; +export * from "./NamespaceReadBinding.ts"; +export * from "./NamespaceReadHttp.ts"; +export * from "./NamespaceReadWrite.ts"; +export * from "./NamespaceReadWriteBinding.ts"; +export * from "./NamespaceReadWriteHttp.ts"; +export * from "./NamespaceTypes.ts"; +export * from "./NamespaceWrite.ts"; +export * from "./NamespaceWriteBinding.ts"; +export * from "./NamespaceWriteHttp.ts"; diff --git a/packages/alchemy/src/Cloudflare/Providers.ts b/packages/alchemy/src/Cloudflare/Providers.ts index 5b5cb337a..e1808a908 100644 --- a/packages/alchemy/src/Cloudflare/Providers.ts +++ b/packages/alchemy/src/Cloudflare/Providers.ts @@ -218,7 +218,7 @@ export const providers = () => CloudforceOne.CloudforceOneScanConfig, Command, Connectivity.DirectoryService, - Containers.Container, + Containers.ContainerPlatform, ContentScanning.ContentScanning, ContentScanning.ContentScanningExpression, CustomCertificates.CustomCertificate, @@ -298,7 +298,6 @@ export const providers = () => KeylessCertificate.KeylessCertificate, KeyPair, KV.KVNamespace, - KV.KVNamespaceBindingPolicy, LeakedCredentialCheck.LeakedCredentialCheck, LeakedCredentialCheck.LeakedCredentialDetection, LoadBalancer.LoadBalancer, @@ -342,12 +341,10 @@ export const providers = () => Pipelines.PipelineSink, Pipelines.PipelineStream, Queue.Queue, - Queue.QueueBindingPolicy, Queue.QueueConsumer, Queue.QueueEventSourcePolicy, Queue.QueueSubscription, R2.R2Bucket, - R2.R2BucketBindingPolicy, R2.R2BucketEventNotification, R2.R2BucketSippy, R2DataCatalog.R2DataCatalog, @@ -462,13 +459,10 @@ export const providers = () => Hyperdrive.HyperdriveBindingPolicyLive, Hyperdrive.HyperdriveProvider(), Images.ImagesBindingPolicyLive, - KV.KVNamespaceBindingPolicyLive, KV.KVNamespaceProvider(), - Queue.QueueBindingPolicyLive, Queue.QueueConsumerProvider(), Queue.QueueEventSourcePolicyLive, Queue.QueueProvider(), - R2.R2BucketBindingPolicyLive, R2.R2BucketProvider(), RateLimit.RateLimitBindingPolicyLive, Ruleset.RulesetProvider(), @@ -635,7 +629,6 @@ export const providers = () => ), Layer.mergeAll( KeylessCertificate.KeylessCertificateProvider(), - KV.KVNamespaceBindingPolicyLive, KV.KVNamespaceProvider(), LeakedCredentialCheck.LeakedCredentialCheckProvider(), LeakedCredentialCheck.LeakedCredentialDetectionProvider(), @@ -675,12 +668,10 @@ export const providers = () => Pipelines.PipelineProvider(), Pipelines.PipelineSinkProvider(), Pipelines.PipelineStreamProvider(), - Queue.QueueBindingPolicyLive, Queue.QueueConsumerProvider(), Queue.QueueEventSourcePolicyLive, Queue.QueueProvider(), Queue.QueueSubscriptionProvider(), - R2.R2BucketBindingPolicyLive, R2.R2BucketEventNotificationProvider(), R2.R2BucketProvider(), R2.R2BucketSippyProvider(), diff --git a/packages/alchemy/src/Cloudflare/Queue/Queue.ts b/packages/alchemy/src/Cloudflare/Queue/Queue.ts index e22dec27e..36768ae79 100644 --- a/packages/alchemy/src/Cloudflare/Queue/Queue.ts +++ b/packages/alchemy/src/Cloudflare/Queue/Queue.ts @@ -18,7 +18,7 @@ import { LocalRuntimeState, } from "../LocalRuntime.ts"; import type { Providers } from "../Providers.ts"; -import { QueueBinding } from "./QueueBinding.ts"; +import { QueueWrite } from "./QueueWrite.ts"; export const isQueue = (value: unknown): value is Queue => typeof value === "object" && (value as any)?.Type === "Cloudflare.Queue"; @@ -66,10 +66,10 @@ export type Queue = Resource< * ``` * * @section Binding to a Worker - * In an Effect-style Worker, use `Cloudflare.QueueBinding.bind` in - * the init phase and provide `Cloudflare.QueueBindingLive` in the - * runtime layer. The returned `QueueSender` exposes `send` and - * `sendBatch`. + * In an Effect-style Worker, use `Cloudflare.Queues.WriteQueue` in + * the init phase and provide `Cloudflare.Queues.WriteQueueBinding` in + * the runtime layer. The returned `WriteQueueClient` exposes `send` + * and `sendBatch`. * * @example Sending messages from a Worker * ```typescript @@ -84,7 +84,7 @@ export type Queue = Resource< * "Worker", * { main: import.meta.filename }, * Effect.gen(function* () { - * const queue = yield* Cloudflare.QueueBinding.bind(Queue); + * const queue = yield* Cloudflare.Queues.WriteQueue(Queue); * * return { * fetch: Effect.gen(function* () { @@ -100,12 +100,12 @@ export type Queue = Resource< * return HttpServerResponse.text("Not Found", { status: 404 }); * }), * }; - * }).pipe(Effect.provide(Cloudflare.QueueBindingLive)), + * }).pipe(Effect.provide(Cloudflare.Queues.WriteQueueBinding)), * ); * ``` */ export const Queue = Resource("Cloudflare.Queue")({ - bind: QueueBinding.bind, + bind: QueueWrite.bind, }); export const QueueProviderLive = () => diff --git a/packages/alchemy/src/Cloudflare/Queue/QueueBinding.ts b/packages/alchemy/src/Cloudflare/Queue/QueueBinding.ts index 3183d717f..42c4a2da5 100644 --- a/packages/alchemy/src/Cloudflare/Queue/QueueBinding.ts +++ b/packages/alchemy/src/Cloudflare/Queue/QueueBinding.ts @@ -1,146 +1,63 @@ +import type * as runtime from "@cloudflare/workers-types"; import * as Effect from "effect/Effect"; -import * as Layer from "effect/Layer"; -import * as Binding from "../../Binding.ts"; -import type { ResourceLike } from "../../Resource.ts"; -import type { RuntimeContext } from "../../RuntimeContext.ts"; -import { isWorker, WorkerEnvironment } from "../Workers/Worker.ts"; +import { Worker, WorkerEnvironment } from "../Workers/Worker.ts"; import type { Queue } from "./Queue.ts"; - -export interface QueueSender { - raw: Effect.Effect; - send( - body: unknown, - options?: { contentType?: "json" | "text" }, - ): Effect.Effect; - sendBatch( - messages: ReadonlyArray<{ - body: unknown; - contentType?: "json" | "text"; - }>, - ): Effect.Effect; -} - -import * as Data from "effect/Data"; - -export class QueueSendError extends Data.TaggedError("QueueSendError")<{ - message: string; - cause?: unknown; -}> {} +import { QueueSendError } from "./QueueTypes.ts"; /** - * Binding service that turns a {@link Queue} resource into a typed - * {@link QueueSender} you can call from a Worker's runtime Effect. - * @binding - * @product Queues - * @category Storage & Databases - * @section Sending Messages - * Bind the queue in the Worker's init phase, then use `send` for - * single messages or `sendBatch` for many messages in one call. - * Messages can be any JSON-serializable value. - * - * @example Producer route - * ```typescript - * const queue = yield* Cloudflare.QueueBinding.bind(Queue); - * - * return { - * fetch: Effect.gen(function* () { - * yield* queue.send({ text: "hi", sentAt: Date.now() }); - * return HttpServerResponse.empty({ status: 202 }); - * }), - * }; - * ``` + * Shared scaffolding for the Worker-binding implementations of the Queue + * services. * - * @example Sending a batch - * ```typescript - * yield* queue.sendBatch([ - * { body: { event: "click", id: 1 } }, - * { body: { event: "click", id: 2 } }, - * { body: "raw text", contentType: "text" }, - * ]); - * ``` - * - * Provide {@link QueueBindingLive} in the worker's runtime layer to - * resolve the underlying Cloudflare queue at request time. + * Resolves the {@link WorkerEnvironment} and host {@link Worker}, registers + * the `queue` binding at deploy time, then delegates to `makeClient` with the + * shared {@link makeQueueHelpers} to build the producer client. */ -export class QueueBinding extends Binding.Service< - QueueBinding, - (queue: Queue) => Effect.Effect ->()("Cloudflare.Queue") {} - -export const QueueBindingLive = Layer.effect( - QueueBinding, +export const makeQueueBinding = (options: { + makeClient: (helpers: ReturnType) => Client; +}) => Effect.gen(function* () { - const bind = yield* QueueBindingPolicy; const env = yield* WorkerEnvironment; + const host = yield* Worker; return Effect.fn(function* (queue: Queue) { - yield* bind(queue); - const raw = Effect.sync( - () => (env as Record)[queue.LogicalId], - ); - - const tryPromise = ( - fn: () => Promise, - ): Effect.Effect => - Effect.tryPromise({ - try: fn, - catch: (error: any) => - new QueueSendError({ - message: error?.message ?? "Unknown queue error", - cause: error, - }), + if (!globalThis.__ALCHEMY_RUNTIME__) { + yield* host.bind`${queue}`({ + bindings: [ + { + type: "queue", + name: queue.LogicalId, + queueName: queue.queueName, + }, + ], }); + } - return { - raw, - send: (body: unknown, options?: { contentType?: "json" | "text" }) => - raw.pipe( - Effect.flatMap((q) => tryPromise(() => q.send(body, options))), - ), - sendBatch: ( - messages: ReadonlyArray<{ - body: unknown; - contentType?: "json" | "text"; - }>, - ) => - raw.pipe( - Effect.flatMap((q) => - tryPromise(() => - q.sendBatch( - messages.map((m) => ({ - body: m.body, - ...(m.contentType ? { contentType: m.contentType } : {}), - })), - ), - ), - ), - ), - } satisfies QueueSender; + return options.makeClient(makeQueueHelpers(env, queue)); + }); + }); + +/** Primitives shared by the Worker-binding producer clients. */ +export const makeQueueHelpers = (env: Record, queue: Queue) => { + const raw = Effect.sync( + () => (env as Record>)[queue.LogicalId]!, + ); + + const tryPromise = ( + fn: () => Promise, + ): Effect.Effect => + Effect.tryPromise({ + try: fn, + catch: (error: any) => + new QueueSendError({ + message: error?.message ?? "Unknown queue error", + cause: error, + }), }); - }), -); -export class QueueBindingPolicy extends Binding.Policy< - QueueBindingPolicy, - (queue: Queue) => Effect.Effect ->()("Cloudflare.Queue") {} + const use = ( + fn: (raw: runtime.Queue) => Promise, + ): Effect.Effect => + raw.pipe(Effect.flatMap((raw) => tryPromise(() => fn(raw)))); -export const QueueBindingPolicyLive = QueueBindingPolicy.layer.succeed( - Effect.fnUntraced(function* (host: ResourceLike, queue: Queue) { - if (isWorker(host)) { - yield* host.bind`${queue}`({ - bindings: [ - { - type: "queue", - name: queue.LogicalId, - queueName: queue.queueName, - }, - ], - }); - } else { - return yield* Effect.die( - new Error(`QueueBinding does not support runtime '${host.Type}'`), - ); - } - }), -); + return { raw, use, tryPromise }; +}; diff --git a/packages/alchemy/src/Cloudflare/Queue/QueueEventSource.ts b/packages/alchemy/src/Cloudflare/Queue/QueueEventSource.ts index e473024e5..958222b45 100644 --- a/packages/alchemy/src/Cloudflare/Queue/QueueEventSource.ts +++ b/packages/alchemy/src/Cloudflare/Queue/QueueEventSource.ts @@ -13,6 +13,7 @@ import * as DurationUtil from "../../Util/Duration.ts"; import { isWorker, isWorkerEvent } from "../Workers/Worker.ts"; import type { Queue } from "./Queue.ts"; import { QueueConsumer } from "./QueueConsumer.ts"; +import type { Providers } from "../Providers.ts"; /** * Subscriber settings — the same shape Cloudflare's `QueueConsumer` @@ -150,7 +151,8 @@ export class QueueEventSource extends Context.Service< */ export class QueueEventSourcePolicy extends Binding.Policy< QueueEventSourcePolicy, - (queue: Queue, props: MessagesProps) => Effect.Effect + (queue: Queue, props: MessagesProps) => Effect.Effect, + Providers >()("Cloudflare.Queue.QueueEventSource") {} export const QueueEventSourcePolicyLive = QueueEventSourcePolicy.layer.succeed( @@ -197,7 +199,7 @@ export const QueueEventSourcePolicyLive = QueueEventSourcePolicy.layer.succeed( * `Cloudflare.QueueConsumer` resource. * * Provide alongside other Cloudflare runtime layers (e.g. - * `QueueBindingLive`) on the Worker effect. + * `WriteQueueBinding`) on the Worker effect. */ export const QueueEventSourceLive = Layer.effect( QueueEventSource, diff --git a/packages/alchemy/src/Cloudflare/Queue/QueueHttp.ts b/packages/alchemy/src/Cloudflare/Queue/QueueHttp.ts new file mode 100644 index 000000000..728b8a6b7 --- /dev/null +++ b/packages/alchemy/src/Cloudflare/Queue/QueueHttp.ts @@ -0,0 +1,86 @@ +import * as Effect from "effect/Effect"; +import type * as Redacted from "effect/Redacted"; +import { Self } from "../../Self.ts"; +import { AccountApiToken } from "../ApiToken/AccountApiToken.ts"; +import type { ApiTokenPermissionGroupRef } from "../ApiToken/Common.ts"; +import { CloudflareEnvironment } from "../CloudflareEnvironment.ts"; +import type { Queue } from "./Queue.ts"; +import { QueueSendError } from "./QueueTypes.ts"; + +export interface QueueHttpToken { + value: Effect.Effect>; + accountId: Effect.Effect; +} + +export interface QueueHttpScope { + accountId: string; + queueId: string; +} + +const QUEUE_HTTP_PERMISSION_GROUPS: ApiTokenPermissionGroupRef[] = [ + "Queues Read", + "Queues Write", +]; + +type PermissionGroup = (typeof QUEUE_HTTP_PERMISSION_GROUPS)[number]; + +/** + * Shared scaffolding for the HTTP-backed Queue services. + * + * Creates a scoped {@link AccountApiToken}, binds its `value` / + * `accountId` into the host Worker at deploy time, then delegates to + * `makeClient` with the bound token and the queue's `queueId`. + */ +export const makeHttpQueueBinding = (options: { + permissionGroups: PermissionGroup[]; + makeClient: (token: QueueHttpToken, queueId: Effect.Effect) => Client; +}) => + Effect.gen(function* () { + const Token = yield* AccountApiToken; + const self = yield* Self; + const env = yield* CloudflareEnvironment; + + return Effect.fn(function* (queue: Queue) { + const { accountId } = yield* env; + const token = yield* Token(`${self.LogicalId}Token`); + if (!globalThis.__ALCHEMY_RUNTIME__) { + yield* token.bind`${queue.LogicalId}`({ + policies: [ + { + effect: "allow", + permissionGroups: options.permissionGroups, + resources: { + [`com.cloudflare.api.account.${accountId}`]: "*", + }, + }, + ], + }); + } + const bound = { + value: yield* token.value, + accountId: yield* token.accountId, + } satisfies QueueHttpToken; + const queueId = yield* queue.queueId; + return options.makeClient(bound, queueId); + }); + }); + +/** Resolve the account and queue id once per operation. */ +export const makeQueueHttpScope = ( + token: QueueHttpToken, + queueId: Effect.Effect, +): Effect.Effect => + Effect.gen(function* () { + const accountId = yield* token.accountId; + const id = yield* queueId; + return { accountId, queueId: id }; + }); + +export const toQueueSendError = (error: unknown): QueueSendError => + new QueueSendError({ + message: + typeof error === "object" && error !== null && "message" in error + ? String((error as { message: unknown }).message) + : "Unknown queue error", + cause: error, + }); diff --git a/packages/alchemy/src/Cloudflare/Queue/QueueTypes.ts b/packages/alchemy/src/Cloudflare/Queue/QueueTypes.ts new file mode 100644 index 000000000..356fa7c0a --- /dev/null +++ b/packages/alchemy/src/Cloudflare/Queue/QueueTypes.ts @@ -0,0 +1,17 @@ +import * as Data from "effect/Data"; + +/** Options accepted by a single {@link WriteQueueClient.send} call. */ +export interface QueueSendOptions { + contentType?: "json" | "text"; +} + +/** A single message handed to {@link WriteQueueClient.sendBatch}. */ +export interface QueueSendMessage { + body: unknown; + contentType?: "json" | "text"; +} + +export class QueueSendError extends Data.TaggedError("QueueSendError")<{ + message: string; + cause?: unknown; +}> {} diff --git a/packages/alchemy/src/Cloudflare/Queue/QueueWrite.ts b/packages/alchemy/src/Cloudflare/Queue/QueueWrite.ts new file mode 100644 index 000000000..146e40dca --- /dev/null +++ b/packages/alchemy/src/Cloudflare/Queue/QueueWrite.ts @@ -0,0 +1,64 @@ +import type * as runtime from "@cloudflare/workers-types"; +import * as Effect from "effect/Effect"; +import * as Binding from "../../Binding.ts"; +import type { RuntimeContext } from "../../RuntimeContext.ts"; +import type { Queue } from "./Queue.ts"; +import type { + QueueSendError, + QueueSendMessage, + QueueSendOptions, +} from "./QueueTypes.ts"; + +/** + * Binding service that turns a {@link Queue} resource into a typed + * {@link WriteQueueClient} you can call from a Worker's runtime Effect. + * + * The Cloudflare Worker queue binding is producer-only — `send` for a + * single message and `sendBatch` for many in one call. Messages can be + * any JSON-serializable value. + * @binding + * @product Queues + * @category Storage & Databases + * @section Sending Messages + * @example Producer route + * ```typescript + * const queue = yield* Cloudflare.Queues.WriteQueue(Queue); + * + * return { + * fetch: Effect.gen(function* () { + * yield* queue.send({ text: "hi", sentAt: Date.now() }); + * return HttpServerResponse.empty({ status: 202 }); + * }), + * }; + * ``` + * + * @example Sending a batch + * ```typescript + * yield* queue.sendBatch([ + * { body: { event: "click", id: 1 } }, + * { body: { event: "click", id: 2 } }, + * { body: "raw text", contentType: "text" }, + * ]); + * ``` + * + * Provide {@link WriteQueueBinding} (native Worker binding) or + * {@link WriteQueueHttp} (scoped HTTP token) in the worker's runtime + * layer to resolve the underlying queue at request time. + */ +export class QueueWrite extends Binding.Service< + QueueWrite, + (queue: Queue) => Effect.Effect +>()("Cloudflare.Queue") {} + +export const WriteQueue = QueueWrite.bind; + +export interface WriteQueueClient { + raw: Effect.Effect, never, RuntimeContext>; + send( + body: unknown, + options?: QueueSendOptions, + ): Effect.Effect; + sendBatch( + messages: ReadonlyArray, + ): Effect.Effect; +} diff --git a/packages/alchemy/src/Cloudflare/Queue/QueueWriteBinding.ts b/packages/alchemy/src/Cloudflare/Queue/QueueWriteBinding.ts new file mode 100644 index 000000000..4dbc1adb2 --- /dev/null +++ b/packages/alchemy/src/Cloudflare/Queue/QueueWriteBinding.ts @@ -0,0 +1,33 @@ +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import { makeQueueBinding, makeQueueHelpers } from "./QueueBinding.ts"; +import type { QueueSendMessage, QueueSendOptions } from "./QueueTypes.ts"; +import { QueueWrite, type WriteQueueClient } from "./QueueWrite.ts"; + +/** + * Implementation of the {@link QueueWrite} service that uses a native Worker + * queue binding. + */ +export const WriteQueueBinding = Layer.effect( + QueueWrite, + Effect.suspend(() => makeQueueBinding({ makeClient: makeWriteQueueClient })), +); + +/** Build the producer client over a native Worker queue binding. */ +export const makeWriteQueueClient = ({ + raw, + use, +}: ReturnType): WriteQueueClient => ({ + raw, + send: (body: unknown, options?: QueueSendOptions) => + use((q) => q.send(body, options)), + sendBatch: (messages: ReadonlyArray) => + use((q) => + q.sendBatch( + messages.map((m) => ({ + body: m.body, + ...(m.contentType ? { contentType: m.contentType } : {}), + })), + ), + ), +}); diff --git a/packages/alchemy/src/Cloudflare/Queue/QueueWriteHttp.ts b/packages/alchemy/src/Cloudflare/Queue/QueueWriteHttp.ts new file mode 100644 index 000000000..59982e88e --- /dev/null +++ b/packages/alchemy/src/Cloudflare/Queue/QueueWriteHttp.ts @@ -0,0 +1,82 @@ +import * as queues from "@distilled.cloud/cloudflare/queues"; +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import { authorizeWith } from "../HttpClientUtils.ts"; +import { + makeHttpQueueBinding, + makeQueueHttpScope, + toQueueSendError, + type QueueHttpToken, +} from "./QueueHttp.ts"; +import { + QueueSendError, + type QueueSendMessage, + type QueueSendOptions, +} from "./QueueTypes.ts"; +import { QueueWrite, type WriteQueueClient } from "./QueueWrite.ts"; + +/** + * HTTP-backed implementation of the {@link QueueWrite} service. + * + * It creates a scoped {@link AccountApiToken} with the `Queues Write` + * permission and pushes messages via the Cloudflare Queues HTTP API. + */ +export const WriteQueueHttp = Layer.effect( + QueueWrite, + Effect.suspend(() => + makeHttpQueueBinding({ + permissionGroups: ["Queues Write"], + makeClient: makeWriteQueueHttpClient, + }), + ), +); + +/** Encode a message body for the HTTP push API. */ +const encodeBody = ( + body: unknown, + contentType: "json" | "text" | undefined, +): string => + contentType === "text" + ? typeof body === "string" + ? body + : String(body) + : JSON.stringify(body); + +/** Build the producer client over the Queues HTTP push API. */ +export const makeWriteQueueHttpClient = ( + token: QueueHttpToken, + queueId: Effect.Effect, +): WriteQueueClient => { + const authorize = authorizeWith(token); + const scope = makeQueueHttpScope(token, queueId); + + const push = (message: QueueSendMessage) => + scope.pipe( + Effect.flatMap(({ accountId, queueId }) => + authorize( + queues.pushMessage({ + accountId, + queueId, + body: encodeBody(message.body, message.contentType), + contentType: message.contentType ?? "json", + }), + ), + ), + Effect.mapError(toQueueSendError), + Effect.asVoid, + ); + + return { + raw: Effect.die( + new QueueSendError({ + message: + "Queue HTTP client does not expose a native Queue binding; use send/sendBatch.", + cause: new Error("unsupported"), + }), + ), + send: (body: unknown, options?: QueueSendOptions) => + push({ body, contentType: options?.contentType }), + sendBatch: (messages: ReadonlyArray) => + Effect.forEach(messages, push, { discard: true }), + }; +}; diff --git a/packages/alchemy/src/Cloudflare/Queue/index.ts b/packages/alchemy/src/Cloudflare/Queue/index.ts index ae28383ef..fd5035cc0 100644 --- a/packages/alchemy/src/Cloudflare/Queue/index.ts +++ b/packages/alchemy/src/Cloudflare/Queue/index.ts @@ -1,5 +1,8 @@ export * from "./Queue.ts"; -export * from "./QueueBinding.ts"; export * from "./QueueConsumer.ts"; export * from "./QueueEventSource.ts"; +export * from "./QueueTypes.ts"; +export * from "./QueueWrite.ts"; +export * from "./QueueWriteBinding.ts"; +export * from "./QueueWriteHttp.ts"; export * from "./Subscription.ts"; diff --git a/packages/alchemy/src/Cloudflare/R2/R2Bucket.ts b/packages/alchemy/src/Cloudflare/R2/Bucket.ts similarity index 99% rename from packages/alchemy/src/Cloudflare/R2/R2Bucket.ts rename to packages/alchemy/src/Cloudflare/R2/Bucket.ts index a5532e70b..13faccc96 100644 --- a/packages/alchemy/src/Cloudflare/R2/R2Bucket.ts +++ b/packages/alchemy/src/Cloudflare/R2/Bucket.ts @@ -10,9 +10,8 @@ import { Resource } from "../../Resource.ts"; import { CloudflareEnvironment } from "../CloudflareEnvironment.ts"; import type * as Cloudflare from "../Providers.ts"; import * as Zone from "../Zone/index.ts"; -import { R2BucketBinding } from "./R2BucketBinding.ts"; -export const isR2Bucket = (value: unknown): value is R2Bucket => +export const isR2Bucket = (value: any): value is R2Bucket => typeof value === "object" && (value as any)?.Type === "Cloudflare.R2Bucket"; export type R2BucketName = string; @@ -173,7 +172,7 @@ export type R2Bucket = Resource< * @section Binding to a Worker * @example Reading and writing objects * ```typescript - * const bucket = yield* Cloudflare.R2Bucket.bind(MyBucket); + * const bucket = yield* Cloudflare.R2.ReadWrite(MyBucket); * * // Write an object * yield* bucket.put("hello.txt", "Hello, World!"); @@ -187,7 +186,7 @@ export type R2Bucket = Resource< * * @example Streaming upload with content length * ```typescript - * const bucket = yield* Cloudflare.R2Bucket.bind(MyBucket); + * const bucket = yield* Cloudflare.R2.ReadWrite(MyBucket); * * yield* bucket.put("upload.bin", request.stream, { * contentLength: Number(request.headers["content-length"] ?? 0), @@ -300,9 +299,7 @@ export type R2Bucket = Resource< * }); * ``` */ -export const R2Bucket = Resource("Cloudflare.R2Bucket")({ - bind: R2BucketBinding.bind, -}); +export const R2Bucket = Resource("Cloudflare.R2Bucket"); export declare namespace R2Bucket { export type StorageClass = "Standard" | "InfrequentAccess"; diff --git a/packages/alchemy/src/Cloudflare/R2/BucketBinding.ts b/packages/alchemy/src/Cloudflare/R2/BucketBinding.ts new file mode 100644 index 000000000..e01c49f90 --- /dev/null +++ b/packages/alchemy/src/Cloudflare/R2/BucketBinding.ts @@ -0,0 +1,115 @@ +import type * as runtime from "@cloudflare/workers-types"; +import * as Effect from "effect/Effect"; +import * as Stream from "effect/Stream"; +import * as Output from "../../Output.ts"; +import { Worker, WorkerEnvironment } from "../Workers/Worker.ts"; +import type { R2Bucket } from "./Bucket.ts"; +import type { R2Object, R2ObjectBody } from "./BucketTypes.ts"; +import { R2Error } from "./BucketTypes.ts"; + +/** + * Shared scaffolding for the Worker-binding implementations of the R2 services. + * + * Resolves the {@link WorkerEnvironment} and host {@link Worker}, registers the + * `r2_bucket` binding at deploy time, then delegates to `makeClient` with the + * shared {@link Helpers} to build the read/write/read-write client. + */ +export const makeBucketBinding = (options: { + makeClient: (helpers: ReturnType) => Client; +}) => + Effect.gen(function* () { + const env = yield* WorkerEnvironment; + const host = yield* Worker; + + return Effect.fn(function* (bucket: R2Bucket) { + if (!globalThis.__ALCHEMY_RUNTIME__) { + yield* host.bind`${bucket}`({ + bindings: [ + { + type: "r2_bucket", + name: bucket.LogicalId, + bucketName: bucket.bucketName, + jurisdiction: bucket.jurisdiction.pipe( + Output.map((jurisdiction) => + jurisdiction === "default" ? undefined : jurisdiction, + ), + ), + }, + ], + }); + } + + return options.makeClient(makeHelpers(env, bucket)); + }); + }); + +/** + * Helpers shared by both the read and write halves of the binding client. + * + * Read-only (`wrapR2Objects`) and write-only (`wrapR2MultipartUpload`) wrappers + * live in {@link makeRead}/{@link makeWrite} respectively — only the primitives + * used by both sides are exposed here. + */ +export const makeHelpers = (env: Record, bucket: R2Bucket) => { + const raw = Effect.sync( + () => (env as Record)[bucket.LogicalId]!, + ); + const tryPromise = (fn: () => Promise): Effect.Effect => + Effect.tryPromise({ + try: fn, + catch: (error: any) => + new R2Error({ + message: error.message ?? "Unknown error", + cause: error, + }), + }); + + const use = ( + fn: (raw: runtime.R2Bucket) => Promise, + ): Effect.Effect => + raw.pipe(Effect.flatMap((raw) => tryPromise(() => fn(raw)))); + + const wrapR2Object = (object: runtime.R2Object): R2Object => ({ + ...object, + writeHttpMetadata: (headers: Headers) => + Effect.sync(() => object.writeHttpMetadata(headers)), + }); + const wrapR2ObjectBody = (object: runtime.R2ObjectBody): R2ObjectBody => ({ + ...wrapR2Object(object), + body: Stream.fromReadableStream({ + evaluate: () => + object.body as any as ReadableStream>, + onError: (error: any) => + new R2Error({ + message: error.message ?? "Unknown error", + cause: error, + }), + }), + bodyUsed: object.bodyUsed, + arrayBuffer: () => tryPromise(() => object.arrayBuffer()), + bytes: () => tryPromise(() => object.bytes()), + text: () => tryPromise(() => object.text()), + json: () => tryPromise(() => object.json()), + blob: () => tryPromise(() => object.blob()), + }); + + const isR2ObjectBody = (object: any): object is runtime.R2ObjectBody => + object !== null && typeof object === "object" && "body" in object; + + const wrapR2ObjectOrBody = ( + object: runtime.R2Object | runtime.R2ObjectBody | null, + ): R2Object | R2ObjectBody | null => + object === null + ? object + : isR2ObjectBody(object) + ? wrapR2ObjectBody(object) + : wrapR2Object(object); + + return { + raw, + use, + tryPromise, + wrapR2Object, + wrapR2ObjectOrBody, + }; +}; diff --git a/packages/alchemy/src/Cloudflare/R2/BucketEventNotification.ts b/packages/alchemy/src/Cloudflare/R2/BucketEventNotification.ts index 9ff95a4ce..60687b685 100644 --- a/packages/alchemy/src/Cloudflare/R2/BucketEventNotification.ts +++ b/packages/alchemy/src/Cloudflare/R2/BucketEventNotification.ts @@ -9,7 +9,7 @@ import * as Provider from "../../Provider.ts"; import { Resource } from "../../Resource.ts"; import { CloudflareEnvironment } from "../CloudflareEnvironment.ts"; import type { Providers } from "../Providers.ts"; -import type { R2Bucket } from "./R2Bucket.ts"; +import type { R2Bucket } from "./Bucket.ts"; const R2BucketEventNotificationTypeId = "Cloudflare.R2.BucketEventNotification" as const; diff --git a/packages/alchemy/src/Cloudflare/R2/BucketHttp.ts b/packages/alchemy/src/Cloudflare/R2/BucketHttp.ts new file mode 100644 index 000000000..19b30e3e1 --- /dev/null +++ b/packages/alchemy/src/Cloudflare/R2/BucketHttp.ts @@ -0,0 +1,201 @@ +import * as Effect from "effect/Effect"; +import type * as Redacted from "effect/Redacted"; +import * as Stream from "effect/Stream"; +import { Self } from "../../Self.ts"; +import { AccountApiToken } from "../ApiToken/AccountApiToken.ts"; +import type { ApiTokenPermissionGroupRef } from "../ApiToken/Common.ts"; +import { CloudflareEnvironment } from "../CloudflareEnvironment.ts"; +import type { R2Bucket } from "./Bucket.ts"; +import { R2Error, type R2Object } from "./BucketTypes.ts"; + +export interface R2HttpScope { + accountId: string; + bucketName: string; + cfR2Jurisdiction: string | undefined; +} +export interface R2HttpToken { + value: Effect.Effect>; + accountId: Effect.Effect; +} + +const R2_HTTP_PERMISSION_GROUPS: ApiTokenPermissionGroupRef[] = [ + "Workers R2 Storage Read", + "Workers R2 Storage Write", +]; + +type PermissionGroup = (typeof R2_HTTP_PERMISSION_GROUPS)[number]; + +export const makeHttpBucketBinding = (options: { + permissionGroups: PermissionGroup[]; + makeClient: ( + token: R2HttpToken, + bucketName: Effect.Effect, + jurisdiction: Effect.Effect, + ) => Client; +}) => + Effect.gen(function* () { + const Token = yield* AccountApiToken; + const self = yield* Self; + const env = yield* CloudflareEnvironment; + + return Effect.fn(function* (bucket: R2Bucket) { + const { accountId } = yield* env; + const token = yield* Token(`${self.LogicalId}Token`); + if (!globalThis.__ALCHEMY_RUNTIME__) { + yield* token.bind`${bucket.LogicalId}`({ + policies: [ + { + effect: "allow", + permissionGroups: options.permissionGroups, + resources: { + [`com.cloudflare.api.account.${accountId}`]: "*", + }, + }, + ], + }); + } + const bound = { + value: yield* token.value, + accountId: yield* token.accountId, + } satisfies R2HttpToken; + const bucketName = yield* bucket.bucketName; + const jurisdiction = yield* bucket.jurisdiction; + return options.makeClient(bound, bucketName, jurisdiction); + }); + }); + +/** Resolve the account, bucket, and jurisdiction once per operation. */ +export const makeR2HttpScope = ( + token: R2HttpToken, + bucketName: Effect.Effect, + jurisdiction: Effect.Effect, +): Effect.Effect => + Effect.gen(function* () { + const accountId = yield* token.accountId; + const bucket = yield* bucketName; + const j = yield* jurisdiction; + return { + accountId, + bucketName: bucket, + cfR2Jurisdiction: j === "default" ? undefined : j, + }; + }); + +/** + * Bind the token's `value` (as `secret_text`) and `accountId` (as `plain_text`) + * into the Worker so they can be read at runtime. + */ + +export const toR2Error = (error: unknown): R2Error => + new R2Error({ + message: + typeof error === "object" && error !== null && "message" in error + ? String((error as { message: unknown }).message) + : "Unknown R2 error", + cause: error instanceof Error ? error : new Error(String(error)), + }); + +const stripQuotes = (etag: string | undefined): string | undefined => + etag === undefined ? undefined : etag.replace(/^"|"$/g, ""); + +export interface HttpMetadata { + contentType?: string; + contentEncoding?: string; + contentDisposition?: string; + contentLanguage?: string; + cacheControl?: string; + cacheExpiry?: Date; +} + +/** Normalize the caller's `httpMetadata` option (object or `Headers`). */ +export const readHttpMetadata = ( + options: { httpMetadata?: unknown } | undefined, +): HttpMetadata | undefined => { + const meta = options?.httpMetadata; + if (!meta) return undefined; + if (meta instanceof Headers) { + return { + contentType: meta.get("content-type") ?? undefined, + contentEncoding: meta.get("content-encoding") ?? undefined, + contentDisposition: meta.get("content-disposition") ?? undefined, + contentLanguage: meta.get("content-language") ?? undefined, + cacheControl: meta.get("cache-control") ?? undefined, + }; + } + return meta as HttpMetadata; +}; + +/** Write an object's HTTP metadata onto a `Headers` instance. */ +const applyHttpMetadata = (headers: Headers, meta: HttpMetadata): void => { + if (meta.contentType) headers.set("content-type", meta.contentType); + if (meta.contentEncoding) + headers.set("content-encoding", meta.contentEncoding); + if (meta.contentDisposition) + headers.set("content-disposition", meta.contentDisposition); + if (meta.contentLanguage) + headers.set("content-language", meta.contentLanguage); + if (meta.cacheControl) headers.set("cache-control", meta.cacheControl); +}; + +export const baseObject = ( + key: string, + meta: HttpMetadata, + attrs: { + size?: number; + etag?: string; + uploaded?: Date; + storageClass?: string; + customMetadata?: Record; + }, +): R2Object => + ({ + key, + version: "", + size: attrs.size ?? 0, + etag: stripQuotes(attrs.etag) ?? "", + httpEtag: attrs.etag ?? "", + checksums: {}, + uploaded: attrs.uploaded ?? new Date(0), + httpMetadata: meta, + customMetadata: attrs.customMetadata ?? {}, + range: undefined, + storageClass: attrs.storageClass ?? "Standard", + writeHttpMetadata: (headers: Headers) => + Effect.sync(() => applyHttpMetadata(headers, meta)), + }) as unknown as R2Object; + +/** Collect a put `value` into a body accepted by the R2 HTTP API. */ +export const toBody = ( + value: + | ReadableStream + | ArrayBuffer + | ArrayBufferView + | string + | null + | Blob + | Stream.Stream, +): Effect.Effect< + { body: Blob | Uint8Array | ArrayBuffer | string; contentLength?: number }, + R2Error +> => + Effect.gen(function* () { + if (value === null) return { body: new Uint8Array(0), contentLength: 0 }; + if (typeof value === "string") return { body: value }; + if (value instanceof Blob) + return { body: value, contentLength: value.size }; + if (value instanceof ArrayBuffer) + return { body: value, contentLength: value.byteLength }; + if (Stream.isStream(value) || value instanceof ReadableStream) { + const readable = Stream.isStream(value) + ? Stream.toReadableStream(value) + : value; + const buffer = yield* Effect.tryPromise({ + try: () => new Response(readable as any).arrayBuffer(), + catch: toR2Error, + }); + return { body: new Uint8Array(buffer), contentLength: buffer.byteLength }; + } + const view = value as ArrayBufferView; + const bytes = new Uint8Array(view.buffer, view.byteOffset, view.byteLength); + return { body: bytes, contentLength: bytes.byteLength }; + }); diff --git a/packages/alchemy/src/Cloudflare/R2/BucketRead.ts b/packages/alchemy/src/Cloudflare/R2/BucketRead.ts new file mode 100644 index 000000000..c3a53a02b --- /dev/null +++ b/packages/alchemy/src/Cloudflare/R2/BucketRead.ts @@ -0,0 +1,43 @@ +import type * as runtime from "@cloudflare/workers-types"; +import * as Effect from "effect/Effect"; +import * as Binding from "../../Binding.ts"; +import type { RuntimeContext } from "../../RuntimeContext.ts"; +import type { R2Bucket } from "./Bucket.ts"; +import type { + R2Error, + R2GetOptions, + R2ListOptions, + R2Object, + R2ObjectBody, + R2Objects, +} from "./BucketTypes.ts"; + +/** + * @binding + * @product R2 + * @category Storage & Databases + */ +export class BucketRead extends Binding.Service< + BucketRead, + (bucket: R2Bucket) => Effect.Effect +>()("Cloudflare.R2Bucket") {} + +export const ReadBucket = BucketRead.bind; + +export interface ReadBucketClient { + raw: Effect.Effect; + head(key: string): Effect.Effect; + get( + key: string, + options: R2GetOptions & { + onlyIf: runtime.R2Conditional | Headers; + }, + ): Effect.Effect; + get( + key: string, + options?: R2GetOptions, + ): Effect.Effect; + list( + options?: R2ListOptions, + ): Effect.Effect; +} diff --git a/packages/alchemy/src/Cloudflare/R2/BucketReadBinding.ts b/packages/alchemy/src/Cloudflare/R2/BucketReadBinding.ts new file mode 100644 index 000000000..6ddfd4597 --- /dev/null +++ b/packages/alchemy/src/Cloudflare/R2/BucketReadBinding.ts @@ -0,0 +1,44 @@ +import type * as runtime from "@cloudflare/workers-types"; +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import { makeBucketBinding, makeHelpers } from "./BucketBinding.ts"; +import { BucketRead, type ReadBucketClient } from "./BucketRead.ts"; +import type { R2GetOptions, R2ListOptions, R2Objects } from "./BucketTypes.ts"; + +/** + * Implementation of the {@link BucketRead} service that uses a Worker binding. + */ +export const ReadBucketBinding = Layer.effect( + BucketRead, + Effect.suspend(() => makeBucketBinding({ makeClient: makeRead })), +); + +/** Build the read half of the binding client. */ +export const makeRead = ({ + raw, + use, + wrapR2Object, + wrapR2ObjectOrBody, +}: ReturnType): ReadBucketClient => { + const wrapR2Objects = (objects: runtime.R2Objects): R2Objects => + ({ + objects: objects.objects.map(wrapR2Object), + delimitedPrefixes: objects.delimitedPrefixes, + ...("cursor" in objects ? { cursor: objects.cursor } : {}), + ...("truncated" in objects ? { truncated: objects.truncated } : {}), + }) as R2Objects; + + return { + raw, + head: (key: string) => + use((raw) => raw.head(key)).pipe( + Effect.map((object) => (object ? wrapR2Object(object) : object)), + ), + get: ((key: string, options?: R2GetOptions) => + use((raw) => raw.get(key, options)).pipe( + Effect.map(wrapR2ObjectOrBody), + )) as any, + list: (options?: R2ListOptions) => + use((raw) => raw.list(options)).pipe(Effect.map(wrapR2Objects)), + }; +}; diff --git a/packages/alchemy/src/Cloudflare/R2/BucketReadHttp.ts b/packages/alchemy/src/Cloudflare/R2/BucketReadHttp.ts new file mode 100644 index 000000000..d0985fcb8 --- /dev/null +++ b/packages/alchemy/src/Cloudflare/R2/BucketReadHttp.ts @@ -0,0 +1,176 @@ +import * as r2 from "@distilled.cloud/cloudflare/r2"; +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import * as Stream from "effect/Stream"; +import { authorizeWith } from "../HttpClientUtils.ts"; +import { + baseObject, + makeHttpBucketBinding, + makeR2HttpScope, + toR2Error, + type HttpMetadata, + type R2HttpToken, +} from "./BucketHttp.ts"; +import { BucketRead, type ReadBucketClient } from "./BucketRead.ts"; +import { + R2Error, + type R2GetOptions, + type R2ListOptions, + type R2Objects, +} from "./BucketTypes.ts"; + +/** + * HTTP-backed implementation of the {@link BucketRead} service. + * + * It creates a scoped {@link AccountApiToken} with the `Workers R2 Storage Read` and `Workers R2 Storage Write` permissions. + */ +export const ReadBucketHttp = Layer.effect( + BucketRead, + Effect.suspend(() => + makeHttpBucketBinding({ + permissionGroups: ["Workers R2 Storage Read"], + makeClient: makeReadR2HttpClient, + }), + ), +); + +export const makeReadR2HttpClient = ( + token: R2HttpToken, + bucketName: Effect.Effect, + jurisdiction: Effect.Effect, +): ReadBucketClient => { + const authorize = authorizeWith(token); + const scope = makeR2HttpScope(token, bucketName, jurisdiction); + + return { + raw: Effect.die( + new R2Error({ + message: + "R2BucketBindingHttp does not expose a native `raw` R2Bucket; use the binary HTTP methods instead.", + cause: new Error("unsupported"), + }), + ), + head: (key: string) => + scope.pipe( + Effect.flatMap(({ accountId, bucketName, cfR2Jurisdiction }) => + authorize( + r2.getObject({ + accountId, + bucketName, + objectName: key, + cfR2Jurisdiction, + }), + ), + ), + Effect.mapError(toR2Error), + // The HTTP body is lazy, so reading headers does not download it. + Effect.map((res) => + baseObject(key, httpMetadataOf(res), { + size: res.contentLength, + etag: res.etag, + uploaded: res.lastModified ? new Date(res.lastModified) : undefined, + storageClass: res.cfR2StorageClass, + }), + ), + ), + get: ((key: string, _options?: R2GetOptions) => + scope.pipe( + Effect.flatMap(({ accountId, bucketName, cfR2Jurisdiction }) => + authorize( + r2.getObject({ + accountId, + bucketName, + objectName: key, + cfR2Jurisdiction, + }), + ), + ), + Effect.mapError(toR2Error), + Effect.map((res) => objectBodyFromResponse(key, res)), + )) as any, + list: (options?: R2ListOptions) => + scope.pipe( + Effect.flatMap(({ accountId, bucketName, cfR2Jurisdiction }) => + authorize( + r2.listObjects({ + accountId, + bucketName, + cfR2Jurisdiction, + prefix: options?.prefix, + delimiter: options?.delimiter, + cursor: options?.cursor, + startAfter: options?.startAfter, + perPage: options?.limit, + }), + ), + ), + Effect.mapError(toR2Error), + Effect.map((res) => { + const objects = res.result.map((o) => + baseObject( + o.key ?? "", + {}, + { + size: o.size ?? undefined, + etag: o.etag ?? undefined, + uploaded: o.lastModified ? new Date(o.lastModified) : undefined, + storageClass: o.storageClass ?? undefined, + customMetadata: + (o.customMetadata as Record | null) ?? + undefined, + }, + ), + ); + const cursor = res.resultInfo?.cursor ?? undefined; + return ( + cursor + ? { objects, delimitedPrefixes: [], truncated: true, cursor } + : { objects, delimitedPrefixes: [], truncated: false } + ) as R2Objects; + }), + ), + }; +}; + +const objectBodyFromResponse = ( + key: string, + res: r2.GetObjectResponse, +): R2ObjectBody => { + const meta = httpMetadataOf(res); + // The HTTP body is a single-consumption stream — expose it both as a Stream + // and via the buffering accessors, but the caller may only read it once. + let response: Response | undefined; + const getResponse = () => + (response ??= new Response(Stream.toReadableStream(res.body) as any)); + const consume = ( + fn: (r: Response) => Promise, + ): Effect.Effect => + Effect.tryPromise({ try: () => fn(getResponse()), catch: toR2Error }); + return { + ...baseObject(key, meta, { + size: res.contentLength, + etag: res.etag, + uploaded: res.lastModified ? new Date(res.lastModified) : undefined, + storageClass: res.cfR2StorageClass, + }), + body: Stream.fromReadableStream({ + evaluate: () => getResponse().body as ReadableStream, + onError: toR2Error, + }), + bodyUsed: false, + arrayBuffer: () => consume((r) => r.arrayBuffer()), + bytes: () => consume((r) => r.arrayBuffer().then((b) => new Uint8Array(b))), + text: () => consume((r) => r.text()), + json: () => consume((r) => r.json() as Promise), + blob: () => consume((r) => r.blob()), + } as unknown as R2ObjectBody; +}; + +const httpMetadataOf = (res: r2.GetObjectResponse): HttpMetadata => ({ + contentType: res.contentType, + contentEncoding: res.contentEncoding, + contentDisposition: res.contentDisposition, + contentLanguage: res.contentLanguage, + cacheControl: res.cacheControl, + cacheExpiry: res.expires ? new Date(res.expires) : undefined, +}); diff --git a/packages/alchemy/src/Cloudflare/R2/BucketReadWrite.ts b/packages/alchemy/src/Cloudflare/R2/BucketReadWrite.ts new file mode 100644 index 000000000..141853828 --- /dev/null +++ b/packages/alchemy/src/Cloudflare/R2/BucketReadWrite.ts @@ -0,0 +1,20 @@ +import * as Effect from "effect/Effect"; +import * as Binding from "../../Binding.ts"; +import type { R2Bucket } from "./Bucket.ts"; +import type { ReadBucketClient } from "./BucketRead.ts"; +import type { WriteBucketClient } from "./BucketWrite.ts"; + +/** + * @binding + * @product R2 + * @category Storage & Databases + */ +export class BucketReadWrite extends Binding.Service< + BucketReadWrite, + (bucket: R2Bucket) => Effect.Effect +>()("Cloudflare.R2Bucket") {} + +export const ReadWriteBucket = BucketReadWrite.bind; + +export interface ReadWriteBucketClient + extends ReadBucketClient, WriteBucketClient {} diff --git a/packages/alchemy/src/Cloudflare/R2/BucketReadWriteBinding.ts b/packages/alchemy/src/Cloudflare/R2/BucketReadWriteBinding.ts new file mode 100644 index 000000000..62d6f31e7 --- /dev/null +++ b/packages/alchemy/src/Cloudflare/R2/BucketReadWriteBinding.ts @@ -0,0 +1,26 @@ +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import { makeBucketBinding, makeHelpers } from "./BucketBinding.ts"; +import { makeRead } from "./BucketReadBinding.ts"; +import { + BucketReadWrite, + type ReadWriteBucketClient, +} from "./BucketReadWrite.ts"; +import { makeWrite } from "./BucketWriteBinding.ts"; + +/** + * Implementation of the {@link BucketReadWrite} service that uses a Worker binding. + */ +export const ReadWriteBucketBinding = Layer.effect( + BucketReadWrite, + Effect.suspend(() => makeBucketBinding({ makeClient: makeReadWrite })), +); + +/** Build the read-write binding client from its read and write halves. */ +export const makeReadWrite = ( + helpers: ReturnType, +): ReadWriteBucketClient => + ({ + ...makeRead(helpers), + ...makeWrite(helpers), + }) satisfies ReadWriteBucketClient; diff --git a/packages/alchemy/src/Cloudflare/R2/BucketReadWriteHttp.ts b/packages/alchemy/src/Cloudflare/R2/BucketReadWriteHttp.ts new file mode 100644 index 000000000..b2fee1573 --- /dev/null +++ b/packages/alchemy/src/Cloudflare/R2/BucketReadWriteHttp.ts @@ -0,0 +1,35 @@ +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import { makeHttpBucketBinding, type R2HttpToken } from "./BucketHttp.ts"; +import { makeReadR2HttpClient } from "./BucketReadHttp.ts"; +import { + BucketReadWrite, + type ReadWriteBucketClient, +} from "./BucketReadWrite.ts"; +import { makeWriteR2HttpClient } from "./BucketWriteHttp.ts"; + +/** + * HTTP-backed implementation of the {@link BucketReadWrite} service. + * + * It creates a scoped {@link AccountApiToken} with the `Workers R2 Storage Read` and `Workers R2 Storage Write` permissions. + */ +export const ReadWriteBucketHttp = Layer.effect( + BucketReadWrite, + Effect.suspend(() => + makeHttpBucketBinding({ + permissionGroups: ["Workers R2 Storage Read", "Workers R2 Storage Write"], + makeClient: makeReadWriteR2HttpClient, + }), + ), +); + +/** Build the HTTP-backed {@link ReadWrite} over a bound token + bucket. */ +export const makeReadWriteR2HttpClient = ( + token: R2HttpToken, + bucketName: Effect.Effect, + jurisdiction: Effect.Effect, +): ReadWriteBucketClient => + ({ + ...makeReadR2HttpClient(token, bucketName, jurisdiction), + ...makeWriteR2HttpClient(token, bucketName, jurisdiction), + }) satisfies ReadWriteBucketClient; diff --git a/packages/alchemy/src/Cloudflare/R2/BucketSippy.ts b/packages/alchemy/src/Cloudflare/R2/BucketSippy.ts index e9b3195d9..6f419f509 100644 --- a/packages/alchemy/src/Cloudflare/R2/BucketSippy.ts +++ b/packages/alchemy/src/Cloudflare/R2/BucketSippy.ts @@ -9,7 +9,7 @@ import * as Provider from "../../Provider.ts"; import { Resource } from "../../Resource.ts"; import { CloudflareEnvironment } from "../CloudflareEnvironment.ts"; import type { Providers } from "../Providers.ts"; -import type { R2Bucket } from "./R2Bucket.ts"; +import type { R2Bucket } from "./Bucket.ts"; const R2BucketSippyTypeId = "Cloudflare.R2.BucketSippy" as const; type R2BucketSippyTypeId = typeof R2BucketSippyTypeId; diff --git a/packages/alchemy/src/Cloudflare/R2/BucketTypes.ts b/packages/alchemy/src/Cloudflare/R2/BucketTypes.ts new file mode 100644 index 000000000..8935bce12 --- /dev/null +++ b/packages/alchemy/src/Cloudflare/R2/BucketTypes.ts @@ -0,0 +1,59 @@ +import type * as runtime from "@cloudflare/workers-types"; +import * as Data from "effect/Data"; +import * as Effect from "effect/Effect"; +import * as Stream from "effect/Stream"; + +export interface R2Object extends Omit { + writeHttpMetadata(headers: Headers): Effect.Effect; +} + +export interface R2ObjectBody extends R2Object { + get body(): Stream.Stream; + get bodyUsed(): boolean; + arrayBuffer(): Effect.Effect; + bytes(): Effect.Effect; + text(): Effect.Effect; + json(): Effect.Effect; + blob(): Effect.Effect; +} + +export type R2GetOptions = runtime.R2GetOptions; +export type R2PutOptions = runtime.R2PutOptions & { + contentLength?: number; +}; + +export type R2ListOptions = runtime.R2ListOptions; +export type R2Objects = { + objects: R2Object[]; + delimitedPrefixes: string[]; +} & ( + | { + truncated: true; + cursor: string; + } + | { + truncated: false; + } +); +export type R2Conditional = runtime.R2Conditional; + +export class R2Error extends Data.TaggedError("R2Error")<{ + message: string; + cause: Error; +}> {} + +export interface R2MultipartUpload { + raw: runtime.R2MultipartUpload; + readonly key: string; + readonly uploadId: string; + uploadPart( + partNumber: number, + value: ReadableStream | (ArrayBuffer | ArrayBufferView) | string | Blob, + options?: R2UploadPartOptions, + ): Effect.Effect; + abort(): Effect.Effect; + complete(uploadedParts: R2UploadedPart[]): Effect.Effect; +} +export type R2MultipartOptions = runtime.R2MultipartOptions; +export type R2UploadedPart = runtime.R2UploadedPart; +export interface R2UploadPartOptions extends runtime.R2UploadPartOptions {} diff --git a/packages/alchemy/src/Cloudflare/R2/BucketWrite.ts b/packages/alchemy/src/Cloudflare/R2/BucketWrite.ts new file mode 100644 index 000000000..8c73c3324 --- /dev/null +++ b/packages/alchemy/src/Cloudflare/R2/BucketWrite.ts @@ -0,0 +1,77 @@ +import * as Effect from "effect/Effect"; +import * as Stream from "effect/Stream"; +import * as Binding from "../../Binding.ts"; +import type { RuntimeContext } from "../../RuntimeContext.ts"; +import type { R2Bucket } from "./Bucket.ts"; +import type { + R2Conditional, + R2Error, + R2MultipartOptions, + R2MultipartUpload, + R2Object, + R2PutOptions, +} from "./BucketTypes.ts"; + +/** + * @binding + * @product R2 + * @category Storage & Databases + */ +export class BucketWrite extends Binding.Service< + BucketWrite, + (bucket: R2Bucket) => Effect.Effect +>()("Cloudflare.R2.BucketWrite") {} + +export const WriteBucket = BucketWrite.bind; + +export interface WriteBucketClient { + put( + key: string, + value: + | ReadableStream + | ArrayBuffer + | ArrayBufferView + | string + | null + | Blob + | Stream.Stream, + options?: R2PutOptions & { + onlyIf: R2Conditional | Headers; + contentLength?: number; + }, + ): Effect.Effect; + put( + key: string, + value: + | ReadableStream + | ArrayBuffer + | ArrayBufferView + | string + | null + | Blob, + options?: R2PutOptions, + ): Effect.Effect; + put( + key: string, + value: + | ReadableStream + | ArrayBuffer + | ArrayBufferView + | string + | null + | Blob + | Stream.Stream, + options: R2PutOptions & { + contentLength: number; + }, + ): Effect.Effect; + delete(keys: string | string[]): Effect.Effect; + createMultipartUpload( + key: string, + options?: R2MultipartOptions, + ): Effect.Effect; + resumeMultipartUpload( + key: string, + uploadId: string, + ): Effect.Effect; +} diff --git a/packages/alchemy/src/Cloudflare/R2/BucketWriteBinding.ts b/packages/alchemy/src/Cloudflare/R2/BucketWriteBinding.ts new file mode 100644 index 000000000..7f853dc7f --- /dev/null +++ b/packages/alchemy/src/Cloudflare/R2/BucketWriteBinding.ts @@ -0,0 +1,111 @@ +import type * as runtime from "@cloudflare/workers-types"; +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import * as Stream from "effect/Stream"; +import { getRawStream } from "../../Util/Stream.ts"; +import { makeBucketBinding, makeHelpers } from "./BucketBinding.ts"; +import type { + R2Conditional, + R2MultipartOptions, + R2MultipartUpload, + R2PutOptions, + R2UploadPartOptions, + R2UploadedPart, +} from "./BucketTypes.ts"; +import { BucketWrite, type WriteBucketClient } from "./BucketWrite.ts"; + +/** + * Implementation of the {@link BucketWrite} service that uses a Worker binding. + */ +export const WriteBucketBinding = Layer.effect( + BucketWrite, + Effect.suspend(() => makeBucketBinding({ makeClient: makeWrite })), +); + +/** Build the write half of the binding client. */ +export const makeWrite = ({ + raw, + use, + tryPromise, + wrapR2Object, + wrapR2ObjectOrBody, +}: ReturnType): WriteBucketClient => { + const wrapR2MultipartUpload = ( + upload: runtime.R2MultipartUpload, + ): R2MultipartUpload => ({ + ...upload, + raw: upload, + uploadId: upload.uploadId, + abort: () => tryPromise(() => upload.abort()), + complete: (uploadedParts: R2UploadedPart[]) => + tryPromise(() => upload.complete(uploadedParts)).pipe( + Effect.map(wrapR2Object), + ), + uploadPart: ( + partNumber: number, + value: + | ReadableStream + | ArrayBuffer + | ArrayBufferView + | string + | Blob + | Stream.Stream, + options?: R2UploadPartOptions, + ) => + tryPromise(() => + upload.uploadPart( + partNumber, + Stream.isStream(value) + ? value.pipe(Stream.toReadableStream()) + : (value as any), + options, + ), + ), + }); + + return { + // @ts-expect-error + put: ( + key: string, + value: + | ReadableStream + | ArrayBuffer + | ArrayBufferView + | string + | null + | Blob + | Stream.Stream, + options?: R2PutOptions & { + onlyIf: R2Conditional | Headers; + contentLength?: number; + }, + ) => + use((raw) => { + if (Stream.isStream(value)) { + const rawStream = getRawStream(value); + if (rawStream) { + return raw.put(key, rawStream as any, options); + } else if (!options?.contentLength) { + throw new Error("Content length is required"); + } + // content length myst be known, so we pipe through fixed length stream + // TODO(sam): is it more efficient to just assign the contentLength as a property? + const readable = Stream.toReadableStream(value).pipeThrough( + new FixedLengthStream(options.contentLength), + ); + return raw.put(key, readable as any); + } + return raw.put(key, value as any, options); + }).pipe(Effect.map(wrapR2ObjectOrBody)) as any, + delete: (keys: string | string[]) => use((raw) => raw.delete(keys)), + createMultipartUpload: (key: string, options?: R2MultipartOptions) => + use((raw) => raw.createMultipartUpload(key, options)).pipe( + Effect.map(wrapR2MultipartUpload), + ), + resumeMultipartUpload: (key: string, uploadId: string) => + raw.pipe( + Effect.map((raw) => raw.resumeMultipartUpload(key, uploadId)), + Effect.map(wrapR2MultipartUpload), + ), + }; +}; diff --git a/packages/alchemy/src/Cloudflare/R2/BucketWriteHttp.ts b/packages/alchemy/src/Cloudflare/R2/BucketWriteHttp.ts new file mode 100644 index 000000000..c0f6f6c1a --- /dev/null +++ b/packages/alchemy/src/Cloudflare/R2/BucketWriteHttp.ts @@ -0,0 +1,144 @@ +import * as r2 from "@distilled.cloud/cloudflare/r2"; +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import * as Stream from "effect/Stream"; +import { authorizeWith } from "../HttpClientUtils.ts"; +import { + baseObject, + makeHttpBucketBinding, + makeR2HttpScope, + readHttpMetadata, + toBody, + toR2Error, + type R2HttpToken, +} from "./BucketHttp.ts"; +import { R2Error, type R2PutOptions } from "./BucketTypes.ts"; +import { BucketWrite, type WriteBucketClient } from "./BucketWrite.ts"; + +/** + * HTTP-backed implementation of the {@link BucketWrite} service. + * + * It creates a scoped {@link AccountApiToken} with the `Workers R2 Storage Read` and `Workers R2 Storage Write` permissions. + */ +export const WriteBucketHttp = Layer.effect( + BucketWrite, + Effect.suspend(() => + makeHttpBucketBinding({ + permissionGroups: ["Workers R2 Storage Write"], + makeClient: makeWriteR2HttpClient, + }), + ), +); + +/** Build the write half of the HTTP-backed {@link ReadWrite} client. */ +export const makeWriteR2HttpClient = ( + token: R2HttpToken, + bucketName: Effect.Effect, + jurisdiction: Effect.Effect, +): WriteBucketClient => { + const authorize = authorizeWith(token); + const scope = makeR2HttpScope(token, bucketName, jurisdiction); + + return { + put: (( + key: string, + value: + | ReadableStream + | ArrayBuffer + | ArrayBufferView + | string + | null + | Blob + | Stream.Stream, + options?: R2PutOptions, + ) => + scope.pipe( + Effect.flatMap(({ accountId, bucketName, cfR2Jurisdiction }) => + toBody(value).pipe( + Effect.flatMap(({ body, contentLength }) => { + const meta = readHttpMetadata(options); + return authorize( + r2.putObject({ + accountId, + bucketName, + objectName: key, + cfR2Jurisdiction, + body, + contentType: meta?.contentType, + contentEncoding: meta?.contentEncoding, + contentDisposition: meta?.contentDisposition, + contentLanguage: meta?.contentLanguage, + cacheControl: meta?.cacheControl, + contentLength: + options?.contentLength != null + ? String(options.contentLength) + : contentLength != null + ? String(contentLength) + : undefined, + cfR2StorageClass: ( + options as { storageClass?: string } | undefined + )?.storageClass, + }), + ).pipe( + Effect.map(() => + baseObject(key, meta ?? {}, { + size: contentLength, + customMetadata: ( + options as + | { customMetadata?: Record } + | undefined + )?.customMetadata, + storageClass: ( + options as { storageClass?: string } | undefined + )?.storageClass, + uploaded: new Date(), + }), + ), + ); + }), + ), + ), + Effect.mapError(toR2Error), + )) as any, + delete: (keys: string | string[]) => + scope.pipe( + Effect.flatMap(({ accountId, bucketName, cfR2Jurisdiction }) => + Array.isArray(keys) + ? authorize( + r2.deleteObjects({ + accountId, + bucketName, + cfR2Jurisdiction, + body: keys, + }), + ) + : authorize( + r2.deleteObject({ + accountId, + bucketName, + objectName: keys, + cfR2Jurisdiction, + }), + ), + ), + Effect.mapError(toR2Error), + Effect.asVoid, + ), + createMultipartUpload: () => + Effect.die( + new R2Error({ + message: + "R2BucketBindingHttp does not support multipart uploads over the HTTP API.", + cause: new Error("unsupported"), + }), + ), + resumeMultipartUpload: () => + Effect.die( + new R2Error({ + message: + "R2BucketBindingHttp does not support multipart uploads over the HTTP API.", + cause: new Error("unsupported"), + }), + ), + }; +}; diff --git a/packages/alchemy/src/Cloudflare/R2/R2BucketBinding.ts b/packages/alchemy/src/Cloudflare/R2/R2BucketBinding.ts deleted file mode 100644 index 5529290de..000000000 --- a/packages/alchemy/src/Cloudflare/R2/R2BucketBinding.ts +++ /dev/null @@ -1,339 +0,0 @@ -import type * as runtime from "@cloudflare/workers-types"; -import * as Data from "effect/Data"; -import * as Effect from "effect/Effect"; -import * as Layer from "effect/Layer"; -import * as Stream from "effect/Stream"; -import * as Binding from "../../Binding.ts"; -import * as Output from "../../Output.ts"; -import type { ResourceLike } from "../../Resource.ts"; -import type { RuntimeContext } from "../../RuntimeContext.ts"; -import { getRawStream } from "../../Util/Stream.ts"; -import { isWorker, WorkerEnvironment } from "../Workers/Worker.ts"; -import type { R2Bucket } from "./R2Bucket.ts"; - -export interface R2Object extends Omit { - writeHttpMetadata(headers: Headers): Effect.Effect; -} - -export interface R2ObjectBody extends R2Object { - get body(): Stream.Stream; - get bodyUsed(): boolean; - arrayBuffer(): Effect.Effect; - bytes(): Effect.Effect; - text(): Effect.Effect; - json(): Effect.Effect; - blob(): Effect.Effect; -} - -export type R2GetOptions = runtime.R2GetOptions; -export type R2PutOptions = runtime.R2PutOptions & { - contentLength?: number; -}; - -export type R2ListOptions = runtime.R2ListOptions; -export type R2Objects = { - objects: R2Object[]; - delimitedPrefixes: string[]; -} & ( - | { - truncated: true; - cursor: string; - } - | { - truncated: false; - } -); -export type R2Conditional = runtime.R2Conditional; - -export class R2Error extends Data.TaggedError("R2Error")<{ - message: string; - cause: Error; -}> {} - -export interface R2MultipartUpload { - raw: runtime.R2MultipartUpload; - readonly key: string; - readonly uploadId: string; - uploadPart( - partNumber: number, - value: ReadableStream | (ArrayBuffer | ArrayBufferView) | string | Blob, - options?: R2UploadPartOptions, - ): Effect.Effect; - abort(): Effect.Effect; - complete(uploadedParts: R2UploadedPart[]): Effect.Effect; -} -export type R2MultipartOptions = runtime.R2MultipartOptions; -export type R2UploadedPart = runtime.R2UploadedPart; -export interface R2UploadPartOptions extends runtime.R2UploadPartOptions {} - -export interface R2BucketClient { - raw: Effect.Effect; - head(key: string): Effect.Effect; - get( - key: string, - options: R2GetOptions & { - onlyIf: runtime.R2Conditional | Headers; - }, - ): Effect.Effect; - get( - key: string, - options?: R2GetOptions, - ): Effect.Effect; - put( - key: string, - value: - | ReadableStream - | ArrayBuffer - | ArrayBufferView - | string - | null - | Blob - | Stream.Stream, - options?: R2PutOptions & { - onlyIf: R2Conditional | Headers; - contentLength?: number; - }, - ): Effect.Effect; - put( - key: string, - value: - | ReadableStream - | ArrayBuffer - | ArrayBufferView - | string - | null - | Blob, - options?: R2PutOptions, - ): Effect.Effect; - put( - key: string, - value: - | ReadableStream - | ArrayBuffer - | ArrayBufferView - | string - | null - | Blob - | Stream.Stream, - options: R2PutOptions & { - contentLength: number; - }, - ): Effect.Effect; - delete(keys: string | string[]): Effect.Effect; - list( - options?: R2ListOptions, - ): Effect.Effect; - createMultipartUpload( - key: string, - options?: R2MultipartOptions, - ): Effect.Effect; - resumeMultipartUpload( - key: string, - uploadId: string, - ): Effect.Effect; -} - -/** - * @binding - * @product R2 - * @category Storage & Databases - */ -export class R2BucketBinding extends Binding.Service< - R2BucketBinding, - (bucket: R2Bucket) => Effect.Effect ->()("Cloudflare.R2Bucket") {} - -export const R2BucketBindingLive = Layer.effect( - R2BucketBinding, - Effect.gen(function* () { - const bind = yield* R2BucketBindingPolicy; - const env = yield* WorkerEnvironment; - - return Effect.fn(function* (bucket: R2Bucket) { - yield* bind(bucket); - const raw = Effect.sync( - () => (env as Record)[bucket.LogicalId]!, - ); - const tryPromise = (fn: () => Promise): Effect.Effect => - Effect.tryPromise({ - try: fn, - catch: (error: any) => - new R2Error({ - message: error.message ?? "Unknown error", - cause: error, - }), - }); - - const use = ( - fn: (raw: runtime.R2Bucket) => Promise, - ): Effect.Effect => - raw.pipe(Effect.flatMap((raw) => tryPromise(() => fn(raw)))); - - const wrapR2Object = (object: runtime.R2Object): R2Object => ({ - ...object, - writeHttpMetadata: (headers: Headers) => - Effect.sync(() => object.writeHttpMetadata(headers)), - }); - const wrapR2ObjectBody = ( - object: runtime.R2ObjectBody, - ): R2ObjectBody => ({ - ...wrapR2Object(object), - body: Stream.fromReadableStream({ - evaluate: () => - object.body as any as ReadableStream>, - onError: (error: any) => - new R2Error({ - message: error.message ?? "Unknown error", - cause: error, - }), - }), - bodyUsed: object.bodyUsed, - arrayBuffer: () => tryPromise(() => object.arrayBuffer()), - bytes: () => tryPromise(() => object.bytes()), - text: () => tryPromise(() => object.text()), - json: () => tryPromise(() => object.json()), - blob: () => tryPromise(() => object.blob()), - }); - - const wrapR2Objects = (objects: runtime.R2Objects): R2Objects => - ({ - objects: objects.objects.map(wrapR2Object), - delimitedPrefixes: objects.delimitedPrefixes, - ...("cursor" in objects ? { cursor: objects.cursor } : {}), - ...("truncated" in objects ? { truncated: objects.truncated } : {}), - }) as R2Objects; - - const wrapR2ObjectOrBody = ( - object: runtime.R2Object | runtime.R2ObjectBody | null, - ): R2Object | R2ObjectBody | null => - object === null - ? object - : isR2ObjectBody(object) - ? wrapR2ObjectBody(object) - : wrapR2Object(object); - - const wrapR2MultipartUpload = ( - upload: runtime.R2MultipartUpload, - ): R2MultipartUpload => ({ - ...upload, - raw: upload, - uploadId: upload.uploadId, - abort: () => tryPromise(() => upload.abort()), - complete: (uploadedParts: R2UploadedPart[]) => - tryPromise(() => upload.complete(uploadedParts)).pipe( - Effect.map(wrapR2Object), - ), - uploadPart: ( - partNumber: number, - value: - | ReadableStream - | ArrayBuffer - | ArrayBufferView - | string - | Blob - | Stream.Stream, - options?: R2UploadPartOptions, - ) => - tryPromise(() => - upload.uploadPart( - partNumber, - Stream.isStream(value) - ? value.pipe(Stream.toReadableStream()) - : (value as any), - options, - ), - ), - }); - - const isR2ObjectBody = (object: any): object is runtime.R2ObjectBody => - object !== null && typeof object === "object" && "body" in object; - - return { - raw: raw, - head: (key: string) => - use((raw) => raw.head(key)).pipe( - Effect.map((object) => (object ? wrapR2Object(object) : object)), - ), - get: (key: string, options?: R2GetOptions) => - use((raw) => raw.get(key, options)).pipe( - Effect.map(wrapR2ObjectOrBody), - ) as any, - // @ts-expect-error - put: ( - key: string, - value: - | ReadableStream - | ArrayBuffer - | ArrayBufferView - | string - | null - | Blob - | Stream.Stream, - options?: R2PutOptions & { - onlyIf: R2Conditional | Headers; - contentLength?: number; - }, - ) => - use((raw) => { - if (Stream.isStream(value)) { - const rawStream = getRawStream(value); - if (rawStream) { - return raw.put(key, rawStream as any, options); - } else if (!options?.contentLength) { - throw new Error("Content length is required"); - } - // content length myst be known, so we pipe through fixed length stream - // TODO(sam): is it more efficient to just assign the contentLength as a property? - const readable = Stream.toReadableStream(value).pipeThrough( - new FixedLengthStream(options.contentLength), - ); - return raw.put(key, readable as any); - } - return raw.put(key, value as any, options); - }).pipe(Effect.map(wrapR2ObjectOrBody)) as any, - delete: (keys: string | string[]) => use((raw) => raw.delete(keys)), - list: (options?: R2ListOptions) => - use((raw) => raw.list(options)).pipe(Effect.map(wrapR2Objects)), - createMultipartUpload: (key: string, options?: R2MultipartOptions) => - use((raw) => raw.createMultipartUpload(key, options)).pipe( - Effect.map(wrapR2MultipartUpload), - ), - resumeMultipartUpload: (key: string, uploadId: string) => - raw.pipe( - Effect.map((raw) => raw.resumeMultipartUpload(key, uploadId)), - Effect.map(wrapR2MultipartUpload), - ), - } satisfies R2BucketClient as R2BucketClient; - }); - }), -); - -export class R2BucketBindingPolicy extends Binding.Policy< - R2BucketBindingPolicy, - (bucket: R2Bucket) => Effect.Effect ->()("Cloudflare.R2Bucket") {} - -export const R2BucketBindingPolicyLive = R2BucketBindingPolicy.layer.succeed( - Effect.fnUntraced(function* (host: ResourceLike, bucket: R2Bucket) { - if (isWorker(host)) { - yield* host.bind`${bucket}`({ - bindings: [ - { - type: "r2_bucket", - name: bucket.LogicalId, - bucketName: bucket.bucketName, - jurisdiction: bucket.jurisdiction.pipe( - Output.map((jurisdiction) => - jurisdiction === "default" ? undefined : jurisdiction, - ), - ), - }, - ], - }); - } else { - return yield* Effect.die( - new Error(`BucketBinding does not support runtime '${host.Type}'`), - ); - } - }), -); diff --git a/packages/alchemy/src/Cloudflare/R2/index.ts b/packages/alchemy/src/Cloudflare/R2/index.ts index 0f9581e82..6f7f5aa5d 100644 --- a/packages/alchemy/src/Cloudflare/R2/index.ts +++ b/packages/alchemy/src/Cloudflare/R2/index.ts @@ -1,4 +1,12 @@ +export * from "./Bucket.ts"; export * from "./BucketEventNotification.ts"; +export * from "./BucketRead.ts"; +export * from "./BucketReadBinding.ts"; +export * from "./BucketReadHttp.ts"; +export * from "./BucketReadWrite.ts"; +export * from "./BucketReadWriteBinding.ts"; +export * from "./BucketReadWriteHttp.ts"; export * from "./BucketSippy.ts"; -export * from "./R2Bucket.ts"; -export * from "./R2BucketBinding.ts"; +export * from "./BucketWrite.ts"; +export * from "./BucketWriteBinding.ts"; +export * from "./BucketWriteHttp.ts"; diff --git a/packages/alchemy/src/Cloudflare/RateLimit/RateLimitBinding.ts b/packages/alchemy/src/Cloudflare/RateLimit/RateLimitBinding.ts index 522be1797..14b0ec5e0 100644 --- a/packages/alchemy/src/Cloudflare/RateLimit/RateLimitBinding.ts +++ b/packages/alchemy/src/Cloudflare/RateLimit/RateLimitBinding.ts @@ -8,6 +8,7 @@ import * as Binding from "../../Binding.ts"; import type { ResourceLike } from "../../Resource.ts"; import { isWorker, WorkerEnvironment } from "../Workers/Worker.ts"; import { type RateLimit as RateLimitLike } from "./RateLimit.ts"; +import type { Providers } from "../Providers.ts"; export class RateLimitError extends Data.TaggedError("RateLimitError")<{ message: string; @@ -70,7 +71,8 @@ export const RateLimitBindingLive = Layer.effect( export class RateLimitBindingPolicy extends Binding.Policy< RateLimitBindingPolicy, - (rateLimit: RateLimitLike) => Effect.Effect + (rateLimit: RateLimitLike) => Effect.Effect, + Providers >()("Cloudflare.RateLimit.Binding") {} export const RateLimitBindingPolicyLive = RateLimitBindingPolicy.layer.succeed( diff --git a/packages/alchemy/src/Cloudflare/SecretsStore/SecretBinding.ts b/packages/alchemy/src/Cloudflare/SecretsStore/SecretBinding.ts index 88842499d..ac185aaa7 100644 --- a/packages/alchemy/src/Cloudflare/SecretsStore/SecretBinding.ts +++ b/packages/alchemy/src/Cloudflare/SecretsStore/SecretBinding.ts @@ -8,6 +8,7 @@ import type { ResourceLike } from "../../Resource.ts"; import type { RuntimeContext } from "../../RuntimeContext.ts"; import { isWorker, WorkerEnvironment } from "../Workers/Worker.ts"; import type { Secret } from "./Secret.ts"; +import type { Providers } from "../Providers.ts"; export class SecretError extends Data.TaggedError("SecretError")<{ message: string; @@ -85,7 +86,8 @@ export const SecretBindingLive = Layer.effect( export class SecretBindingPolicy extends Binding.Policy< SecretBindingPolicy, - (secret: Secret) => Effect.Effect + (secret: Secret) => Effect.Effect, + Providers >()("Cloudflare.SecretsStore.Secret") {} export const SecretBindingPolicyLive = SecretBindingPolicy.layer.succeed( diff --git a/packages/alchemy/src/Cloudflare/StateStore/State.ts b/packages/alchemy/src/Cloudflare/StateStore/State.ts index 17cb7e443..b9c36bf78 100644 --- a/packages/alchemy/src/Cloudflare/StateStore/State.ts +++ b/packages/alchemy/src/Cloudflare/StateStore/State.ts @@ -21,6 +21,7 @@ import { ALCHEMY_PROFILE, ProfileLive } from "../../Auth/Profile.ts"; import * as Cloudflare from "../../Cloudflare/Providers.ts"; import { deploy } from "../../Deploy.ts"; import * as Output from "../../Output.ts"; +import { RandomProvider } from "../../Random.ts"; import * as Alchemy from "../../Stack.ts"; import { StateApi } from "../../State/HttpStateApi.ts"; import { @@ -325,7 +326,7 @@ const deployStateStore = ({ stack: Alchemy.Stack( "CloudflareStateStore", { - providers: Cloudflare.providers(), + providers: Layer.mergeAll(Cloudflare.providers(), RandomProvider()), state: stateLayer, }, Effect.gen(function* () { diff --git a/packages/alchemy/src/Cloudflare/StateStore/Store.ts b/packages/alchemy/src/Cloudflare/StateStore/Store.ts index e22d2c619..53a464ac2 100644 --- a/packages/alchemy/src/Cloudflare/StateStore/Store.ts +++ b/packages/alchemy/src/Cloudflare/StateStore/Store.ts @@ -7,7 +7,7 @@ import type { ResourceState, } from "../../State/ResourceState.ts"; import { encodeState } from "../../State/StateEncoding.ts"; -import * as Secret from "../SecretsStore/Secret.ts"; +import * as Secret from "../SecretsStore/index.ts"; import { DurableObjectNamespace } from "../Workers/DurableObjectNamespace.ts"; import { DurableObjectState } from "../Workers/DurableObjectState.ts"; import { EncryptionKey } from "./Token.ts"; @@ -19,13 +19,10 @@ export default class Store extends DurableObjectNamespace()( // The actual secret read happens inside each DO instance below, // since `SecretClient.get()` needs the per-instance worker env. const encryptionSecret = yield* Secret.Secret.bind(EncryptionKey); + const state = yield* DurableObjectState; + const storage = state.storage; return Effect.gen(function* () { - // Inner (per-instance) phase — set up storage and the AES key. - // The key is imported once per DO boot and reused thereafter. - const doState = yield* DurableObjectState; - const storage = doState.storage; - const keyHex = yield* encryptionSecret .get() .pipe(Effect.map(Redacted.value), Effect.orDie); @@ -256,7 +253,7 @@ export default class Store extends DurableObjectNamespace()( ), }; }); - }), + }).pipe(Effect.provide(Secret.SecretBindingLive)), ) { /** * Well-known DO name whose sole job is to track the set of stacks diff --git a/packages/alchemy/src/Cloudflare/Tunnel/TunnelBinding.ts b/packages/alchemy/src/Cloudflare/Tunnel/TunnelBinding.ts index 7ed2bc0a4..e05f914ac 100644 --- a/packages/alchemy/src/Cloudflare/Tunnel/TunnelBinding.ts +++ b/packages/alchemy/src/Cloudflare/Tunnel/TunnelBinding.ts @@ -1,14 +1,6 @@ -import { - Credentials, - fromApiToken, -} from "@distilled.cloud/cloudflare/Credentials"; import * as Effect from "effect/Effect"; -import * as Layer from "effect/Layer"; -import * as Redacted from "effect/Redacted"; -import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient"; -import type { HttpClient } from "effect/unstable/http/HttpClient"; +import type * as Redacted from "effect/Redacted"; import type * as Binding from "../../Binding.ts"; -import { RuntimeContext } from "../../RuntimeContext.ts"; import { AccountApiToken } from "../ApiToken/AccountApiToken.ts"; import type { ApiTokenPermissionGroupRef } from "../ApiToken/Common.ts"; import { CloudflareEnvironment } from "../CloudflareEnvironment.ts"; @@ -38,27 +30,6 @@ export const bindTunnelToken = (token: AccountApiToken) => return { value, accountId } satisfies TunnelToken; }); -/** - * Resolve credentials from a bound token and provide them (plus the - * fetch-based HTTP client) to a raw SDK operation. - */ -export const authorizeWith = - (token: TunnelToken) => - ( - eff: Effect.Effect, - ): Effect.Effect => - token.value.pipe( - Effect.flatMap((value) => - eff.pipe( - Effect.provide( - fromApiToken({ apiToken: Redacted.value(value) }).pipe( - Layer.provideMerge(FetchHttpClient.layer), - ), - ), - ), - ), - ); - /** * Shared runtime body for a tunnel binding: create a scoped token, attach the * (narrow) policy, bind the token's outputs into the Worker, then build the diff --git a/packages/alchemy/src/Cloudflare/Tunnel/TunnelRead.ts b/packages/alchemy/src/Cloudflare/Tunnel/TunnelRead.ts index 37c23522d..57f3dd622 100644 --- a/packages/alchemy/src/Cloudflare/Tunnel/TunnelRead.ts +++ b/packages/alchemy/src/Cloudflare/Tunnel/TunnelRead.ts @@ -18,11 +18,12 @@ import type { AccountApiToken } from "../ApiToken/AccountApiToken.ts"; import type { CloudflareEnvironment } from "../CloudflareEnvironment.ts"; import type { Worker } from "../Workers/Worker.ts"; import { - authorizeWith, makeTunnelClient, makeTunnelPolicyLive, type TunnelToken, } from "./TunnelBinding.ts"; +import { authorizeWith } from "../HttpClientUtils.ts"; +import type { Providers } from "../Providers.ts"; /** List-tunnels request, minus the account id (supplied by the binding). */ export type ListTunnelsRequest = Omit< @@ -154,7 +155,8 @@ export class TunnelRead extends Binding.Service< */ export class TunnelReadPolicy extends Binding.Policy< TunnelReadPolicy, - (token: AccountApiToken) => Effect.Effect + (token: AccountApiToken) => Effect.Effect, + Providers >()("Cloudflare.TunnelRead") {} /** Runtime layer for {@link TunnelRead}. */ diff --git a/packages/alchemy/src/Cloudflare/Tunnel/TunnelReadWrite.ts b/packages/alchemy/src/Cloudflare/Tunnel/TunnelReadWrite.ts index 1429b5b9b..f1c024b02 100644 --- a/packages/alchemy/src/Cloudflare/Tunnel/TunnelReadWrite.ts +++ b/packages/alchemy/src/Cloudflare/Tunnel/TunnelReadWrite.ts @@ -11,6 +11,7 @@ import { } from "./TunnelBinding.ts"; import { readClient, type TunnelReadClient } from "./TunnelRead.ts"; import { writeClient, type TunnelWriteClient } from "./TunnelWrite.ts"; +import type { Providers } from "../Providers.ts"; /** Combined read + write tunnel operations. */ export interface TunnelReadWriteClient @@ -78,7 +79,8 @@ export class TunnelReadWrite extends Binding.Service< */ export class TunnelReadWritePolicy extends Binding.Policy< TunnelReadWritePolicy, - (token: AccountApiToken) => Effect.Effect + (token: AccountApiToken) => Effect.Effect, + Providers >()("Cloudflare.TunnelReadWrite") {} /** Runtime layer for {@link TunnelReadWrite}. */ diff --git a/packages/alchemy/src/Cloudflare/Tunnel/TunnelWrite.ts b/packages/alchemy/src/Cloudflare/Tunnel/TunnelWrite.ts index 8115285ba..ea6e1cc21 100644 --- a/packages/alchemy/src/Cloudflare/Tunnel/TunnelWrite.ts +++ b/packages/alchemy/src/Cloudflare/Tunnel/TunnelWrite.ts @@ -20,11 +20,12 @@ import type { AccountApiToken } from "../ApiToken/AccountApiToken.ts"; import type { CloudflareEnvironment } from "../CloudflareEnvironment.ts"; import type { Worker } from "../Workers/Worker.ts"; import { - authorizeWith, makeTunnelClient, makeTunnelPolicyLive, type TunnelToken, } from "./TunnelBinding.ts"; +import { authorizeWith } from "../HttpClientUtils.ts"; +import type { Providers } from "../Providers.ts"; /** Create-tunnel request, minus the account id (supplied by the binding). */ export type CreateTunnelRequest = Omit< @@ -180,7 +181,8 @@ export class TunnelWrite extends Binding.Service< */ export class TunnelWritePolicy extends Binding.Policy< TunnelWritePolicy, - (token: AccountApiToken) => Effect.Effect + (token: AccountApiToken) => Effect.Effect, + Providers >()("Cloudflare.TunnelWrite") {} /** Runtime layer for {@link TunnelWrite}. */ diff --git a/packages/alchemy/src/Cloudflare/Vectorize/VectorizeIndexBinding.ts b/packages/alchemy/src/Cloudflare/Vectorize/VectorizeIndexBinding.ts index 7902404d4..eb9af8a0b 100644 --- a/packages/alchemy/src/Cloudflare/Vectorize/VectorizeIndexBinding.ts +++ b/packages/alchemy/src/Cloudflare/Vectorize/VectorizeIndexBinding.ts @@ -6,6 +6,7 @@ import * as Binding from "../../Binding.ts"; import type { ResourceLike } from "../../Resource.ts"; import { isWorker, WorkerEnvironment } from "../Workers/Worker.ts"; import type { VectorizeIndex } from "./VectorizeIndex.ts"; +import type { Providers } from "../Providers.ts"; export interface VectorizeIndexClient { /** @@ -83,7 +84,8 @@ export const VectorizeIndexBindingLive = Layer.effect( export class VectorizeIndexBindingPolicy extends Binding.Policy< VectorizeIndexBindingPolicy, - (index: VectorizeIndex) => Effect.Effect + (index: VectorizeIndex) => Effect.Effect, + Providers >()("Cloudflare.Vectorize.IndexBinding") {} export const VectorizeIndexBindingPolicyLive = diff --git a/packages/alchemy/src/Cloudflare/Workers/CronEventSource.ts b/packages/alchemy/src/Cloudflare/Workers/CronEventSource.ts index 56d9cab1f..1d730669e 100644 --- a/packages/alchemy/src/Cloudflare/Workers/CronEventSource.ts +++ b/packages/alchemy/src/Cloudflare/Workers/CronEventSource.ts @@ -7,6 +7,7 @@ import type { ResourceLike } from "../../Resource.ts"; import { RuntimeContext } from "../../RuntimeContext.ts"; import type { FunctionContext } from "../../Serverless/Function.ts"; import { isWorker, isWorkerEvent } from "./Worker.ts"; +import type { Providers } from "../Providers.ts"; /** * Subscribe to Cloudflare Cron Triggers with an Effect handler. @@ -47,7 +48,8 @@ export class CronEventSource extends Context.Service< export class CronEventSourcePolicy extends Binding.Policy< CronEventSourcePolicy, - (expression: string) => Effect.Effect + (expression: string) => Effect.Effect, + Providers >()("Cloudflare.Workers.CronEventSource") {} export const CronEventSourcePolicyLive = CronEventSourcePolicy.layer.succeed( diff --git a/packages/alchemy/src/Cloudflare/Workers/DurableObjectBridge.ts b/packages/alchemy/src/Cloudflare/Workers/DurableObjectBridge.ts index 0b647400d..acd8af940 100644 --- a/packages/alchemy/src/Cloudflare/Workers/DurableObjectBridge.ts +++ b/packages/alchemy/src/Cloudflare/Workers/DurableObjectBridge.ts @@ -64,17 +64,19 @@ export const makeDurableObjectBridge = this.#instance = state.blockConcurrencyWhile(() => this.#exported.pipe( - Effect.flatMap(({ constructor, services }) => - constructor.pipe( - Effect.provide( - Layer.succeed( - DurableObjectState, - fromDurableObjectState(this.#state), - ).pipe(Layer.provideMerge(Layer.succeedContext(services))), + Effect.flatMap(({ constructor, services }) => { + const context = Layer.succeed( + DurableObjectState, + fromDurableObjectState(this.#state), + ).pipe(Layer.provideMerge(Layer.succeedContext(services))); + return constructor.pipe( + Effect.provide(context), + Effect.flatMap((instance) => + instance.pipe(Effect.provide(context)), ), Effect.map((instance) => ({ instance, services })), - ), - ), + ); + }), Effect.provide(this.#globalContext), Effect.runPromise, ), diff --git a/packages/alchemy/src/Cloudflare/Workers/DurableObjectChatPersistence.ts b/packages/alchemy/src/Cloudflare/Workers/DurableObjectChatPersistence.ts index 0e27ba713..ffa3a5022 100644 --- a/packages/alchemy/src/Cloudflare/Workers/DurableObjectChatPersistence.ts +++ b/packages/alchemy/src/Cloudflare/Workers/DurableObjectChatPersistence.ts @@ -6,6 +6,7 @@ import { PersistenceError, type BackingPersistenceStore, } from "effect/unstable/persistence/Persistence"; +import { RuntimeContext } from "../../RuntimeContext.ts"; import { DurableObjectState } from "./DurableObjectState.ts"; /** @@ -93,7 +94,10 @@ export const DurableObjectChatPersistence = Layer.effect(BackingPersistence)( get: (key) => storage .get(prefixed(key)) - .pipe(Effect.mapError(wrapErr("get", key))), + .pipe( + Effect.mapError(wrapErr("get", key)), + Effect.provide(RuntimeContext.phantom), + ), getMany: (keys) => storage.get(keys.map(prefixed)).pipe( Effect.mapError(wrapErr("getMany")), @@ -103,21 +107,32 @@ export const DurableObjectChatPersistence = Layer.effect(BackingPersistence)( object | undefined >, ), + Effect.provide(RuntimeContext.phantom), ), set: (key, value, _ttl) => storage .put(prefixed(key), value) - .pipe(Effect.mapError(wrapErr("set", key))), + .pipe( + Effect.mapError(wrapErr("set", key)), + Effect.provide(RuntimeContext.phantom), + ), setMany: (entries) => storage .put( Object.fromEntries(entries.map(([k, v]) => [prefixed(k), v])), ) - .pipe(Effect.mapError(wrapErr("setMany"))), + .pipe( + Effect.mapError(wrapErr("setMany")), + Effect.provide(RuntimeContext.phantom), + ), remove: (key) => storage .delete(prefixed(key)) - .pipe(Effect.asVoid, Effect.mapError(wrapErr("remove", key))), + .pipe( + Effect.asVoid, + Effect.mapError(wrapErr("remove", key)), + Effect.provide(RuntimeContext.phantom), + ), clear: storage.list({ prefix: `${storeId}:` }).pipe( Effect.flatMap((map) => { const ks = [...map.keys()]; @@ -125,6 +140,7 @@ export const DurableObjectChatPersistence = Layer.effect(BackingPersistence)( return Effect.asVoid(storage.delete(ks)); }), Effect.mapError(wrapErr("clear")), + Effect.provide(RuntimeContext.phantom), ), } satisfies BackingPersistenceStore; }), diff --git a/packages/alchemy/src/Cloudflare/Workers/DurableObjectNamespace.ts b/packages/alchemy/src/Cloudflare/Workers/DurableObjectNamespace.ts index 645653282..f9168d1c9 100644 --- a/packages/alchemy/src/Cloudflare/Workers/DurableObjectNamespace.ts +++ b/packages/alchemy/src/Cloudflare/Workers/DurableObjectNamespace.ts @@ -2,6 +2,7 @@ import type * as cf from "@cloudflare/workers-types"; import * as Context from "effect/Context"; import * as Effect from "effect/Effect"; import * as Layer from "effect/Layer"; +import type { Scope } from "effect/Scope"; import type { HttpServerError } from "effect/unstable/http/HttpServerError"; import * as HttpServerRequest from "effect/unstable/http/HttpServerRequest"; import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse"; @@ -10,10 +11,15 @@ import type { HttpEffect } from "../../Http.ts"; import type { Input } from "../../Input.ts"; import * as Output from "../../Output.ts"; import { ALCHEMY_PHASE } from "../../Phase.ts"; -import type { PlatformServices } from "../../Platform.ts"; +import type { MainRpc, PlatformServices } from "../../Platform.ts"; +import type { RuntimeContext } from "../../RuntimeContext.ts"; import { effectClass, taggedFunction } from "../../Util/effect.ts"; import { asEffect } from "../../Util/types.ts"; -import { DurableObjectState } from "./DurableObjectState.ts"; +import type { Container } from "../Container/Container.ts"; +import { + DurableObjectState, + fromDurableObjectState, +} from "./DurableObjectState.ts"; import { makeRpcStub } from "./Rpc.ts"; import { type DurableWebSocket } from "./WebSocket.ts"; import { Worker, WorkerEnvironment, type WorkerServices } from "./Worker.ts"; @@ -21,7 +27,7 @@ import { Worker, WorkerEnvironment, type WorkerServices } from "./Worker.ts"; export interface DurableObjectExport { readonly kind: "durableObject"; readonly constructor: Effect.Effect< - DurableObjectShape, + Effect.Effect, never, DurableObjectState >; @@ -79,7 +85,7 @@ export interface DurableObjectNamespace< } export interface DurableObjectShape { - fetch?: HttpEffect; + fetch?: HttpEffect; alarm?: ( alarmInfo?: AlarmInvocationInfo, ) => Effect.Effect; @@ -140,31 +146,38 @@ export interface DurableObjectNamespaceClass extends Effect.Effect< | Dependencies | Effect.Effect, never, Req>, ): Effect.Effect, never, Worker | Req>; - make( + make( impl: Effect.Effect< - Effect.Effect, + Effect.Effect< + Shape, + never, + RuntimeContext | DurableObjectState | Scope + >, never, - InitReq + DurableObjectServices | Req >, - ): Layer.Layer< - Self, - never, - Worker | Exclude - >; + ): Layer.Layer; }; }; (): { - ( + < + Shape extends MainRpc, + Req extends DurableObjectServices | Container.Application = never, + >( name: string, impl: Effect.Effect< - Effect.Effect, + Effect.Effect< + Shape, + never, + RuntimeContext | DurableObjectState | Scope + >, never, - InitReq + Req >, ): Effect.Effect< DurableObjectNamespace, never, - Worker | Exclude + Worker | Extract> > & { new (_: never): Shape; }; @@ -175,11 +188,7 @@ export interface DurableObjectNamespaceClass extends Effect.Effect< ): DurableObjectNamespaceLike; ( name: string, - impl: Effect.Effect< - Effect.Effect, - never, - InitReq - >, + impl: Effect.Effect, ): Effect.Effect< DurableObjectNamespace, never, @@ -836,7 +845,9 @@ export const DurableObjectNamespace: DurableObjectNamespaceClass = const make = Effect.fnUntraced(function* ( impl: Effect.Effect< - Effect.Effect + Effect.Effect, + never, + DurableObjectState >, ) { // Register the local DO binding (no `scriptName`) and obtain the @@ -845,12 +856,29 @@ export const DurableObjectNamespace: DurableObjectNamespaceClass = // and also return it so a `Layer.effect(tag, make(impl))` Layer // resolves the tag to a concrete namespace value. const self = yield* binding(); + const phase = yield* ALCHEMY_PHASE; + const constructor = impl.pipe( + Effect.provide( + Layer.succeed(DurableObjectNamespaceScope, self as any), + ), + ); + if (phase === "plan") { + // during plan time, we evaluate the constructor with a mock DurableObjectState + // to trigger discovery of bindings + yield* constructor.pipe( + Effect.provide( + Layer.succeed( + DurableObjectState, + // mock during plan time + fromDurableObjectState({ storage: {} } as any), + ), + ), + ); + } yield* (yield* Worker).export(namespace, { kind: "durableObject", // initialize the object's constructor (apply infra dependencies) - constructor: yield* impl.pipe( - Effect.provideService(DurableObjectNamespaceScope, self as any), - ), + constructor, // grab the object's infra dependencies so we can apply them when calling the instance's methods services: yield* Effect.context>(), } satisfies DurableObjectExport); @@ -869,7 +897,19 @@ export const DurableObjectNamespace: DurableObjectNamespaceClass = } else if (Effect.isEffect(propsOrImpl)) { // inline Effect DO return effectClass( - Effect.tap(binding(), () => make(propsOrImpl as any)), + Effect.tap(binding(), () => + make(propsOrImpl as any).pipe( + Effect.provideService( + DurableObjectState, + fromDurableObjectState( + // everything is lazy, so this should build a proper mock + { + storage: {}, + } as any, + ), + ), + ), + ), ); } else { // Tagged Effect DO. Yielding the class resolves the `tag`, which diff --git a/packages/alchemy/src/Cloudflare/Workers/DurableObjectState.ts b/packages/alchemy/src/Cloudflare/Workers/DurableObjectState.ts index d630dc64c..dfd7044fe 100644 --- a/packages/alchemy/src/Cloudflare/Workers/DurableObjectState.ts +++ b/packages/alchemy/src/Cloudflare/Workers/DurableObjectState.ts @@ -1,6 +1,7 @@ import type * as cf from "@cloudflare/workers-types"; import * as Context from "effect/Context"; import * as Effect from "effect/Effect"; +import type { RuntimeContext } from "../../RuntimeContext.ts"; import { fromDurableObjectStorage, type DurableObjectStorage, @@ -14,23 +15,36 @@ export class DurableObjectState extends Context.Service< readonly storage: DurableObjectStorage; container?: cf.Container; blockConcurrencyWhile( - callback: () => Effect.Effect, - ): Effect.Effect; - acceptWebSocket(ws: DurableWebSocket, tags?: string[]): Effect.Effect; - getWebSockets(tag?: string): Effect.Effect; + callback: () => Effect.Effect, + ): Effect.Effect; + acceptWebSocket( + ws: DurableWebSocket, + tags?: string[], + ): Effect.Effect; + getWebSockets( + tag?: string, + ): Effect.Effect; setWebSocketAutoResponse( maybeReqResp?: cf.WebSocketRequestResponsePair, - ): Effect.Effect; - getWebSocketAutoResponse(): Effect.Effect; + ): Effect.Effect; + getWebSocketAutoResponse(): Effect.Effect< + cf.WebSocketRequestResponsePair | null, + never, + RuntimeContext + >; getWebSocketAutoResponseTimestamp( ws: cf.WebSocket, - ): Effect.Effect; + ): Effect.Effect; setHibernatableWebSocketEventTimeout( timeoutMs?: number, - ): Effect.Effect; - getHibernatableWebSocketEventTimeout(): Effect.Effect; - getTags(ws: cf.WebSocket): Effect.Effect; - abort(reason?: string): Effect.Effect; + ): Effect.Effect; + getHibernatableWebSocketEventTimeout(): Effect.Effect< + number | null, + never, + RuntimeContext + >; + getTags(ws: cf.WebSocket): Effect.Effect; + abort(reason?: string): Effect.Effect; } >()("Cloudflare.DurableObjectState") {} diff --git a/packages/alchemy/src/Cloudflare/Workers/DurableObjectStorage.ts b/packages/alchemy/src/Cloudflare/Workers/DurableObjectStorage.ts index 9fcc55a10..5f9683587 100644 --- a/packages/alchemy/src/Cloudflare/Workers/DurableObjectStorage.ts +++ b/packages/alchemy/src/Cloudflare/Workers/DurableObjectStorage.ts @@ -1,6 +1,7 @@ import type * as cf from "@cloudflare/workers-types"; import * as Effect from "effect/Effect"; import * as Stream from "effect/Stream"; +import type { RuntimeContext } from "../../RuntimeContext.ts"; // --------------------------------------------------------------------------- // SqlStorage — Effect-native wrapper around cf.SqlStorage @@ -12,14 +13,16 @@ export interface SqlCursor< T extends Record, > extends Stream.Stream { next(): Effect.Effect< - { done?: false; value: T } | { done: true; value?: never } + { done?: false; value: T } | { done: true; value?: never }, + never, + RuntimeContext >; - toArray(): Effect.Effect; - one(): Effect.Effect; - raw(): Stream.Stream; + toArray(): Effect.Effect; + one(): Effect.Effect; + raw(): Stream.Stream; readonly columnNames: string[]; - readonly rowsRead: Effect.Effect; - readonly rowsWritten: Effect.Effect; + readonly rowsRead: Effect.Effect; + readonly rowsWritten: Effect.Effect; } export interface SqlStorage { @@ -33,7 +36,7 @@ export interface SqlStorage { exec>( query: string, ...bindings: any[] - ): Effect.Effect>; + ): Effect.Effect, never, RuntimeContext>; readonly databaseSize: number; } @@ -75,40 +78,42 @@ export interface DurableObjectTransaction { get( key: string, options?: cf.DurableObjectGetOptions, - ): Effect.Effect; + ): Effect.Effect; get( keys: string[], options?: cf.DurableObjectGetOptions, - ): Effect.Effect>; + ): Effect.Effect, never, RuntimeContext>; list( options?: cf.DurableObjectListOptions, - ): Effect.Effect>; + ): Effect.Effect, never, RuntimeContext>; put( key: string, value: T, options?: cf.DurableObjectPutOptions, - ): Effect.Effect; + ): Effect.Effect; put( entries: Record, options?: cf.DurableObjectPutOptions, - ): Effect.Effect; + ): Effect.Effect; delete( key: string, options?: cf.DurableObjectPutOptions, - ): Effect.Effect; + ): Effect.Effect; delete( keys: string[], options?: cf.DurableObjectPutOptions, - ): Effect.Effect; - rollback(): Effect.Effect; + ): Effect.Effect; + rollback(): Effect.Effect; getAlarm( options?: cf.DurableObjectGetAlarmOptions, - ): Effect.Effect; + ): Effect.Effect; setAlarm( scheduledTime: number | Date, options?: cf.DurableObjectSetAlarmOptions, - ): Effect.Effect; - deleteAlarm(options?: cf.DurableObjectSetAlarmOptions): Effect.Effect; + ): Effect.Effect; + deleteAlarm( + options?: cf.DurableObjectSetAlarmOptions, + ): Effect.Effect; } // --------------------------------------------------------------------------- @@ -119,50 +124,59 @@ export interface DurableObjectStorage { get( key: string, options?: cf.DurableObjectGetOptions, - ): Effect.Effect; + ): Effect.Effect; get( keys: string[], options?: cf.DurableObjectGetOptions, - ): Effect.Effect>; + ): Effect.Effect, never, RuntimeContext>; list( options?: cf.DurableObjectListOptions, - ): Effect.Effect>; + ): Effect.Effect, never, RuntimeContext>; put( key: string, value: T, options?: cf.DurableObjectPutOptions, - ): Effect.Effect; + ): Effect.Effect; put( entries: Record, options?: cf.DurableObjectPutOptions, - ): Effect.Effect; + ): Effect.Effect; delete( key: string, options?: cf.DurableObjectPutOptions, - ): Effect.Effect; + ): Effect.Effect; delete( keys: string[], options?: cf.DurableObjectPutOptions, - ): Effect.Effect; - deleteAll(options?: cf.DurableObjectPutOptions): Effect.Effect; + ): Effect.Effect; + deleteAll( + options?: cf.DurableObjectPutOptions, + ): Effect.Effect; transaction( - closure: (txn: DurableObjectTransaction) => Effect.Effect, - ): Effect.Effect; + closure: ( + txn: DurableObjectTransaction, + ) => Effect.Effect, + ): Effect.Effect; getAlarm( options?: cf.DurableObjectGetAlarmOptions, - ): Effect.Effect; + ): Effect.Effect; setAlarm( scheduledTime: number | Date, options?: cf.DurableObjectSetAlarmOptions, - ): Effect.Effect; - deleteAlarm(options?: cf.DurableObjectSetAlarmOptions): Effect.Effect; - sync(): Effect.Effect; + ): Effect.Effect; + deleteAlarm( + options?: cf.DurableObjectSetAlarmOptions, + ): Effect.Effect; + sync(): Effect.Effect; sql: SqlStorage; kv: cf.SyncKvStorage; - transactionSync(closure: () => T): T; - getCurrentBookmark(): Effect.Effect; - getBookmarkForTime(timestamp: number | Date): Effect.Effect; - onNextSessionRestoreBookmark(bookmark: string): Effect.Effect; + getCurrentBookmark(): Effect.Effect; + getBookmarkForTime( + timestamp: number | Date, + ): Effect.Effect; + onNextSessionRestoreBookmark( + bookmark: string, + ): Effect.Effect; } // --------------------------------------------------------------------------- @@ -254,7 +268,6 @@ export const fromDurableObjectStorage = ( sync: () => Effect.tryPromise(() => storage.sync()), sql: fromSqlStorage(storage.sql), kv: storage.kv, - transactionSync: (closure: () => T) => storage.transactionSync(closure), getCurrentBookmark: () => Effect.tryPromise(() => storage.getCurrentBookmark()), getBookmarkForTime: (timestamp: number | Date) => diff --git a/packages/alchemy/src/Cloudflare/Workers/Fetch.ts b/packages/alchemy/src/Cloudflare/Workers/Fetch.ts index 8eddb522f..a430f9808 100644 --- a/packages/alchemy/src/Cloudflare/Workers/Fetch.ts +++ b/packages/alchemy/src/Cloudflare/Workers/Fetch.ts @@ -10,6 +10,7 @@ import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"; import * as UrlParams from "effect/unstable/http/UrlParams"; import * as Binding from "../../Binding.ts"; import { isWorker, type Worker, WorkerEnvironment } from "./Worker.ts"; +import type { Providers } from "../Providers.ts"; /** * @binding @@ -123,7 +124,8 @@ const doFetch = ( export class FetchPolicy extends Binding.Policy< FetchPolicy, - (worker: Worker) => Effect.Effect + (worker: Worker) => Effect.Effect, + Providers >()("Cloudflare.Fetch") {} export const FetchPolicyLive = FetchPolicy.layer.succeed( diff --git a/packages/alchemy/src/Cloudflare/Workers/GitHubRepositoryEventSource.ts b/packages/alchemy/src/Cloudflare/Workers/GitHubRepositoryEventSource.ts index 296412767..2ba3b9f0b 100644 --- a/packages/alchemy/src/Cloudflare/Workers/GitHubRepositoryEventSource.ts +++ b/packages/alchemy/src/Cloudflare/Workers/GitHubRepositoryEventSource.ts @@ -14,6 +14,7 @@ import { import { Webhook } from "../../GitHub/Webhook.ts"; import * as Output from "../../Output.ts"; import { isWorker, isWorkerEvent, Worker } from "./Worker.ts"; +import type { Providers } from "../Providers.ts"; /** * Deploy-time half of the GitHub event source for Cloudflare Workers. @@ -28,7 +29,8 @@ import { isWorker, isWorkerEvent, Worker } from "./Worker.ts"; */ export class GitHubRepositoryEventSourcePolicy extends Binding.Policy< GitHubRepositoryEventSourcePolicy, - (props: RepositoryEventSourceProps) => Effect.Effect + (props: RepositoryEventSourceProps) => Effect.Effect, + Providers >()("GitHub.RepositoryEventSourcePolicy") {} export const GitHubRepositoryEventSourcePolicyLive = diff --git a/packages/alchemy/src/Cloudflare/Workers/RpcDurableObjectNamespace.ts b/packages/alchemy/src/Cloudflare/Workers/RpcDurableObjectNamespace.ts index 46d971e93..2858bb013 100644 --- a/packages/alchemy/src/Cloudflare/Workers/RpcDurableObjectNamespace.ts +++ b/packages/alchemy/src/Cloudflare/Workers/RpcDurableObjectNamespace.ts @@ -8,6 +8,7 @@ import * as RpcClientError from "effect/unstable/rpc/RpcClientError"; import type { Dependencies } from "../../Dependencies.ts"; import type { HttpEffect } from "../../Http.ts"; import type { Input } from "../../Input.ts"; +import type { RuntimeContext } from "../../RuntimeContext.ts"; import { effectClass, taggedFunction } from "../../Util/effect.ts"; import { DurableObjectNamespace, @@ -110,9 +111,9 @@ export interface RpcDurableObjectNamespaceClass extends Effect.Effect< make( impl: Effect.Effect< Effect.Effect< - Effect.Effect, never, InnerR>, + Effect.Effect, never, InnerR | RuntimeContext>, never, - DurableObjectServices + DurableObjectServices | RuntimeContext >, ConfigError, InitReq @@ -129,9 +130,9 @@ export interface RpcDurableObjectNamespaceClass extends Effect.Effect< props: { readonly schema: RpcGroup.RpcGroup }, impl: Effect.Effect< Effect.Effect< - Effect.Effect, never, InnerR>, + Effect.Effect, never, InnerR | RuntimeContext>, never, - DurableObjectServices + DurableObjectServices | RuntimeContext >, ConfigError, InitReq diff --git a/packages/alchemy/src/Cloudflare/Workers/RpcWorker.ts b/packages/alchemy/src/Cloudflare/Workers/RpcWorker.ts index e7adc6d29..9d24f8114 100644 --- a/packages/alchemy/src/Cloudflare/Workers/RpcWorker.ts +++ b/packages/alchemy/src/Cloudflare/Workers/RpcWorker.ts @@ -13,6 +13,7 @@ import { import type * as RpcClientError from "effect/unstable/rpc/RpcClientError"; import type { Dependencies } from "../../Dependencies.ts"; import type { HttpEffect } from "../../Http.ts"; +import type { InputProps } from "../../Input.ts"; import type { Rpc as RpcShape } from "../../Rpc.ts"; import { effectClass, taggedFunction } from "../../Util/effect.ts"; import type { Worker, WorkerProps } from "./Worker.ts"; @@ -22,10 +23,7 @@ import { Worker as WorkerCtor, WorkerEnvironment } from "./Worker.ts"; * Props for {@link RpcWorker}. Same shape as {@link WorkerProps} with * an additional `schema` field carrying the rpc group definition. */ -export type RpcWorkerProps = Omit< - WorkerProps, - "schema" -> & { +export type RpcWorkerProps = { /** * The {@link RpcGroup.RpcGroup} served on this worker's `fetch` * handler. The same value should be importable by any consumer of @@ -106,6 +104,7 @@ export interface RpcWorkerClass extends Effect.Effect< ): RpcWorkerYieldable & { new (_: never): {}; make( + props: InputProps, impl: Effect.Effect< Effect.Effect, never, InnerR>, ConfigError, @@ -116,7 +115,7 @@ export interface RpcWorkerClass extends Effect.Effect< /** Inline-impl form. */ ( id: string, - props: RpcWorkerProps, + props: RpcWorkerProps & InputProps, impl: Effect.Effect< Effect.Effect, never, InnerR>, ConfigError, @@ -515,19 +514,20 @@ const wrapImpl = (impl: Effect.Effect>>) => ); const buildModular = (id: string, props: RpcWorkerProps) => { - const { schema, ...workerProps } = props; + const { schema } = props; // Delegate to `Cloudflare.Worker()(id, props)` (modular form) // so we inherit its `static make(impl)` plumbing for free. We just // wrap the user's HttpEffect-returning impl into the `{ fetch }` // shape `Cloudflare.Worker` expects, and stash the rpc schema on // the class so `RpcWorker.bind(WorkerClass)` can recover it. - const Underlying: any = (WorkerCtor as any)()(id, workerProps); + const Underlying: any = (WorkerCtor as any)()(id); // `Underlying` is itself an Effect (the no-impl Worker class), so hand it // straight to `effectClass` rather than reaching for a removed `.asEffect()`. const klass = class extends effectClass(Underlying as Effect.Effect) { static make = ( + props: InputProps, impl: Effect.Effect>>, - ): Layer.Layer => Underlying.make(wrapImpl(impl)); + ): Layer.Layer => Underlying.make(props, wrapImpl(impl)); } as unknown as Record; klass[SchemaSymbol] = schema; return klass; diff --git a/packages/alchemy/src/Cloudflare/Workers/ScheduledEvents.ts b/packages/alchemy/src/Cloudflare/Workers/ScheduledEvents.ts index 704bed697..0c201dd48 100644 --- a/packages/alchemy/src/Cloudflare/Workers/ScheduledEvents.ts +++ b/packages/alchemy/src/Cloudflare/Workers/ScheduledEvents.ts @@ -1,6 +1,7 @@ import * as Effect from "effect/Effect"; import * as Option from "effect/Option"; import * as Stream from "effect/Stream"; +import type { RuntimeContext } from "../../RuntimeContext.ts"; import { DurableObjectState } from "./DurableObjectState.ts"; import type { SqlStorageValue } from "./DurableObjectStorage.ts"; @@ -99,7 +100,7 @@ export const cancelEvent = Effect.fnUntraced(function* (id: string) { export const listEvents: Effect.Effect< ScheduledEvent[], never, - DurableObjectState + DurableObjectState | RuntimeContext > = Effect.gen(function* () { yield* ensureTable; const ctx = yield* DurableObjectState; @@ -132,7 +133,7 @@ export const listEvents: Effect.Effect< export const processScheduledEvents: Effect.Effect< ScheduledEvent[], never, - DurableObjectState + DurableObjectState | RuntimeContext > = Effect.gen(function* () { yield* ensureTable; const ctx = yield* DurableObjectState; @@ -167,19 +168,22 @@ export const processScheduledEvents: Effect.Effect< /** * Set the DO alarm to the earliest pending event, or clear it if none remain. */ -const reconcileAlarm: Effect.Effect = - Effect.gen(function* () { - const ctx = yield* DurableObjectState; - - const next = yield* (yield* ctx.storage.sql.exec<{ - run_at: number; - }>( - `SELECT run_at FROM alchemy_scheduled_events ORDER BY run_at ASC LIMIT 1`, - )).pipe(Stream.take(1), Stream.runHead); - - if (Option.isSome(next)) { - yield* ctx.storage.setAlarm(next.value.run_at); - } else { - yield* ctx.storage.deleteAlarm(); - } - }); +const reconcileAlarm: Effect.Effect< + void, + never, + DurableObjectState | RuntimeContext +> = Effect.gen(function* () { + const ctx = yield* DurableObjectState; + + const next = yield* (yield* ctx.storage.sql.exec<{ + run_at: number; + }>( + `SELECT run_at FROM alchemy_scheduled_events ORDER BY run_at ASC LIMIT 1`, + )).pipe(Stream.take(1), Stream.runHead); + + if (Option.isSome(next)) { + yield* ctx.storage.setAlarm(next.value.run_at); + } else { + yield* ctx.storage.deleteAlarm(); + } +}); diff --git a/packages/alchemy/src/Cloudflare/Workers/VersionMetadataBinding.ts b/packages/alchemy/src/Cloudflare/Workers/VersionMetadataBinding.ts index e19ddd9d1..c00cb7dec 100644 --- a/packages/alchemy/src/Cloudflare/Workers/VersionMetadataBinding.ts +++ b/packages/alchemy/src/Cloudflare/Workers/VersionMetadataBinding.ts @@ -4,6 +4,7 @@ import * as Binding from "../../Binding.ts"; import type { ResourceLike } from "../../Resource.ts"; import type { VersionMetadata as VersionMetadataLike } from "./VersionMetadata.ts"; import { isWorker, WorkerEnvironment } from "./Worker.ts"; +import type { Providers } from "../Providers.ts"; /** * Runtime value Cloudflare exposes for a `version_metadata` binding — the @@ -58,7 +59,8 @@ export const VersionMetadataBindingLive = Layer.effect( export class VersionMetadataBindingPolicy extends Binding.Policy< VersionMetadataBindingPolicy, - (versionMetadata: VersionMetadataLike) => Effect.Effect + (versionMetadata: VersionMetadataLike) => Effect.Effect, + Providers >()("Cloudflare.VersionMetadata") {} export const VersionMetadataBindingPolicyLive = diff --git a/packages/alchemy/src/Cloudflare/Workers/Worker.ts b/packages/alchemy/src/Cloudflare/Workers/Worker.ts index 9d82ebb5e..2adac8530 100644 --- a/packages/alchemy/src/Cloudflare/Workers/Worker.ts +++ b/packages/alchemy/src/Cloudflare/Workers/Worker.ts @@ -1,63 +1,42 @@ import type * as cf from "@cloudflare/workers-types"; import * as workers from "@distilled.cloud/cloudflare/workers"; -import * as zones from "@distilled.cloud/cloudflare/zones"; import type * as Config from "effect/Config"; import type { ConfigError } from "effect/Config"; import * as Context from "effect/Context"; -import * as Data from "effect/Data"; import * as Effect from "effect/Effect"; -import * as Path from "effect/Path"; -import * as Predicate from "effect/Predicate"; +import type * as Layer from "effect/Layer"; import * as Redacted from "effect/Redacted"; -import * as Schedule from "effect/Schedule"; -import * as Stream from "effect/Stream"; -import * as crypto from "node:crypto"; -import { Unowned } from "../../AdoptPolicy.ts"; -import * as Artifacts from "../../Artifacts.ts"; -import { hashDirectory, type MemoOptions } from "../../Build/Memo.ts"; +import { type MemoOptions } from "../../Build/Memo.ts"; import * as Bundle from "../../Bundle/Bundle.ts"; -import type { ScopedPlanStatusSession } from "../../Cli/Cli.ts"; -import { isResolved } from "../../Diff.ts"; +import type { Dependencies } from "../../Dependencies.ts"; import type { InputProps } from "../../Input.ts"; -import * as ProviderLayer from "../../Local/ProviderLayer.ts"; -import { Platform, type Main, type PlatformProps } from "../../Platform.ts"; -import * as Provider from "../../Provider.ts"; -import { Resource, type ResourceBinding } from "../../Resource.ts"; -import { Stack } from "../../Stack.ts"; +import type { Named, Tag } from "../../Named.ts"; +import { + Platform, + type Main, + type MainRpc, + type MakeShape, + type PlatformProps, + type PlatformServices, +} from "../../Platform.ts"; +import { Resource, type ResourceClassLike } from "../../Resource.ts"; +import type { Rpc } from "../../Rpc.ts"; +import type { RuntimeContext } from "../../RuntimeContext.ts"; import { CloudflareEnvironment } from "../CloudflareEnvironment.ts"; +import type { Container } from "../Container/Container.ts"; import type { DevContainerImage } from "../Container/ContainerApplication.ts"; import type { HyperdriveDevOrigin } from "../Hyperdrive/Hyperdrive.ts"; -import { CloudflareLogs } from "../Logs.ts"; import type { Providers } from "../Providers.ts"; -import { - readAssets, - uploadAssets, - type Assets, - type AssetsProps, -} from "./Assets.ts"; -import { getCompatibility } from "./Compatibility.ts"; -import { - isDurableObjectExport, - type DurableObjectExport, -} from "./DurableObjectNamespace.ts"; -import { LocalWorkerProvider } from "./LocalWorkerProvider.ts"; +import { type Assets, type AssetsProps } from "./Assets.ts"; +import { type DurableObjectExport } from "./DurableObjectNamespace.ts"; import { Request } from "./Request.ts"; -import { - bindWorkerAsyncBindings, - getCronBindings, -} from "./WorkerAsyncBindings.ts"; +import { bindWorkerAsyncBindings } from "./WorkerAsyncBindings.ts"; import type { WorkerBinding, WorkerBindingResource, WorkerBindings, - WorkerSettingsBinding, } from "./WorkerBinding.ts"; -import { - readPrebuiltWorkerBundle, - WorkerBundle, - type ModuleRule, -} from "./WorkerBundle.ts"; -import { createWorkerName } from "./WorkerName.ts"; +import { type ModuleRule } from "./WorkerBundle.ts"; import { makeWorkerRuntimeContext, type WorkerRuntimeContext, @@ -143,9 +122,11 @@ export type WorkerServices = | Request | WorkerExecutionContext | WorkerEnvironment - | CloudflareEnvironment; + | CloudflareEnvironment + | Container.Application; -export type WorkerShape = Main; +export type WorkerShape = Main & + MainRpc; export type WorkerEnv = Record< string, @@ -392,7 +373,7 @@ export type Worker = Resource< * ```typescript * Effect.gen(function* () { * // Phase 1: bind resources (runs at deploy time) - * const kv = yield* Cloudflare.KVNamespace.bind(MyKV); + * const kv = yield* Cloudflare.KV.ReadWriteNamespace(MyKV); * * return { * // Phase 2: runtime handlers (runs on each request) @@ -473,7 +454,7 @@ export type Worker = Resource< * { main: import.meta.filename }, * Effect.gen(function* () { * // init: bind resources - * const kv = yield* Cloudflare.KVNamespace.bind(MyKV); + * const kv = yield* Cloudflare.KV.ReadWriteNamespace(MyKV); * * return { * // runtime: use them @@ -511,7 +492,7 @@ export type Worker = Resource< * export default WorkerB.make( * Effect.gen(function* () { * // init: bind resources - * const kv = yield* Cloudflare.KVNamespace.bind(MyKV); + * const kv = yield* Cloudflare.KV.ReadWriteNamespace(MyKV); * * return { * // runtime: use them @@ -622,7 +603,7 @@ export type Worker = Resource< * @example Binding and using R2 * ```typescript * // init - * const bucket = yield* Cloudflare.R2Bucket.bind(MyBucket); + * const bucket = yield* Cloudflare.R2.ReadWrite(MyBucket); * * return { * fetch: Effect.gen(function* () { @@ -643,14 +624,14 @@ export type Worker = Resource< * ``` * * @section KV Namespace - * Bind a KV namespace with `Cloudflare.KVNamespace.bind`. KV provides + * Bind a KV namespace with `Cloudflare.KV.ReadWriteNamespace`. KV provides * eventually-consistent, low-latency key-value reads replicated * globally across Cloudflare's edge. * * @example Binding and using KV * ```typescript * // init - * const kv = yield* Cloudflare.KVNamespace.bind(MyKV); + * const kv = yield* Cloudflare.KV.ReadWriteNamespace(MyKV); * * return { * fetch: Effect.gen(function* () { @@ -755,36 +736,101 @@ export type Worker = Resource< * }; * ``` */ -export const Worker: Platform< - Worker, - WorkerServices, - WorkerShape, - WorkerRuntimeContext -> & { - < - const Bindings extends WorkerBindingProps = {}, - const Assets extends WorkerAssetsConfig | undefined = undefined, - Req = never, - >( - id: string, - props: - | InputProps> - | Effect.Effect< - InputProps>, - ConfigError, - Req - >, - ): Effect.Effect< - Worker<{ - [binding in keyof NormalizedBindings< - Bindings, - Assets - >]: NormalizedBindings[binding]; - }>, +export const Worker: ResourceClassLike & + Effect.Effect< + Worker & WorkerRuntimeContext & RuntimeContext, never, - Req | Providers - >; -} = Platform(WorkerTypeId, { + Worker + > & { + (): { + ( + id: Id, + ): Effect.Effect< + Worker & Rpc & Dependencies, + never, + Self | Extract> | Providers + > & + Named & { + new (_: never): MakeShape & Named & Tag; + of(shape: Shape & WorkerShape): MakeShape; + make( + props: + | InputProps + | Effect.Effect, ConfigError, PropsReq>, + impl: Effect.Effect, + ): Layer.Layer< + Self, + never, + | Extract> + | Providers + | Exclude + >; + }; + }; + (): { + < + const Id extends string, + Shape extends WorkerShape, + Req extends + | WorkerServices + | Container.Application + | PlatformServices + | Tag, + >( + id: Id, + props: InputProps, + impl: Effect.Effect, + ): Effect.Effect< + Worker & Rpc, + never, + Extract> | Providers + > & + Named & { + new (): MakeShape & Named & Tag; + }; + }; + < + const Bindings extends WorkerBindingProps = {}, + const Assets extends WorkerAssetsConfig | undefined = undefined, + Req = never, + >( + id: string, + props: + | InputProps> + | Effect.Effect< + InputProps>, + ConfigError, + Req + >, + ): Effect.Effect< + Worker<{ + [binding in keyof NormalizedBindings< + Bindings, + Assets + >]: NormalizedBindings[binding]; + }> & + Rpc<{}>, + never, + Req | Providers + >; + < + const Id extends string, + Shape extends WorkerShape, + Req extends + | WorkerServices + | Container.Application + | PlatformServices, + >( + id: string, + props: InputProps, + impl: Effect.Effect, + ): Effect.Effect< + Worker & Rpc, + never, + Extract> | Providers + > & + Named; + } = Platform(WorkerTypeId, { // Both hooks are wrapped in arrows so the imported references are resolved // at call time rather than at module-load time. Worker.ts forms import // cycles with both WorkerAsyncBindings.ts (which imports `isWorker` here) @@ -796,1709 +842,3 @@ export const Worker: Platform< bindWorkerAsyncBindings(resource as Worker, props), createRuntimeContext: (id) => makeWorkerRuntimeContext(id), }); - -class MissingDurableObjectNamespaces extends Data.TaggedError( - "MissingDurableObjectNamespaces", -)<{ - scriptName: string; - expected: string[]; -}> {} - -export const WorkerProvider = () => - ProviderLayer.select({ - live: () => LiveWorkerProvider(), - local: () => LocalWorkerProvider(), - }); - -export const LiveWorkerProvider = () => - Provider.effect( - Worker, - Effect.gen(function* () { - const path = yield* Path.Path; - - const bundler = yield* WorkerBundle; - const stack = yield* Stack; - - // const createScriptSubdomain = yield* workers.createScriptSubdomain; - // const deleteScript = yield* workers.deleteScript; - // const getScriptSubdomain = yield* workers.getScriptSubdomain; - // const getScriptSchedule = yield* workers.getScriptSchedule; - // const getScriptSettings = yield* workers.getScriptScriptAndVersionSetting; - // const getSubdomain = yield* workers.getSubdomain; - // const putScript = yield* workers.putScript; - // const putScriptSchedule = yield* workers.putScriptSchedule; - // const putDomain = yield* workers.putDomain; - // const listDomains = yield* workers.listDomains; - // const deleteDomain = yield* workers.deleteDomain; - // const listZones = yield* zones.listZones; - const telemetry = yield* CloudflareLogs; - - const getAccountSubdomain = (accountId: string) => - workers - .getSubdomain({ - accountId, - }) - .pipe(Effect.map((result) => result.subdomain)); - - // Toggle the workers.dev subdomain via `POST /subdomain` with - // `enabled: true | false`. Mirrors the upstream Alchemy - // implementation in `.vendor/alchemy/.../worker-subdomain.ts`. - // When enabling we also set `previewsEnabled: true` so the - // script is reachable both at its stable workers.dev URL and at - // version-preview URLs; on disable we send just `enabled: false`. - const setWorkerSubdomain = Effect.fn(function* ( - name: string, - enabled: boolean, - ) { - const { accountId } = yield* yield* CloudflareEnvironment; - return yield* workers.createScriptSubdomain({ - accountId, - scriptName: name, - enabled, - previewsEnabled: enabled ? true : undefined, - }); - }); - - // Convert non-ASCII hostnames (emoji, IDN, etc.) to punycode so the - // Cloudflare API receives the form it stores domains in. `new URL(...)` - // does IDNA via WHATWG URL parsing — `📦.alchemy.run` → `xn--5z8h.alchemy.run`. - const toPunycode = (hostname: string): string => { - try { - return new URL(`https://${hostname}`).hostname; - } catch { - return hostname; - } - }; - - const normalizeDomains = ( - domain: string | string[] | undefined, - ): string[] => - domain === undefined - ? [] - : Array.from( - new Set( - (Array.isArray(domain) ? domain : [domain]).map(toPunycode), - ), - ); - - const normalizeCrons = (crons: string[] | undefined): string[] => - Array.from(new Set(crons ?? [])); - - const getWorkerCrons = Effect.fn(function* (scriptName: string) { - const { accountId } = yield* yield* CloudflareEnvironment; - return yield* workers - .getScriptSchedule({ - accountId, - scriptName, - }) - .pipe( - Effect.map((response) => - normalizeCrons( - response.schedules.map((schedule) => schedule.cron), - ), - ), - Effect.catchTag("WorkerNotFound", () => Effect.succeed([])), - ); - }); - - const reconcileCrons = ( - scriptName: string, - desired: string[], - previous: string[], - session: ScopedPlanStatusSession, - ) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - const live = yield* getWorkerCrons(scriptName); - const desiredSorted = [...desired].sort(); - const liveSorted = [...live].sort(); - const changed = - desiredSorted.length !== liveSorted.length || - desiredSorted.some((cron, index) => cron !== liveSorted[index]); - - if (!changed) return live; - - if (desired.length > 0 || previous.length > 0 || live.length > 0) { - yield* session.note( - `Reconciling Cron Triggers (${desired.length}) ...`, - ); - } - - const result = yield* workers - .putScriptSchedule({ - accountId, - scriptName, - body: desired.map((cron) => ({ cron })), - }) - .pipe( - Effect.retry({ - while: (error) => error._tag === "WorkerNotFound", - schedule: Schedule.exponential(200).pipe( - Schedule.both(Schedule.recurs(15)), - ), - }), - ); - return normalizeCrons( - result.schedules.map((schedule) => schedule.cron), - ); - }); - - /** - * Infer the Cloudflare Zone ID for a given hostname by listing the - * account's zones and matching the hostname against each zone's name — - * walking up the DNS label hierarchy until a match is found. - */ - const inferZoneIdForHostname = ( - hostname: string, - zoneCache: Map, - ) => - Effect.gen(function* () { - const cached = zoneCache.get(hostname); - if (cached) return cached; - - const zoneList = yield* zones - .listZones({}) - .pipe(Effect.map((response) => response.result ?? [])); - for (const zone of zoneList) { - zoneCache.set(zone.name, zone.id); - } - - const parts = hostname.split("."); - for (let i = 0; i < parts.length - 1; i++) { - const candidate = parts.slice(i).join("."); - const match = zoneList.find((z) => z.name === candidate); - if (match) { - zoneCache.set(hostname, match.id); - return match.id; - } - } - return yield* Effect.die( - `Could not infer Cloudflare Zone for hostname "${hostname}". ` + - "Ensure the parent zone exists in this account.", - ); - }); - - const reconcileDomains = (scriptName: string, desired: string[]) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - // Always query the live state of domains attached to *this* - // Worker rather than trusting `_previous` from local state. - // State may have been wiped, populated by another machine, or - // simply be out of date. Without this we PUT domains that are - // already registered to this same Worker and Cloudflare - // returns a confusing "hostname already in use" error. - const liveAll = yield* workers - .listDomains({ - accountId, - service: scriptName, - }) - .pipe( - Effect.map((r) => - (r.result ?? []).flatMap((d) => - d.id && d.hostname && d.zoneId - ? [ - { - id: d.id, - hostname: d.hostname, - zoneId: d.zoneId, - service: d.service ?? undefined, - }, - ] - : [], - ), - ), - Effect.catch(() => Effect.succeed([])), - ); - - const desiredSet = new Set(desired); - const liveByHostname = new Map(liveAll.map((d) => [d.hostname, d])); - - // Detach what's no longer wanted. Use the live list so we - // don't try to delete domains we no longer track. - const toRemove = liveAll.filter((d) => !desiredSet.has(d.hostname)); - yield* Effect.all( - toRemove.map((d) => - workers - .deleteDomain({ accountId, domainId: d.id }) - .pipe(Effect.catchTag("DomainNotFound", () => Effect.void)), - ), - { concurrency: "unbounded" }, - ); - - if (desired.length === 0) return []; - - const zoneCache = new Map(); - - // Attach `hostname` to this Worker. Skip the PUT entirely if - // the hostname is already attached to *this* Worker — that's a - // no-op for Cloudflare and avoids the "already in use" 409. - // If it's attached to a *different* Worker, refuse with a - // clear message rather than silently re-routing traffic. - const attachDomain = Effect.fn(function* (hostname: string) { - const live = liveByHostname.get(hostname); - if (live) { - return { - hostname: live.hostname, - id: live.id, - zoneId: live.zoneId, - }; - } - - // Not attached to this Worker — but it could still belong - // to another Worker. Check before we try to PUT so we can - // emit a helpful error instead of the raw 409. - const otherOwner = yield* workers - .listDomains({ - accountId, - hostname, - }) - .pipe( - Effect.map((r) => - (r.result ?? []).find( - (d) => d.hostname === hostname && d.service !== scriptName, - ), - ), - Effect.catch(() => Effect.succeed(undefined)), - ); - if (otherOwner?.id) { - return yield* Effect.die( - new Error( - `Cannot attach hostname '${hostname}' to Worker '${scriptName}': ` + - `it is already attached to Worker '${otherOwner.service ?? ""}'. ` + - `Detach it from that Worker first, or pick a different hostname.`, - ), - ); - } - - const zoneId = yield* inferZoneIdForHostname(hostname, zoneCache); - // Same eventual-consistency window as `setWorkerSubdomain`: - // PUT /accounts/.../workers/domains right after `putScript` - // can return `WorkerNotFound` until Cloudflare's script - // registry has propagated. Retry on that specific tag. - const res = yield* workers - .putDomain({ - accountId, - hostname, - service: scriptName, - zoneId, - }) - .pipe( - Effect.retry({ - while: (error) => error._tag === "WorkerNotFound", - schedule: Schedule.exponential(200).pipe( - Schedule.both(Schedule.recurs(15)), - ), - }), - ); - return { - hostname, - id: res.id ?? "", - zoneId: res.zoneId ?? zoneId, - }; - }); - - const applied = yield* Effect.all(desired.map(attachDomain), { - concurrency: "unbounded", - }); - return applied; - }); - - const createAlchemyWorkerTags = (id: string) => [ - `alchemy:stack:${stack.name}`, - `alchemy:stage:${stack.stage}`, - `alchemy:id:${id}`, - ]; - - const hasAlchemyWorkerTags = ( - id: string, - tags: readonly string[] | undefined, - ) => { - const actualTags = new Set(tags ?? []); - return createAlchemyWorkerTags(id).every((tag) => actualTags.has(tag)); - }; - - const getDurableObjectNamespaces = ( - bindings: readonly WorkerSettingsBinding[] | null | undefined, - ) => { - const namespaces = Object.fromEntries( - (bindings ?? []).flatMap((binding) => - binding.type === "durable_object_namespace" && - binding.className && - binding.namespaceId - ? [[binding.className, binding.namespaceId]] - : [], - ), - ); - return namespaces; - }; - - const getExpectedDurableObjectClassNames = ( - bindings: readonly WorkerBinding[] | undefined, - workerName: string, - ) => - Array.from( - new Set( - bindings?.flatMap((binding) => - binding.type === "durable_object_namespace" && - binding.className && - (binding.scriptName === undefined || - binding.scriptName === workerName) - ? [binding.className] - : [], - ) ?? [], - ), - ); - - const getWorkerSettingsWithDurableObjects = Effect.fn(function* ( - scriptName: string, - expectedClassNames: readonly string[], - ) { - const { accountId } = yield* yield* CloudflareEnvironment; - return yield* workers - .getScriptScriptAndVersionSetting({ - accountId, - scriptName, - }) - .pipe( - Effect.map((settings) => { - const namespaces = getDurableObjectNamespaces(settings.bindings); - const missing = expectedClassNames.filter( - (className) => !namespaces[className], - ); - if (missing.length > 0) { - return Effect.fail( - new MissingDurableObjectNamespaces({ - scriptName, - expected: missing, - }), - ); - } - return Effect.succeed({ - settings, - durableObjectNamespaces: namespaces, - }); - }), - Effect.flatten, - Effect.retry({ - while: (error) => error._tag === "MissingDurableObjectNamespaces", - schedule: Schedule.exponential(100).pipe( - Schedule.both(Schedule.recurs(20)), - ), - }), - ); - }); - - const prepareAssets = Effect.fn(function* ( - assets: WorkerProps["assets"], - ) { - if (!assets) { - return undefined; - } - - if (typeof assets === "object" && "hash" in assets) { - const { hash: _, ...config } = assets; - return yield* readAssets(config); - } - - // Handle string path or AssetsProps - return yield* readAssets( - typeof assets === "string" ? { directory: assets } : assets, - ); - }); - - const prepareBundle = (id: string, props: WorkerProps) => - (props.bundle === false - ? readPrebuiltWorkerBundle({ - main: props.main!, - rules: props.rules, - }) - : bundler.build({ - id, - main: props.main!, - compatibility: getCompatibility(props), - entry: props.isExternal - ? { - kind: "external", - } - : { - kind: "effect", - exports: props.exports ?? {}, - }, - stack: { name: stack.name, stage: stack.stage }, - extraOptions: props.build, - }) - ).pipe(Artifacts.cached("build")); - - const hashScript = (script: string) => - Effect.sync(() => - crypto.createHash("sha256").update(script).digest("hex"), - ); - - const viteBuild = Effect.fn(function* (props: WorkerProps) { - const compatibility = getCompatibility(props); - // Loaded lazily: `./Vite.ts` pulls in `@distilled.cloud/cloudflare-vite-plugin` - // (~0.5s), which is only needed for vite-based workers at build time — - // not for every Worker definition at module-load time. - const Vite = yield* Effect.promise(() => import("./Vite.ts")); - const { assetsDirectory, serverBundle } = yield* Vite.viteBuild( - props.vite?.rootDir, - Object.fromEntries( - (yield* Effect.all( - Object.entries(props.env ?? {}).map( - Effect.fn(function* ([key, value]) { - return [ - key, - typeof value === "string" - ? value - : Redacted.isRedacted(value) && - typeof Redacted.value(value) === "string" - ? Redacted.value(value) - : Effect.isEffect(value) - ? yield* value as Effect.Effect - : undefined, - ]; - }), - ), - )).filter(([_, value]) => value !== undefined), - ), - { - compatibilityDate: compatibility.date, - compatibilityFlags: compatibility.flags, - }, - ); - - if (!assetsDirectory && !serverBundle) { - return yield* Effect.die( - new Error("Vite build produced neither server nor client output"), - ); - } - const [assets, bundle] = yield* Effect.all( - [ - assetsDirectory - ? readAssets({ - ...(props.assets && typeof props.assets !== "string" - ? props.assets - : undefined), - directory: assetsDirectory, - }) - : Effect.succeed(undefined), - serverBundle - ? Bundle.bundleOutputFromRolldownOutputBundle(serverBundle) - : Effect.succeed(undefined), - ], - { concurrency: "unbounded" }, - ); - return { assets, bundle }; - }); - - const prepareAssetsAndBundle = ( - id: string, - props: WorkerProps, - opts: { skipAssetsRead?: boolean } = {}, - ) => - Effect.gen(function* () { - if (props.script !== undefined) { - const [assets, bundleHash] = yield* Effect.all( - [ - opts.skipAssetsRead - ? Effect.succeed(undefined) - : prepareAssets(props.assets), - hashScript(props.script), - ], - { concurrency: "unbounded" }, - ); - return { - assets, - bundle: { - files: [{ path: "main.js", content: props.script }], - hash: bundleHash, - }, - }; - } - if (props.vite) { - const [{ assets, bundle }, input] = yield* Effect.all( - [ - viteBuild(props), - // hashDirectory expects `{ cwd, memo }`. The vite props - // store the project root under `rootDir`, so map it - // here. Without this, `cwd` falls back to - // `process.cwd()` and the input hash is computed over - // the wrong directory tree (often the entire monorepo - // root), making it both slow and unable to detect - // changes scoped to the actual Vite project. - hashDirectory({ - cwd: props.vite.rootDir, - memo: props.vite.memo, - }), - ], - { concurrency: "unbounded" }, - ); - return { assets, bundle, input }; - } - const [assets, bundle] = yield* Effect.all( - [ - opts.skipAssetsRead - ? Effect.succeed(undefined) - : prepareAssets(props.assets), - prepareBundle(id, props), - ], - { concurrency: "unbounded" }, - ); - return { assets, bundle }; - }).pipe( - Effect.map(({ assets, bundle, input }) => ({ - assets, - bundle: { - main: bundle?.files[0].path, - files: bundle?.files.map( - (file) => - new File([file.content as BlobPart], file.path, { - type: contentTypeFromExtension(path.extname(file.path)), - }), - ), - }, - hash: { - assets: assets?.hash, - bundle: bundle?.hash, - input, - } satisfies Worker["Attributes"]["hash"], - })), - ); - - const normalizePrebuiltAssets = ( - assets: WorkerProps["assets"], - output: Worker["Attributes"] | undefined, - ) => { - if (!Predicate.hasProperty(assets, "hash")) return undefined; - const { directory: _, hash, ...config } = assets; - return { config, hash, skip: hash === output?.hash?.assets }; - }; - - const putWorker = Effect.fn(function* ( - id: string, - news: WorkerProps, - bindings: ResourceBinding[], - olds: WorkerProps | undefined, - output: Worker["Attributes"] | undefined, - session: ScopedPlanStatusSession, - existingSettings?: workers.GetScriptScriptAndVersionSettingResponse, - ) { - const { accountId } = yield* yield* CloudflareEnvironment; - const name = yield* createWorkerName(id, news.name); - yield* Effect.logInfo( - `Cloudflare Worker ${olds ? "update" : "create"}: preparing bundle for ${name}`, - ); - // If the caller handed us a precomputed asset hash that matches - // what we previously stored, we can skip walking the directory - // entirely and tell Cloudflare to keep the assets it already - // has bound to this script. The disk read is the expensive - // part; the script PUT happens either way. - const prebuiltAssets = normalizePrebuiltAssets(news.assets, output); - const { - assets, - bundle, - hash: preparedHash, - } = yield* prepareAssetsAndBundle(id, news, { - skipAssetsRead: prebuiltAssets?.skip, - }); - // When the caller supplied a precomputed hash (e.g. via - // `Build.Command`), store *that* hash in output state so the - // next diff can short-circuit by comparing it directly. The - // hash that `readAssets` produces is the manifest-derived - // hash, which is shaped differently from any upstream - // build-input hash and will never match it on the next pass. - const hash = { - ...preparedHash, - assets: prebuiltAssets?.hash ?? preparedHash.assets, - } satisfies Worker["Attributes"]["hash"]; - const metadataBindings = bindings.flatMap((b) => b.data.bindings ?? []); - const expectedDurableObjectClassNames = - getExpectedDurableObjectClassNames(metadataBindings, name); - let metadataAssets: - | workers.PutScriptRequest["metadata"]["assets"] - | undefined; - let keepAssets = false; - if (prebuiltAssets?.skip) { - // Hash matched what's already on Cloudflare: keep the - // existing asset manifest and skip the upload session. - yield* Effect.logInfo( - `Cloudflare Worker update: assets unchanged for ${name}, keeping existing`, - ); - keepAssets = true; - metadataAssets = { config: prebuiltAssets.config }; - metadataBindings.push({ - type: "assets", - name: "ASSETS", - }); - } else if (assets) { - // We had to read the directory. Even after the read, the - // computed hash may match what's already deployed (e.g. - // legacy `string` / `AssetsProps` shapes that don't carry a - // precomputed hash, or a precomputed hash that disagreed with - // disk). In that case still keep the existing manifest and - // skip the upload session — Cloudflare's content-addressed - // session would no-op on every byte anyway. - if (assets.hash === prebuiltAssets?.hash) { - yield* Effect.logInfo( - `Cloudflare Worker update: assets unchanged for ${name}, keeping existing`, - ); - keepAssets = true; - metadataAssets = { config: assets.config }; - } else { - yield* Effect.logInfo( - `Cloudflare Worker ${olds ? "update" : "create"}: uploading assets for ${name}`, - ); - const { jwt } = yield* uploadAssets( - accountId, - name, - assets, - session, - ); - metadataAssets = { - jwt, - config: assets.config, - }; - } - metadataBindings.push({ - type: "assets", - name: "ASSETS", - }); - } - metadataBindings.push( - { - type: "plain_text", - name: "ALCHEMY_PHASE", - text: "runtime", - }, - { - type: "plain_text", - name: "ALCHEMY_STACK_NAME", - text: stack.name, - }, - { - type: "plain_text", - name: "ALCHEMY_STAGE", - text: stack.stage, - }, - { - type: "plain_text", - name: "ALCHEMY_CLOUDFLARE_ACCOUNT_ID", - text: accountId, - }, - ); - // Add environment variables as metadata bindings - if (news.env) { - for (const [key, value] of Object.entries(news.env)) { - if (value === undefined) continue; - if (metadataBindings.some((b) => b.name === key)) continue; - if (Redacted.isRedacted(value)) { - const unredacted = Redacted.value(value); - metadataBindings.push({ - type: "secret_text", - name: key, - text: - typeof unredacted === "string" - ? unredacted - : JSON.stringify(unredacted), - }); - } else if (typeof value === "string") { - metadataBindings.push({ - type: "plain_text", - name: key, - text: value, - }); - } else { - metadataBindings.push({ - type: "json", - name: key, - json: value, - }); - } - } - } - yield* Effect.logInfo( - `Cloudflare Worker ${olds ? "update" : "create"}: uploading script for ${name}`, - ); - const size = - bundle.files - ?.filter((file) => !file.name.endsWith(".map")) - .reduce((acc, file) => acc + file.size, 0) ?? 0; - const sizeKB = size / 1024; - const sizeMB = sizeKB / 1024; - const bundleSize = `${sizeKB > 1024 ? `${sizeMB.toFixed(2)} MB` : `${sizeKB.toFixed(2)} KB`}`; - yield* session.note(`Uploading worker (${bundleSize}) ...`); - - // Read existing worker settings for migration tracking - const oldSettings = - existingSettings ?? - (yield* workers - .getScriptScriptAndVersionSetting({ - accountId, - scriptName: name, - }) - .pipe( - Effect.map((s) => s as typeof s | undefined), - Effect.catch(() => Effect.succeed(undefined)), - )); - - const oldTags = Array.from(new Set(oldSettings?.tags ?? [])); - const oldBindings = oldSettings?.bindings ?? []; - - // Parse alchemy:do:{logicalId}:{className} tags - const oldDoClassNameByLogicalId = getDurableObjectTagMap(oldTags); - const currentDoBindings = getDurableObjectBindings(bindings, name); - const currentDoClassNameByLogicalId = Object.fromEntries( - currentDoBindings.map((binding) => [ - binding.logicalId, - binding.className, - ]), - ); - - // Parse alchemy:migration-tag:{version} - const oldMigrationTag = oldTags.flatMap((tag) => - tag.startsWith("alchemy:migration-tag:") - ? [tag.slice("alchemy:migration-tag:".length)] - : [], - )[0]; - const newMigrationTag = bumpMigrationTagVersion(oldMigrationTag); - - // Compute deleted classes - const deletedClasses: string[] = []; - for (const [logicalId, className] of Object.entries( - oldDoClassNameByLogicalId, - )) { - if (!currentDoClassNameByLogicalId[logicalId]) { - deletedClasses.push(className); - } - } - - // Backward compatibility for old workers that have DO bindings but no - // alchemy:do tags yet. Cross-script bindings (`scriptName` set to - // anything other than this worker) are NEVER candidates for - // delete-class migrations — the class lives on the foreign script - // and we don't own its lifecycle. - if (Object.keys(oldDoClassNameByLogicalId).length === 0) { - for (const oldBinding of oldBindings) { - const ownedLocally = - !("scriptName" in oldBinding) || oldBinding.scriptName === name; - if ( - oldBinding.type === "durable_object_namespace" && - "className" in oldBinding && - oldBinding.className && - ownedLocally && - !currentDoBindings.some( - (binding) => binding.bindingName === oldBinding.name, - ) - ) { - deletedClasses.push(oldBinding.className); - } - } - } - - // Collect container-backed class names so we can send container metadata - const containerClassNames = new Set( - bindings.flatMap((b) => - (b.data.containers ?? []).map((c) => c.className), - ), - ); - - // Compute new and renamed classes - const newClasses: string[] = []; - const newSqliteClasses: string[] = []; - const renamedClasses: { from: string; to: string }[] = []; - for (const binding of currentDoBindings) { - const previousClassName = - oldDoClassNameByLogicalId[binding.logicalId]; - if (!previousClassName) { - // Default all new Durable Object classes to SQLite. Cloudflare - // recommends SQLite for new namespaces, and container-backed - // Durable Objects require it. - newSqliteClasses.push(binding.className); - } else if (previousClassName !== binding.className) { - renamedClasses.push({ - from: previousClassName, - to: binding.className, - }); - } - } - - yield* Effect.logInfo( - `Cloudflare Worker put: durable object reconciliation ${JSON.stringify( - { - oldDoClassNameByLogicalId, - currentDoClassNameByLogicalId, - deletedClasses, - renamedClasses, - newSqliteClasses, - }, - )}`, - ); - - // Build alchemy:do:{logicalId}:{className} tags for each DO binding - const alchemyDoTags: string[] = []; - for (const binding of currentDoBindings) { - alchemyDoTags.push( - `alchemy:do:${binding.logicalId}:${binding.className}`, - ); - } - - const metadataTags = Array.from( - new Set([ - ...createAlchemyWorkerTags(id), - ...alchemyDoTags, - ...(newMigrationTag - ? [`alchemy:migration-tag:${newMigrationTag}`] - : []), - ...(news.tags ?? []), - ]), - ); - - const migrations = { - oldTag: oldMigrationTag, - newTag: newMigrationTag, - newClasses, - deletedClasses, - renamedClasses, - transferredClasses: [] as { from: string; to: string }[], - newSqliteClasses, - }; - - const metadataContainers = [...containerClassNames].map( - (className) => ({ - className, - }), - ); - - const compatibility = getCompatibility(news); - const metadata: workers.PutScriptRequest["metadata"] = { - assets: metadataAssets, - bindings: metadataBindings, - bodyPart: undefined, - compatibilityDate: compatibility.date, - compatibilityFlags: compatibility.flags, - containers: - metadataContainers.length > 0 ? metadataContainers : undefined, - keepAssets, - keepBindings: undefined, - limits: news.limits, - logpush: news.logpush, - mainModule: bundle.main, - migrations, - observability: news.observability ?? { - enabled: true, - logs: { - enabled: true, - invocationLogs: true, - }, - }, - placement: news.placement, - tags: metadataTags, - tailConsumers: undefined, - usageModel: undefined, - }; - const worker = yield* workers - .putScript({ - accountId, - scriptName: name, - metadata, - files: bundle.files, - }) - .pipe( - Effect.catch((err) => { - // When adopting a Worker managed by Wrangler (or after a previous - // deploy with mismatched migrations), the old_tag precondition - // fails. The only way to discover the actual tag is through the - // error message — getScriptSettings is meant to return it but - // doesn't at runtime. - const msg = String( - typeof err === "object" && err !== null && "message" in err - ? err.message - : err, - ); - const expectedTag = msg.match( - /when expected tag is ['"]?([^'"]+)['"]?/, - )?.[1]; - if (expectedTag) { - return workers.putScript({ - accountId, - scriptName: name, - metadata: { - ...metadata, - migrations: { - ...migrations, - oldTag: expectedTag, - newTag: bumpMigrationTagVersion(expectedTag), - }, - }, - files: bundle.files, - }); - } - return Effect.fail(err as any); - }), - ); - const { settings, durableObjectNamespaces } = - yield* getWorkerSettingsWithDurableObjects( - name, - expectedDurableObjectClassNames, - ); - // Reconcile workers.dev subdomain against observed cloud state. - // We can't diff `news.url` against `olds.url` here because both - // default to `undefined` (meaning "enable") — that comparison - // would skip the API call on every deploy where the user never - // explicitly set `url`, leaving the subdomain in whatever state - // Cloudflare currently has it (disabled by default, or whatever - // a previous failed/external action left it as). - const desiredSubdomainEnabled = news.url !== false; - const observedSubdomain = yield* workers - .getScriptSubdomain({ - accountId, - scriptName: name, - }) - .pipe( - Effect.orElseSucceed(() => ({ - enabled: false, - previewsEnabled: false, - })), - ); - if ( - desiredSubdomainEnabled !== observedSubdomain.enabled || - desiredSubdomainEnabled !== observedSubdomain.previewsEnabled - ) { - yield* session.note( - `${desiredSubdomainEnabled ? "Enabling" : "Disabling"} workers.dev subdomain...`, - ); - // Cloudflare's script registry is eventually consistent — for the - // first few hundred ms after `putScript` returns, POST /subdomain - // can still get back `WorkerNotFound` (a generic "unknown error" - // body), or a bare 500 surfaced as `InternalServerError` / - // `UnknownCloudflareError` (code 10013). Bigger uploads race harder. - // Retry the subdomain toggle on those transient tags with a short - // exponential backoff; same pattern we use elsewhere in this - // provider for DO-namespace propagation and for `putScript` itself. - yield* setWorkerSubdomain(name, desiredSubdomainEnabled).pipe( - Effect.retry({ - while: (error) => - error._tag === "WorkerNotFound" || - error._tag === "InternalServerError" || - error._tag === "UnknownCloudflareError", - schedule: Schedule.exponential(200).pipe( - Schedule.both(Schedule.recurs(15)), - ), - }), - ); - } - const desiredDomains = normalizeDomains(news.domain); - const previousDomains = output?.domains ?? []; - if (desiredDomains.length > 0 || previousDomains.length > 0) { - yield* session.note( - `Reconciling custom domains (${desiredDomains.length}) ...`, - ); - } - const reconciled = yield* reconcileDomains(name, desiredDomains); - const workersDevUrl = - news.url !== false - ? `https://${name}.${yield* getAccountSubdomain(accountId)}.workers.dev` - : undefined; - const domains = [ - ...reconciled.map((d) => `https://${d.hostname}`), - ...(workersDevUrl ? [workersDevUrl] : []), - ]; - const crons = yield* reconcileCrons( - name, - normalizeCrons([...getCronBindings(bindings), ...(news.crons ?? [])]), - output?.crons ?? [], - session, - ); - return { - workerId: worker.id ?? name, - workerName: name, - logpush: worker.logpush ?? undefined, - url: domains[0], - tags: settings.tags ?? metadata.tags, - durableObjectNamespaces, - accountId, - domains, - crons, - hash, - } satisfies Worker["Attributes"]; - }); - - const hasChanged = Effect.fn(function* ( - id: string, - props: WorkerProps, - output: Worker["Attributes"], - ) { - if (props.script !== undefined) { - const scriptHash = yield* hashScript(props.script); - if (scriptHash !== output.hash?.bundle) { - return true; - } - if (!props.assets) { - return false; - } - const assetsHash = Predicate.hasProperty(props.assets, "hash") - ? props.assets.hash - : undefined; - if (assetsHash === undefined) { - return true; - } - return assetsHash !== output.hash?.assets; - } - if (props.vite) { - const input = yield* hashDirectory({ - cwd: props.vite.rootDir, - memo: props.vite.memo, - }); - return input !== output.hash?.input; - } - const bundleHash = yield* prepareBundle(id, props).pipe( - Effect.map((b) => b.hash), - ); - if (bundleHash !== output.hash?.bundle) { - return true; - } - if (!props.assets) { - return false; - } - // We deliberately don't read the assets directory during diff. - // For `AssetsWithHash` (the documented contract) the upstream - // `Build.Command` already gave us an authoritative hash — we - // just compare strings. Reading the directory here would - // (a) hash the same tree twice per apply (`putWorker` reads - // again when an upload is actually required), and (b) crash - // when the prior state was written on a different machine - // and `path` doesn't exist locally — blocking any local - // reapply even though the precomputed hash is right there - // in props. - // - // For the legacy `string` / `AssetsProps` shapes there's no - // hash in props to compare against, so we conservatively - // assume the assets changed; `putWorker` will read once, - // hash, and use `keepAssets` if it turns out nothing actually - // changed. - const assetsHash = Predicate.hasProperty(props.assets, "hash") - ? props.assets.hash - : undefined; - if (assetsHash === undefined) { - return true; - } - return assetsHash !== output.hash?.assets; - }); - - return Worker.Provider.of({ - stables: ["workerId", "workerName"], - list: () => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - // Account-scoped enumeration of every Worker script. The - // per-script `read` makes several extra calls (subdomain, - // settings, domains, schedule) to fully hydrate - // url/durableObjectNamespaces/domains/crons. Doing that for - // every script on the account is both expensive (4 calls × N) - // and fragile — a single script with a binding shape the - // settings schema doesn't know about would break the whole - // listing (the same reason `read` deliberately avoids - // `listScripts`). For `list()` we hydrate the core identifying - // and settings fields that come straight from the script - // metadata and leave the binding-derived fields at the same - // defaults `read` returns when those sub-resources are absent - // (`url: undefined`, `durableObjectNamespaces: {}`, - // `domains: []`, `crons: []`). `accountId` + `workerName` are - // sufficient for `delete`. - return yield* workers.listScripts.pages({ accountId }).pipe( - Stream.runCollect, - Effect.map((chunk) => - Array.from(chunk).flatMap((page) => - // Annotate the element type as the full `Attributes` shape - // (incl. the optional `hash`) so it matches `read` exactly. - // `list()` is an inference source for the provider's resource - // type; a narrower element (e.g. via `satisfies`, which omits - // `hash`) would derail `Res` inference and cascade every - // lifecycle method's requirement channel to `never`. - (page.result ?? []).flatMap( - (script): Worker["Attributes"][] => - script.id - ? [ - { - accountId, - workerId: script.id, - workerName: script.id, - logpush: script.logpush ?? undefined, - url: undefined, - tags: script.tags ?? undefined, - durableObjectNamespaces: {}, - domains: [], - crons: [], - }, - ] - : [], - ), - ), - ), - ); - }), - diff: Effect.fn(function* ({ id, news, olds, output, newBindings }) { - const { accountId } = yield* yield* CloudflareEnvironment; - if (!isResolved(news)) return undefined; - if ((output?.accountId ?? accountId) !== accountId) { - return { action: "replace" }; - } - const workerName = yield* createWorkerName(id, news.name); - const oldWorkerName = output?.workerName - ? output.workerName - : yield* createWorkerName(id, olds?.name); - if (workerName !== oldWorkerName) { - return { action: "replace" }; - } - if (!output) { - return; - } - const newDomains = normalizeDomains(news.domain) - .map((h) => `https://${h}`) - .sort(); - const oldDomains = (output?.domains ?? []) - .filter((u) => !u.endsWith(".workers.dev")) - .sort(); - const domainsChanged = - newDomains.length !== oldDomains.length || - newDomains.some((d, i) => d !== oldDomains[i]); - const newCrons = normalizeCrons([ - ...(Array.isArray(newBindings) - ? getCronBindings( - newBindings as ResourceBinding[], - ) - : []), - ...(news.crons ?? []), - ]).sort(); - const oldCrons = [...(output?.crons ?? [])].sort(); - const cronsChanged = - newCrons.length !== oldCrons.length || - newCrons.some((cron, index) => cron !== oldCrons[index]); - // `url` is `domains[0]`: the first custom domain in user order if - // any, otherwise the workers.dev URL (derived from the stable - // worker name + account subdomain). It's stable across this update - // exactly when that first domain is unchanged — which is NOT the - // same as "the domain set is unchanged": adding a second custom - // domain leaves `url` put, while reordering changes it even though - // the set is equal. Compute the resulting `url` and carry it - // forward as a stable only when it matches the old one, so - // downstream resources that reference `worker.url` (e.g. a GitHub - // Webhook delivery URL built via `Output.interpolate`) resolve it - // to a concrete value during planning instead of an unresolved - // Output — otherwise every worker update spuriously re-updates them. - const newCustomDomains = normalizeDomains(news.domain); - const newUrl = - newCustomDomains.length > 0 - ? `https://${newCustomDomains[0]}` - : news.url !== false - ? (output.domains ?? []).find((u) => u.endsWith(".workers.dev")) - : undefined; - const urlStable = newUrl !== undefined && newUrl === output.url; - // `durableObjectNamespaces` maps each hosted DO class name to the - // namespace id Cloudflare assigned it. Those ids are permanent for - // the lifetime of a (worker, class) pair, so the map only changes - // when a class is added or removed — never on a plain code/config - // update. Carry it forward as a stable whenever the set of local DO - // class names is unchanged, for the same reason as `url` above: - // downstream resources that bind a DO namespace via - // `worker.durableObjectNamespaces[name]` (e.g. a Container attached - // to a DO) must resolve it to a concrete value during planning. - // Otherwise the binding holds an unresolved Output, which - // `diffBindings` treats as "changed", spuriously re-updating the - // bound resource on every deploy. Class names are structural (not the - // namespace id), so this comparison holds even when `newBindings` is - // otherwise unresolved. - const newDoClassNames = Array.isArray(newBindings) - ? getExpectedDurableObjectClassNames( - (newBindings as ResourceBinding[]).flatMap( - (b) => b.data.bindings ?? [], - ), - workerName, - ).sort() - : []; - const oldDoClassNames = Object.keys( - output.durableObjectNamespaces ?? {}, - ).sort(); - const doNamespacesStable = - oldWorkerName === workerName && - newDoClassNames.length === oldDoClassNames.length && - newDoClassNames.every((name, i) => name === oldDoClassNames[i]); - if ( - domainsChanged || - cronsChanged || - (yield* hasChanged(id, news, output)) - ) { - // `workerId` is always stable across an update; seed it so it - // survives now that `diff.stables` overrides `provider.stables` - // rather than being merged with it. - const stables: string[] = ["workerId"]; - if (oldWorkerName === workerName) { - stables.push("workerName"); - } - if (urlStable) { - stables.push("url"); - } - if (doNamespacesStable) { - stables.push("durableObjectNamespaces"); - } - return { - action: "update", - stables: stables.length > 0 ? stables : undefined, - }; - } - }), - precreate: Effect.fn(function* ({ id, news, session }) { - const { accountId } = yield* yield* CloudflareEnvironment; - const name = yield* createWorkerName(id, news.name); - const exportMap = news.exports ?? {}; - const durableObjects = Object.keys(exportMap) - .filter((logicalId) => isDurableObjectExport(exportMap[logicalId])) - .map((logicalId) => ({ - logicalId, - className: logicalId, - })); - const doClasses = durableObjects.map((binding) => binding.className); - const containers = doClasses.map((className) => ({ className })); - const alchemyDoTags = durableObjects.map( - ({ logicalId, className }) => - `alchemy:do:${logicalId}:${className}`, - ); - const tags = Array.from( - new Set([ - ...createAlchemyWorkerTags(id), - ...alchemyDoTags, - ...(news.tags ?? []), - ]), - ); - yield* Effect.logInfo( - `Cloudflare Worker precreate: starting ${name}`, - ); - yield* Effect.logInfo( - `Cloudflare Worker precreate: durable objects ${JSON.stringify( - durableObjects, - )}`, - ); - const existingSettings = yield* workers - .getScriptScriptAndVersionSetting({ - accountId, - scriptName: name, - }) - .pipe( - Effect.catchTag("WorkerNotFound", () => - Effect.succeed(undefined), - ), - ); - let durableObjectNamespaces = getDurableObjectNamespaces( - existingSettings?.bindings, - ); - - if (existingSettings) { - // Engine has already cleared this resource for write via - // `read` + AdoptPolicy. Either we own it (matching tags) or - // the user opted in to a takeover (`--adopt` / `adopt(true)`). - yield* Effect.logInfo( - `Cloudflare Worker precreate: reusing existing ${name}`, - ); - } else { - yield* session.note("Pre-creating worker..."); - const mainModule = "main.js"; - 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 - .map( - (className) => - `export class ${className} extends DurableObject {}`, - ) - .join("\n")}`; - yield* workers - .putScript({ - accountId, - scriptName: name, - metadata: { - mainModule, - bindings: - doClasses.length > 0 - ? doClasses.map((className) => ({ - type: "durable_object_namespace" as const, - name: className, - className, - })) - : undefined, - ...getCompatibility(news), - containers, - migrations: - doClasses.length > 0 - ? { - oldTag: undefined, - newTag: undefined, - newClasses: [], - deletedClasses: [], - renamedClasses: [], - transferredClasses: [], - newSqliteClasses: doClasses, - } - : undefined, - observability: news.observability ?? { - enabled: true, - logs: { - enabled: true, - invocationLogs: true, - }, - }, - tags, - }, - files: [ - new File([placeholderScript], mainModule, { - type: "application/javascript+module", - }), - ], - }) - .pipe( - // Cloudflare's PUT /workers/scripts/{name} intermittently - // returns code 10002 / "An unknown error has occurred" on the - // first put for a fresh worker name. Surfaced as the shared - // `InternalServerError` upstream (alchemy-run/distilled#290). - // Also match `UnknownCloudflareError` for older - // @distilled.cloud/cloudflare versions that haven't picked - // up the patch yet. - Effect.retry({ - while: (e) => - e._tag === "InternalServerError" || - e._tag === "UnknownCloudflareError", - schedule: Schedule.exponential(1000).pipe( - Schedule.both(Schedule.recurs(5)), - ), - }), - ); - if (doClasses.length > 0) { - ({ durableObjectNamespaces } = - yield* getWorkerSettingsWithDurableObjects(name, doClasses)); - } - } - - if (existingSettings && doClasses.length > 0) { - ({ durableObjectNamespaces } = - yield* getWorkerSettingsWithDurableObjects(name, doClasses)); - } - - return { - workerId: name, - workerName: name, - logpush: existingSettings?.logpush ?? undefined, - url: undefined, - tags: existingSettings?.tags ?? tags, - durableObjectNamespaces, - accountId, - domains: [], - crons: [], - } satisfies Worker["Attributes"]; - }), - read: Effect.fn( - function* ({ id, output, olds }) { - const { accountId } = yield* yield* CloudflareEnvironment; - const workerName = - output?.workerName ?? (yield* createWorkerName(id, olds?.name)); - yield* Effect.logInfo( - `Cloudflare Worker read: checking ${workerName}`, - ); - // We deliberately don't call `listScripts({ accountId })` here: - // it pulls every Worker on the account back through a strict - // schema decode, and a single existing Worker the schema doesn't - // know about (e.g. `placement_mode: "targeted"`) breaks the - // entire read. `getScriptSettings` already fails with - // `WorkerNotFound` if the script doesn't exist, which the - // surrounding `Effect.catchTag` turns into `undefined` — that's - // all the existence check we need. - const [subdomain, settings, domainsList] = yield* Effect.all([ - workers.getScriptSubdomain({ - accountId, - scriptName: workerName, - }), - workers.getScriptScriptAndVersionSetting({ - accountId, - scriptName: workerName, - }), - workers - .listDomains({ - accountId, - service: workerName, - }) - .pipe(Effect.map((r) => r.result ?? [])), - ]); - // Preserve the order the user provided in `olds.domain`. The - // Cloudflare API returns domains in non-deterministic order, - // which would cause downstream `worker.domains[0]` reads to flip - // between deploys. Drift (domains we don't know about) is - // appended after the user-ordered ones. - const userOrder = normalizeDomains(olds?.domain); - const orderedHostnames = [ - ...userOrder.flatMap( - (h) => - domainsList.find((d) => d.hostname === h)?.hostname ?? [], - ), - ...domainsList.flatMap((d) => - d.hostname && !userOrder.includes(d.hostname) - ? [d.hostname] - : [], - ), - ]; - const workersDevUrl = subdomain.enabled - ? `https://${workerName}.${yield* getAccountSubdomain(accountId)}.workers.dev` - : undefined; - const domains = [ - ...orderedHostnames.map((h) => `https://${h}`), - ...(workersDevUrl ? [workersDevUrl] : []), - ]; - const crons = yield* getWorkerCrons(workerName); - yield* Effect.logInfo( - `Cloudflare Worker read: found ${workerName}`, - ); - const attrs = { - accountId, - workerId: workerName, - workerName, - logpush: settings.logpush ?? undefined, - url: domains[0], - tags: settings.tags ?? undefined, - durableObjectNamespaces: getDurableObjectNamespaces( - settings.bindings, - ), - domains, - crons, - } satisfies Worker["Attributes"]; - - // Centralized ownership decision: the engine routes `read`'s - // return value based on `AdoptPolicy`. We hand it the attrs - // either as-is (owned: alchemy tags identify this stack/stage/id, - // safe to silently adopt even without `--adopt`) or branded with - // `Unowned` (caller must opt in via `--adopt` or the engine - // raises `OwnedBySomeoneElse`). - return hasAlchemyWorkerTags(id, settings.tags ?? []) - ? attrs - : Unowned(attrs); - }, - (effect) => - effect.pipe( - Effect.catchTag("WorkerNotFound", () => - Effect.succeed(undefined), - ), - ), - ), - reconcile: Effect.fn(function* ({ - id, - news, - olds, - bindings, - output, - session, - }) { - const { accountId } = yield* yield* CloudflareEnvironment; - const name = - output?.workerName ?? (yield* createWorkerName(id, news.name)); - const durableObjects = getDurableObjectBindings(bindings, name).map( - ({ logicalId, className }) => ({ - logicalId, - className, - }), - ); - yield* Effect.logInfo( - `Cloudflare Worker reconcile: starting ${name}`, - ); - yield* Effect.logInfo( - `Cloudflare Worker reconcile: durable objects ${JSON.stringify( - durableObjects, - )}`, - ); - - // Observe — fetch the script's current settings if it already exists. - // `putWorker` is a true upsert against the Cloudflare API; the - // existing settings inform asset/migration decisions and let the - // reconciler converge whether the worker is brand-new, adopted, or - // an in-place update. - const existingSettings = yield* workers - .getScriptScriptAndVersionSetting({ - accountId, - scriptName: name, - }) - .pipe( - Effect.catchTag("WorkerNotFound", () => - Effect.succeed(undefined), - ), - ); - yield* Effect.logInfo( - `Cloudflare Worker reconcile: existing durable object tags ${JSON.stringify( - (existingSettings?.tags ?? []).filter((tag) => - tag.startsWith("alchemy:do:"), - ), - )}`, - ); - yield* Effect.logInfo( - `Cloudflare Worker reconcile: previous durable object tags ${JSON.stringify( - (output?.tags ?? []).filter((tag) => - tag.startsWith("alchemy:do:"), - ), - )}`, - ); - return yield* putWorker( - id, - news, - bindings, - olds, - output, - session, - existingSettings, - ); - }), - delete: Effect.fn(function* ({ output }) { - yield* Effect.logInfo( - `Cloudflare Worker delete: deleting ${output.workerName}`, - ); - // Look up live domain IDs rather than trusting persisted state. - // We no longer track `{ id, zoneId }` on the output; fetching - // straight from Cloudflare handles both the normal case and - // adopted workers whose domains we never recorded. - const liveDomains = yield* workers - .listDomains({ - accountId: output.accountId, - service: output.workerName, - }) - .pipe( - Effect.map((r) => r.result ?? []), - Effect.catch(() => Effect.succeed([])), - ); - if (liveDomains.length) { - yield* Effect.all( - liveDomains.flatMap((d) => - d.id - ? [ - workers - .deleteDomain({ - accountId: output.accountId, - domainId: d.id, - }) - .pipe( - Effect.catchTag("DomainNotFound", () => Effect.void), - ), - ] - : [], - ), - { concurrency: "unbounded" }, - ); - } - yield* workers - .deleteScript({ - accountId: output.accountId, - scriptName: output.workerName, - // Force teardown of queue consumers, durable object classes, and - // service bindings hanging off this worker. Without `force`, those - // conditions raise QueueConsumerConflict / ServiceBindingConflict - // and leave the script in CF. Alchemy is the source of truth for - // the worker, so we want a hard delete on teardown. - force: true, - }) - .pipe(Effect.catchTag("WorkerNotFound", () => Effect.void)); - }), - tail: ({ output }) => - telemetry.tailScript({ - accountId: output.accountId, - scriptName: output.workerName, - }), - logs: ({ output, options }) => - telemetry.queryLogs({ - accountId: output.accountId, - filters: [ - { - key: "$workers.scriptName", - operation: "eq", - type: "string", - value: output.workerName, - }, - ], - options, - }), - }); - }), - ); - -const contentTypeFromExtension = (extension: string) => { - switch (extension) { - case ".wasm": - return "application/wasm"; - case ".txt": - case ".html": - case ".sql": - case ".custom": - return "text/plain"; - case ".bin": - return "application/octet-stream"; - case ".mjs": - case ".js": - return "application/javascript+module"; - case ".cjs": - return "application/javascript"; - case ".map": - return "application/source-map"; - default: - return "application/octet-stream"; - } -}; - -function bumpMigrationTagVersion( - oldTag: string | undefined, -): string | undefined { - if (!oldTag) return undefined; - const version = oldTag.match(/^(alchemy:)?v(\d+)$/)?.[2]; - if (!version) return "alchemy:v1"; - return `alchemy:v${parseInt(version, 10) + 1}`; -} - -function getDurableObjectBindings( - bindings: ReadonlyArray, - workerName: string, -) { - // Resource authors (and the `make`/`yield* Tag`/plan-vs-apply machinery) - // can register the same DO binding multiple times under the same logical - // id — `binding()` is a plain `worker.bind` and intentionally has no - // dedup. Collapse duplicates here so each `(logicalId, bindingName, - // className)` tuple appears at most once. We also exclude cross-script - // references: a `scriptName` pointing to *another* worker means this - // worker just references a foreign class — ship the binding to - // Cloudflare, but don't drive class migrations for it. - const seen = new Set(); - return bindings.flatMap((binding) => - (binding.data.bindings ?? []).flatMap((item: WorkerBinding) => { - if ( - item.type !== "durable_object_namespace" || - !("className" in item) || - !item.className - ) { - return []; - } - if (item.scriptName !== undefined && item.scriptName !== workerName) { - return []; - } - const dedupKey = `${binding.sid}::${item.name}::${item.className}`; - if (seen.has(dedupKey)) return []; - seen.add(dedupKey); - return [ - { - logicalId: binding.sid, - bindingName: item.name, - className: item.className, - }, - ]; - }), - ); -} - -function getDurableObjectTagMap(tags: ReadonlyArray) { - return Object.fromEntries( - tags.flatMap((tag) => { - if (!tag.startsWith("alchemy:do:")) { - return []; - } - const parts = tag.split(":"); - const logicalId = parts[2]; - const className = parts.slice(3).join(":"); - return logicalId && className ? [[logicalId, className]] : []; - }), - ); -} diff --git a/packages/alchemy/src/Cloudflare/Workers/WorkerAsyncBindings.ts b/packages/alchemy/src/Cloudflare/Workers/WorkerAsyncBindings.ts index 3909787f0..b4c37b20d 100644 --- a/packages/alchemy/src/Cloudflare/Workers/WorkerAsyncBindings.ts +++ b/packages/alchemy/src/Cloudflare/Workers/WorkerAsyncBindings.ts @@ -17,9 +17,9 @@ import { isFlagshipApp } from "../Flagship/App.ts"; import { isHyperdrive } from "../Hyperdrive/Hyperdrive.ts"; import { getHyperdriveDevOrigin } from "../Hyperdrive/HyperdriveBinding.ts"; import { isImages } from "../Images/Images.ts"; -import { isKVNamespace } from "../KV/KVNamespace.ts"; +import { isKVNamespace } from "../KV/Namespace.ts"; import { isQueue } from "../Queue/Queue.ts"; -import { isR2Bucket } from "../R2/R2Bucket.ts"; +import { isR2Bucket } from "../R2/Bucket.ts"; import { isRateLimit } from "../RateLimit/RateLimit.ts"; import { isSecret } from "../SecretsStore/Secret.ts"; import { isVectorizeIndex } from "../Vectorize/VectorizeIndex.ts"; diff --git a/packages/alchemy/src/Cloudflare/Workers/WorkerBinding.ts b/packages/alchemy/src/Cloudflare/Workers/WorkerBinding.ts index 63a24fb53..42c869ec9 100644 --- a/packages/alchemy/src/Cloudflare/Workers/WorkerBinding.ts +++ b/packages/alchemy/src/Cloudflare/Workers/WorkerBinding.ts @@ -17,9 +17,10 @@ import { SendEmail } from "../Email/SendEmail.ts"; import type { FlagshipApp } from "../Flagship/App.ts"; import { Hyperdrive } from "../Hyperdrive/Hyperdrive.ts"; import { Images } from "../Images/Images.ts"; -import type { KVNamespace } from "../KV/KVNamespace.ts"; +import type { KVNamespace } from "../KV/Namespace.ts"; +import type { Providers } from "../Providers.ts"; import type { Queue } from "../Queue/Queue.ts"; -import type { R2Bucket } from "../R2/R2Bucket.ts"; +import type { R2Bucket } from "../R2/Bucket.ts"; import type { RateLimit } from "../RateLimit/RateLimit.ts"; import type { Secret } from "../SecretsStore/Secret.ts"; import type { VectorizeIndex } from "../Vectorize/VectorizeIndex.ts"; @@ -110,7 +111,8 @@ export const bindWorker = Effect.fnUntraced(function* ( */ export class BindWorkerPolicy extends Binding.Policy< BindWorkerPolicy, - (worker: Worker) => Effect.Effect + (worker: Worker) => Effect.Effect, + Providers >()("Cloudflare.Worker.Bind") {} export const BindWorkerPolicyLive = BindWorkerPolicy.layer.succeed( diff --git a/packages/alchemy/src/Cloudflare/Workers/WorkerLoader.ts b/packages/alchemy/src/Cloudflare/Workers/WorkerLoader.ts index 3f2fbb8a3..ae1eaba13 100644 --- a/packages/alchemy/src/Cloudflare/Workers/WorkerLoader.ts +++ b/packages/alchemy/src/Cloudflare/Workers/WorkerLoader.ts @@ -54,15 +54,15 @@ export interface WorkerLoaderEffect extends Effect.Effect< never, WorkerEnvironment | Worker > { - "alchemy/Kind": WorkerLoaderTypeId; + "~alchemy/Kind": WorkerLoaderTypeId; name: string; } export const isWorkerLoader = (value: unknown): value is WorkerLoader => typeof value === "object" && value !== null && - "alchemy/Kind" in value && - (value as WorkerLoaderEffect)["alchemy/Kind"] === WorkerLoaderTypeId; + "~alchemy/Kind" in value && + (value as WorkerLoaderEffect)["~alchemy/Kind"] === WorkerLoaderTypeId; export interface WorkerLoaderClass extends Context.Service< WorkerLoader, @@ -281,8 +281,8 @@ export const WorkerLoader: WorkerLoaderClass = Object.assign( } satisfies WorkerLoader; }), { - kind: WorkerLoaderTypeId, - name, + "~alchemy/Name": name, + "~alchemy/Kind": WorkerLoaderTypeId, }, ), ), diff --git a/packages/alchemy/src/Cloudflare/Workers/WorkerProvider.ts b/packages/alchemy/src/Cloudflare/Workers/WorkerProvider.ts new file mode 100644 index 000000000..6759d94e7 --- /dev/null +++ b/packages/alchemy/src/Cloudflare/Workers/WorkerProvider.ts @@ -0,0 +1,1736 @@ +import * as workers from "@distilled.cloud/cloudflare/workers"; +import * as zones from "@distilled.cloud/cloudflare/zones"; +import * as Data from "effect/Data"; +import * as Effect from "effect/Effect"; +import * as Path from "effect/Path"; +import * as Predicate from "effect/Predicate"; +import * as Redacted from "effect/Redacted"; +import * as Schedule from "effect/Schedule"; +import * as Stream from "effect/Stream"; +import * as crypto from "node:crypto"; +import { Unowned } from "../../AdoptPolicy.ts"; +import * as Artifacts from "../../Artifacts.ts"; +import { hashDirectory } from "../../Build/Memo.ts"; +import * as Bundle from "../../Bundle/Bundle.ts"; +import type { ScopedPlanStatusSession } from "../../Cli/Cli.ts"; +import { isResolved } from "../../Diff.ts"; +import * as ProviderLayer from "../../Local/ProviderLayer.ts"; +import * as Provider from "../../Provider.ts"; +import { type ResourceBinding } from "../../Resource.ts"; +import { Stack } from "../../Stack.ts"; +import { CloudflareEnvironment } from "../CloudflareEnvironment.ts"; +import { CloudflareLogs } from "../Logs.ts"; +import { readAssets, uploadAssets } from "./Assets.ts"; +import { getCompatibility } from "./Compatibility.ts"; +import { isDurableObjectExport } from "./DurableObjectNamespace.ts"; +import { LocalWorkerProvider } from "./LocalWorkerProvider.ts"; +import { Worker, type WorkerProps } from "./Worker.ts"; +import { getCronBindings } from "./WorkerAsyncBindings.ts"; +import type { WorkerBinding, WorkerSettingsBinding } from "./WorkerBinding.ts"; +import { readPrebuiltWorkerBundle, WorkerBundle } from "./WorkerBundle.ts"; +import { createWorkerName } from "./WorkerName.ts"; +class MissingDurableObjectNamespaces extends Data.TaggedError( + "MissingDurableObjectNamespaces", +)<{ + scriptName: string; + expected: string[]; +}> {} + +export const WorkerProvider = () => + ProviderLayer.select({ + live: () => LiveWorkerProvider(), + local: () => LocalWorkerProvider(), + }); + +export const LiveWorkerProvider = () => + Provider.effect( + Worker, + Effect.gen(function* () { + const path = yield* Path.Path; + + const bundler = yield* WorkerBundle; + const stack = yield* Stack; + + // const createScriptSubdomain = yield* workers.createScriptSubdomain; + // const deleteScript = yield* workers.deleteScript; + // const getScriptSubdomain = yield* workers.getScriptSubdomain; + // const getScriptSchedule = yield* workers.getScriptSchedule; + // const getScriptSettings = yield* workers.getScriptScriptAndVersionSetting; + // const getSubdomain = yield* workers.getSubdomain; + // const putScript = yield* workers.putScript; + // const putScriptSchedule = yield* workers.putScriptSchedule; + // const putDomain = yield* workers.putDomain; + // const listDomains = yield* workers.listDomains; + // const deleteDomain = yield* workers.deleteDomain; + // const listZones = yield* zones.listZones; + const telemetry = yield* CloudflareLogs; + + const getAccountSubdomain = (accountId: string) => + workers + .getSubdomain({ + accountId, + }) + .pipe(Effect.map((result) => result.subdomain)); + + // Toggle the workers.dev subdomain via `POST /subdomain` with + // `enabled: true | false`. Mirrors the upstream Alchemy + // implementation in `.vendor/alchemy/.../worker-subdomain.ts`. + // When enabling we also set `previewsEnabled: true` so the + // script is reachable both at its stable workers.dev URL and at + // version-preview URLs; on disable we send just `enabled: false`. + const setWorkerSubdomain = Effect.fn(function* ( + name: string, + enabled: boolean, + ) { + const { accountId } = yield* yield* CloudflareEnvironment; + return yield* workers.createScriptSubdomain({ + accountId, + scriptName: name, + enabled, + previewsEnabled: enabled ? true : undefined, + }); + }); + + // Convert non-ASCII hostnames (emoji, IDN, etc.) to punycode so the + // Cloudflare API receives the form it stores domains in. `new URL(...)` + // does IDNA via WHATWG URL parsing — `📦.alchemy.run` → `xn--5z8h.alchemy.run`. + const toPunycode = (hostname: string): string => { + try { + return new URL(`https://${hostname}`).hostname; + } catch { + return hostname; + } + }; + + const normalizeDomains = ( + domain: string | string[] | undefined, + ): string[] => + domain === undefined + ? [] + : Array.from( + new Set( + (Array.isArray(domain) ? domain : [domain]).map(toPunycode), + ), + ); + + const normalizeCrons = (crons: string[] | undefined): string[] => + Array.from(new Set(crons ?? [])); + + const getWorkerCrons = Effect.fn(function* (scriptName: string) { + const { accountId } = yield* yield* CloudflareEnvironment; + return yield* workers + .getScriptSchedule({ + accountId, + scriptName, + }) + .pipe( + Effect.map((response) => + normalizeCrons( + response.schedules.map((schedule) => schedule.cron), + ), + ), + Effect.catchTag("WorkerNotFound", () => Effect.succeed([])), + ); + }); + + const reconcileCrons = ( + scriptName: string, + desired: string[], + previous: string[], + session: ScopedPlanStatusSession, + ) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + const live = yield* getWorkerCrons(scriptName); + const desiredSorted = [...desired].sort(); + const liveSorted = [...live].sort(); + const changed = + desiredSorted.length !== liveSorted.length || + desiredSorted.some((cron, index) => cron !== liveSorted[index]); + + if (!changed) return live; + + if (desired.length > 0 || previous.length > 0 || live.length > 0) { + yield* session.note( + `Reconciling Cron Triggers (${desired.length}) ...`, + ); + } + + const result = yield* workers + .putScriptSchedule({ + accountId, + scriptName, + body: desired.map((cron) => ({ cron })), + }) + .pipe( + Effect.retry({ + while: (error) => error._tag === "WorkerNotFound", + schedule: Schedule.exponential(200).pipe( + Schedule.both(Schedule.recurs(15)), + ), + }), + ); + return normalizeCrons( + result.schedules.map((schedule) => schedule.cron), + ); + }); + + /** + * Infer the Cloudflare Zone ID for a given hostname by listing the + * account's zones and matching the hostname against each zone's name — + * walking up the DNS label hierarchy until a match is found. + */ + const inferZoneIdForHostname = ( + hostname: string, + zoneCache: Map, + ) => + Effect.gen(function* () { + const cached = zoneCache.get(hostname); + if (cached) return cached; + + const zoneList = yield* zones + .listZones({}) + .pipe(Effect.map((response) => response.result ?? [])); + for (const zone of zoneList) { + zoneCache.set(zone.name, zone.id); + } + + const parts = hostname.split("."); + for (let i = 0; i < parts.length - 1; i++) { + const candidate = parts.slice(i).join("."); + const match = zoneList.find((z) => z.name === candidate); + if (match) { + zoneCache.set(hostname, match.id); + return match.id; + } + } + return yield* Effect.die( + `Could not infer Cloudflare Zone for hostname "${hostname}". ` + + "Ensure the parent zone exists in this account.", + ); + }); + + const reconcileDomains = (scriptName: string, desired: string[]) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + // Always query the live state of domains attached to *this* + // Worker rather than trusting `_previous` from local state. + // State may have been wiped, populated by another machine, or + // simply be out of date. Without this we PUT domains that are + // already registered to this same Worker and Cloudflare + // returns a confusing "hostname already in use" error. + const liveAll = yield* workers + .listDomains({ + accountId, + service: scriptName, + }) + .pipe( + Effect.map((r) => + (r.result ?? []).flatMap((d) => + d.id && d.hostname && d.zoneId + ? [ + { + id: d.id, + hostname: d.hostname, + zoneId: d.zoneId, + service: d.service ?? undefined, + }, + ] + : [], + ), + ), + Effect.catch(() => Effect.succeed([])), + ); + + const desiredSet = new Set(desired); + const liveByHostname = new Map(liveAll.map((d) => [d.hostname, d])); + + // Detach what's no longer wanted. Use the live list so we + // don't try to delete domains we no longer track. + const toRemove = liveAll.filter((d) => !desiredSet.has(d.hostname)); + yield* Effect.all( + toRemove.map((d) => + workers + .deleteDomain({ accountId, domainId: d.id }) + .pipe(Effect.catchTag("DomainNotFound", () => Effect.void)), + ), + { concurrency: "unbounded" }, + ); + + if (desired.length === 0) return []; + + const zoneCache = new Map(); + + // Attach `hostname` to this Worker. Skip the PUT entirely if + // the hostname is already attached to *this* Worker — that's a + // no-op for Cloudflare and avoids the "already in use" 409. + // If it's attached to a *different* Worker, refuse with a + // clear message rather than silently re-routing traffic. + const attachDomain = Effect.fn(function* (hostname: string) { + const live = liveByHostname.get(hostname); + if (live) { + return { + hostname: live.hostname, + id: live.id, + zoneId: live.zoneId, + }; + } + + // Not attached to this Worker — but it could still belong + // to another Worker. Check before we try to PUT so we can + // emit a helpful error instead of the raw 409. + const otherOwner = yield* workers + .listDomains({ + accountId, + hostname, + }) + .pipe( + Effect.map((r) => + (r.result ?? []).find( + (d) => d.hostname === hostname && d.service !== scriptName, + ), + ), + Effect.catch(() => Effect.succeed(undefined)), + ); + if (otherOwner?.id) { + return yield* Effect.die( + new Error( + `Cannot attach hostname '${hostname}' to Worker '${scriptName}': ` + + `it is already attached to Worker '${otherOwner.service ?? ""}'. ` + + `Detach it from that Worker first, or pick a different hostname.`, + ), + ); + } + + const zoneId = yield* inferZoneIdForHostname(hostname, zoneCache); + // Same eventual-consistency window as `setWorkerSubdomain`: + // PUT /accounts/.../workers/domains right after `putScript` + // can return `WorkerNotFound` until Cloudflare's script + // registry has propagated. Retry on that specific tag. + const res = yield* workers + .putDomain({ + accountId, + hostname, + service: scriptName, + zoneId, + }) + .pipe( + Effect.retry({ + while: (error) => error._tag === "WorkerNotFound", + schedule: Schedule.exponential(200).pipe( + Schedule.both(Schedule.recurs(15)), + ), + }), + ); + return { + hostname, + id: res.id ?? "", + zoneId: res.zoneId ?? zoneId, + }; + }); + + const applied = yield* Effect.all(desired.map(attachDomain), { + concurrency: "unbounded", + }); + return applied; + }); + + const createAlchemyWorkerTags = (id: string) => [ + `alchemy:stack:${stack.name}`, + `alchemy:stage:${stack.stage}`, + `alchemy:id:${id}`, + ]; + + const hasAlchemyWorkerTags = ( + id: string, + tags: readonly string[] | undefined, + ) => { + const actualTags = new Set(tags ?? []); + return createAlchemyWorkerTags(id).every((tag) => actualTags.has(tag)); + }; + + const getDurableObjectNamespaces = ( + bindings: readonly WorkerSettingsBinding[] | null | undefined, + ) => { + const namespaces = Object.fromEntries( + (bindings ?? []).flatMap((binding) => + binding.type === "durable_object_namespace" && + binding.className && + binding.namespaceId + ? [[binding.className, binding.namespaceId]] + : [], + ), + ); + return namespaces; + }; + + const getExpectedDurableObjectClassNames = ( + bindings: readonly WorkerBinding[] | undefined, + workerName: string, + ) => + Array.from( + new Set( + bindings?.flatMap((binding) => + binding.type === "durable_object_namespace" && + binding.className && + (binding.scriptName === undefined || + binding.scriptName === workerName) + ? [binding.className] + : [], + ) ?? [], + ), + ); + + const getWorkerSettingsWithDurableObjects = Effect.fn(function* ( + scriptName: string, + expectedClassNames: readonly string[], + ) { + const { accountId } = yield* yield* CloudflareEnvironment; + return yield* workers + .getScriptScriptAndVersionSetting({ + accountId, + scriptName, + }) + .pipe( + Effect.map((settings) => { + const namespaces = getDurableObjectNamespaces(settings.bindings); + const missing = expectedClassNames.filter( + (className) => !namespaces[className], + ); + if (missing.length > 0) { + return Effect.fail( + new MissingDurableObjectNamespaces({ + scriptName, + expected: missing, + }), + ); + } + return Effect.succeed({ + settings, + durableObjectNamespaces: namespaces, + }); + }), + Effect.flatten, + Effect.retry({ + while: (error) => error._tag === "MissingDurableObjectNamespaces", + schedule: Schedule.exponential(100).pipe( + Schedule.both(Schedule.recurs(20)), + ), + }), + ); + }); + + const prepareAssets = Effect.fn(function* ( + assets: WorkerProps["assets"], + ) { + if (!assets) { + return undefined; + } + + if (typeof assets === "object" && "hash" in assets) { + const { hash: _, ...config } = assets; + return yield* readAssets(config); + } + + // Handle string path or AssetsProps + return yield* readAssets( + typeof assets === "string" ? { directory: assets } : assets, + ); + }); + + const prepareBundle = (id: string, props: WorkerProps) => + (props.bundle === false + ? readPrebuiltWorkerBundle({ + main: props.main!, + rules: props.rules, + }) + : bundler.build({ + id, + main: props.main!, + compatibility: getCompatibility(props), + entry: props.isExternal + ? { + kind: "external", + } + : { + kind: "effect", + exports: props.exports ?? {}, + }, + stack: { name: stack.name, stage: stack.stage }, + extraOptions: props.build, + }) + ).pipe(Artifacts.cached("build")); + + const hashScript = (script: string) => + Effect.sync(() => + crypto.createHash("sha256").update(script).digest("hex"), + ); + + const viteBuild = Effect.fn(function* (props: WorkerProps) { + const compatibility = getCompatibility(props); + // Loaded lazily: `./Vite.ts` pulls in `@distilled.cloud/cloudflare-vite-plugin` + // (~0.5s), which is only needed for vite-based workers at build time — + // not for every Worker definition at module-load time. + const Vite = yield* Effect.promise(() => import("./Vite.ts")); + const { assetsDirectory, serverBundle } = yield* Vite.viteBuild( + props.vite?.rootDir, + Object.fromEntries( + (yield* Effect.all( + Object.entries(props.env ?? {}).map( + Effect.fn(function* ([key, value]) { + return [ + key, + typeof value === "string" + ? value + : Redacted.isRedacted(value) && + typeof Redacted.value(value) === "string" + ? Redacted.value(value) + : Effect.isEffect(value) + ? yield* value as Effect.Effect + : undefined, + ]; + }), + ), + )).filter(([_, value]) => value !== undefined), + ), + { + compatibilityDate: compatibility.date, + compatibilityFlags: compatibility.flags, + }, + ); + + if (!assetsDirectory && !serverBundle) { + return yield* Effect.die( + new Error("Vite build produced neither server nor client output"), + ); + } + const [assets, bundle] = yield* Effect.all( + [ + assetsDirectory + ? readAssets({ + ...(props.assets && typeof props.assets !== "string" + ? props.assets + : undefined), + directory: assetsDirectory, + }) + : Effect.succeed(undefined), + serverBundle + ? Bundle.bundleOutputFromRolldownOutputBundle(serverBundle) + : Effect.succeed(undefined), + ], + { concurrency: "unbounded" }, + ); + return { assets, bundle }; + }); + + const prepareAssetsAndBundle = ( + id: string, + props: WorkerProps, + opts: { skipAssetsRead?: boolean } = {}, + ) => + Effect.gen(function* () { + if (props.script !== undefined) { + const [assets, bundleHash] = yield* Effect.all( + [ + opts.skipAssetsRead + ? Effect.succeed(undefined) + : prepareAssets(props.assets), + hashScript(props.script), + ], + { concurrency: "unbounded" }, + ); + return { + assets, + bundle: { + files: [{ path: "main.js", content: props.script }], + hash: bundleHash, + }, + }; + } + if (props.vite) { + const [{ assets, bundle }, input] = yield* Effect.all( + [ + viteBuild(props), + // hashDirectory expects `{ cwd, memo }`. The vite props + // store the project root under `rootDir`, so map it + // here. Without this, `cwd` falls back to + // `process.cwd()` and the input hash is computed over + // the wrong directory tree (often the entire monorepo + // root), making it both slow and unable to detect + // changes scoped to the actual Vite project. + hashDirectory({ + cwd: props.vite.rootDir, + memo: props.vite.memo, + }), + ], + { concurrency: "unbounded" }, + ); + return { assets, bundle, input }; + } + const [assets, bundle] = yield* Effect.all( + [ + opts.skipAssetsRead + ? Effect.succeed(undefined) + : prepareAssets(props.assets), + prepareBundle(id, props), + ], + { concurrency: "unbounded" }, + ); + return { assets, bundle }; + }).pipe( + Effect.map(({ assets, bundle, input }) => ({ + assets, + bundle: { + main: bundle?.files[0].path, + files: bundle?.files.map( + (file) => + new File([file.content as BlobPart], file.path, { + type: contentTypeFromExtension(path.extname(file.path)), + }), + ), + }, + hash: { + assets: assets?.hash, + bundle: bundle?.hash, + input, + } satisfies Worker["Attributes"]["hash"], + })), + ); + + const normalizePrebuiltAssets = ( + assets: WorkerProps["assets"], + output: Worker["Attributes"] | undefined, + ) => { + if (!Predicate.hasProperty(assets, "hash")) return undefined; + const { directory: _, hash, ...config } = assets; + return { config, hash, skip: hash === output?.hash?.assets }; + }; + + const putWorker = Effect.fn(function* ( + id: string, + news: WorkerProps, + bindings: ResourceBinding[], + olds: WorkerProps | undefined, + output: Worker["Attributes"] | undefined, + session: ScopedPlanStatusSession, + existingSettings?: workers.GetScriptScriptAndVersionSettingResponse, + ) { + const { accountId } = yield* yield* CloudflareEnvironment; + const name = yield* createWorkerName(id, news.name); + yield* Effect.logInfo( + `Cloudflare Worker ${olds ? "update" : "create"}: preparing bundle for ${name}`, + ); + // If the caller handed us a precomputed asset hash that matches + // what we previously stored, we can skip walking the directory + // entirely and tell Cloudflare to keep the assets it already + // has bound to this script. The disk read is the expensive + // part; the script PUT happens either way. + const prebuiltAssets = normalizePrebuiltAssets(news.assets, output); + const { + assets, + bundle, + hash: preparedHash, + } = yield* prepareAssetsAndBundle(id, news, { + skipAssetsRead: prebuiltAssets?.skip, + }); + // When the caller supplied a precomputed hash (e.g. via + // `Build.Command`), store *that* hash in output state so the + // next diff can short-circuit by comparing it directly. The + // hash that `readAssets` produces is the manifest-derived + // hash, which is shaped differently from any upstream + // build-input hash and will never match it on the next pass. + const hash = { + ...preparedHash, + assets: prebuiltAssets?.hash ?? preparedHash.assets, + } satisfies Worker["Attributes"]["hash"]; + const metadataBindings = bindings.flatMap((b) => b.data.bindings ?? []); + const expectedDurableObjectClassNames = + getExpectedDurableObjectClassNames(metadataBindings, name); + let metadataAssets: + | workers.PutScriptRequest["metadata"]["assets"] + | undefined; + let keepAssets = false; + if (prebuiltAssets?.skip) { + // Hash matched what's already on Cloudflare: keep the + // existing asset manifest and skip the upload session. + yield* Effect.logInfo( + `Cloudflare Worker update: assets unchanged for ${name}, keeping existing`, + ); + keepAssets = true; + metadataAssets = { config: prebuiltAssets.config }; + metadataBindings.push({ + type: "assets", + name: "ASSETS", + }); + } else if (assets) { + // We had to read the directory. Even after the read, the + // computed hash may match what's already deployed (e.g. + // legacy `string` / `AssetsProps` shapes that don't carry a + // precomputed hash, or a precomputed hash that disagreed with + // disk). In that case still keep the existing manifest and + // skip the upload session — Cloudflare's content-addressed + // session would no-op on every byte anyway. + if (assets.hash === prebuiltAssets?.hash) { + yield* Effect.logInfo( + `Cloudflare Worker update: assets unchanged for ${name}, keeping existing`, + ); + keepAssets = true; + metadataAssets = { config: assets.config }; + } else { + yield* Effect.logInfo( + `Cloudflare Worker ${olds ? "update" : "create"}: uploading assets for ${name}`, + ); + const { jwt } = yield* uploadAssets( + accountId, + name, + assets, + session, + ); + metadataAssets = { + jwt, + config: assets.config, + }; + } + metadataBindings.push({ + type: "assets", + name: "ASSETS", + }); + } + metadataBindings.push( + { + type: "plain_text", + name: "ALCHEMY_PHASE", + text: "runtime", + }, + { + type: "plain_text", + name: "ALCHEMY_STACK_NAME", + text: stack.name, + }, + { + type: "plain_text", + name: "ALCHEMY_STAGE", + text: stack.stage, + }, + { + type: "plain_text", + name: "ALCHEMY_CLOUDFLARE_ACCOUNT_ID", + text: accountId, + }, + ); + // Add environment variables as metadata bindings + if (news.env) { + for (const [key, value] of Object.entries(news.env)) { + if (value === undefined) continue; + if (metadataBindings.some((b) => b.name === key)) continue; + if (Redacted.isRedacted(value)) { + const unredacted = Redacted.value(value); + metadataBindings.push({ + type: "secret_text", + name: key, + text: + typeof unredacted === "string" + ? unredacted + : JSON.stringify(unredacted), + }); + } else if (typeof value === "string") { + metadataBindings.push({ + type: "plain_text", + name: key, + text: value, + }); + } else { + metadataBindings.push({ + type: "json", + name: key, + json: value, + }); + } + } + } + yield* Effect.logInfo( + `Cloudflare Worker ${olds ? "update" : "create"}: uploading script for ${name}`, + ); + const size = + bundle.files + ?.filter((file) => !file.name.endsWith(".map")) + .reduce((acc, file) => acc + file.size, 0) ?? 0; + const sizeKB = size / 1024; + const sizeMB = sizeKB / 1024; + const bundleSize = `${sizeKB > 1024 ? `${sizeMB.toFixed(2)} MB` : `${sizeKB.toFixed(2)} KB`}`; + yield* session.note(`Uploading worker (${bundleSize}) ...`); + + // Read existing worker settings for migration tracking + const oldSettings = + existingSettings ?? + (yield* workers + .getScriptScriptAndVersionSetting({ + accountId, + scriptName: name, + }) + .pipe( + Effect.map((s) => s as typeof s | undefined), + Effect.catch(() => Effect.succeed(undefined)), + )); + + const oldTags = Array.from(new Set(oldSettings?.tags ?? [])); + const oldBindings = oldSettings?.bindings ?? []; + + // Parse alchemy:do:{logicalId}:{className} tags + const oldDoClassNameByLogicalId = getDurableObjectTagMap(oldTags); + const currentDoBindings = getDurableObjectBindings(bindings, name); + const currentDoClassNameByLogicalId = Object.fromEntries( + currentDoBindings.map((binding) => [ + binding.logicalId, + binding.className, + ]), + ); + + // Parse alchemy:migration-tag:{version} + const oldMigrationTag = oldTags.flatMap((tag) => + tag.startsWith("alchemy:migration-tag:") + ? [tag.slice("alchemy:migration-tag:".length)] + : [], + )[0]; + const newMigrationTag = bumpMigrationTagVersion(oldMigrationTag); + + // Compute deleted classes + const deletedClasses: string[] = []; + for (const [logicalId, className] of Object.entries( + oldDoClassNameByLogicalId, + )) { + if (!currentDoClassNameByLogicalId[logicalId]) { + deletedClasses.push(className); + } + } + + // Backward compatibility for old workers that have DO bindings but no + // alchemy:do tags yet. Cross-script bindings (`scriptName` set to + // anything other than this worker) are NEVER candidates for + // delete-class migrations — the class lives on the foreign script + // and we don't own its lifecycle. + if (Object.keys(oldDoClassNameByLogicalId).length === 0) { + for (const oldBinding of oldBindings) { + const ownedLocally = + !("scriptName" in oldBinding) || oldBinding.scriptName === name; + if ( + oldBinding.type === "durable_object_namespace" && + "className" in oldBinding && + oldBinding.className && + ownedLocally && + !currentDoBindings.some( + (binding) => binding.bindingName === oldBinding.name, + ) + ) { + deletedClasses.push(oldBinding.className); + } + } + } + + // Collect container-backed class names so we can send container metadata + const containerClassNames = new Set( + bindings.flatMap((b) => + (b.data.containers ?? []).map((c) => c.className), + ), + ); + + // Compute new and renamed classes + const newClasses: string[] = []; + const newSqliteClasses: string[] = []; + const renamedClasses: { from: string; to: string }[] = []; + for (const binding of currentDoBindings) { + const previousClassName = + oldDoClassNameByLogicalId[binding.logicalId]; + if (!previousClassName) { + // Default all new Durable Object classes to SQLite. Cloudflare + // recommends SQLite for new namespaces, and container-backed + // Durable Objects require it. + newSqliteClasses.push(binding.className); + } else if (previousClassName !== binding.className) { + renamedClasses.push({ + from: previousClassName, + to: binding.className, + }); + } + } + + yield* Effect.logInfo( + `Cloudflare Worker put: durable object reconciliation ${JSON.stringify( + { + oldDoClassNameByLogicalId, + currentDoClassNameByLogicalId, + deletedClasses, + renamedClasses, + newSqliteClasses, + }, + )}`, + ); + + // Build alchemy:do:{logicalId}:{className} tags for each DO binding + const alchemyDoTags: string[] = []; + for (const binding of currentDoBindings) { + alchemyDoTags.push( + `alchemy:do:${binding.logicalId}:${binding.className}`, + ); + } + + const metadataTags = Array.from( + new Set([ + ...createAlchemyWorkerTags(id), + ...alchemyDoTags, + ...(newMigrationTag + ? [`alchemy:migration-tag:${newMigrationTag}`] + : []), + ...(news.tags ?? []), + ]), + ); + + const migrations = { + oldTag: oldMigrationTag, + newTag: newMigrationTag, + newClasses, + deletedClasses, + renamedClasses, + transferredClasses: [] as { from: string; to: string }[], + newSqliteClasses, + }; + + const metadataContainers = [...containerClassNames].map( + (className) => ({ + className, + }), + ); + + const compatibility = getCompatibility(news); + const metadata: workers.PutScriptRequest["metadata"] = { + assets: metadataAssets, + bindings: metadataBindings, + bodyPart: undefined, + compatibilityDate: compatibility.date, + compatibilityFlags: compatibility.flags, + containers: + metadataContainers.length > 0 ? metadataContainers : undefined, + keepAssets, + keepBindings: undefined, + limits: news.limits, + logpush: news.logpush, + mainModule: bundle.main, + migrations, + observability: news.observability ?? { + enabled: true, + logs: { + enabled: true, + invocationLogs: true, + }, + }, + placement: news.placement, + tags: metadataTags, + tailConsumers: undefined, + usageModel: undefined, + }; + const worker = yield* workers + .putScript({ + accountId, + scriptName: name, + metadata, + files: bundle.files, + }) + .pipe( + Effect.catch((err) => { + // When adopting a Worker managed by Wrangler (or after a previous + // deploy with mismatched migrations), the old_tag precondition + // fails. The only way to discover the actual tag is through the + // error message — getScriptSettings is meant to return it but + // doesn't at runtime. + const msg = String( + typeof err === "object" && err !== null && "message" in err + ? err.message + : err, + ); + const expectedTag = msg.match( + /when expected tag is ['"]?([^'"]+)['"]?/, + )?.[1]; + if (expectedTag) { + return workers.putScript({ + accountId, + scriptName: name, + metadata: { + ...metadata, + migrations: { + ...migrations, + oldTag: expectedTag, + newTag: bumpMigrationTagVersion(expectedTag), + }, + }, + files: bundle.files, + }); + } + return Effect.fail(err as any); + }), + ); + const { settings, durableObjectNamespaces } = + yield* getWorkerSettingsWithDurableObjects( + name, + expectedDurableObjectClassNames, + ); + // Reconcile workers.dev subdomain against observed cloud state. + // We can't diff `news.url` against `olds.url` here because both + // default to `undefined` (meaning "enable") — that comparison + // would skip the API call on every deploy where the user never + // explicitly set `url`, leaving the subdomain in whatever state + // Cloudflare currently has it (disabled by default, or whatever + // a previous failed/external action left it as). + const desiredSubdomainEnabled = news.url !== false; + const observedSubdomain = yield* workers + .getScriptSubdomain({ + accountId, + scriptName: name, + }) + .pipe( + Effect.orElseSucceed(() => ({ + enabled: false, + previewsEnabled: false, + })), + ); + if ( + desiredSubdomainEnabled !== observedSubdomain.enabled || + desiredSubdomainEnabled !== observedSubdomain.previewsEnabled + ) { + yield* session.note( + `${desiredSubdomainEnabled ? "Enabling" : "Disabling"} workers.dev subdomain...`, + ); + // Cloudflare's script registry is eventually consistent — for the + // first few hundred ms after `putScript` returns, POST /subdomain + // can still get back `WorkerNotFound` (a generic "unknown error" + // body), or a bare 500 surfaced as `InternalServerError` / + // `UnknownCloudflareError` (code 10013). Bigger uploads race harder. + // Retry the subdomain toggle on those transient tags with a short + // exponential backoff; same pattern we use elsewhere in this + // provider for DO-namespace propagation and for `putScript` itself. + yield* setWorkerSubdomain(name, desiredSubdomainEnabled).pipe( + Effect.retry({ + while: (error) => + error._tag === "WorkerNotFound" || + error._tag === "InternalServerError" || + error._tag === "UnknownCloudflareError", + schedule: Schedule.exponential(200).pipe( + Schedule.both(Schedule.recurs(15)), + ), + }), + ); + } + const desiredDomains = normalizeDomains(news.domain); + const previousDomains = output?.domains ?? []; + if (desiredDomains.length > 0 || previousDomains.length > 0) { + yield* session.note( + `Reconciling custom domains (${desiredDomains.length}) ...`, + ); + } + const reconciled = yield* reconcileDomains(name, desiredDomains); + const workersDevUrl = + news.url !== false + ? `https://${name}.${yield* getAccountSubdomain(accountId)}.workers.dev` + : undefined; + const domains = [ + ...reconciled.map((d) => `https://${d.hostname}`), + ...(workersDevUrl ? [workersDevUrl] : []), + ]; + const crons = yield* reconcileCrons( + name, + normalizeCrons([...getCronBindings(bindings), ...(news.crons ?? [])]), + output?.crons ?? [], + session, + ); + return { + workerId: worker.id ?? name, + workerName: name, + logpush: worker.logpush ?? undefined, + url: domains[0], + tags: settings.tags ?? metadata.tags, + durableObjectNamespaces, + accountId, + domains, + crons, + hash, + } satisfies Worker["Attributes"]; + }); + + const hasChanged = Effect.fn(function* ( + id: string, + props: WorkerProps, + output: Worker["Attributes"], + ) { + if (props.script !== undefined) { + const scriptHash = yield* hashScript(props.script); + if (scriptHash !== output.hash?.bundle) { + return true; + } + if (!props.assets) { + return false; + } + const assetsHash = Predicate.hasProperty(props.assets, "hash") + ? props.assets.hash + : undefined; + if (assetsHash === undefined) { + return true; + } + return assetsHash !== output.hash?.assets; + } + if (props.vite) { + const input = yield* hashDirectory({ + cwd: props.vite.rootDir, + memo: props.vite.memo, + }); + return input !== output.hash?.input; + } + const bundleHash = yield* prepareBundle(id, props).pipe( + Effect.map((b) => b.hash), + ); + if (bundleHash !== output.hash?.bundle) { + return true; + } + if (!props.assets) { + return false; + } + // We deliberately don't read the assets directory during diff. + // For `AssetsWithHash` (the documented contract) the upstream + // `Build.Command` already gave us an authoritative hash — we + // just compare strings. Reading the directory here would + // (a) hash the same tree twice per apply (`putWorker` reads + // again when an upload is actually required), and (b) crash + // when the prior state was written on a different machine + // and `path` doesn't exist locally — blocking any local + // reapply even though the precomputed hash is right there + // in props. + // + // For the legacy `string` / `AssetsProps` shapes there's no + // hash in props to compare against, so we conservatively + // assume the assets changed; `putWorker` will read once, + // hash, and use `keepAssets` if it turns out nothing actually + // changed. + const assetsHash = Predicate.hasProperty(props.assets, "hash") + ? props.assets.hash + : undefined; + if (assetsHash === undefined) { + return true; + } + return assetsHash !== output.hash?.assets; + }); + + return Worker.Provider.of({ + stables: ["workerId", "workerName"], + list: () => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + // Account-scoped enumeration of every Worker script. The + // per-script `read` makes several extra calls (subdomain, + // settings, domains, schedule) to fully hydrate + // url/durableObjectNamespaces/domains/crons. Doing that for + // every script on the account is both expensive (4 calls × N) + // and fragile — a single script with a binding shape the + // settings schema doesn't know about would break the whole + // listing (the same reason `read` deliberately avoids + // `listScripts`). For `list()` we hydrate the core identifying + // and settings fields that come straight from the script + // metadata and leave the binding-derived fields at the same + // defaults `read` returns when those sub-resources are absent + // (`url: undefined`, `durableObjectNamespaces: {}`, + // `domains: []`, `crons: []`). `accountId` + `workerName` are + // sufficient for `delete`. + return yield* workers.listScripts.pages({ accountId }).pipe( + Stream.runCollect, + Effect.map((chunk) => + Array.from(chunk).flatMap((page) => + // Annotate the element type as the full `Attributes` shape + // (incl. the optional `hash`) so it matches `read` exactly. + // `list()` is an inference source for the provider's resource + // type; a narrower element (e.g. via `satisfies`, which omits + // `hash`) would derail `Res` inference and cascade every + // lifecycle method's requirement channel to `never`. + (page.result ?? []).flatMap( + (script): Worker["Attributes"][] => + script.id + ? [ + { + accountId, + workerId: script.id, + workerName: script.id, + logpush: script.logpush ?? undefined, + url: undefined, + tags: script.tags ?? undefined, + durableObjectNamespaces: {}, + domains: [], + crons: [], + }, + ] + : [], + ), + ), + ), + ); + }), + diff: Effect.fn(function* ({ id, news, olds, output, newBindings }) { + const { accountId } = yield* yield* CloudflareEnvironment; + if (!isResolved(news)) return undefined; + if ((output?.accountId ?? accountId) !== accountId) { + return { action: "replace" }; + } + const workerName = yield* createWorkerName(id, news.name); + const oldWorkerName = output?.workerName + ? output.workerName + : yield* createWorkerName(id, olds?.name); + if (workerName !== oldWorkerName) { + return { action: "replace" }; + } + if (!output) { + return; + } + const newDomains = normalizeDomains(news.domain) + .map((h) => `https://${h}`) + .sort(); + const oldDomains = (output?.domains ?? []) + .filter((u) => !u.endsWith(".workers.dev")) + .sort(); + const domainsChanged = + newDomains.length !== oldDomains.length || + newDomains.some((d, i) => d !== oldDomains[i]); + const newCrons = normalizeCrons([ + ...(Array.isArray(newBindings) + ? getCronBindings( + newBindings as ResourceBinding[], + ) + : []), + ...(news.crons ?? []), + ]).sort(); + const oldCrons = [...(output?.crons ?? [])].sort(); + const cronsChanged = + newCrons.length !== oldCrons.length || + newCrons.some((cron, index) => cron !== oldCrons[index]); + // `url` is `domains[0]`: the first custom domain in user order if + // any, otherwise the workers.dev URL (derived from the stable + // worker name + account subdomain). It's stable across this update + // exactly when that first domain is unchanged — which is NOT the + // same as "the domain set is unchanged": adding a second custom + // domain leaves `url` put, while reordering changes it even though + // the set is equal. Compute the resulting `url` and carry it + // forward as a stable only when it matches the old one, so + // downstream resources that reference `worker.url` (e.g. a GitHub + // Webhook delivery URL built via `Output.interpolate`) resolve it + // to a concrete value during planning instead of an unresolved + // Output — otherwise every worker update spuriously re-updates them. + const newCustomDomains = normalizeDomains(news.domain); + const newUrl = + newCustomDomains.length > 0 + ? `https://${newCustomDomains[0]}` + : news.url !== false + ? (output.domains ?? []).find((u) => u.endsWith(".workers.dev")) + : undefined; + const urlStable = newUrl !== undefined && newUrl === output.url; + // `durableObjectNamespaces` maps each hosted DO class name to the + // namespace id Cloudflare assigned it. Those ids are permanent for + // the lifetime of a (worker, class) pair, so the map only changes + // when a class is added or removed — never on a plain code/config + // update. Carry it forward as a stable whenever the set of local DO + // class names is unchanged, for the same reason as `url` above: + // downstream resources that bind a DO namespace via + // `worker.durableObjectNamespaces[name]` (e.g. a Container attached + // to a DO) must resolve it to a concrete value during planning. + // Otherwise the binding holds an unresolved Output, which + // `diffBindings` treats as "changed", spuriously re-updating the + // bound resource on every deploy. Class names are structural (not the + // namespace id), so this comparison holds even when `newBindings` is + // otherwise unresolved. + const newDoClassNames = Array.isArray(newBindings) + ? getExpectedDurableObjectClassNames( + (newBindings as ResourceBinding[]).flatMap( + (b) => b.data.bindings ?? [], + ), + workerName, + ).sort() + : []; + const oldDoClassNames = Object.keys( + output.durableObjectNamespaces ?? {}, + ).sort(); + const doNamespacesStable = + oldWorkerName === workerName && + newDoClassNames.length === oldDoClassNames.length && + newDoClassNames.every((name, i) => name === oldDoClassNames[i]); + if ( + domainsChanged || + cronsChanged || + (yield* hasChanged(id, news, output)) + ) { + // `workerId` is always stable across an update; seed it so it + // survives now that `diff.stables` overrides `provider.stables` + // rather than being merged with it. + const stables: string[] = ["workerId"]; + if (oldWorkerName === workerName) { + stables.push("workerName"); + } + if (urlStable) { + stables.push("url"); + } + if (doNamespacesStable) { + stables.push("durableObjectNamespaces"); + } + return { + action: "update", + stables: stables.length > 0 ? stables : undefined, + }; + } + }), + precreate: Effect.fn(function* ({ id, news, session }) { + const { accountId } = yield* yield* CloudflareEnvironment; + const name = yield* createWorkerName(id, news.name); + const exportMap = news.exports ?? {}; + const durableObjects = Object.keys(exportMap) + .filter((logicalId) => isDurableObjectExport(exportMap[logicalId])) + .map((logicalId) => ({ + logicalId, + className: logicalId, + })); + const doClasses = durableObjects.map((binding) => binding.className); + const containers = doClasses.map((className) => ({ className })); + const alchemyDoTags = durableObjects.map( + ({ logicalId, className }) => + `alchemy:do:${logicalId}:${className}`, + ); + const tags = Array.from( + new Set([ + ...createAlchemyWorkerTags(id), + ...alchemyDoTags, + ...(news.tags ?? []), + ]), + ); + yield* Effect.logInfo( + `Cloudflare Worker precreate: starting ${name}`, + ); + yield* Effect.logInfo( + `Cloudflare Worker precreate: durable objects ${JSON.stringify( + durableObjects, + )}`, + ); + const existingSettings = yield* workers + .getScriptScriptAndVersionSetting({ + accountId, + scriptName: name, + }) + .pipe( + Effect.catchTag("WorkerNotFound", () => + Effect.succeed(undefined), + ), + ); + let durableObjectNamespaces = getDurableObjectNamespaces( + existingSettings?.bindings, + ); + + if (existingSettings) { + // Engine has already cleared this resource for write via + // `read` + AdoptPolicy. Either we own it (matching tags) or + // the user opted in to a takeover (`--adopt` / `adopt(true)`). + yield* Effect.logInfo( + `Cloudflare Worker precreate: reusing existing ${name}`, + ); + } else { + yield* session.note("Pre-creating worker..."); + const mainModule = "main.js"; + 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 + .map( + (className) => + `export class ${className} extends DurableObject {}`, + ) + .join("\n")}`; + yield* workers + .putScript({ + accountId, + scriptName: name, + metadata: { + mainModule, + bindings: + doClasses.length > 0 + ? doClasses.map((className) => ({ + type: "durable_object_namespace" as const, + name: className, + className, + })) + : undefined, + ...getCompatibility(news), + containers, + migrations: + doClasses.length > 0 + ? { + oldTag: undefined, + newTag: undefined, + newClasses: [], + deletedClasses: [], + renamedClasses: [], + transferredClasses: [], + newSqliteClasses: doClasses, + } + : undefined, + observability: news.observability ?? { + enabled: true, + logs: { + enabled: true, + invocationLogs: true, + }, + }, + tags, + }, + files: [ + new File([placeholderScript], mainModule, { + type: "application/javascript+module", + }), + ], + }) + .pipe( + // Cloudflare's PUT /workers/scripts/{name} intermittently + // returns code 10002 / "An unknown error has occurred" on the + // first put for a fresh worker name. Surfaced as the shared + // `InternalServerError` upstream (alchemy-run/distilled#290). + // Also match `UnknownCloudflareError` for older + // @distilled.cloud/cloudflare versions that haven't picked + // up the patch yet. + Effect.retry({ + while: (e) => + e._tag === "InternalServerError" || + e._tag === "UnknownCloudflareError", + schedule: Schedule.exponential(1000).pipe( + Schedule.both(Schedule.recurs(5)), + ), + }), + ); + if (doClasses.length > 0) { + ({ durableObjectNamespaces } = + yield* getWorkerSettingsWithDurableObjects(name, doClasses)); + } + } + + if (existingSettings && doClasses.length > 0) { + ({ durableObjectNamespaces } = + yield* getWorkerSettingsWithDurableObjects(name, doClasses)); + } + + return { + workerId: name, + workerName: name, + logpush: existingSettings?.logpush ?? undefined, + url: undefined, + tags: existingSettings?.tags ?? tags, + durableObjectNamespaces, + accountId, + domains: [], + crons: [], + } satisfies Worker["Attributes"]; + }), + read: Effect.fn( + function* ({ id, output, olds }) { + const { accountId } = yield* yield* CloudflareEnvironment; + const workerName = + output?.workerName ?? (yield* createWorkerName(id, olds?.name)); + yield* Effect.logInfo( + `Cloudflare Worker read: checking ${workerName}`, + ); + // We deliberately don't call `listScripts({ accountId })` here: + // it pulls every Worker on the account back through a strict + // schema decode, and a single existing Worker the schema doesn't + // know about (e.g. `placement_mode: "targeted"`) breaks the + // entire read. `getScriptSettings` already fails with + // `WorkerNotFound` if the script doesn't exist, which the + // surrounding `Effect.catchTag` turns into `undefined` — that's + // all the existence check we need. + const [subdomain, settings, domainsList] = yield* Effect.all([ + workers.getScriptSubdomain({ + accountId, + scriptName: workerName, + }), + workers.getScriptScriptAndVersionSetting({ + accountId, + scriptName: workerName, + }), + workers + .listDomains({ + accountId, + service: workerName, + }) + .pipe(Effect.map((r) => r.result ?? [])), + ]); + // Preserve the order the user provided in `olds.domain`. The + // Cloudflare API returns domains in non-deterministic order, + // which would cause downstream `worker.domains[0]` reads to flip + // between deploys. Drift (domains we don't know about) is + // appended after the user-ordered ones. + const userOrder = normalizeDomains(olds?.domain); + const orderedHostnames = [ + ...userOrder.flatMap( + (h) => + domainsList.find((d) => d.hostname === h)?.hostname ?? [], + ), + ...domainsList.flatMap((d) => + d.hostname && !userOrder.includes(d.hostname) + ? [d.hostname] + : [], + ), + ]; + const workersDevUrl = subdomain.enabled + ? `https://${workerName}.${yield* getAccountSubdomain(accountId)}.workers.dev` + : undefined; + const domains = [ + ...orderedHostnames.map((h) => `https://${h}`), + ...(workersDevUrl ? [workersDevUrl] : []), + ]; + const crons = yield* getWorkerCrons(workerName); + yield* Effect.logInfo( + `Cloudflare Worker read: found ${workerName}`, + ); + const attrs = { + accountId, + workerId: workerName, + workerName, + logpush: settings.logpush ?? undefined, + url: domains[0], + tags: settings.tags ?? undefined, + durableObjectNamespaces: getDurableObjectNamespaces( + settings.bindings, + ), + domains, + crons, + } satisfies Worker["Attributes"]; + + // Centralized ownership decision: the engine routes `read`'s + // return value based on `AdoptPolicy`. We hand it the attrs + // either as-is (owned: alchemy tags identify this stack/stage/id, + // safe to silently adopt even without `--adopt`) or branded with + // `Unowned` (caller must opt in via `--adopt` or the engine + // raises `OwnedBySomeoneElse`). + return hasAlchemyWorkerTags(id, settings.tags ?? []) + ? attrs + : Unowned(attrs); + }, + (effect) => + effect.pipe( + Effect.catchTag("WorkerNotFound", () => + Effect.succeed(undefined), + ), + ), + ), + reconcile: Effect.fn(function* ({ + id, + news, + olds, + bindings, + output, + session, + }) { + const { accountId } = yield* yield* CloudflareEnvironment; + const name = + output?.workerName ?? (yield* createWorkerName(id, news.name)); + const durableObjects = getDurableObjectBindings(bindings, name).map( + ({ logicalId, className }) => ({ + logicalId, + className, + }), + ); + yield* Effect.logInfo( + `Cloudflare Worker reconcile: starting ${name}`, + ); + yield* Effect.logInfo( + `Cloudflare Worker reconcile: durable objects ${JSON.stringify( + durableObjects, + )}`, + ); + + // Observe — fetch the script's current settings if it already exists. + // `putWorker` is a true upsert against the Cloudflare API; the + // existing settings inform asset/migration decisions and let the + // reconciler converge whether the worker is brand-new, adopted, or + // an in-place update. + const existingSettings = yield* workers + .getScriptScriptAndVersionSetting({ + accountId, + scriptName: name, + }) + .pipe( + Effect.catchTag("WorkerNotFound", () => + Effect.succeed(undefined), + ), + ); + yield* Effect.logInfo( + `Cloudflare Worker reconcile: existing durable object tags ${JSON.stringify( + (existingSettings?.tags ?? []).filter((tag) => + tag.startsWith("alchemy:do:"), + ), + )}`, + ); + yield* Effect.logInfo( + `Cloudflare Worker reconcile: previous durable object tags ${JSON.stringify( + (output?.tags ?? []).filter((tag) => + tag.startsWith("alchemy:do:"), + ), + )}`, + ); + return yield* putWorker( + id, + news, + bindings, + olds, + output, + session, + existingSettings, + ); + }), + delete: Effect.fn(function* ({ output }) { + yield* Effect.logInfo( + `Cloudflare Worker delete: deleting ${output.workerName}`, + ); + // Look up live domain IDs rather than trusting persisted state. + // We no longer track `{ id, zoneId }` on the output; fetching + // straight from Cloudflare handles both the normal case and + // adopted workers whose domains we never recorded. + const liveDomains = yield* workers + .listDomains({ + accountId: output.accountId, + service: output.workerName, + }) + .pipe( + Effect.map((r) => r.result ?? []), + Effect.catch(() => Effect.succeed([])), + ); + if (liveDomains.length) { + yield* Effect.all( + liveDomains.flatMap((d) => + d.id + ? [ + workers + .deleteDomain({ + accountId: output.accountId, + domainId: d.id, + }) + .pipe( + Effect.catchTag("DomainNotFound", () => Effect.void), + ), + ] + : [], + ), + { concurrency: "unbounded" }, + ); + } + yield* workers + .deleteScript({ + accountId: output.accountId, + scriptName: output.workerName, + // Force teardown of queue consumers, durable object classes, and + // service bindings hanging off this worker. Without `force`, those + // conditions raise QueueConsumerConflict / ServiceBindingConflict + // and leave the script in CF. Alchemy is the source of truth for + // the worker, so we want a hard delete on teardown. + force: true, + }) + .pipe(Effect.catchTag("WorkerNotFound", () => Effect.void)); + }), + tail: ({ output }) => + telemetry.tailScript({ + accountId: output.accountId, + scriptName: output.workerName, + }), + logs: ({ output, options }) => + telemetry.queryLogs({ + accountId: output.accountId, + filters: [ + { + key: "$workers.scriptName", + operation: "eq", + type: "string", + value: output.workerName, + }, + ], + options, + }), + }); + }), + ); + +const contentTypeFromExtension = (extension: string) => { + switch (extension) { + case ".wasm": + return "application/wasm"; + case ".txt": + case ".html": + case ".sql": + case ".custom": + return "text/plain"; + case ".bin": + return "application/octet-stream"; + case ".mjs": + case ".js": + return "application/javascript+module"; + case ".cjs": + return "application/javascript"; + case ".map": + return "application/source-map"; + default: + return "application/octet-stream"; + } +}; + +function bumpMigrationTagVersion( + oldTag: string | undefined, +): string | undefined { + if (!oldTag) return undefined; + const version = oldTag.match(/^(alchemy:)?v(\d+)$/)?.[2]; + if (!version) return "alchemy:v1"; + return `alchemy:v${parseInt(version, 10) + 1}`; +} + +function getDurableObjectBindings( + bindings: ReadonlyArray, + workerName: string, +) { + // Resource authors (and the `make`/`yield* Tag`/plan-vs-apply machinery) + // can register the same DO binding multiple times under the same logical + // id — `binding()` is a plain `worker.bind` and intentionally has no + // dedup. Collapse duplicates here so each `(logicalId, bindingName, + // className)` tuple appears at most once. We also exclude cross-script + // references: a `scriptName` pointing to *another* worker means this + // worker just references a foreign class — ship the binding to + // Cloudflare, but don't drive class migrations for it. + const seen = new Set(); + return bindings.flatMap((binding) => + (binding.data.bindings ?? []).flatMap((item: WorkerBinding) => { + if ( + item.type !== "durable_object_namespace" || + !("className" in item) || + !item.className + ) { + return []; + } + if (item.scriptName !== undefined && item.scriptName !== workerName) { + return []; + } + const dedupKey = `${binding.sid}::${item.name}::${item.className}`; + if (seen.has(dedupKey)) return []; + seen.add(dedupKey); + return [ + { + logicalId: binding.sid, + bindingName: item.name, + className: item.className, + }, + ]; + }), + ); +} + +function getDurableObjectTagMap(tags: ReadonlyArray) { + return Object.fromEntries( + tags.flatMap((tag) => { + if (!tag.startsWith("alchemy:do:")) { + return []; + } + const parts = tag.split(":"); + const logicalId = parts[2]; + const className = parts.slice(3).join(":"); + return logicalId && className ? [[logicalId, className]] : []; + }), + ); +} diff --git a/packages/alchemy/src/Cloudflare/Workers/Workflow.ts b/packages/alchemy/src/Cloudflare/Workers/Workflow.ts index fab05122b..3eff4c5d9 100644 --- a/packages/alchemy/src/Cloudflare/Workers/Workflow.ts +++ b/packages/alchemy/src/Cloudflare/Workers/Workflow.ts @@ -9,6 +9,7 @@ import { ALCHEMY_PHASE } from "../../Phase.ts"; import type { PlatformServices } from "../../Platform.ts"; import * as Provider from "../../Provider.ts"; import { Resource } from "../../Resource.ts"; +import type { RuntimeContext } from "../../RuntimeContext.ts"; import { effectClass, taggedFunction } from "../../Util/effect.ts"; import { CloudflareEnvironment } from "../CloudflareEnvironment.ts"; import { Worker, WorkerEnvironment, type WorkerServices } from "./Worker.ts"; @@ -115,7 +116,10 @@ export type WorkflowRunServices = | WorkerServices | ExecutionContext; -export type WorkflowServices = WorkflowRunServices | PlatformServices; +export type WorkflowServices = + | WorkflowRunServices + | PlatformServices + | RuntimeContext; /** * Metadata stored in the worker export map to distinguish workflow exports @@ -275,7 +279,7 @@ export class WorkflowScope extends Context.Service< * * ```typescript * Effect.gen(function* () { - * const kv = yield* Cloudflare.KVNamespace.bind(KV); + * const kv = yield* Cloudflare.KV.ReadWriteNamespace(KV); * * return Effect.fn(function* (input: { roomId: string; message: string }) { * const { roomId, message } = input; diff --git a/packages/alchemy/src/Cloudflare/Workers/index.ts b/packages/alchemy/src/Cloudflare/Workers/index.ts index ffee35a07..71a7f4c26 100644 --- a/packages/alchemy/src/Cloudflare/Workers/index.ts +++ b/packages/alchemy/src/Cloudflare/Workers/index.ts @@ -3,7 +3,6 @@ export * from "./Assets.ts"; export * from "./ConfigProvider.ts"; export * from "./CronEventSource.ts"; export * from "./DurableObjectBridge.ts"; -export * from "./DurableObjectChatPersistence.ts"; export * from "./DurableObjectNamespace.ts"; export * from "./DurableObjectState.ts"; export * from "./DurableObjectStorage.ts"; @@ -27,5 +26,6 @@ export * from "./Worker.ts"; export * from "./WorkerBinding.ts"; export * from "./WorkerBridge.ts"; export * from "./WorkerLoader.ts"; +export * from "./WorkerProvider.ts"; export * from "./Workflow.ts"; export * from "./WorkflowBridge.ts"; diff --git a/packages/alchemy/src/Cloudflare/index.ts b/packages/alchemy/src/Cloudflare/index.ts index 257889165..7f059a4d0 100644 --- a/packages/alchemy/src/Cloudflare/index.ts +++ b/packages/alchemy/src/Cloudflare/index.ts @@ -69,6 +69,7 @@ export * from "./Images/index.ts"; export * from "./Intel/index.ts"; export * from "./KeylessCertificate/index.ts"; export * from "./KV/index.ts"; +export * as KV from "./KV/index.ts"; export * from "./LeakedCredentialCheck/index.ts"; export * from "./LoadBalancer/index.ts"; export * from "./Logpush/index.ts"; @@ -89,7 +90,9 @@ export * from "./PageShield/index.ts"; export * from "./Pipelines/index.ts"; export * from "./Providers.ts"; export * from "./Queue/index.ts"; +export * as Queues from "./Queue/index.ts"; export * from "./R2/index.ts"; +export * as R2 from "./R2/index.ts"; export * from "./R2DataCatalog/index.ts"; export * from "./RateLimit/index.ts"; export * from "./RealtimeKit/index.ts"; diff --git a/packages/alchemy/src/Construct.ts b/packages/alchemy/src/Construct.ts deleted file mode 100644 index 45784697c..000000000 --- a/packages/alchemy/src/Construct.ts +++ /dev/null @@ -1,71 +0,0 @@ -import * as Effect from "effect/Effect"; -import * as Namespace from "./Namespace.ts"; - -/** - * Create a reusable construct that composes child resources under a shared - * namespace. - * - * `Construct.fn` wraps a generator function and automatically pushes the - * construct `id` onto the current namespace so nested resources become stable - * children of the construct. - * - * @section Creating Constructs - * @example Simple Reusable Construct - * ```typescript - * import * as Construct from "alchemy/Construct"; - * import { Bucket } from "alchemy/AWS/S3"; - * - * export const Logs = Construct.fn(function* ( - * id: string, - * props: { forceDestroy?: boolean }, - * ) { - * const bucket = yield* Bucket("Bucket", { - * forceDestroy: props.forceDestroy, - * }); - * - * return { bucket }; - * }); - * ``` - * - * @example Composing Website Resources - * ```typescript - * export const App = Construct.fn(function* ( - * id: string, - * props: { sourcePath: string }, - * ) { - * const site = yield* StaticSite("Web", { - * sourcePath: props.sourcePath, - * cdn: false, - * }); - * - * const router = yield* Router("Router", { - * routes: { - * "/*": site.routeTarget, - * }, - * }); - * - * return { site, router }; - * }); - * ``` - */ -export const fn: { - , AEff, Props extends object>( - body: (id: string, props: Props) => Generator, - ): ( - id: string, - props: Props, - ) => Effect.Effect< - AEff, - [Eff] extends [never] - ? never - : [Eff] extends [Effect.Effect] - ? E - : never, - [Eff] extends [never] - ? never - : [Eff] extends [Effect.Effect] - ? R - : never - >; -} = (body) => (id, props) => - Effect.gen(() => body(id, props)).pipe(Namespace.push(id)); diff --git a/packages/alchemy/src/Local/RpcProvider.ts b/packages/alchemy/src/Local/RpcProvider.ts index 0e2e7e34d..799839403 100644 --- a/packages/alchemy/src/Local/RpcProvider.ts +++ b/packages/alchemy/src/Local/RpcProvider.ts @@ -10,7 +10,7 @@ import { Artifacts, ArtifactStore, makeScopedArtifacts } from "../Artifacts.ts"; import { InstanceId } from "../InstanceId.ts"; import type { Platform } from "../Platform.ts"; import * as Provider from "../Provider.ts"; -import type { ResourceClass, ResourceLike } from "../Resource.ts"; +import type { ResourceClassLike, ResourceLike } from "../Resource.ts"; import { Stack } from "../Stack.ts"; import { Stage } from "../Stage.ts"; import { RpcProviderProxy } from "./RpcProviderProxy.ts"; @@ -165,7 +165,7 @@ export const effect = < LogsReq = never, ListReq = never, >( - cls: ResourceClass | Platform, + cls: ResourceClassLike | Platform, serverEntryUrl: string, eff: Effect.Effect< RpcProviderService< diff --git a/packages/alchemy/src/Named.ts b/packages/alchemy/src/Named.ts new file mode 100644 index 000000000..b9e3d0e84 --- /dev/null +++ b/packages/alchemy/src/Named.ts @@ -0,0 +1,6 @@ +export type Named = { + readonly "~alchemy/Id": Id; +}; +export type Tag = { + readonly "~alchemy/Tag": K; +}; diff --git a/packages/alchemy/src/Phase.ts b/packages/alchemy/src/Phase.ts index ad7355b7b..279bb6783 100644 --- a/packages/alchemy/src/Phase.ts +++ b/packages/alchemy/src/Phase.ts @@ -1,6 +1,22 @@ import * as Config from "effect/Config"; import * as Effect from "effect/Effect"; +declare global { + /** + * Build-time flag marking the runtime (post-bundle) phase. + * + * The bundler folds this to `true` in every runtime artifact (see + * `ALCHEMY_DEFINE` in `Bundle/Bundle.ts`), so plan-only code guarded by + * `if (!globalThis.__ALCHEMY_RUNTIME__)` is dead-code-eliminated from deployed + * Workers/Lambdas/Containers. + * + * When running source directly with bun/node (no bundler) it is `undefined` + * (falsy), so plan-only branches run. Reading it never throws because it is a + * property access on `globalThis`. + */ + var __ALCHEMY_RUNTIME__: boolean | undefined; +} + export type AlchemyPhase = "plan" | "runtime"; export const ALCHEMY_PHASE = Config.string("ALCHEMY_PHASE").pipe( diff --git a/packages/alchemy/src/Platform.ts b/packages/alchemy/src/Platform.ts index ec8e6b700..103479b64 100644 --- a/packages/alchemy/src/Platform.ts +++ b/packages/alchemy/src/Platform.ts @@ -1,3 +1,4 @@ +import type { NodeServices } from "@effect/platform-node/NodeServices"; import * as ConfigError from "effect/Config"; import * as ConfigProvider from "effect/ConfigProvider"; import * as Context from "effect/Context"; @@ -7,12 +8,15 @@ import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as Redacted from "effect/Redacted"; import type { Scope } from "effect/Scope"; +import type * as Stream from "effect/Stream"; import type { HttpClient } from "effect/unstable/http/HttpClient"; +import type { HttpServerRequest } from "effect/unstable/http/HttpServerRequest"; import type { PolicyLike } from "./Binding.ts"; import type { Dependencies } from "./Dependencies.ts"; import type { ExecutionContext } from "./ExecutionContext.ts"; import type { HttpEffect } from "./Http.ts"; import type { InputProps } from "./Input.ts"; +import type { Named, Tag } from "./Named.ts"; import * as Output from "./Output.ts"; import { ALCHEMY_PHASE } from "./Phase.ts"; import type { Provider, ProviderCollectionLike } from "./Provider.ts"; @@ -38,18 +42,62 @@ export interface PlatformProps { } export type Main = void | { - fetch: - | HttpEffect + fetch?: + | HttpEffect | Effect.Effect< - HttpEffect, + HttpEffect, never, InitServices | PlatformServices >; }; +export interface MainRpc { + [key: string]: + | Effect.Effect< + any, + any, + PlatformServices | RuntimeContext | HttpServerRequest | Scope | Req + > + | Stream.Stream< + any, + any, + PlatformServices | RuntimeContext | HttpServerRequest | Scope | Req + > + | (( + ...args: any[] + ) => + | Effect.Effect< + any, + any, + PlatformServices | RuntimeContext | Scope | Req + > + | Stream.Stream< + any, + any, + PlatformServices | RuntimeContext | Scope | Req + >); +} + +// Strip `void`/`undefined`/`never` from `Shape` before intersecting it with +// `BaseShape`. This matters when `Shape` fails its `extends MainShape` +// constraint (e.g. a `fetch` handler that leaks an error): TS clamps `Shape` +// to the constraint union `void | { fetch: ... }`, and a *distributive* +// conditional would split that into `BaseShape | ({ fetch } & BaseShape)` — a +// union. Feeding a union into the `new (_: never): ...` construct signature +// makes the base class a union type, which surfaces as the cryptic +// ts(2509) "Base constructor return type ... is not an object type" instead of +// the real assignability error on the `impl` argument. Excluding `void` here +// keeps the construct-sig return a single object type, so only the actionable +// error remains. +export type MakeShape = [ + Exclude, +] extends [never] + ? Exclude + : Exclude & Exclude; + // services provided to the Resource export type PlatformServices = - | RuntimeContext + | NodeServices | ExecutionContext | HttpClient | PolicyLike @@ -71,39 +119,43 @@ export interface Platform< Provider: Provider; (): { - ( - id: string, - props: - | InputProps - | Effect.Effect< - InputProps, - ConfigError.ConfigError, - PropsReq - >, + ( + id: Id, ): Effect.Effect< Resource & Rpc & Dependencies, never, - Resource["Providers"] | PropsReq - > & { - make( - impl: Effect.Effect, - ): Layer.Layer< - Self, - never, - | Resource["Providers"] - | Exclude - >; - new (_: never): MakeShape; - of(shape: Shape & MainShape): MakeShape; - }; + Resource["Providers"] + > & + Named & { + make( + props: + | InputProps + | Effect.Effect< + InputProps, + ConfigError.ConfigError, + PropsReq + >, + impl: Effect.Effect, + ): Layer.Layer< + Self, + never, + | Resource["Providers"] + | Exclude + >; + new ( + _: never, + ): MakeShape & Named & Tag; + of(shape: Shape & MainShape): MakeShape; + }; }; (): { < + const Id extends string, Shape extends MainShape, PropsReq = never, InitReq extends Services | PlatformServices | Resource = never, >( - id: string, + id: Id, props: | InputProps | Effect.Effect, @@ -112,79 +164,73 @@ export interface Platform< Resource & Rpc, never, | Resource["Providers"] - | PropsReq + | Exclude | Exclude - > & { - new (_: never): MakeShape; - }; - ( - id: string, - props: - | InputProps - | Effect.Effect< - InputProps, - ConfigError.ConfigError, - PropsReq - >, - ): Effect.Effect< - Resource & Rpc, - never, - Resource["Providers"] | PropsReq - > & { - make( - impl: Effect.Effect, - ): Layer.Layer< - Self, - never, - | Resource["Providers"] - | Exclude - >; - new (_: never): MakeShape; - } & (( - impl: Effect.Effect, - ) => Effect.Effect< - Resource & Rpc, - never, - | Resource["Providers"] - | PropsReq - | Exclude - >); + > & + Named & { + new ( + _: never, + ): MakeShape & Named & Tag; + }; + + ( + id: Id, + ): Effect.Effect, never, Resource["Providers"]> & + Named & { + make< + PropsReq = never, + InitReq extends Services | PlatformServices | Resource = never, + >( + props: + | InputProps + | Effect.Effect< + InputProps, + ConfigError.ConfigError, + PropsReq + >, + impl: Effect.Effect, + ): Layer.Layer< + Self, + never, + | Resource["Providers"] + | Exclude + >; + new (_: never): BaseShape & Named & Tag; + }; }; - // ( - // id: string, - // props: - // | InputProps - // | Effect.Effect, never, PropsReq>, - // ): Effect.Effect< - // Resource, - // never, - // | Resource["Providers"] - // | PropsReq - // | Exclude - // >; + ( + id: string, + props: + | InputProps + | Effect.Effect, never, PropsReq>, + ): Effect.Effect< + Resource, + never, + | Resource["Providers"] + | PropsReq + | Exclude + >; < + const Id extends string, Shape extends MainShape, PropsReq = never, InitReq extends Services | PlatformServices = never, >( - id: string, + id: Id, props: | InputProps | Effect.Effect, never, PropsReq>, impl: Effect.Effect, ): Effect.Effect< - Resource & Rpc, + Resource & Rpc & Named, never, | Resource["Providers"] | PropsReq | Exclude - >; + > & + Named; } -type MakeShape = Shape extends never | undefined | void - ? BaseShape - : Shape & BaseShape; - export const Platform = < R extends ResourceLike< string, @@ -223,7 +269,7 @@ export const Platform = < return (id: string, props?: any, impl?: Impl) => constructor(id, props, impl, true); } else if (!impl) { - const cls = makeClass(id, props); + const cls = makeClass(id); const evaluate = () => (!isTag ? // this is a non-tagged resource yielded without providing an implementation @@ -270,8 +316,8 @@ export const Platform = < ), ); return Object.assign( - function (impl: Impl) { - return cls.Self.pipe(Effect.provide(cls.make(impl))); + function (props: Props, impl: Impl) { + return cls.Self.pipe(Effect.provide(cls.make(props, impl))); }, // we splice in the Effect so this can be yielded to indicate a non-Effect native instance // e.g. here, we yield it - in this case we don't want to provide an implementation @@ -291,19 +337,19 @@ export const Platform = < // impl was provided inline, this is a non-tagged eager instance // e.g. // export default Cloudflare.Worker("id", { main: "./src/worker.ts" }, Effect.gen(function* () { .. }) - const cls = makeClass(id, props); - return cls.Self.pipe(Effect.provide(cls.make(impl)), effectClass); + const cls = makeClass(id); + return cls.Self.pipe(Effect.provide(cls.make(props, impl)), effectClass); } }; - const makeClass = (id: string, props: Props) => { + const makeClass = (id: string) => { class Platform { static readonly Self = Self(`${type}<${id}>`); static readonly Platform = Context.Service( `Platform<${type}<${id}>>`, ); static of = (shape: any) => shape; - static make = (impl: Impl) => { + static make = (props: Props, impl: Impl) => { // build the Layer once for the root Self const SelfLayer = Layer.effect( Self, @@ -343,7 +389,6 @@ export const Platform = < }) ?? Effect.die("No serve handler")) : Effect.void, ), - Effect.provide( Layer.effect( ConfigProvider.ConfigProvider, diff --git a/packages/alchemy/src/Provider.ts b/packages/alchemy/src/Provider.ts index b6ff88496..9b0578b43 100644 --- a/packages/alchemy/src/Provider.ts +++ b/packages/alchemy/src/Provider.ts @@ -13,6 +13,7 @@ import type { Platform } from "./Platform.ts"; import type { ResourceBinding, ResourceClass, + ResourceClassLike, ResourceLike, } from "./Resource.ts"; @@ -251,7 +252,7 @@ export const effect = < LogsReq = never, ListReq = never, >( - cls: ResourceClass | Platform, + cls: ResourceClassLike | Platform, eff: Effect.Effect< ProviderService< R, @@ -352,7 +353,7 @@ export interface ProviderCollectionService { export const collection = < R extends - | ResourceClass + | ResourceClassLike | Platform | Policy, >( @@ -428,7 +429,7 @@ export const findProviderByType: { */ export const findProvider: { ( - resource: ResourceClass | Platform, + resource: ResourceClassLike | Platform, ): Effect.Effect>;

>( policy: P, diff --git a/packages/alchemy/src/Resource.ts b/packages/alchemy/src/Resource.ts index 4dd19b39f..a0c1bd20a 100644 --- a/packages/alchemy/src/Resource.ts +++ b/packages/alchemy/src/Resource.ts @@ -39,10 +39,14 @@ export type ResourceConstructor = { ): Effect.Effect; }; -export type ResourceClassWithMethods< - R extends ResourceLike, - Methods extends { [key: string]: any }, -> = ResourceConstructor< +export interface ResourceClassLike { + Type: R["Type"]; + Props: R["Props"]; + Self: Self; + Provider: Provider; +} + +export type ResourceClass = ResourceConstructor< R, R["Providers"] extends undefined ? Provider : R["Providers"] > & @@ -53,9 +57,12 @@ export type ResourceClassWithMethods< id: string, options?: { stage?: string; stack?: string }, ): Effect.Effect; - } & Methods; + }; -export type ResourceClass = ResourceConstructor< +export type ResourceClassWithMethods< + R extends ResourceLike, + Methods extends { [key: string]: any }, +> = ResourceConstructor< R, R["Providers"] extends undefined ? Provider : R["Providers"] > & @@ -66,7 +73,7 @@ export type ResourceClass = ResourceConstructor< id: string, options?: { stage?: string; stack?: string }, ): Effect.Effect; - }; + } & Methods; export type LogicalId = string; diff --git a/packages/alchemy/src/RuntimeContext.ts b/packages/alchemy/src/RuntimeContext.ts index 88cbfebdd..1400164c5 100644 --- a/packages/alchemy/src/RuntimeContext.ts +++ b/packages/alchemy/src/RuntimeContext.ts @@ -51,7 +51,9 @@ export const sanitizeKey = (key: string): string => export class RuntimeContext extends Context.Service< RuntimeContext, BaseRuntimeContext ->()("RuntimeContext") {} +>()("RuntimeContext") { + static phantom = Layer.empty as Layer.Layer; +} export const CurrentRuntimeContext = Effect.serviceOption(RuntimeContext).pipe( Effect.map(Option.getOrUndefined), diff --git a/packages/alchemy/src/Self.ts b/packages/alchemy/src/Self.ts index 5475649f0..6fde4d9e4 100644 --- a/packages/alchemy/src/Self.ts +++ b/packages/alchemy/src/Self.ts @@ -2,9 +2,12 @@ import * as Context from "effect/Context"; import { GenericService } from "./Util/service.ts"; export interface Self< - R extends { Type: string } = { Type: string }, + R extends { Type: string; LogicalId: string } = { + Type: string; + LogicalId: string; + }, > extends Context.ServiceClass, `Self<${R["Type"]}>`, R> {} export const Self = GenericService<{ - (type: R["Type"]): Self; + (type: R["Type"]): Self; }>()("Alchemy::Self"); diff --git a/packages/alchemy/src/Server/S3BucketEventSource.ts b/packages/alchemy/src/Server/S3BucketEventSource.ts index b2a7e58b5..2ac99938a 100644 --- a/packages/alchemy/src/Server/S3BucketEventSource.ts +++ b/packages/alchemy/src/Server/S3BucketEventSource.ts @@ -9,6 +9,7 @@ import type { import * as S3 from "../AWS/S3/index.ts"; import type { S3EventType } from "../AWS/S3/S3Event.ts"; import * as SQS from "../AWS/SQS/index.ts"; +import type { Providers } from "../AWS/Providers.ts"; import * as Binding from "../Binding.ts"; import { SQSQueueEventSource } from "./SQSQueueEventSource.ts"; @@ -64,7 +65,8 @@ export class S3BucketEventSourcePolicy extends Binding.Policy< queue: SQS.Queue; events?: S3.S3EventType[]; }, - ) => Effect.Effect + ) => Effect.Effect, + Providers >()("Process.S3BucketEventSource") {} export const S3BucketEventSourcePolicyLive = diff --git a/packages/alchemy/src/Stack.ts b/packages/alchemy/src/Stack.ts index 0f1dee018..99d0cf6a0 100644 --- a/packages/alchemy/src/Stack.ts +++ b/packages/alchemy/src/Stack.ts @@ -17,9 +17,11 @@ import { type ArtifactStore, provideFreshArtifactStore } from "./Artifacts.ts"; import { AuthProviders } from "./Auth/AuthProvider.ts"; import { CredentialsStore, CredentialsStoreLive } from "./Auth/Credentials.ts"; import { AlchemyProfile, ProfileLive } from "./Auth/Profile.ts"; +import type { PolicyLike } from "./Binding.ts"; import { Cli } from "./Cli/Cli.ts"; import type { Input, InputProps } from "./Input.ts"; import * as Output from "./Output.ts"; +import type { Provider, ProviderCollectionLike } from "./Provider.ts"; import type { ResourceBinding, ResourceLike } from "./Resource.ts"; import { Stage } from "./Stage.ts"; import type { State } from "./State/State.ts"; @@ -43,6 +45,23 @@ export type StackServices = | CredentialsStore | Cli; +export type ProviderServices = + | ProviderCollectionLike + | Provider + | PolicyLike + | EnvironmentLike + | CredentialsLike; + +// tagged type to allow types like AWSEnvironment/AWS Region to bubble through +export interface EnvironmentLike { + readonly kind: "Environment"; +} + +// tagged type to allow types like AWS Credentials to bubble through +export interface CredentialsLike { + readonly kind: "Credentials"; +} + export type StackEffect = Effect.Effect< A, Err, @@ -65,7 +84,7 @@ export type Stack = Context.ServiceClass.Shape< >; export interface StackProps { - providers: Layer.Layer, never, StackServices>; + providers: Layer.Layer, never, StackServices>; state: Layer.Layer; } @@ -97,7 +116,6 @@ export const Stack: Context.ServiceClass< }; }; (): { - Shape: Shape; (stackName: string): Effect.Effect & { new (_: never): Output.ToOutput; make: ( @@ -109,10 +127,10 @@ export const Stack: Context.ServiceClass< }; }; }; - ( + ( stackName: string, options: StackProps>, - eff: Effect.Effect, + eff: Effect.Effect, ): Effect.Effect, ConfigError>; } = Object.assign( taggedFunction( @@ -135,11 +153,14 @@ export const Stack: Context.ServiceClass< make: ( options: StackProps>, eff: Effect.Effect, - ) => Stack(stackName, options, eff), + ) => + // @ts-expect-error + Stack(stackName, options, eff), }, ); } return eff!.pipe( + // @ts-expect-error make({ name: stackName, ...options!, diff --git a/packages/alchemy/src/Util/effect.ts b/packages/alchemy/src/Util/effect.ts index 1eee33083..f5af7cf0e 100644 --- a/packages/alchemy/src/Util/effect.ts +++ b/packages/alchemy/src/Util/effect.ts @@ -83,7 +83,8 @@ export const isEffectClassLike = ( export const isYieldableEffectLike = ( value: unknown, ): value is YieldableEffectLike => - isYieldableEffect(value) || isEffectClassLike(value); + (isYieldableEffect(value) || isEffectClassLike(value)) && + !("~alchemy/Kind" in value); export type UnwrapEffect = T extends Effect.Effect ? A : T; diff --git a/packages/alchemy/src/index.ts b/packages/alchemy/src/index.ts index abd522298..e3cb40ead 100644 --- a/packages/alchemy/src/index.ts +++ b/packages/alchemy/src/index.ts @@ -5,6 +5,7 @@ export { type ActionLike, } from "./Action.ts"; export * as AdoptPolicy from "./AdoptPolicy.ts"; +export * from "./AI/index.ts"; export * from "./AlchemyContext.ts"; export * from "./Apply.ts"; export { @@ -37,13 +38,12 @@ export { Stack } from "./Stack.ts"; export * from "./Stage.ts"; export { inMemoryState, localState } from "./State/index.ts"; -export * as Construct from "./Construct.ts"; - // Re-export internal types so they can be portably named in // downstream `.d.ts` emissions (fixes TS2883 in user files). export { AuthProviders } from "./Auth/AuthProvider.ts"; export { Cli } from "./Cli/Cli.ts"; export type { Dependencies } from "./Dependencies.ts"; +export type { Named } from "./Named.ts"; export type * from "./Platform.ts"; export { Platform } from "./Platform.ts"; export type { ProviderCollectionLike } from "./Provider.ts"; diff --git a/packages/alchemy/test/AWS/AutoScaling/LaunchTemplate.test.ts b/packages/alchemy/test/AWS/AutoScaling/LaunchTemplate.test.ts index 8b41097d6..0ffd7c756 100644 --- a/packages/alchemy/test/AWS/AutoScaling/LaunchTemplate.test.ts +++ b/packages/alchemy/test/AWS/AutoScaling/LaunchTemplate.test.ts @@ -42,7 +42,7 @@ test.provider.skipIf(!process.env.ALCHEMY_TEST_LAUNCH_TEMPLATE_LIST)( const template = yield* stack.deploy( Effect.gen(function* () { - return yield* LaunchTemplate<{}>()("ListLaunchTemplate", { + return yield* LaunchTemplate("ListLaunchTemplate", { launchTemplateName: "alchemy-test-lt-list", imageId, instanceType: "t3.micro", diff --git a/packages/alchemy/test/AWS/AutoScaling/ScalingPolicy.test.ts b/packages/alchemy/test/AWS/AutoScaling/ScalingPolicy.test.ts index 1ddae9ab4..0f2c9e836 100644 --- a/packages/alchemy/test/AWS/AutoScaling/ScalingPolicy.test.ts +++ b/packages/alchemy/test/AWS/AutoScaling/ScalingPolicy.test.ts @@ -48,7 +48,7 @@ test.provider.skipIf(!process.env.AWS_TEST_SCALING_POLICY_LIST)( vpcId: vpc.vpcId, cidrBlock: "10.0.1.0/24", }); - const template = yield* LaunchTemplate<{}>()("ListPolicyTemplate", { + const template = yield* LaunchTemplate("ListPolicyTemplate", { launchTemplateName: "alchemy-test-policy-lt-list", imageId, instanceType: "t3.micro", diff --git a/packages/alchemy/test/AWS/DynamoDB/Stream.test.ts b/packages/alchemy/test/AWS/DynamoDB/Stream.test.ts index 219f7f8b5..9ad6cdf16 100644 --- a/packages/alchemy/test/AWS/DynamoDB/Stream.test.ts +++ b/packages/alchemy/test/AWS/DynamoDB/Stream.test.ts @@ -14,76 +14,72 @@ import DynamoDBStreamFunctionLive, { const { test } = Test.make({ providers: AWS.providers() }); -describe - .skipIf(!!process.env.NO_SLOW_TESTS) - .sequential("AWS.DynamoDB.Stream", () => { - test.provider( - "processes real DynamoDB stream records through Lambda", - (stack) => - Effect.gen(function* () { - yield* Effect.logInfo( - "DynamoDB Stream test: destroying previous resources", - ); - yield* stack.destroy(); - - yield* Effect.logInfo( - "DynamoDB Stream test: deploying stream fixture", - ); - const { table, queue, streamFunction } = yield* stack.deploy( - Effect.gen(function* () { - const { table, queue } = yield* TableAndQueue; - - const func = yield* DynamoDBStreamFunction; - - return { table, queue, streamFunction: func }; - }).pipe(Effect.provide(DynamoDBStreamFunctionLive)), - ); - - const streamState = yield* waitForTableStreamSpecification( - table.tableName, - { - StreamEnabled: true, - StreamViewType: "NEW_AND_OLD_IMAGES", - }, - ); - expect(streamState.Table?.StreamSpecification).toEqual({ +describe.skipIf(!!process.env.FAST).sequential("AWS.DynamoDB.Stream", () => { + test.provider( + "processes real DynamoDB stream records through Lambda", + (stack) => + Effect.gen(function* () { + yield* Effect.logInfo( + "DynamoDB Stream test: destroying previous resources", + ); + yield* stack.destroy(); + + yield* Effect.logInfo("DynamoDB Stream test: deploying stream fixture"); + const { table, queue, streamFunction } = yield* stack.deploy( + Effect.gen(function* () { + const { table, queue } = yield* TableAndQueue; + + const func = yield* DynamoDBStreamFunction; + + return { table, queue, streamFunction: func }; + }).pipe(Effect.provide(DynamoDBStreamFunctionLive)), + ); + + const streamState = yield* waitForTableStreamSpecification( + table.tableName, + { StreamEnabled: true, StreamViewType: "NEW_AND_OLD_IMAGES", - }); - expect(streamState.Table?.LatestStreamArn).toBeDefined(); - - yield* waitForEventSourceMappingEnabled( - streamFunction.functionName, - streamState.Table?.LatestStreamArn!, - ); - - yield* Effect.logInfo( - `DynamoDB Stream test: writing item into ${table.tableName}`, - ); - yield* DynamoDB.putItem({ - TableName: table.tableName, - Item: { - pk: { S: "stream#1" }, - sk: { S: "item#1" }, - data: { S: "payload" }, - }, - }); - - const message = yield* waitForQueueMessage(queue.queueUrl); - const body = JSON.parse(message.Body!); - - expect(body.eventName).toEqual("INSERT"); - expect(body.keys.pk.S).toEqual("stream#1"); - expect(body.keys.sk.S).toEqual("item#1"); - expect(body.newImage.data.S).toEqual("payload"); - expect(body.oldImage).toBeUndefined(); - - yield* Effect.logInfo("DynamoDB Stream test: destroying fixture"); - yield* stack.destroy(); - }), - { timeout: 600_000 }, - ); - }); + }, + ); + expect(streamState.Table?.StreamSpecification).toEqual({ + StreamEnabled: true, + StreamViewType: "NEW_AND_OLD_IMAGES", + }); + expect(streamState.Table?.LatestStreamArn).toBeDefined(); + + yield* waitForEventSourceMappingEnabled( + streamFunction.functionName, + streamState.Table?.LatestStreamArn!, + ); + + yield* Effect.logInfo( + `DynamoDB Stream test: writing item into ${table.tableName}`, + ); + yield* DynamoDB.putItem({ + TableName: table.tableName, + Item: { + pk: { S: "stream#1" }, + sk: { S: "item#1" }, + data: { S: "payload" }, + }, + }); + + const message = yield* waitForQueueMessage(queue.queueUrl); + const body = JSON.parse(message.Body!); + + expect(body.eventName).toEqual("INSERT"); + expect(body.keys.pk.S).toEqual("stream#1"); + expect(body.keys.sk.S).toEqual("item#1"); + expect(body.newImage.data.S).toEqual("payload"); + expect(body.oldImage).toBeUndefined(); + + yield* Effect.logInfo("DynamoDB Stream test: destroying fixture"); + yield* stack.destroy(); + }), + { timeout: 600_000 }, + ); +}); const waitForEventSourceMappingEnabled = Effect.fn(function* ( functionName: string, diff --git a/packages/alchemy/test/AWS/DynamoDB/Table.test.ts b/packages/alchemy/test/AWS/DynamoDB/Table.test.ts index bad7e3394..7a08c00e8 100644 --- a/packages/alchemy/test/AWS/DynamoDB/Table.test.ts +++ b/packages/alchemy/test/AWS/DynamoDB/Table.test.ts @@ -12,7 +12,7 @@ import * as Schedule from "effect/Schedule"; const { test } = Test.make({ providers: AWS.providers() }); -describe.skipIf(!!process.env.NO_SLOW_TESTS)("AWS.DynamoDB.Table", () => { +describe.skipIf(!!process.env.FAST)("AWS.DynamoDB.Table", () => { const longGlobalSecondaryIndexStabilization = Schedule.fixed( "10 seconds", ).pipe(Schedule.both(Schedule.recurs(180))); diff --git a/packages/alchemy/test/AWS/DynamoDB/handler.ts b/packages/alchemy/test/AWS/DynamoDB/handler.ts index 29e96cde4..a4d50a072 100644 --- a/packages/alchemy/test/AWS/DynamoDB/handler.ts +++ b/packages/alchemy/test/AWS/DynamoDB/handler.ts @@ -10,13 +10,13 @@ const main = path.resolve(import.meta.dirname, "handler.ts"); export class DynamoDBTestFunction extends Lambda.Function()( "DynamoDBTestFunction", +) {} + +export default DynamoDBTestFunction.make( { main, url: true, }, -) {} - -export default DynamoDBTestFunction.make( Effect.gen(function* () { const sourceTable = yield* DynamoDB.Table("TestTable", { partitionKey: "pk", diff --git a/packages/alchemy/test/AWS/DynamoDB/stream-handler.ts b/packages/alchemy/test/AWS/DynamoDB/stream-handler.ts index 46032d5ab..636327349 100644 --- a/packages/alchemy/test/AWS/DynamoDB/stream-handler.ts +++ b/packages/alchemy/test/AWS/DynamoDB/stream-handler.ts @@ -6,9 +6,6 @@ import * as Stream from "effect/Stream"; export class DynamoDBStreamFunction extends AWS.Lambda.Function()( "DynamoDBStreamFunction", - { - main: import.meta.filename, - }, ) {} export class TableAndQueue extends Context.Service< @@ -38,37 +35,41 @@ const TableAndQueueLive = Layer.effect( }), ); -const eff = Effect.gen(function* () { - const { table, queue } = yield* TableAndQueue; - const sink = yield* AWS.SQS.QueueSink.bind(queue); +export default DynamoDBStreamFunction.make( + { + main: import.meta.filename, + }, + Effect.gen(function* () { + const { table, queue } = yield* TableAndQueue; + const sink = yield* AWS.SQS.QueueSink.bind(queue); - yield* AWS.DynamoDB.stream(table, { - streamViewType: "NEW_AND_OLD_IMAGES", - startingPosition: "TRIM_HORIZON", - batchSize: 10, - }).process((stream) => - stream.pipe( - Stream.map((record) => - JSON.stringify({ - eventName: record.eventName, - keys: record.dynamodb.Keys, - newImage: record.dynamodb.NewImage, - oldImage: record.dynamodb.OldImage, - }), + yield* AWS.DynamoDB.stream(table, { + streamViewType: "NEW_AND_OLD_IMAGES", + startingPosition: "TRIM_HORIZON", + batchSize: 10, + }).process((stream) => + stream.pipe( + Stream.map((record) => + JSON.stringify({ + eventName: record.eventName, + keys: record.dynamodb.Keys, + newImage: record.dynamodb.NewImage, + oldImage: record.dynamodb.OldImage, + }), + ), + Stream.run(sink), + ), + ); + }).pipe( + Effect.provide( + Layer.provideMerge( + Layer.mergeAll( + AWS.Lambda.TableEventSource, + AWS.SQS.QueueSinkLive, + TableAndQueueLive, + ), + Layer.mergeAll(AWS.SQS.SendMessageBatchLive), ), - Stream.run(sink), - ), - ); -}).pipe( - Effect.provide( - Layer.provideMerge( - Layer.mergeAll(AWS.Lambda.TableEventSource, AWS.SQS.QueueSinkLive), - Layer.mergeAll(AWS.SQS.SendMessageBatchLive), ), ), ); - -// @ts-expect-error -export default DynamoDBStreamFunction.make(eff).pipe( - Layer.provideMerge(TableAndQueueLive), -); diff --git a/packages/alchemy/test/AWS/EC2/Instance.test.ts b/packages/alchemy/test/AWS/EC2/Instance.test.ts index 1116f3a7b..d71e9882e 100644 --- a/packages/alchemy/test/AWS/EC2/Instance.test.ts +++ b/packages/alchemy/test/AWS/EC2/Instance.test.ts @@ -37,7 +37,7 @@ test.provider( vpcId: vpc.vpcId, cidrBlock: "10.0.1.0/24", }); - return yield* Instance<{}>()("ListInstance", { + return yield* Instance("ListInstance", { imageId, instanceType: "t3.micro", subnetId: subnet.subnetId, diff --git a/packages/alchemy/test/AWS/Kinesis/Stream.test.ts b/packages/alchemy/test/AWS/Kinesis/Stream.test.ts index 0c89adcb3..b5e1b4d4c 100644 --- a/packages/alchemy/test/AWS/Kinesis/Stream.test.ts +++ b/packages/alchemy/test/AWS/Kinesis/Stream.test.ts @@ -12,7 +12,7 @@ import * as Schedule from "effect/Schedule"; const { test } = Test.make({ providers: AWS.providers() }); -describe.skipIf(!!process.env.NO_SLOW_TESTS)("AWS.Kinesis.Stream", () => { +describe.skipIf(!!process.env.FAST)("AWS.Kinesis.Stream", () => { test.provider( "create and delete stream with default props", (stack) => diff --git a/packages/alchemy/test/AWS/Kinesis/handler.ts b/packages/alchemy/test/AWS/Kinesis/handler.ts index 500a05512..3878990f6 100644 --- a/packages/alchemy/test/AWS/Kinesis/handler.ts +++ b/packages/alchemy/test/AWS/Kinesis/handler.ts @@ -8,14 +8,9 @@ import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; import { HttpServerRequest } from "effect/unstable/http/HttpServerRequest"; import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse"; -import path from "pathe"; export class KinesisApiFunction extends AWS.Lambda.Function()( "KinesisApiFunction", - { - main: import.meta.filename, - url: true, - }, ) {} export class StreamAndConsumer extends Context.Service< @@ -52,6 +47,10 @@ export const StreamAndConsumerLive = Layer.effect( ); export const KinesisApiFunctionLive = KinesisApiFunction.make( + { + main: import.meta.filename, + url: true, + }, Effect.gen(function* () { const { stream, consumer } = yield* StreamAndConsumer; diff --git a/packages/alchemy/test/AWS/Kinesis/stream-handler.ts b/packages/alchemy/test/AWS/Kinesis/stream-handler.ts index 83aab9018..13e7c84a3 100644 --- a/packages/alchemy/test/AWS/Kinesis/stream-handler.ts +++ b/packages/alchemy/test/AWS/Kinesis/stream-handler.ts @@ -7,10 +7,6 @@ import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse"; export class KinesisStreamFunction extends AWS.Lambda.Function()( "KinesisStreamFunction", - { - main: import.meta.filename, - url: true, - }, ) {} export class StreamAndQueue extends Context.Service< @@ -37,6 +33,10 @@ export const StreamAndQueueLive = Layer.effect( }), ); export default KinesisStreamFunction.make( + { + main: import.meta.filename, + url: true, + }, Effect.gen(function* () { const { stream, queue } = yield* StreamAndQueue; const sink = yield* AWS.SQS.QueueSink.bind(queue); diff --git a/packages/alchemy/test/AWS/Lambda/Alias.test.ts b/packages/alchemy/test/AWS/Lambda/Alias.test.ts index 0e6469919..85359f85f 100644 --- a/packages/alchemy/test/AWS/Lambda/Alias.test.ts +++ b/packages/alchemy/test/AWS/Lambda/Alias.test.ts @@ -30,7 +30,7 @@ test.provider( }; }) => Effect.gen(function* () { - const fn = yield* AWS.Lambda.Function<{}>()("AliasFn", { + const fn = yield* AWS.Lambda.Function("AliasFn", { main: timeoutHandlerPath, handler: "handler", isExternal: true, diff --git a/packages/alchemy/test/AWS/Lambda/Function.test.ts b/packages/alchemy/test/AWS/Lambda/Function.test.ts index 4a6eded26..3c446fde7 100644 --- a/packages/alchemy/test/AWS/Lambda/Function.test.ts +++ b/packages/alchemy/test/AWS/Lambda/Function.test.ts @@ -64,7 +64,7 @@ test.provider( (stack) => Effect.gen(function* () { const initial = yield* stack.deploy( - AWS.Lambda.Function<{}>()("TimeoutFn", { + AWS.Lambda.Function("TimeoutFn", { main: timeoutHandlerPath, handler: "handler", isExternal: true, @@ -79,7 +79,7 @@ test.provider( expect(initialConfig.Configuration?.Timeout).toBe(15); yield* stack.deploy( - AWS.Lambda.Function<{}>()("TimeoutFn", { + AWS.Lambda.Function("TimeoutFn", { main: timeoutHandlerPath, handler: "handler", isExternal: true, @@ -116,7 +116,7 @@ test.provider( yield* stack.destroy(); const initial = yield* stack.deploy( - AWS.Lambda.Function<{}>()("ArchitectureFn", { + AWS.Lambda.Function("ArchitectureFn", { main: timeoutHandlerPath, handler: "handler", isExternal: true, @@ -128,7 +128,7 @@ test.provider( yield* waitForArchitecture(initial.functionName, "arm64"); const updated = yield* stack.deploy( - AWS.Lambda.Function<{}>()("ArchitectureFn", { + AWS.Lambda.Function("ArchitectureFn", { main: timeoutHandlerPath, handler: "handler", isExternal: true, @@ -152,7 +152,7 @@ test.provider( yield* stack.destroy(); const initial = yield* stack.deploy( - AWS.Lambda.Function<{}>()("ConcurrencyFn", { + AWS.Lambda.Function("ConcurrencyFn", { main: timeoutHandlerPath, handler: "handler", isExternal: true, @@ -164,7 +164,7 @@ test.provider( yield* waitForReservedConcurrency(initial.functionName, undefined); const updated = yield* stack.deploy( - AWS.Lambda.Function<{}>()("ConcurrencyFn", { + AWS.Lambda.Function("ConcurrencyFn", { main: timeoutHandlerPath, handler: "handler", isExternal: true, @@ -178,7 +178,7 @@ test.provider( yield* waitForReservedConcurrency(updated.functionName, 0); const removed = yield* stack.deploy( - AWS.Lambda.Function<{}>()("ConcurrencyFn", { + AWS.Lambda.Function("ConcurrencyFn", { main: timeoutHandlerPath, handler: "handler", isExternal: true, @@ -207,7 +207,7 @@ test.provider( yield* stack.destroy(); const deployed = yield* stack.deploy( - AWS.Lambda.Function<{}>()("ListFn", { + AWS.Lambda.Function("ListFn", { main: timeoutHandlerPath, handler: "handler", isExternal: true, @@ -233,7 +233,7 @@ test.provider( (stack) => Effect.gen(function* () { const initial = yield* stack.deploy( - AWS.Lambda.Function<{}>()("IamUrlFn", { + AWS.Lambda.Function("IamUrlFn", { main: timeoutHandlerPath, handler: "handler", isExternal: true, @@ -252,7 +252,7 @@ test.provider( }); const updated = yield* stack.deploy( - AWS.Lambda.Function<{}>()("IamUrlFn", { + AWS.Lambda.Function("IamUrlFn", { main: timeoutHandlerPath, handler: "handler", isExternal: true, diff --git a/packages/alchemy/test/AWS/Lambda/fixtures/event-source-mapping-handler.ts b/packages/alchemy/test/AWS/Lambda/fixtures/event-source-mapping-handler.ts index 8d58aa23b..ca31e596b 100644 --- a/packages/alchemy/test/AWS/Lambda/fixtures/event-source-mapping-handler.ts +++ b/packages/alchemy/test/AWS/Lambda/fixtures/event-source-mapping-handler.ts @@ -12,13 +12,13 @@ import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse"; // resource and grants the role the SQS read permissions the mapping needs. export class EventSourceMappingFunction extends Lambda.Function()( "EventSourceMappingFunction", +) {} + +export default EventSourceMappingFunction.make( { main: import.meta.filename, url: false, }, -) {} - -export default EventSourceMappingFunction.make( Effect.gen(function* () { const queue = yield* SQS.Queue("EventSourceMappingQueue"); diff --git a/packages/alchemy/test/AWS/Lambda/handler.ts b/packages/alchemy/test/AWS/Lambda/handler.ts index 724cf9303..2c2f642da 100644 --- a/packages/alchemy/test/AWS/Lambda/handler.ts +++ b/packages/alchemy/test/AWS/Lambda/handler.ts @@ -6,13 +6,13 @@ const main = import.meta.filename; export class TestFunction extends Lambda.Function()( "TestFunction", +) {} + +export const TestFunctionLive = TestFunction.make( { main, url: true, }, -) {} - -export const TestFunctionLive = TestFunction.make( Effect.gen(function* () { return { fetch: Effect.gen(function* () { diff --git a/packages/alchemy/test/AWS/S3/fixtures/event-source-handler.ts b/packages/alchemy/test/AWS/S3/fixtures/event-source-handler.ts index 0a9b283cf..569c17d21 100644 --- a/packages/alchemy/test/AWS/S3/fixtures/event-source-handler.ts +++ b/packages/alchemy/test/AWS/S3/fixtures/event-source-handler.ts @@ -15,13 +15,13 @@ const PROCESSED_PREFIX = "processed/"; export class BucketEventSourceFunction extends Lambda.Function()( "BucketEventSourceFunction", +) {} + +export default BucketEventSourceFunction.make( { main: import.meta.filename, url: true, }, -) {} - -export default BucketEventSourceFunction.make( Effect.gen(function* () { const bucket = yield* S3.Bucket("EventSourceBucket", { forceDestroy: true, @@ -84,14 +84,12 @@ export default BucketEventSourceFunction.make( ), // Object not written yet — the test polls until it appears. Effect.catchTag("NoSuchKey", () => - Effect.succeed( - HttpServerResponse.json({ processed: null }, { status: 404 }), - ), + HttpServerResponse.json({ processed: null }, { status: 404 }), ), ); } - return HttpServerResponse.json( + return yield* HttpServerResponse.json( { error: "Not found", pathname }, { status: 404 }, ); diff --git a/packages/alchemy/test/AWS/SNS/handler.ts b/packages/alchemy/test/AWS/SNS/handler.ts index bb19bac00..6da778061 100644 --- a/packages/alchemy/test/AWS/SNS/handler.ts +++ b/packages/alchemy/test/AWS/SNS/handler.ts @@ -11,14 +11,14 @@ const main = path.resolve(import.meta.dirname, "handler.ts"); export class SNSEventFunction extends AWS.Lambda.Function()( "SNSEventFunction", +) {} + +export const SNSEventFunctionLive = SNSEventFunction.make( { main, handler: "SNSEventFunctionLive", url: true, }, -) {} - -export const SNSEventFunctionLive = SNSEventFunction.make( Effect.gen(function* () { // no-op, we're just gonna be targeted manualy by the Subscription }), @@ -80,6 +80,9 @@ export const TopicAndQueueLive = Layer.effect( export class SNSApiFunction extends AWS.Lambda.Function()( "SNSApiFunction", +) {} + +export const SNSApiFunctionLive = SNSApiFunction.make( { main, url: true, @@ -87,9 +90,6 @@ export class SNSApiFunction extends AWS.Lambda.Function()( DEBUG: "true", }, }, -) {} - -export const SNSApiFunctionLive = SNSApiFunction.make( Effect.gen(function* () { const { topic, queue, subscription, queueSubscription } = yield* TopicAndQueue; diff --git a/packages/alchemy/test/AWS/SQS/sink-handler.ts b/packages/alchemy/test/AWS/SQS/sink-handler.ts index 53c48fc90..bded90cdf 100644 --- a/packages/alchemy/test/AWS/SQS/sink-handler.ts +++ b/packages/alchemy/test/AWS/SQS/sink-handler.ts @@ -25,13 +25,13 @@ export const TestQueueLive = Layer.effect( export class QueueSinkFunction extends AWS.Lambda.Function()( "QueueSinkFunction", +) {} + +export const QueueSinkFunctionLive = QueueSinkFunction.make( { main, url: true, }, -) {} - -export const QueueSinkFunctionLive = QueueSinkFunction.make( Effect.gen(function* () { const { queue } = yield* TestQueue; const sink = yield* AWS.SQS.QueueSink.bind(queue); diff --git a/packages/alchemy/test/AWS/Secret/fixtures/handler.ts b/packages/alchemy/test/AWS/Secret/fixtures/handler.ts index c143c4434..7bc98cd47 100644 --- a/packages/alchemy/test/AWS/Secret/fixtures/handler.ts +++ b/packages/alchemy/test/AWS/Secret/fixtures/handler.ts @@ -29,13 +29,13 @@ export const CONFIG_SECRET_ENV_KEY = "ALCHEMY_AWS_SECRET_TEST_SOURCE"; export class SecretsTestFunction extends Lambda.Function()( "SecretsTestFunction", +) {} + +export const SecretsTestFunctionLive = SecretsTestFunction.make( { main, url: true, }, -) {} - -export const SecretsTestFunctionLive = SecretsTestFunction.make( Effect.gen(function* () { // Secret from a literal — `Alchemy.Secret` coerces the literal to // `Redacted` and the Lambda runtime accessor rebuilds the wrapper @@ -107,7 +107,7 @@ export const SecretsTestFunctionLive = SecretsTestFunction.make( }); } default: - return HttpServerResponse.json( + return yield* HttpServerResponse.json( { error: "Not found", pathname }, { status: 404 }, ); diff --git a/packages/alchemy/test/Cloudflare/Access/Application.test.ts b/packages/alchemy/test/Cloudflare/Access/Application.test.ts index af2422083..3df3cbca1 100644 --- a/packages/alchemy/test/Cloudflare/Access/Application.test.ts +++ b/packages/alchemy/test/Cloudflare/Access/Application.test.ts @@ -8,6 +8,9 @@ import { expect } from "@effect/vitest"; import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -24,7 +27,7 @@ const logLevel = Effect.provideService( const zoneName = process.env.CLOUDFLARE_TEST_ACCESS_ZONE_NAME ?? "alchemy-test-2.us"; -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create and delete a self_hosted application gated by a reusable policy", (stack) => Effect.gen(function* () { @@ -117,43 +120,47 @@ test.provider( }).pipe(logLevel), ); -test.provider("list enumerates the deployed access application", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const domain = `alchemy-test-list-app.${zoneName}`; - const app = yield* stack.deploy( - Effect.gen(function* () { - yield* Cloudflare.Zone("TestZone", { - name: zoneName, - }).pipe(AdoptPolicy.adopt(true)); - const policy = yield* Cloudflare.AccessPolicy("ListAllowDomain", { - name: "Allow example.com", - decision: "allow", - include: [{ emailDomain: { domain: "example.com" } }], - }); - return yield* Cloudflare.AccessApplication("ListApp", { - type: "self_hosted", - domain, - sessionDuration: "24h", - policies: [policy.policyId], - }); - }), - ); - - const provider = yield* Provider.findProvider(Cloudflare.AccessApplication); - const all = yield* provider.list(); - - const match = all.find((a) => a.applicationId === app.applicationId); - expect(match).toBeDefined(); - expect(match?.type).toEqual("self_hosted"); - expect(match?.aud.length).toBeGreaterThan(0); - - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed access application", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const domain = `alchemy-test-list-app.${zoneName}`; + const app = yield* stack.deploy( + Effect.gen(function* () { + yield* Cloudflare.Zone("TestZone", { + name: zoneName, + }).pipe(AdoptPolicy.adopt(true)); + const policy = yield* Cloudflare.AccessPolicy("ListAllowDomain", { + name: "Allow example.com", + decision: "allow", + include: [{ emailDomain: { domain: "example.com" } }], + }); + return yield* Cloudflare.AccessApplication("ListApp", { + type: "self_hosted", + domain, + sessionDuration: "24h", + policies: [policy.policyId], + }); + }), + ); + + const provider = yield* Provider.findProvider( + Cloudflare.AccessApplication, + ); + const all = yield* provider.list(); + + const match = all.find((a) => a.applicationId === app.applicationId); + expect(match).toBeDefined(); + expect(match?.type).toEqual("self_hosted"); + expect(match?.aud.length).toBeGreaterThan(0); + + yield* stack.destroy(); + }).pipe(logLevel), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "update policies in place keeps the applicationId stable", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Access/Bookmark.test.ts b/packages/alchemy/test/Cloudflare/Access/Bookmark.test.ts index 45fa42d45..f014f8f37 100644 --- a/packages/alchemy/test/Cloudflare/Access/Bookmark.test.ts +++ b/packages/alchemy/test/Cloudflare/Access/Bookmark.test.ts @@ -7,6 +7,9 @@ import { expect } from "@effect/vitest"; import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers(), state: Cloudflare.state(), @@ -27,7 +30,7 @@ const logLevel = Effect.provideService( // always runs and pins the typed tag. const entitled = !!process.env.CLOUDFLARE_TEST_ACCESS_BOOKMARKS; -test.provider.skipIf(entitled)( +test.provider.skipIf(entitled || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "legacy bookmark creation surfaces the typed AccessBookmarkNotFound error", (stack) => Effect.gen(function* () { @@ -115,7 +118,7 @@ test.provider.skipIf(!entitled)( // account (it returns `[]` when there are no records), so the ungated case // always exercises `list()` and asserts it hydrates the `read` shape. The // entitled path additionally deploys a bookmark and asserts its presence. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates access bookmarks at the account scope", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Access/Certificate.test.ts b/packages/alchemy/test/Cloudflare/Access/Certificate.test.ts index 4e76322e2..60dd6e64a 100644 --- a/packages/alchemy/test/Cloudflare/Access/Certificate.test.ts +++ b/packages/alchemy/test/Cloudflare/Access/Certificate.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import { CA_CERT_1, CA_CERT_2 } from "../MtlsCertificate/fixtures/certs.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers(), state: Cloudflare.state(), @@ -26,7 +29,7 @@ const logLevel = Effect.provideService( // quota tag. const entitled = !!process.env.CLOUDFLARE_TEST_ACCESS_MTLS; -test.provider.skipIf(entitled)( +test.provider.skipIf(entitled || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "unentitled accounts surface the typed AccessCertificateQuotaExceeded error", (stack) => Effect.gen(function* () { @@ -135,7 +138,7 @@ test.provider.skipIf(!entitled)( // create quota, so the probe (which only reads) always runs. On an entitled // account we additionally deploy a certificate and assert it appears in the // exhaustively-paginated result. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates account access certificates", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Access/CustomPage.test.ts b/packages/alchemy/test/Cloudflare/Access/CustomPage.test.ts index ba0ed4b69..d7dac59b3 100644 --- a/packages/alchemy/test/Cloudflare/Access/CustomPage.test.ts +++ b/packages/alchemy/test/Cloudflare/Access/CustomPage.test.ts @@ -7,6 +7,9 @@ import { expect } from "@effect/vitest"; import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers(), state: Cloudflare.state(), @@ -28,7 +31,7 @@ const entitled = !!process.env.CLOUDFLARE_TEST_ACCESS_CUSTOM_PAGES; const HTML_A = "

Access denied

"; const HTML_B = "

Still denied

"; -test.provider.skipIf(entitled)( +test.provider.skipIf(entitled || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "unentitled accounts surface the typed AccessCustomPagesNotEntitled error", (stack) => Effect.gen(function* () { @@ -140,7 +143,7 @@ test.provider.skipIf(!entitled)( // `[]` when there are no records), so the ungated case always exercises // `list()` and asserts it hydrates the `read` shape. The entitled path // additionally deploys a page and asserts its presence. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates access custom pages at the account scope", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Access/Group.test.ts b/packages/alchemy/test/Cloudflare/Access/Group.test.ts index 46f0c65af..88d3ec563 100644 --- a/packages/alchemy/test/Cloudflare/Access/Group.test.ts +++ b/packages/alchemy/test/Cloudflare/Access/Group.test.ts @@ -7,6 +7,9 @@ import { expect } from "@effect/vitest"; import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers(), state: Cloudflare.state(), @@ -17,156 +20,166 @@ const logLevel = Effect.provideService( process.env.DEBUG ? "Debug" : "Info", ); -test.provider("create, update rules, and delete group", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const group = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AccessGroup("BasicGroup", { - include: [{ emailDomain: { domain: "example.com" } }], - }); - }), - ); - - expect(group.groupId).toBeDefined(); - expect(group.accountId).toEqual(accountId); - expect(group.name).toBeDefined(); - - const actual = yield* zeroTrust.getAccessGroupForAccount({ - accountId, - groupId: group.groupId, - }); - expect(actual.id).toEqual(group.groupId); - expect(actual.name).toEqual(group.name); - expect(actual.include?.length).toEqual(1); - - // Update — add an include arm plus exclude/require rule groups. The - // group must converge in place (same id). - const updated = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AccessGroup("BasicGroup", { - include: [ - { emailDomain: { domain: "example.com" } }, - { geo: { countryCode: "US" } }, - ], - exclude: [{ email: { email: "intern@example.com" } }], - require: [{ emailDomain: { domain: "example.com" } }], - }); - }), - ); - expect(updated.groupId).toEqual(group.groupId); - - const afterUpdate = yield* zeroTrust.getAccessGroupForAccount({ - accountId, - groupId: group.groupId, - }); - expect(afterUpdate.include?.length).toEqual(2); - expect(afterUpdate.exclude?.length).toEqual(1); - expect(afterUpdate.require?.length).toEqual(1); - - yield* stack.destroy(); - - const afterDestroy = yield* zeroTrust - .getAccessGroupForAccount({ accountId, groupId: group.groupId }) - .pipe( - Effect.catchTag("AccessGroupNotFound", () => Effect.succeed(undefined)), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, update rules, and delete group", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const group = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AccessGroup("BasicGroup", { + include: [{ emailDomain: { domain: "example.com" } }], + }); + }), ); - expect(afterDestroy?.id ?? undefined).toBeUndefined(); - }).pipe(logLevel), -); -test.provider("rename updates the group in place", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const group = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AccessGroup("RenameGroup", { - name: "alchemy-test-access-group-rename-a", - include: [{ everyone: {} }], - }); - }), - ); - expect(group.name).toEqual("alchemy-test-access-group-rename-a"); - - const renamed = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AccessGroup("RenameGroup", { - name: "alchemy-test-access-group-rename-b", - include: [{ everyone: {} }], - }); - }), - ); - expect(renamed.groupId).toEqual(group.groupId); - expect(renamed.name).toEqual("alchemy-test-access-group-rename-b"); - - const actual = yield* zeroTrust.getAccessGroupForAccount({ - accountId, - groupId: group.groupId, - }); - expect(actual.name).toEqual("alchemy-test-access-group-rename-b"); - - yield* stack.destroy(); - }).pipe(logLevel), + expect(group.groupId).toBeDefined(); + expect(group.accountId).toEqual(accountId); + expect(group.name).toBeDefined(); + + const actual = yield* zeroTrust.getAccessGroupForAccount({ + accountId, + groupId: group.groupId, + }); + expect(actual.id).toEqual(group.groupId); + expect(actual.name).toEqual(group.name); + expect(actual.include?.length).toEqual(1); + + // Update — add an include arm plus exclude/require rule groups. The + // group must converge in place (same id). + const updated = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AccessGroup("BasicGroup", { + include: [ + { emailDomain: { domain: "example.com" } }, + { geo: { countryCode: "US" } }, + ], + exclude: [{ email: { email: "intern@example.com" } }], + require: [{ emailDomain: { domain: "example.com" } }], + }); + }), + ); + expect(updated.groupId).toEqual(group.groupId); + + const afterUpdate = yield* zeroTrust.getAccessGroupForAccount({ + accountId, + groupId: group.groupId, + }); + expect(afterUpdate.include?.length).toEqual(2); + expect(afterUpdate.exclude?.length).toEqual(1); + expect(afterUpdate.require?.length).toEqual(1); + + yield* stack.destroy(); + + const afterDestroy = yield* zeroTrust + .getAccessGroupForAccount({ accountId, groupId: group.groupId }) + .pipe( + Effect.catchTag("AccessGroupNotFound", () => + Effect.succeed(undefined), + ), + ); + expect(afterDestroy?.id ?? undefined).toBeUndefined(); + }).pipe(logLevel), ); -test.provider("list enumerates the deployed access group", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "rename updates the group in place", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const group = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AccessGroup("RenameGroup", { + name: "alchemy-test-access-group-rename-a", + include: [{ everyone: {} }], + }); + }), + ); + expect(group.name).toEqual("alchemy-test-access-group-rename-a"); + + const renamed = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AccessGroup("RenameGroup", { + name: "alchemy-test-access-group-rename-b", + include: [{ everyone: {} }], + }); + }), + ); + expect(renamed.groupId).toEqual(group.groupId); + expect(renamed.name).toEqual("alchemy-test-access-group-rename-b"); - const group = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AccessGroup("ListGroup", { - include: [{ emailDomain: { domain: "example.com" } }], - }); - }), - ); + const actual = yield* zeroTrust.getAccessGroupForAccount({ + accountId, + groupId: group.groupId, + }); + expect(actual.name).toEqual("alchemy-test-access-group-rename-b"); - const provider = yield* Provider.findProvider(Cloudflare.AccessGroup); - const all = yield* provider.list(); + yield* stack.destroy(); + }).pipe(logLevel), +); + +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed access group", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const group = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AccessGroup("ListGroup", { + include: [{ emailDomain: { domain: "example.com" } }], + }); + }), + ); - const found = all.find((g) => g.groupId === group.groupId); - expect(found).toBeDefined(); - expect(found?.accountId).toEqual(group.accountId); - expect(found?.name).toEqual(group.name); + const provider = yield* Provider.findProvider(Cloudflare.AccessGroup); + const all = yield* provider.list(); - yield* stack.destroy(); - }).pipe(logLevel), + const found = all.find((g) => g.groupId === group.groupId); + expect(found).toBeDefined(); + expect(found?.accountId).toEqual(group.accountId); + expect(found?.name).toEqual(group.name); + + yield* stack.destroy(); + }).pipe(logLevel), ); -test.provider("group can be referenced from an access policy", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const { group, policy } = yield* stack.deploy( - Effect.gen(function* () { - const group = yield* Cloudflare.AccessGroup("PolicyGroup", { - include: [{ emailDomain: { domain: "example.com" } }], - }); - const policy = yield* Cloudflare.AccessPolicy("GroupPolicy", { - decision: "allow", - include: [{ group: { id: group.groupId } }], - }); - return { group, policy }; - }), - ); - - const actual = yield* zeroTrust.getAccessPolicy({ - accountId, - policyId: policy.policyId, - }); - const includes = (actual.include ?? []) as Array<{ - group?: { id: string }; - }>; - expect(includes[0]?.group?.id).toEqual(group.groupId); - - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "group can be referenced from an access policy", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const { group, policy } = yield* stack.deploy( + Effect.gen(function* () { + const group = yield* Cloudflare.AccessGroup("PolicyGroup", { + include: [{ emailDomain: { domain: "example.com" } }], + }); + const policy = yield* Cloudflare.AccessPolicy("GroupPolicy", { + decision: "allow", + include: [{ group: { id: group.groupId } }], + }); + return { group, policy }; + }), + ); + + const actual = yield* zeroTrust.getAccessPolicy({ + accountId, + policyId: policy.policyId, + }); + const includes = (actual.include ?? []) as Array<{ + group?: { id: string }; + }>; + expect(includes[0]?.group?.id).toEqual(group.groupId); + + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Access/IdentityProvider.test.ts b/packages/alchemy/test/Cloudflare/Access/IdentityProvider.test.ts index 18f2c0586..41fbd2cf8 100644 --- a/packages/alchemy/test/Cloudflare/Access/IdentityProvider.test.ts +++ b/packages/alchemy/test/Cloudflare/Access/IdentityProvider.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -53,160 +56,170 @@ const oidcConfig = { scopes: ["openid", "email", "profile"], }; -test.provider("create, verify, and destroy an OIDC IdP", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const idp = yield* stack.deploy( - Cloudflare.AccessIdentityProvider("BasicOidc", { - name: "alchemy-zt-idp-basic", - type: "oidc", - config: oidcConfig, - }), - ); - - expect(idp.identityProviderId).toBeTruthy(); - expect(idp.accountId).toEqual(accountId); - expect(idp.name).toEqual("alchemy-zt-idp-basic"); - expect(idp.type).toEqual("oidc"); - - const live = yield* getIdp(accountId, idp.identityProviderId); - expect(live.name).toEqual("alchemy-zt-idp-basic"); - expect(live.type).toEqual("oidc"); - // Cloudflare masks the client secret on read. - expect( - (live.config as { clientSecret?: string | null }).clientSecret ?? null, - ).toBeNull(); - - yield* stack.destroy(); - yield* expectGone(accountId, idp.identityProviderId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, verify, and destroy an OIDC IdP", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const idp = yield* stack.deploy( + Cloudflare.AccessIdentityProvider("BasicOidc", { + name: "alchemy-zt-idp-basic", + type: "oidc", + config: oidcConfig, + }), + ); + + expect(idp.identityProviderId).toBeTruthy(); + expect(idp.accountId).toEqual(accountId); + expect(idp.name).toEqual("alchemy-zt-idp-basic"); + expect(idp.type).toEqual("oidc"); + + const live = yield* getIdp(accountId, idp.identityProviderId); + expect(live.name).toEqual("alchemy-zt-idp-basic"); + expect(live.type).toEqual("oidc"); + // Cloudflare masks the client secret on read. + expect( + (live.config as { clientSecret?: string | null }).clientSecret ?? null, + ).toBeNull(); + + yield* stack.destroy(); + yield* expectGone(accountId, idp.identityProviderId); + }).pipe(logLevel), ); -test.provider("update name and config in place (same id)", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const initial = yield* stack.deploy( - Cloudflare.AccessIdentityProvider("UpdateOidc", { - name: "alchemy-zt-idp-update", - type: "oidc", - config: oidcConfig, - }), - ); - - // Note: assert the config change through `claims` — distilled decodes - // the GET response through a discriminated union whose matched variant - // does not carry the oidc-only fields (authUrl/tokenUrl/…), so those - // are stripped from the decoded value even though Cloudflare returns - // them on the wire. - const updated = yield* stack.deploy( - Cloudflare.AccessIdentityProvider("UpdateOidc", { - name: "alchemy-zt-idp-update-v2", - type: "oidc", - config: { - ...oidcConfig, - claims: ["email", "groups"], - }, - }), - ); - - // Same IdP mutated in place — not a replacement. - expect(updated.identityProviderId).toEqual(initial.identityProviderId); - expect(updated.name).toEqual("alchemy-zt-idp-update-v2"); - - const live = yield* getIdp(accountId, updated.identityProviderId); - expect(live.name).toEqual("alchemy-zt-idp-update-v2"); - expect( - [...((live.config as { claims?: string[] | null }).claims ?? [])].sort(), - ).toEqual(["email", "groups"]); - - // Redeploying identical props is a no-op (still the same IdP). - const noop = yield* stack.deploy( - Cloudflare.AccessIdentityProvider("UpdateOidc", { - name: "alchemy-zt-idp-update-v2", - type: "oidc", - config: { - ...oidcConfig, - claims: ["email", "groups"], - }, - }), - ); - expect(noop.identityProviderId).toEqual(initial.identityProviderId); - - yield* stack.destroy(); - yield* expectGone(accountId, initial.identityProviderId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "update name and config in place (same id)", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const initial = yield* stack.deploy( + Cloudflare.AccessIdentityProvider("UpdateOidc", { + name: "alchemy-zt-idp-update", + type: "oidc", + config: oidcConfig, + }), + ); + + // Note: assert the config change through `claims` — distilled decodes + // the GET response through a discriminated union whose matched variant + // does not carry the oidc-only fields (authUrl/tokenUrl/…), so those + // are stripped from the decoded value even though Cloudflare returns + // them on the wire. + const updated = yield* stack.deploy( + Cloudflare.AccessIdentityProvider("UpdateOidc", { + name: "alchemy-zt-idp-update-v2", + type: "oidc", + config: { + ...oidcConfig, + claims: ["email", "groups"], + }, + }), + ); + + // Same IdP mutated in place — not a replacement. + expect(updated.identityProviderId).toEqual(initial.identityProviderId); + expect(updated.name).toEqual("alchemy-zt-idp-update-v2"); + + const live = yield* getIdp(accountId, updated.identityProviderId); + expect(live.name).toEqual("alchemy-zt-idp-update-v2"); + expect( + [ + ...((live.config as { claims?: string[] | null }).claims ?? []), + ].sort(), + ).toEqual(["email", "groups"]); + + // Redeploying identical props is a no-op (still the same IdP). + const noop = yield* stack.deploy( + Cloudflare.AccessIdentityProvider("UpdateOidc", { + name: "alchemy-zt-idp-update-v2", + type: "oidc", + config: { + ...oidcConfig, + claims: ["email", "groups"], + }, + }), + ); + expect(noop.identityProviderId).toEqual(initial.identityProviderId); + + yield* stack.destroy(); + yield* expectGone(accountId, initial.identityProviderId); + }).pipe(logLevel), ); -test.provider("list enumerates the deployed IdP", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const deployed = yield* stack.deploy( - Cloudflare.AccessIdentityProvider("ListOidc", { - name: "alchemy-zt-idp-list", - type: "oidc", - config: oidcConfig, - }), - ); - - const provider = yield* Provider.findProvider( - Cloudflare.AccessIdentityProvider, - ); - const all = yield* provider.list(); - - expect( - all.some((x) => x.identityProviderId === deployed.identityProviderId), - ).toBe(true); - - yield* stack.destroy(); - yield* expectGone(deployed.accountId, deployed.identityProviderId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed IdP", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const deployed = yield* stack.deploy( + Cloudflare.AccessIdentityProvider("ListOidc", { + name: "alchemy-zt-idp-list", + type: "oidc", + config: oidcConfig, + }), + ); + + const provider = yield* Provider.findProvider( + Cloudflare.AccessIdentityProvider, + ); + const all = yield* provider.list(); + + expect( + all.some((x) => x.identityProviderId === deployed.identityProviderId), + ).toBe(true); + + yield* stack.destroy(); + yield* expectGone(deployed.accountId, deployed.identityProviderId); + }).pipe(logLevel), ); -test.provider("changing the type replaces the IdP", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const oidc = yield* stack.deploy( - Cloudflare.AccessIdentityProvider("ReplaceIdp", { - name: "alchemy-zt-idp-replace", - type: "oidc", - config: oidcConfig, - }), - ); - - // The name is the resource's cold-read identity, so a replacement - // (type change) pairs with a rename — keeping the old name would make - // the engine find the doomed sibling and refuse to adopt it. - const github = yield* stack.deploy( - Cloudflare.AccessIdentityProvider("ReplaceIdp", { - name: "alchemy-zt-idp-replace-github", - type: "github", - config: { - clientId: "alchemy-test-client", - clientSecret: "alchemy-test-secret", - }, - }), - ); - - // Type is immutable in our model — the engine must have replaced it. - expect(github.identityProviderId).not.toEqual(oidc.identityProviderId); - expect(github.type).toEqual("github"); - - const live = yield* getIdp(accountId, github.identityProviderId); - expect(live.type).toEqual("github"); - // The old IdP was deleted by the replacement. - yield* expectGone(accountId, oidc.identityProviderId); - - yield* stack.destroy(); - yield* expectGone(accountId, github.identityProviderId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "changing the type replaces the IdP", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const oidc = yield* stack.deploy( + Cloudflare.AccessIdentityProvider("ReplaceIdp", { + name: "alchemy-zt-idp-replace", + type: "oidc", + config: oidcConfig, + }), + ); + + // The name is the resource's cold-read identity, so a replacement + // (type change) pairs with a rename — keeping the old name would make + // the engine find the doomed sibling and refuse to adopt it. + const github = yield* stack.deploy( + Cloudflare.AccessIdentityProvider("ReplaceIdp", { + name: "alchemy-zt-idp-replace-github", + type: "github", + config: { + clientId: "alchemy-test-client", + clientSecret: "alchemy-test-secret", + }, + }), + ); + + // Type is immutable in our model — the engine must have replaced it. + expect(github.identityProviderId).not.toEqual(oidc.identityProviderId); + expect(github.type).toEqual("github"); + + const live = yield* getIdp(accountId, github.identityProviderId); + expect(live.type).toEqual("github"); + // The old IdP was deleted by the replacement. + yield* expectGone(accountId, oidc.identityProviderId); + + yield* stack.destroy(); + yield* expectGone(accountId, github.identityProviderId); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Access/InfrastructureTarget.test.ts b/packages/alchemy/test/Cloudflare/Access/InfrastructureTarget.test.ts index 5c4e705a2..05659624b 100644 --- a/packages/alchemy/test/Cloudflare/Access/InfrastructureTarget.test.ts +++ b/packages/alchemy/test/Cloudflare/Access/InfrastructureTarget.test.ts @@ -5,37 +5,42 @@ import * as Test from "@/Test/Vitest"; import { expect } from "@effect/vitest"; import * as Effect from "effect/Effect"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers(), state: Cloudflare.state(), }); -test.provider("list enumerates the deployed infrastructure target", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const target = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AccessInfrastructureTarget("ListTarget", { - hostname: "list-test.bastion.internal", - ip: { ipv4: { ipAddr: "10.7.0.42" } }, - }); - }), - ); - - const provider = yield* Provider.findProvider( - Cloudflare.AccessInfrastructureTarget, - ); - const all = yield* provider.list(); - - const found = all.find((t) => t.targetId === target.targetId); - expect(found).toBeDefined(); - expect(found?.accountId).toEqual(accountId); - expect(found?.hostname).toEqual(target.hostname); - expect(found?.ip.ipv4?.ipAddr).toEqual("10.7.0.42"); - - yield* stack.destroy(); - }), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed infrastructure target", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const target = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AccessInfrastructureTarget("ListTarget", { + hostname: "list-test.bastion.internal", + ip: { ipv4: { ipAddr: "10.7.0.42" } }, + }); + }), + ); + + const provider = yield* Provider.findProvider( + Cloudflare.AccessInfrastructureTarget, + ); + const all = yield* provider.list(); + + const found = all.find((t) => t.targetId === target.targetId); + expect(found).toBeDefined(); + expect(found?.accountId).toEqual(accountId); + expect(found?.hostname).toEqual(target.hostname); + expect(found?.ip.ipv4?.ipAddr).toEqual("10.7.0.42"); + + yield* stack.destroy(); + }), ); diff --git a/packages/alchemy/test/Cloudflare/Access/KeyConfiguration.test.ts b/packages/alchemy/test/Cloudflare/Access/KeyConfiguration.test.ts index db369c56a..d87f02a2f 100644 --- a/packages/alchemy/test/Cloudflare/Access/KeyConfiguration.test.ts +++ b/packages/alchemy/test/Cloudflare/Access/KeyConfiguration.test.ts @@ -7,6 +7,9 @@ import { expect } from "@effect/vitest"; import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers(), state: Cloudflare.state(), @@ -17,7 +20,7 @@ const logLevel = Effect.provideService( process.env.DEBUG ? "Debug" : "Info", ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "pins the rotation interval, updates in place, and restores on destroy", (stack) => Effect.gen(function* () { @@ -76,7 +79,7 @@ test.provider( // always exists with a Cloudflare default and there is no enumeration API, // so `list()` reads the single instance and returns it as a one-element // array. Assert exactly one well-typed item scoped to the account. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list returns the account key configuration singleton", () => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Access/McpPortal.test.ts b/packages/alchemy/test/Cloudflare/Access/McpPortal.test.ts index 26ea6d090..34a40d962 100644 --- a/packages/alchemy/test/Cloudflare/Access/McpPortal.test.ts +++ b/packages/alchemy/test/Cloudflare/Access/McpPortal.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Stream from "effect/Stream"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -58,7 +61,7 @@ const cleanupPortalsByHostname = (accountId: string, hostname: string) => Effect.catchTag("Forbidden", () => Effect.void), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create, update in place, and destroy an MCP portal", (stack) => Effect.gen(function* () { @@ -136,7 +139,7 @@ test.provider( // Canonical `list()` test (account collection): deploy a portal, then resolve // the provider via the typed helper and assert the deployed portal appears in // the exhaustively-paginated result. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed MCP portal", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Access/Organization.test.ts b/packages/alchemy/test/Cloudflare/Access/Organization.test.ts index 78f5c1996..5372b9e96 100644 --- a/packages/alchemy/test/Cloudflare/Access/Organization.test.ts +++ b/packages/alchemy/test/Cloudflare/Access/Organization.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -125,26 +128,28 @@ describe.sequential("Organization", () => { // org via the same path `read` uses and returns the one-element array (or // `[]` when the account has never enabled Zero Trust). This is read-only — // it does not mutate the singleton — so it runs unconditionally. - test.provider("list returns the account Access organization", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - const provider = yield* Provider.findProvider( - Cloudflare.AccessOrganization, - ); - const all = yield* provider.list(); - - // Singleton: zero (Zero Trust never enabled) or exactly one. - expect(all.length).toBeLessThanOrEqual(1); - for (const org of all) { - expect(org.accountId).toEqual(accountId); - expect(typeof org.authDomain).toBe("string"); - expect(typeof org.name).toBe("string"); - } - - // `stack` is unused (no resource is deployed), but keep the destroy - // bookend so the harness state stays clean. - yield* stack.destroy(); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list returns the account Access organization", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + const provider = yield* Provider.findProvider( + Cloudflare.AccessOrganization, + ); + const all = yield* provider.list(); + + // Singleton: zero (Zero Trust never enabled) or exactly one. + expect(all.length).toBeLessThanOrEqual(1); + for (const org of all) { + expect(org.accountId).toEqual(accountId); + expect(typeof org.authDomain).toBe("string"); + expect(typeof org.name).toBe("string"); + } + + // `stack` is unused (no resource is deployed), but keep the destroy + // bookend so the harness state stays clean. + yield* stack.destroy(); + }).pipe(logLevel), ); }); diff --git a/packages/alchemy/test/Cloudflare/Access/Policy.test.ts b/packages/alchemy/test/Cloudflare/Access/Policy.test.ts index be6ef3105..1a1ef94a0 100644 --- a/packages/alchemy/test/Cloudflare/Access/Policy.test.ts +++ b/packages/alchemy/test/Cloudflare/Access/Policy.test.ts @@ -7,6 +7,9 @@ import { expect } from "@effect/vitest"; import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers(), state: Cloudflare.state(), @@ -17,147 +20,155 @@ const logLevel = Effect.provideService( process.env.DEBUG ? "Debug" : "Info", ); -test.provider("create and delete basic allow policy", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const policy = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AccessPolicy("BasicAllowPolicy", { - decision: "allow", - include: [{ emailDomain: { domain: "example.com" } }], - }); - }), - ); - - expect(policy.policyId).toBeDefined(); - expect(policy.decision).toEqual("allow"); - expect(policy.accountId).toEqual(accountId); - - const actual = yield* zeroTrust.getAccessPolicy({ - accountId, - policyId: policy.policyId, - }); - expect(actual.id).toEqual(policy.policyId); - expect(actual.decision).toEqual("allow"); - expect(actual.include?.length).toEqual(1); - - yield* stack.destroy(); - - const afterDestroy = yield* zeroTrust - .getAccessPolicy({ accountId, policyId: policy.policyId }) - .pipe(Effect.catch(() => Effect.succeed(undefined))); - expect(afterDestroy).toBeUndefined(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create and delete basic allow policy", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const policy = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AccessPolicy("BasicAllowPolicy", { + decision: "allow", + include: [{ emailDomain: { domain: "example.com" } }], + }); + }), + ); + + expect(policy.policyId).toBeDefined(); + expect(policy.decision).toEqual("allow"); + expect(policy.accountId).toEqual(accountId); + + const actual = yield* zeroTrust.getAccessPolicy({ + accountId, + policyId: policy.policyId, + }); + expect(actual.id).toEqual(policy.policyId); + expect(actual.decision).toEqual("allow"); + expect(actual.include?.length).toEqual(1); + + yield* stack.destroy(); + + const afterDestroy = yield* zeroTrust + .getAccessPolicy({ accountId, policyId: policy.policyId }) + .pipe(Effect.catch(() => Effect.succeed(undefined))); + expect(afterDestroy).toBeUndefined(); + }).pipe(logLevel), ); -test.provider("update mutates includes without replacing", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const initial = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AccessPolicy("UpdatePolicy", { - decision: "allow", - include: [{ emailDomain: { domain: "example.com" } }], - adopt: true, - }); - }), - ); - - expect(initial.policyId).toBeDefined(); - - const updated = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AccessPolicy("UpdatePolicy", { - decision: "allow", - include: [ - { emailDomain: { domain: "example.com" } }, - { emailDomain: { domain: "test.example.com" } }, - ], - adopt: true, - }); - }), - ); - - expect(updated.policyId).toEqual(initial.policyId); - - const actual = yield* zeroTrust.getAccessPolicy({ - accountId, - policyId: updated.policyId, - }); - expect(actual.include?.length).toEqual(2); - - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "update mutates includes without replacing", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const initial = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AccessPolicy("UpdatePolicy", { + decision: "allow", + include: [{ emailDomain: { domain: "example.com" } }], + adopt: true, + }); + }), + ); + + expect(initial.policyId).toBeDefined(); + + const updated = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AccessPolicy("UpdatePolicy", { + decision: "allow", + include: [ + { emailDomain: { domain: "example.com" } }, + { emailDomain: { domain: "test.example.com" } }, + ], + adopt: true, + }); + }), + ); + + expect(updated.policyId).toEqual(initial.policyId); + + const actual = yield* zeroTrust.getAccessPolicy({ + accountId, + policyId: updated.policyId, + }); + expect(actual.include?.length).toEqual(2); + + yield* stack.destroy(); + }).pipe(logLevel), ); -test.provider("adopts an out-of-band reusable policy", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - const name = "alchemy-access-policy-adopt-test"; - - yield* stack.destroy(); - - // Pre-create the policy out of band so adoption has something to find. - const preExisting = yield* zeroTrust.createAccessPolicy({ - accountId, - name, - decision: "allow", - include: [{ emailDomain: { domain: "example.com" } }], - }); - expect(preExisting.id).toBeDefined(); - - const adopted = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AccessPolicy("AdoptPolicy", { - name, - decision: "allow", - include: [{ emailDomain: { domain: "example.com" } }], - adopt: true, - }); - }), - ); - - expect(adopted.policyId).toEqual(preExisting.id); - expect(adopted.accountId).toEqual(accountId); - - yield* stack.destroy(); - - const afterDestroy = yield* zeroTrust - .getAccessPolicy({ accountId, policyId: preExisting.id! }) - .pipe(Effect.catch(() => Effect.succeed(undefined))); - expect(afterDestroy).toBeUndefined(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "adopts an out-of-band reusable policy", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + const name = "alchemy-access-policy-adopt-test"; + + yield* stack.destroy(); + + // Pre-create the policy out of band so adoption has something to find. + const preExisting = yield* zeroTrust.createAccessPolicy({ + accountId, + name, + decision: "allow", + include: [{ emailDomain: { domain: "example.com" } }], + }); + expect(preExisting.id).toBeDefined(); + + const adopted = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AccessPolicy("AdoptPolicy", { + name, + decision: "allow", + include: [{ emailDomain: { domain: "example.com" } }], + adopt: true, + }); + }), + ); + + expect(adopted.policyId).toEqual(preExisting.id); + expect(adopted.accountId).toEqual(accountId); + + yield* stack.destroy(); + + const afterDestroy = yield* zeroTrust + .getAccessPolicy({ accountId, policyId: preExisting.id! }) + .pipe(Effect.catch(() => Effect.succeed(undefined))); + expect(afterDestroy).toBeUndefined(); + }).pipe(logLevel), ); -test.provider("list enumerates the deployed reusable policy", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const policy = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AccessPolicy("ListPolicy", { - decision: "allow", - include: [{ emailDomain: { domain: "example.com" } }], - }); - }), - ); - - const provider = yield* Provider.findProvider(Cloudflare.AccessPolicy); - const all = yield* provider.list(); - - const match = all.find((p) => p.policyId === policy.policyId); - expect(match).toBeDefined(); - expect(match?.accountId).toEqual(accountId); - expect(match?.decision).toEqual("allow"); - - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed reusable policy", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const policy = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AccessPolicy("ListPolicy", { + decision: "allow", + include: [{ emailDomain: { domain: "example.com" } }], + }); + }), + ); + + const provider = yield* Provider.findProvider(Cloudflare.AccessPolicy); + const all = yield* provider.list(); + + const match = all.find((p) => p.policyId === policy.policyId); + expect(match).toBeDefined(); + expect(match?.accountId).toEqual(accountId); + expect(match?.decision).toEqual("allow"); + + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Access/ServiceToken.test.ts b/packages/alchemy/test/Cloudflare/Access/ServiceToken.test.ts index 3a4fc35b9..feaef339d 100644 --- a/packages/alchemy/test/Cloudflare/Access/ServiceToken.test.ts +++ b/packages/alchemy/test/Cloudflare/Access/ServiceToken.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import * as Redacted from "effect/Redacted"; import { MinimumLogLevel } from "effect/References"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers(), state: Cloudflare.state(), @@ -18,146 +21,152 @@ const logLevel = Effect.provideService( process.env.DEBUG ? "Debug" : "Info", ); -test.provider("create, update duration, and delete service token", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const token = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AccessServiceToken("BasicToken", {}); - }), - ); - - expect(token.serviceTokenId).toBeDefined(); - expect(token.accountId).toEqual(accountId); - expect(token.clientId).toBeDefined(); - // The client secret is only revealed on create — it must be captured. - expect(token.clientSecret).toBeDefined(); - expect(Redacted.value(token.clientSecret!).length).toBeGreaterThan(0); - - const actual = yield* zeroTrust.getAccessServiceTokenForAccount({ - accountId, - serviceTokenId: token.serviceTokenId, - }); - expect(actual.id).toEqual(token.serviceTokenId); - expect(actual.clientId).toEqual(token.clientId); - expect(actual.name).toEqual(token.name); - - // Update — change the validity duration in place (same id) and keep - // the previously captured secret. - const updated = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AccessServiceToken("BasicToken", { - duration: "17520h", - }); - }), - ); - expect(updated.serviceTokenId).toEqual(token.serviceTokenId); - expect(updated.duration).toEqual("17520h"); - expect(updated.clientSecret).toBeDefined(); - expect(Redacted.value(updated.clientSecret!)).toEqual( - Redacted.value(token.clientSecret!), - ); - - const afterUpdate = yield* zeroTrust.getAccessServiceTokenForAccount({ - accountId, - serviceTokenId: token.serviceTokenId, - }); - expect(afterUpdate.duration).toEqual("17520h"); - - yield* stack.destroy(); - - const afterDestroy = yield* zeroTrust - .getAccessServiceTokenForAccount({ +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, update duration, and delete service token", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const token = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AccessServiceToken("BasicToken", {}); + }), + ); + + expect(token.serviceTokenId).toBeDefined(); + expect(token.accountId).toEqual(accountId); + expect(token.clientId).toBeDefined(); + // The client secret is only revealed on create — it must be captured. + expect(token.clientSecret).toBeDefined(); + expect(Redacted.value(token.clientSecret!).length).toBeGreaterThan(0); + + const actual = yield* zeroTrust.getAccessServiceTokenForAccount({ accountId, serviceTokenId: token.serviceTokenId, - }) - .pipe( - Effect.catchTag("AccessServiceTokenNotFound", () => - Effect.succeed(undefined), - ), + }); + expect(actual.id).toEqual(token.serviceTokenId); + expect(actual.clientId).toEqual(token.clientId); + expect(actual.name).toEqual(token.name); + + // Update — change the validity duration in place (same id) and keep + // the previously captured secret. + const updated = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AccessServiceToken("BasicToken", { + duration: "17520h", + }); + }), ); - expect(afterDestroy?.id ?? undefined).toBeUndefined(); - }).pipe(logLevel), + expect(updated.serviceTokenId).toEqual(token.serviceTokenId); + expect(updated.duration).toEqual("17520h"); + expect(updated.clientSecret).toBeDefined(); + expect(Redacted.value(updated.clientSecret!)).toEqual( + Redacted.value(token.clientSecret!), + ); + + const afterUpdate = yield* zeroTrust.getAccessServiceTokenForAccount({ + accountId, + serviceTokenId: token.serviceTokenId, + }); + expect(afterUpdate.duration).toEqual("17520h"); + + yield* stack.destroy(); + + const afterDestroy = yield* zeroTrust + .getAccessServiceTokenForAccount({ + accountId, + serviceTokenId: token.serviceTokenId, + }) + .pipe( + Effect.catchTag("AccessServiceTokenNotFound", () => + Effect.succeed(undefined), + ), + ); + expect(afterDestroy?.id ?? undefined).toBeUndefined(); + }).pipe(logLevel), ); -test.provider("list enumerates the deployed service token", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const token = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AccessServiceToken("ListToken", {}); - }), - ); - - const provider = yield* Provider.findProvider( - Cloudflare.AccessServiceToken, - ); - const all = yield* provider.list(); - - expect(all.some((t) => t.serviceTokenId === token.serviceTokenId)).toBe( - true, - ); - // Enumeration never exposes the one-time secret — it matches read. - const found = all.find((t) => t.serviceTokenId === token.serviceTokenId); - expect(found?.clientId).toEqual(token.clientId); - expect(found?.clientSecret).toBeUndefined(); - - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed service token", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const token = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AccessServiceToken("ListToken", {}); + }), + ); + + const provider = yield* Provider.findProvider( + Cloudflare.AccessServiceToken, + ); + const all = yield* provider.list(); + + expect(all.some((t) => t.serviceTokenId === token.serviceTokenId)).toBe( + true, + ); + // Enumeration never exposes the one-time secret — it matches read. + const found = all.find((t) => t.serviceTokenId === token.serviceTokenId); + expect(found?.clientId).toEqual(token.clientId); + expect(found?.clientSecret).toBeUndefined(); + + yield* stack.destroy(); + }).pipe(logLevel), ); -test.provider("incrementing clientSecretVersion rotates the secret", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const token = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AccessServiceToken("RotateToken", {}); - }), - ); - expect(token.clientSecret).toBeDefined(); - expect(token.clientSecretVersion).toEqual(1); - - const rotated = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AccessServiceToken("RotateToken", { - clientSecretVersion: 2, - }); - }), - ); - expect(rotated.serviceTokenId).toEqual(token.serviceTokenId); - expect(rotated.clientId).toEqual(token.clientId); - expect(rotated.clientSecretVersion).toEqual(2); - expect(rotated.clientSecret).toBeDefined(); - expect(Redacted.value(rotated.clientSecret!)).not.toEqual( - Redacted.value(token.clientSecret!), - ); - - // Re-deploying the same version must NOT rotate again. - const stable = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AccessServiceToken("RotateToken", { - clientSecretVersion: 2, - }); - }), - ); - expect(stable.serviceTokenId).toEqual(token.serviceTokenId); - expect(Redacted.value(stable.clientSecret!)).toEqual( - Redacted.value(rotated.clientSecret!), - ); - - const actual = yield* zeroTrust.getAccessServiceTokenForAccount({ - accountId, - serviceTokenId: token.serviceTokenId, - }); - expect(actual.id).toEqual(token.serviceTokenId); - - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "incrementing clientSecretVersion rotates the secret", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const token = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AccessServiceToken("RotateToken", {}); + }), + ); + expect(token.clientSecret).toBeDefined(); + expect(token.clientSecretVersion).toEqual(1); + + const rotated = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AccessServiceToken("RotateToken", { + clientSecretVersion: 2, + }); + }), + ); + expect(rotated.serviceTokenId).toEqual(token.serviceTokenId); + expect(rotated.clientId).toEqual(token.clientId); + expect(rotated.clientSecretVersion).toEqual(2); + expect(rotated.clientSecret).toBeDefined(); + expect(Redacted.value(rotated.clientSecret!)).not.toEqual( + Redacted.value(token.clientSecret!), + ); + + // Re-deploying the same version must NOT rotate again. + const stable = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AccessServiceToken("RotateToken", { + clientSecretVersion: 2, + }); + }), + ); + expect(stable.serviceTokenId).toEqual(token.serviceTokenId); + expect(Redacted.value(stable.clientSecret!)).toEqual( + Redacted.value(rotated.clientSecret!), + ); + + const actual = yield* zeroTrust.getAccessServiceTokenForAccount({ + accountId, + serviceTokenId: token.serviceTokenId, + }); + expect(actual.id).toEqual(token.serviceTokenId); + + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Access/Tag.test.ts b/packages/alchemy/test/Cloudflare/Access/Tag.test.ts index ff60556cc..139ac99fe 100644 --- a/packages/alchemy/test/Cloudflare/Access/Tag.test.ts +++ b/packages/alchemy/test/Cloudflare/Access/Tag.test.ts @@ -7,6 +7,9 @@ import { expect } from "@effect/vitest"; import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers(), state: Cloudflare.state(), @@ -17,117 +20,123 @@ const logLevel = Effect.provideService( process.env.DEBUG ? "Debug" : "Info", ); -test.provider("create, verify out-of-band, and destroy tag", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const tag = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AccessTag("BasicTag", {}); - }), - ); - - expect(tag.name).toBeDefined(); - expect(tag.accountId).toEqual(accountId); - - const actual = yield* zeroTrust.getAccessTag({ - accountId, - tagName: tag.name, - }); - expect(actual.name).toEqual(tag.name); - - // Reconcile again with no changes — existence-only resource converges - // without churn. - const again = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AccessTag("BasicTag", {}); - }), - ); - expect(again.name).toEqual(tag.name); - - yield* stack.destroy(); - - const afterDestroy = yield* zeroTrust - .getAccessTag({ accountId, tagName: tag.name }) - .pipe( - Effect.catchTag("AccessTagNotFound", () => Effect.succeed(undefined)), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, verify out-of-band, and destroy tag", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const tag = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AccessTag("BasicTag", {}); + }), ); - expect(afterDestroy).toBeUndefined(); - }).pipe(logLevel), -); -test.provider("rename replaces the tag", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const tag = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AccessTag("RenameTag", { - name: "alchemy-test-access-tag-a", - }); - }), - ); - expect(tag.name).toEqual("alchemy-test-access-tag-a"); - - const renamed = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AccessTag("RenameTag", { - name: "alchemy-test-access-tag-b", - }); - }), - ); - expect(renamed.name).toEqual("alchemy-test-access-tag-b"); - - // The new identity exists and the old one is gone (replacement). - const current = yield* zeroTrust.getAccessTag({ - accountId, - tagName: "alchemy-test-access-tag-b", - }); - expect(current.name).toEqual("alchemy-test-access-tag-b"); - - const old = yield* zeroTrust - .getAccessTag({ accountId, tagName: "alchemy-test-access-tag-a" }) - .pipe( - Effect.catchTag("AccessTagNotFound", () => Effect.succeed(undefined)), + expect(tag.name).toBeDefined(); + expect(tag.accountId).toEqual(accountId); + + const actual = yield* zeroTrust.getAccessTag({ + accountId, + tagName: tag.name, + }); + expect(actual.name).toEqual(tag.name); + + // Reconcile again with no changes — existence-only resource converges + // without churn. + const again = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AccessTag("BasicTag", {}); + }), ); - expect(old).toBeUndefined(); + expect(again.name).toEqual(tag.name); + + yield* stack.destroy(); - yield* stack.destroy(); + const afterDestroy = yield* zeroTrust + .getAccessTag({ accountId, tagName: tag.name }) + .pipe( + Effect.catchTag("AccessTagNotFound", () => Effect.succeed(undefined)), + ); + expect(afterDestroy).toBeUndefined(); + }).pipe(logLevel), +); + +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "rename replaces the tag", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); - const afterDestroy = yield* zeroTrust - .getAccessTag({ accountId, tagName: "alchemy-test-access-tag-b" }) - .pipe( - Effect.catchTag("AccessTagNotFound", () => Effect.succeed(undefined)), + const tag = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AccessTag("RenameTag", { + name: "alchemy-test-access-tag-a", + }); + }), ); - expect(afterDestroy).toBeUndefined(); - }).pipe(logLevel), + expect(tag.name).toEqual("alchemy-test-access-tag-a"); + + const renamed = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AccessTag("RenameTag", { + name: "alchemy-test-access-tag-b", + }); + }), + ); + expect(renamed.name).toEqual("alchemy-test-access-tag-b"); + + // The new identity exists and the old one is gone (replacement). + const current = yield* zeroTrust.getAccessTag({ + accountId, + tagName: "alchemy-test-access-tag-b", + }); + expect(current.name).toEqual("alchemy-test-access-tag-b"); + + const old = yield* zeroTrust + .getAccessTag({ accountId, tagName: "alchemy-test-access-tag-a" }) + .pipe( + Effect.catchTag("AccessTagNotFound", () => Effect.succeed(undefined)), + ); + expect(old).toBeUndefined(); + + yield* stack.destroy(); + + const afterDestroy = yield* zeroTrust + .getAccessTag({ accountId, tagName: "alchemy-test-access-tag-b" }) + .pipe( + Effect.catchTag("AccessTagNotFound", () => Effect.succeed(undefined)), + ); + expect(afterDestroy).toBeUndefined(); + }).pipe(logLevel), ); -test.provider("list enumerates the deployed access tag", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed access tag", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - yield* stack.destroy(); + yield* stack.destroy(); - const tag = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AccessTag("ListTag", { - name: "alchemy-test-access-tag-list", - }); - }), - ); + const tag = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AccessTag("ListTag", { + name: "alchemy-test-access-tag-list", + }); + }), + ); - const provider = yield* Provider.findProvider(Cloudflare.AccessTag); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider(Cloudflare.AccessTag); + const all = yield* provider.list(); - const found = all.find((t) => t.name === tag.name); - expect(found).toBeDefined(); - expect(found?.accountId).toEqual(accountId); + const found = all.find((t) => t.name === tag.name); + expect(found).toBeDefined(); + expect(found?.accountId).toEqual(accountId); - yield* stack.destroy(); - }).pipe(logLevel), + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Account/Account.test.ts b/packages/alchemy/test/Cloudflare/Account/Account.test.ts index 969940a19..b79bfcd55 100644 --- a/packages/alchemy/test/Cloudflare/Account/Account.test.ts +++ b/packages/alchemy/test/Cloudflare/Account/Account.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -61,28 +64,30 @@ test.provider("inaccessible account surfaces a typed tag", (stack) => // standard token can LIST every account it can access, and the token's own // testing account must appear in the exhaustively-paginated result with the // exact `Attributes` shape `read` produces. -test.provider("list enumerates accessible accounts (read-only)", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates accessible accounts (read-only)", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - yield* stack.destroy(); + yield* stack.destroy(); - const provider = yield* Provider.findProvider(Cloudflare.Account); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider(Cloudflare.Account); + const all = yield* provider.list(); - expect(all.length).toBeGreaterThan(0); + expect(all.length).toBeGreaterThan(0); - const self = all.find((a) => a.accountId === accountId); - expect(self).toBeDefined(); - expect(self?.name).toBeTruthy(); - expect(["standard", "enterprise"]).toContain(self?.type); - expect(typeof self?.enforceTwofactor).toEqual("boolean"); + const self = all.find((a) => a.accountId === accountId); + expect(self).toBeDefined(); + expect(self?.name).toBeTruthy(); + expect(["standard", "enterprise"]).toContain(self?.type); + expect(typeof self?.enforceTwofactor).toEqual("boolean"); - yield* stack.destroy(); - }).pipe(logLevel), + yield* stack.destroy(); + }).pipe(logLevel), ); -test.provider.skipIf(tenantEntitled)( +test.provider.skipIf(tenantEntitled || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "createAccount is entitlement-gated: typed AccountCreationForbidden", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Account/Member.test.ts b/packages/alchemy/test/Cloudflare/Account/Member.test.ts index d237568cd..95b130d6e 100644 --- a/packages/alchemy/test/Cloudflare/Account/Member.test.ts +++ b/packages/alchemy/test/Cloudflare/Account/Member.test.ts @@ -9,6 +9,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -90,152 +93,160 @@ const pickTwoRoles = (accountId: string) => // Read-only: enumerating account members sends no invites. The account // owner is always an accepted member, so `list()` must return a non-empty, // well-typed `Attributes[]` containing at least one accepted membership. -test.provider("list enumerates the account members", (_stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the account members", + (_stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + const provider = yield* Provider.findProvider(Cloudflare.AccountMember); + const all = yield* provider.list().pipe(Effect.retry(forbiddenRetry)); + + expect(all.length).toBeGreaterThan(0); + // Every element is the exact `read` shape, scoped to this account. + for (const member of all) { + expect(member.memberId).toBeTruthy(); + expect(member.accountId).toEqual(accountId); + expect(typeof member.email).toBe("string"); + expect(Array.isArray(member.roles)).toBe(true); + } + // The account owner is an accepted member. + expect(all.some((member) => member.status === "accepted")).toBe(true); + }).pipe(logLevel), +); + +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create member, update roles in place, delete", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + yield* cleanupEmail(accountId, crudEmail); - const provider = yield* Provider.findProvider(Cloudflare.AccountMember); - const all = yield* provider.list().pipe(Effect.retry(forbiddenRetry)); + const [roleA, roleB] = yield* pickTwoRoles(accountId); + + const member = yield* stack.deploy( + Cloudflare.AccountMember("TestMember", { + email: crudEmail, + roles: [roleA.id], + }), + ); - expect(all.length).toBeGreaterThan(0); - // Every element is the exact `read` shape, scoped to this account. - for (const member of all) { expect(member.memberId).toBeTruthy(); expect(member.accountId).toEqual(accountId); - expect(typeof member.email).toBe("string"); - expect(Array.isArray(member.roles)).toBe(true); - } - // The account owner is an accepted member. - expect(all.some((member) => member.status === "accepted")).toBe(true); - }).pipe(logLevel), -); + expect(member.email.toLowerCase()).toEqual(crudEmail); + expect(member.status).toEqual("pending"); + expect(member.roles.map((r) => r.id)).toEqual([roleA.id]); + + // Out-of-band verify the invite exists with the assigned role. + const live = yield* getMember(accountId, member.memberId); + expect(live.email?.toLowerCase()).toEqual(crudEmail); + expect((live.roles ?? []).map((r) => r.id)).toEqual([roleA.id]); + + // Swap the role — same email, so the membership is updated in place. + const updated = yield* stack.deploy( + Cloudflare.AccountMember("TestMember", { + email: crudEmail, + roles: [roleB.id], + }), + ); + expect(updated.memberId).toEqual(member.memberId); + expect(updated.roles.map((r) => r.id)).toEqual([roleB.id]); + + const liveUpdated = yield* getMember(accountId, member.memberId); + expect((liveUpdated.roles ?? []).map((r) => r.id)).toEqual([roleB.id]); + + // Redeploying identical props is a no-op (same membership). + const noop = yield* stack.deploy( + Cloudflare.AccountMember("TestMember", { + email: crudEmail, + roles: [roleB.id], + }), + ); + expect(noop.memberId).toEqual(member.memberId); -test.provider("create member, update roles in place, delete", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - yield* cleanupEmail(accountId, crudEmail); - - const [roleA, roleB] = yield* pickTwoRoles(accountId); - - const member = yield* stack.deploy( - Cloudflare.AccountMember("TestMember", { - email: crudEmail, - roles: [roleA.id], - }), - ); - - expect(member.memberId).toBeTruthy(); - expect(member.accountId).toEqual(accountId); - expect(member.email.toLowerCase()).toEqual(crudEmail); - expect(member.status).toEqual("pending"); - expect(member.roles.map((r) => r.id)).toEqual([roleA.id]); - - // Out-of-band verify the invite exists with the assigned role. - const live = yield* getMember(accountId, member.memberId); - expect(live.email?.toLowerCase()).toEqual(crudEmail); - expect((live.roles ?? []).map((r) => r.id)).toEqual([roleA.id]); - - // Swap the role — same email, so the membership is updated in place. - const updated = yield* stack.deploy( - Cloudflare.AccountMember("TestMember", { - email: crudEmail, - roles: [roleB.id], - }), - ); - expect(updated.memberId).toEqual(member.memberId); - expect(updated.roles.map((r) => r.id)).toEqual([roleB.id]); - - const liveUpdated = yield* getMember(accountId, member.memberId); - expect((liveUpdated.roles ?? []).map((r) => r.id)).toEqual([roleB.id]); - - // Redeploying identical props is a no-op (same membership). - const noop = yield* stack.deploy( - Cloudflare.AccountMember("TestMember", { - email: crudEmail, - roles: [roleB.id], - }), - ); - expect(noop.memberId).toEqual(member.memberId); - - yield* stack.destroy(); - yield* expectGone(accountId, member.memberId); - }).pipe(logLevel), + yield* stack.destroy(); + yield* expectGone(accountId, member.memberId); + }).pipe(logLevel), ); -test.provider("replaces the member when the email changes", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - yield* cleanupEmail(accountId, replaceEmail); - yield* cleanupEmail(accountId, replacedEmail); - - const [roleA] = yield* pickTwoRoles(accountId); - - const original = yield* stack.deploy( - Cloudflare.AccountMember("ReplaceMember", { - email: replaceEmail, - roles: [roleA.id], - }), - ); - - // Changing the email re-invites: a fresh membership id, and the old - // invite is cancelled by the replacement's delete phase. - const replaced = yield* stack.deploy( - Cloudflare.AccountMember("ReplaceMember", { - email: replacedEmail, - roles: [roleA.id], - }), - ); - - expect(replaced.memberId).not.toEqual(original.memberId); - expect(replaced.email.toLowerCase()).toEqual(replacedEmail); - yield* expectGone(accountId, original.memberId); - - yield* stack.destroy(); - yield* expectGone(accountId, replaced.memberId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "replaces the member when the email changes", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + yield* cleanupEmail(accountId, replaceEmail); + yield* cleanupEmail(accountId, replacedEmail); + + const [roleA] = yield* pickTwoRoles(accountId); + + const original = yield* stack.deploy( + Cloudflare.AccountMember("ReplaceMember", { + email: replaceEmail, + roles: [roleA.id], + }), + ); + + // Changing the email re-invites: a fresh membership id, and the old + // invite is cancelled by the replacement's delete phase. + const replaced = yield* stack.deploy( + Cloudflare.AccountMember("ReplaceMember", { + email: replacedEmail, + roles: [roleA.id], + }), + ); + + expect(replaced.memberId).not.toEqual(original.memberId); + expect(replaced.email.toLowerCase()).toEqual(replacedEmail); + yield* expectGone(accountId, original.memberId); + + yield* stack.destroy(); + yield* expectGone(accountId, replaced.memberId); + }).pipe(logLevel), ); -test.provider("recreates after out-of-band delete", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - yield* cleanupEmail(accountId, healEmail); - - const [roleA, roleB] = yield* pickTwoRoles(accountId); - - const member = yield* stack.deploy( - Cloudflare.AccountMember("HealMember", { - email: healEmail, - roles: [roleA.id], - }), - ); - - // Cancel the invite out-of-band. A redeploy with identical props is a - // planner no-op, so change the role to force reconcile — it must - // observe the member as missing and re-invite instead of failing. - yield* accounts - .deleteMember({ accountId, memberId: member.memberId }) - .pipe(Effect.retry(forbiddenRetry)); - - const healed = yield* stack.deploy( - Cloudflare.AccountMember("HealMember", { - email: healEmail, - roles: [roleB.id], - }), - ); - - expect(healed.memberId).not.toEqual(member.memberId); - expect(healed.roles.map((r) => r.id)).toEqual([roleB.id]); - - const live = yield* getMember(accountId, healed.memberId); - expect((live.roles ?? []).map((r) => r.id)).toEqual([roleB.id]); - - yield* stack.destroy(); - yield* expectGone(accountId, healed.memberId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "recreates after out-of-band delete", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + yield* cleanupEmail(accountId, healEmail); + + const [roleA, roleB] = yield* pickTwoRoles(accountId); + + const member = yield* stack.deploy( + Cloudflare.AccountMember("HealMember", { + email: healEmail, + roles: [roleA.id], + }), + ); + + // Cancel the invite out-of-band. A redeploy with identical props is a + // planner no-op, so change the role to force reconcile — it must + // observe the member as missing and re-invite instead of failing. + yield* accounts + .deleteMember({ accountId, memberId: member.memberId }) + .pipe(Effect.retry(forbiddenRetry)); + + const healed = yield* stack.deploy( + Cloudflare.AccountMember("HealMember", { + email: healEmail, + roles: [roleB.id], + }), + ); + + expect(healed.memberId).not.toEqual(member.memberId); + expect(healed.roles.map((r) => r.id)).toEqual([roleB.id]); + + const live = yield* getMember(accountId, healed.memberId); + expect((live.roles ?? []).map((r) => r.id)).toEqual([roleB.id]); + + yield* stack.destroy(); + yield* expectGone(accountId, healed.memberId); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Acm/CustomTrustStore.test.ts b/packages/alchemy/test/Cloudflare/Acm/CustomTrustStore.test.ts index e9a269e5f..69ae67a05 100644 --- a/packages/alchemy/test/Cloudflare/Acm/CustomTrustStore.test.ts +++ b/packages/alchemy/test/Cloudflare/Acm/CustomTrustStore.test.ts @@ -11,6 +11,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import * as pathe from "pathe"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -66,7 +69,7 @@ const getTrustStore = (zoneId: string, id: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Acm/TotalTls.test.ts b/packages/alchemy/test/Cloudflare/Acm/TotalTls.test.ts index 6e8b89235..7ecaad805 100644 --- a/packages/alchemy/test/Cloudflare/Acm/TotalTls.test.ts +++ b/packages/alchemy/test/Cloudflare/Acm/TotalTls.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -55,7 +58,7 @@ const getTotalTls = (zoneId: string) => // Both cases mutate the same zone-level Total TLS singleton; run them serially so they don't corrupt each other's captured `initialEnabled` under the global concurrent test config. describe.sequential("TotalTls", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "converges enabled:false as a no-op on a zone without the ACM entitlement", (stack) => Effect.gen(function* () { @@ -91,7 +94,7 @@ describe.sequential("TotalTls", () => { }).pipe(logLevel), ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed AdvancedCertificateManagerRequired error when enabling without the entitlement", (stack) => Effect.gen(function* () { @@ -170,19 +173,21 @@ describe.sequential("TotalTls", () => { // `listAllZones` and reads the singleton in each (reads succeed without the // ACM entitlement). Assert the result is non-empty and contains the standing // test zone. - test.provider("list enumerates the setting across all zones", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the setting across all zones", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - const provider = yield* Provider.findProvider(Cloudflare.TotalTls); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider(Cloudflare.TotalTls); + const all = yield* provider.list(); - expect(all.length).toBeGreaterThan(0); - expect(all.some((s) => s.zoneId === zoneId)).toBe(true); + expect(all.length).toBeGreaterThan(0); + expect(all.some((s) => s.zoneId === zoneId)).toBe(true); - // `stack` is unused here (the singleton always exists on every zone), - // but keep the destroy bookend so the harness state stays clean. - yield* stack.destroy(); - }).pipe(logLevel), + // `stack` is unused here (the singleton always exists on every zone), + // but keep the destroy bookend so the harness state stays clean. + yield* stack.destroy(); + }).pipe(logLevel), ); }); diff --git a/packages/alchemy/test/Cloudflare/Addressing/AddressMap.test.ts b/packages/alchemy/test/Cloudflare/Addressing/AddressMap.test.ts index eedeb0a27..b97af15d9 100644 --- a/packages/alchemy/test/Cloudflare/Addressing/AddressMap.test.ts +++ b/packages/alchemy/test/Cloudflare/Addressing/AddressMap.test.ts @@ -9,6 +9,9 @@ import * as Result from "effect/Result"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -40,7 +43,7 @@ const getMap = (accountId: string, addressMapId: string) => // with the typed `FeatureNotEnabled` error // (`address_maps_not_enabled_on_account`). The test probes once: unentitled // accounts assert the typed tag; entitled accounts run the full lifecycle. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "address map lifecycle (typed FeatureNotEnabled on unentitled accounts)", (stack) => Effect.gen(function* () { @@ -124,7 +127,7 @@ test.provider( // static-IP entitlement as the lifecycle: unentitled accounts can't create // a map to observe, so we probe once and assert the typed `FeatureNotEnabled` // tag (clean skip); entitled accounts run the full deploy + list assertion. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed address map", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Addressing/BgpPrefix.test.ts b/packages/alchemy/test/Cloudflare/Addressing/BgpPrefix.test.ts index a190a0fae..13f938a56 100644 --- a/packages/alchemy/test/Cloudflare/Addressing/BgpPrefix.test.ts +++ b/packages/alchemy/test/Cloudflare/Addressing/BgpPrefix.test.ts @@ -6,6 +6,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -34,24 +37,26 @@ const retryForbidden = ( // result is naturally an empty `AddressingBgpPrefixAttributes[]` — the exact // shape `read` produces. This exercises the distilled wiring live on every run // without requiring the BYOIP contract/entitlement. -test.provider("list enumerates BGP prefixes across BYOIP prefixes", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const provider = yield* Provider.findProvider( - Cloudflare.AddressingBgpPrefix, - ); - const all = yield* retryForbidden(provider.list()); - - expect(Array.isArray(all)).toBe(true); - for (const p of all) { - expect(typeof p.bgpPrefixId).toBe("string"); - expect(typeof p.prefixId).toBe("string"); - expect(typeof p.accountId).toBe("string"); - expect(typeof p.cidr).toBe("string"); - expect(typeof p.onDemand.advertised).toBe("boolean"); - } - - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates BGP prefixes across BYOIP prefixes", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const provider = yield* Provider.findProvider( + Cloudflare.AddressingBgpPrefix, + ); + const all = yield* retryForbidden(provider.list()); + + expect(Array.isArray(all)).toBe(true); + for (const p of all) { + expect(typeof p.bgpPrefixId).toBe("string"); + expect(typeof p.prefixId).toBe("string"); + expect(typeof p.accountId).toBe("string"); + expect(typeof p.cidr).toBe("string"); + expect(typeof p.onDemand.advertised).toBe("boolean"); + } + + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Addressing/Prefix.test.ts b/packages/alchemy/test/Cloudflare/Addressing/Prefix.test.ts index 630907ade..1c674d73d 100644 --- a/packages/alchemy/test/Cloudflare/Addressing/Prefix.test.ts +++ b/packages/alchemy/test/Cloudflare/Addressing/Prefix.test.ts @@ -9,6 +9,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -50,53 +53,59 @@ const delegateAccountId = process.env.CLOUDFLARE_TEST_BYOIP_DELEGATE_ACCOUNT_ID; // The read-only catalog endpoints are available regardless of the BYOIP // entitlement — exercise the distilled wiring live on every run. -test.provider("lists the services catalog and prefixes (read-only)", (stack) => - Effect.gen(function* () { - const accountId = yield* resolveAccountId; - - yield* stack.destroy(); - - const services = yield* retryForbidden( - addressing.listServices.items({ accountId }).pipe( - Stream.runCollect, - Effect.map((c) => Array.from(c)), - ), - ); - expect(Array.isArray(services)).toBe(true); - - const prefixes = yield* retryForbidden( - addressing.listPrefixes.items({ accountId }).pipe( - Stream.runCollect, - Effect.map((c) => Array.from(c)), - ), - ); - expect(Array.isArray(prefixes)).toBe(true); - - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "lists the services catalog and prefixes (read-only)", + (stack) => + Effect.gen(function* () { + const accountId = yield* resolveAccountId; + + yield* stack.destroy(); + + const services = yield* retryForbidden( + addressing.listServices.items({ accountId }).pipe( + Stream.runCollect, + Effect.map((c) => Array.from(c)), + ), + ); + expect(Array.isArray(services)).toBe(true); + + const prefixes = yield* retryForbidden( + addressing.listPrefixes.items({ accountId }).pipe( + Stream.runCollect, + Effect.map((c) => Array.from(c)), + ), + ); + expect(Array.isArray(prefixes)).toBe(true); + + yield* stack.destroy(); + }).pipe(logLevel), ); // `list()` enumerates account-scoped BYOIP prefixes via the catalog endpoint, // which is available regardless of the BYOIP entitlement (it returns an empty // array on accounts with no onboarded prefixes). The result is a well-typed // `AddressingPrefixAttributes[]` — the exact shape `read` produces. -test.provider("list enumerates account prefixes (read-only)", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const provider = yield* Provider.findProvider(Cloudflare.AddressingPrefix); - const all = yield* retryForbidden(provider.list()); - - expect(Array.isArray(all)).toBe(true); - for (const p of all) { - expect(typeof p.prefixId).toBe("string"); - expect(typeof p.accountId).toBe("string"); - expect(typeof p.cidr).toBe("string"); - expect(typeof p.asn).toBe("number"); - } - - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates account prefixes (read-only)", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const provider = yield* Provider.findProvider( + Cloudflare.AddressingPrefix, + ); + const all = yield* retryForbidden(provider.list()); + + expect(Array.isArray(all)).toBe(true); + for (const p of all) { + expect(typeof p.prefixId).toBe("string"); + expect(typeof p.accountId).toBe("string"); + expect(typeof p.cidr).toBe("string"); + expect(typeof p.asn).toBe("number"); + } + + yield* stack.destroy(); + }).pipe(logLevel), ); test.provider.skipIf(!byoipCidr || !byoipAsn)( diff --git a/packages/alchemy/test/Cloudflare/Addressing/PrefixDelegation.test.ts b/packages/alchemy/test/Cloudflare/Addressing/PrefixDelegation.test.ts index acd9a7e71..4e211ea7e 100644 --- a/packages/alchemy/test/Cloudflare/Addressing/PrefixDelegation.test.ts +++ b/packages/alchemy/test/Cloudflare/Addressing/PrefixDelegation.test.ts @@ -6,6 +6,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -34,24 +37,26 @@ const retryForbidden = ( // prefixes), then listing each prefix's delegations. The result is a // well-typed `AddressingPrefixDelegationAttributes[]` — the exact shape `read` // produces — and is empty on a non-BYOIP account. -test.provider("list enumerates prefix delegations (read-only)", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const provider = yield* Provider.findProvider( - Cloudflare.AddressingPrefixDelegation, - ); - const all = yield* retryForbidden(provider.list()); - - expect(Array.isArray(all)).toBe(true); - for (const d of all) { - expect(typeof d.delegationId).toBe("string"); - expect(typeof d.prefixId).toBe("string"); - expect(typeof d.accountId).toBe("string"); - expect(typeof d.cidr).toBe("string"); - expect(typeof d.delegatedAccountId).toBe("string"); - } - - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates prefix delegations (read-only)", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const provider = yield* Provider.findProvider( + Cloudflare.AddressingPrefixDelegation, + ); + const all = yield* retryForbidden(provider.list()); + + expect(Array.isArray(all)).toBe(true); + for (const d of all) { + expect(typeof d.delegationId).toBe("string"); + expect(typeof d.prefixId).toBe("string"); + expect(typeof d.accountId).toBe("string"); + expect(typeof d.cidr).toBe("string"); + expect(typeof d.delegatedAccountId).toBe("string"); + } + + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Addressing/ServiceBinding.test.ts b/packages/alchemy/test/Cloudflare/Addressing/ServiceBinding.test.ts index db7c47df8..635a5e790 100644 --- a/packages/alchemy/test/Cloudflare/Addressing/ServiceBinding.test.ts +++ b/packages/alchemy/test/Cloudflare/Addressing/ServiceBinding.test.ts @@ -6,6 +6,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -33,7 +36,7 @@ const retryForbidden = ( // are available regardless of the BYOIP entitlement — on an account with no // onboarded prefixes the result is an empty, well-typed // `AddressingServiceBindingAttributes[]` (the exact shape `read` produces). -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates service bindings across prefixes (read-only)", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/AiGateway/AiGateway.test.ts b/packages/alchemy/test/Cloudflare/AiGateway/AiGateway.test.ts index 4f41b5dce..59dcfb955 100644 --- a/packages/alchemy/test/Cloudflare/AiGateway/AiGateway.test.ts +++ b/packages/alchemy/test/Cloudflare/AiGateway/AiGateway.test.ts @@ -14,6 +14,9 @@ import * as HttpClient from "effect/unstable/http/HttpClient"; import { Gateway } from "./fixtures/Gateway.ts"; import TestWorker from "./fixtures/TestWorker.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test, beforeAll, afterAll, deploy, destroy } = Test.make({ providers: Cloudflare.providers(), }); @@ -23,123 +26,129 @@ const logLevel = Effect.provideService( process.env.DEBUG ? "Debug" : "Info", ); -test.provider("create and delete ai gateway with default props", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create and delete ai gateway with default props", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - yield* stack.destroy(); + yield* stack.destroy(); - const gateway = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AiGateway("DefaultGateway", { - id: "alchemy-test-ai-gateway-default", - }); - }), - ); + const gateway = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AiGateway("DefaultGateway", { + id: "alchemy-test-ai-gateway-default", + }); + }), + ); - expect(gateway.gatewayId).toEqual("alchemy-test-ai-gateway-default"); - expect(gateway.cacheInvalidateOnUpdate).toEqual(false); - expect(gateway.cacheTtl).toEqual(null); - expect(gateway.collectLogs).toEqual(true); - expect(gateway.rateLimitingInterval).toEqual(null); - expect(gateway.rateLimitingLimit).toEqual(null); - expect(gateway.rateLimitingTechnique).toEqual("fixed"); + expect(gateway.gatewayId).toEqual("alchemy-test-ai-gateway-default"); + expect(gateway.cacheInvalidateOnUpdate).toEqual(false); + expect(gateway.cacheTtl).toEqual(null); + expect(gateway.collectLogs).toEqual(true); + expect(gateway.rateLimitingInterval).toEqual(null); + expect(gateway.rateLimitingLimit).toEqual(null); + expect(gateway.rateLimitingTechnique).toEqual("fixed"); - const actualGateway = yield* aiGateway.getAiGateway({ - accountId, - id: gateway.gatewayId, - }); - expect(actualGateway.id).toEqual(gateway.gatewayId); + const actualGateway = yield* aiGateway.getAiGateway({ + accountId, + id: gateway.gatewayId, + }); + expect(actualGateway.id).toEqual(gateway.gatewayId); - yield* stack.destroy(); + yield* stack.destroy(); - yield* waitForGatewayToBeDeleted(gateway.gatewayId, accountId); - }).pipe(logLevel), + yield* waitForGatewayToBeDeleted(gateway.gatewayId, accountId); + }).pipe(logLevel), ); -test.provider("create, update, delete ai gateway", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const gateway = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AiGateway("TestGateway", { - id: "alchemy-test-ai-gateway", - cacheTtl: 60, - collectLogs: true, - rateLimitingInterval: 60, - rateLimitingLimit: 100, - rateLimitingTechnique: "fixed", - }); - }), - ); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, update, delete ai gateway", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - const actualGateway = yield* aiGateway.getAiGateway({ - accountId, - id: gateway.gatewayId, - }); - expect(actualGateway.id).toEqual(gateway.gatewayId); - expect(actualGateway.cacheTtl).toEqual(60); - expect(actualGateway.rateLimitingLimit).toEqual(100); - - const updatedGateway = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AiGateway("TestGateway", { - id: "alchemy-test-ai-gateway", - cacheTtl: 120, - collectLogs: true, - rateLimitingInterval: 120, - rateLimitingLimit: 200, - rateLimitingTechnique: "sliding", - }); - }), - ); + yield* stack.destroy(); - const actualUpdatedGateway = yield* aiGateway.getAiGateway({ - accountId, - id: updatedGateway.gatewayId, - }); - expect(actualUpdatedGateway.cacheTtl).toEqual(120); - expect(actualUpdatedGateway.rateLimitingInterval).toEqual(120); - expect(actualUpdatedGateway.rateLimitingLimit).toEqual(200); - expect(actualUpdatedGateway.rateLimitingTechnique).toEqual("sliding"); + const gateway = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AiGateway("TestGateway", { + id: "alchemy-test-ai-gateway", + cacheTtl: 60, + collectLogs: true, + rateLimitingInterval: 60, + rateLimitingLimit: 100, + rateLimitingTechnique: "fixed", + }); + }), + ); - yield* stack.destroy(); + const actualGateway = yield* aiGateway.getAiGateway({ + accountId, + id: gateway.gatewayId, + }); + expect(actualGateway.id).toEqual(gateway.gatewayId); + expect(actualGateway.cacheTtl).toEqual(60); + expect(actualGateway.rateLimitingLimit).toEqual(100); - yield* waitForGatewayToBeDeleted(gateway.gatewayId, accountId); - }).pipe(logLevel), + const updatedGateway = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AiGateway("TestGateway", { + id: "alchemy-test-ai-gateway", + cacheTtl: 120, + collectLogs: true, + rateLimitingInterval: 120, + rateLimitingLimit: 200, + rateLimitingTechnique: "sliding", + }); + }), + ); + + const actualUpdatedGateway = yield* aiGateway.getAiGateway({ + accountId, + id: updatedGateway.gatewayId, + }); + expect(actualUpdatedGateway.cacheTtl).toEqual(120); + expect(actualUpdatedGateway.rateLimitingInterval).toEqual(120); + expect(actualUpdatedGateway.rateLimitingLimit).toEqual(200); + expect(actualUpdatedGateway.rateLimitingTechnique).toEqual("sliding"); + + yield* stack.destroy(); + + yield* waitForGatewayToBeDeleted(gateway.gatewayId, accountId); + }).pipe(logLevel), ); -test.provider("list enumerates the deployed ai gateway", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed ai gateway", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); - const gatewayId = "alchemy-test-ai-gateway-list"; + const gatewayId = "alchemy-test-ai-gateway-list"; - const deployed = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AiGateway("ListGateway", { - id: gatewayId, - }); - }), - ); + const deployed = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AiGateway("ListGateway", { + id: gatewayId, + }); + }), + ); - const provider = yield* Provider.findProvider(Cloudflare.AiGateway); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider(Cloudflare.AiGateway); + const all = yield* provider.list(); - expect(all.some((g) => g.gatewayId === deployed.gatewayId)).toBe(true); + expect(all.some((g) => g.gatewayId === deployed.gatewayId)).toBe(true); - yield* stack.destroy(); - yield* waitForGatewayToBeDeleted(deployed.gatewayId, deployed.accountId); - }).pipe(logLevel), + yield* stack.destroy(); + yield* waitForGatewayToBeDeleted(deployed.gatewayId, deployed.accountId); + }).pipe(logLevel), ); // Engine-level adoption: AI Gateways have no ownership signal (Cloudflare // doesn't expose tags on AI Gateways), so a name match in `read` is treated // as silent adoption. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "existing ai gateway (matching id) is silently adopted without --adopt", (stack) => Effect.gen(function* () { @@ -240,7 +249,7 @@ const Stack = Alchemy.Stack( const stack = beforeAll(deploy(Stack)); afterAll.skipIf(!!process.env.NO_DESTROY)(destroy(Stack)); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "deployed worker can call AiGateway binding (effect-native getUrl)", Effect.gen(function* () { const out = yield* stack; diff --git a/packages/alchemy/test/Cloudflare/AiGateway/AiGatewaySpendingLimit.test.ts b/packages/alchemy/test/Cloudflare/AiGateway/AiGatewaySpendingLimit.test.ts index b1f117cea..94c5e6d39 100644 --- a/packages/alchemy/test/Cloudflare/AiGateway/AiGatewaySpendingLimit.test.ts +++ b/packages/alchemy/test/Cloudflare/AiGateway/AiGatewaySpendingLimit.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers(), }); @@ -23,7 +26,7 @@ const logLevel = Effect.provideService( // Both cases drive the same per-account spending-limit singleton; run them serially so one case's amount doesn't leak into the other's read-back under the global concurrent test config. describe.sequential("AiGatewaySpendingLimit", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create, read-back, and delete the account spending limit", (stack) => Effect.gen(function* () { @@ -73,51 +76,53 @@ describe.sequential("AiGatewaySpendingLimit", () => { }).pipe(logLevel), ); - test.provider("update the spending limit in place", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AiGatewaySpendingLimit("SpendCap", { - amount: 100_00, // cents -> $100.00 - duration: "weekly", - strategy: "fixed", - topUp: { amount: 10_00 }, - }); - }), - ); - - const updated = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AiGatewaySpendingLimit("SpendCap", { - amount: 500_00, // cents -> $500.00 - duration: "monthly", - strategy: "sliding", - topUp: { amount: 10_00 }, - }); - }), - ); - - expect(updated.amount).toEqual(500_00); - expect(updated.duration).toEqual("monthly"); - expect(updated.strategy).toEqual("sliding"); - - const live = yield* aiGateway.getBillingSpendingLimit({ accountId }); - expect(live.config.amount).toEqual(500_00); - expect(live.config.duration).toEqual("monthly"); - - yield* stack.destroy(); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "update the spending limit in place", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AiGatewaySpendingLimit("SpendCap", { + amount: 100_00, // cents -> $100.00 + duration: "weekly", + strategy: "fixed", + topUp: { amount: 10_00 }, + }); + }), + ); + + const updated = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AiGatewaySpendingLimit("SpendCap", { + amount: 500_00, // cents -> $500.00 + duration: "monthly", + strategy: "sliding", + topUp: { amount: 10_00 }, + }); + }), + ); + + expect(updated.amount).toEqual(500_00); + expect(updated.duration).toEqual("monthly"); + expect(updated.strategy).toEqual("sliding"); + + const live = yield* aiGateway.getBillingSpendingLimit({ accountId }); + expect(live.config.amount).toEqual(500_00); + expect(live.config.duration).toEqual("monthly"); + + yield* stack.destroy(); + }).pipe(logLevel), ); // Canonical `list()` test (per-account singleton): there is no collection // API, so `list()` observes the one limit on the ambient account. Deploy a // limit, then assert it appears in the enumerated result. Bracket with // destroy() at start and end so the test is isolated and leaves no residue. - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed account spending limit", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/AiGateway/ChatPersistence.test.ts b/packages/alchemy/test/Cloudflare/AiGateway/ChatPersistence.test.ts index 200364aad..95c4892c6 100644 --- a/packages/alchemy/test/Cloudflare/AiGateway/ChatPersistence.test.ts +++ b/packages/alchemy/test/Cloudflare/AiGateway/ChatPersistence.test.ts @@ -9,6 +9,9 @@ import * as HttpClient from "effect/unstable/http/HttpClient"; import { Gateway } from "./fixtures/Gateway.ts"; import ChatPersistenceTestWorker from "./fixtures/ChatPersistenceWorker.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + // Fresh `workers.dev` URLs return non-200 (404 / 500 "Script not // found") for a few seconds while the edge propagates. Each test uses // `HttpClient.filterStatusOk(yield* HttpClient.HttpClient)` so the @@ -59,7 +62,7 @@ const retryReady = (eff: Effect.Effect) => Effect.retry({ schedule: readinessSchedule, times: 15 }), ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "first turn against a fresh thread persists user + assistant messages", Effect.gen(function* () { const out = yield* stack; @@ -83,7 +86,7 @@ test( { timeout: 180_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "persisted chat survives across DO invocations", Effect.gen(function* () { const out = yield* stack; @@ -118,7 +121,7 @@ test( { timeout: 240_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "distinct thread ids map to isolated histories", Effect.gen(function* () { const out = yield* stack; diff --git a/packages/alchemy/test/Cloudflare/AiGateway/Dataset.test.ts b/packages/alchemy/test/Cloudflare/AiGateway/Dataset.test.ts index 6cbe6fefb..74e9bbd1a 100644 --- a/packages/alchemy/test/Cloudflare/AiGateway/Dataset.test.ts +++ b/packages/alchemy/test/Cloudflare/AiGateway/Dataset.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -42,253 +45,267 @@ const expectGone = (accountId: string, gatewayId: string, datasetId: string) => // delete the gateway out from under another (yielding "Gateway ... not // found"). Run them one at a time so each owns its gateway lifecycle. describe.sequential("AiGatewayDataset", () => { - test.provider("create, update, delete a dataset on a gateway", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const initial = yield* stack.deploy( - Effect.gen(function* () { - const gateway = yield* Cloudflare.AiGateway("DatasetGateway", { - id: GATEWAY_ID, - }); - const dataset = yield* Cloudflare.AiGatewayDataset("Dataset", { - gatewayId: gateway.gatewayId, - name: "alchemy-test-dataset", - filters: [{ key: "success", operator: "eq", value: [true] }], - }); - return { gateway, dataset }; - }), - ); - - expect(initial.dataset.datasetId).toBeDefined(); - expect(initial.dataset.accountId).toEqual(accountId); - expect(initial.dataset.gatewayId).toEqual(GATEWAY_ID); - expect(initial.dataset.name).toEqual("alchemy-test-dataset"); - expect(initial.dataset.enable).toBe(true); - expect(initial.dataset.filters).toEqual([ - { key: "success", operator: "eq", value: [true] }, - ]); - - // Verify out-of-band via the API. - const live = yield* aiGateway.getDataset({ - accountId, - gatewayId: GATEWAY_ID, - id: initial.dataset.datasetId, - }); - expect(live.name).toEqual("alchemy-test-dataset"); - expect(live.enable).toBe(true); - expect(live.filters).toEqual([ - { key: "success", operator: "eq", value: [true] }, - ]); - - // Update mutable props in place — same dataset id. - const updated = yield* stack.deploy( - Effect.gen(function* () { - const gateway = yield* Cloudflare.AiGateway("DatasetGateway", { - id: GATEWAY_ID, - }); - const dataset = yield* Cloudflare.AiGatewayDataset("Dataset", { - gatewayId: gateway.gatewayId, - name: "alchemy-test-dataset-v2", - enable: false, - filters: [ - { key: "provider", operator: "eq", value: ["workers-ai"] }, - { key: "cached", operator: "eq", value: [false] }, - ], - }); - return { gateway, dataset }; - }), - ); - - expect(updated.dataset.datasetId).toEqual(initial.dataset.datasetId); - expect(updated.dataset.name).toEqual("alchemy-test-dataset-v2"); - expect(updated.dataset.enable).toBe(false); - expect(updated.dataset.filters).toHaveLength(2); - - const liveUpdated = yield* aiGateway.getDataset({ - accountId, - gatewayId: GATEWAY_ID, - id: initial.dataset.datasetId, - }); - expect(liveUpdated.name).toEqual("alchemy-test-dataset-v2"); - expect(liveUpdated.enable).toBe(false); - expect(liveUpdated.filters).toHaveLength(2); - - // Redeploying identical props is a no-op (still the same dataset). - const noop = yield* stack.deploy( - Effect.gen(function* () { - const gateway = yield* Cloudflare.AiGateway("DatasetGateway", { - id: GATEWAY_ID, - }); - const dataset = yield* Cloudflare.AiGatewayDataset("Dataset", { - gatewayId: gateway.gatewayId, - name: "alchemy-test-dataset-v2", - enable: false, - filters: [ - { key: "provider", operator: "eq", value: ["workers-ai"] }, - { key: "cached", operator: "eq", value: [false] }, - ], - }); - return { gateway, dataset }; - }), - ); - expect(noop.dataset.datasetId).toEqual(initial.dataset.datasetId); - - yield* stack.destroy(); - - yield* expectGone(accountId, GATEWAY_ID, initial.dataset.datasetId); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, update, delete a dataset on a gateway", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const initial = yield* stack.deploy( + Effect.gen(function* () { + const gateway = yield* Cloudflare.AiGateway("DatasetGateway", { + id: GATEWAY_ID, + }); + const dataset = yield* Cloudflare.AiGatewayDataset("Dataset", { + gatewayId: gateway.gatewayId, + name: "alchemy-test-dataset", + filters: [{ key: "success", operator: "eq", value: [true] }], + }); + return { gateway, dataset }; + }), + ); + + expect(initial.dataset.datasetId).toBeDefined(); + expect(initial.dataset.accountId).toEqual(accountId); + expect(initial.dataset.gatewayId).toEqual(GATEWAY_ID); + expect(initial.dataset.name).toEqual("alchemy-test-dataset"); + expect(initial.dataset.enable).toBe(true); + expect(initial.dataset.filters).toEqual([ + { key: "success", operator: "eq", value: [true] }, + ]); + + // Verify out-of-band via the API. + const live = yield* aiGateway.getDataset({ + accountId, + gatewayId: GATEWAY_ID, + id: initial.dataset.datasetId, + }); + expect(live.name).toEqual("alchemy-test-dataset"); + expect(live.enable).toBe(true); + expect(live.filters).toEqual([ + { key: "success", operator: "eq", value: [true] }, + ]); + + // Update mutable props in place — same dataset id. + const updated = yield* stack.deploy( + Effect.gen(function* () { + const gateway = yield* Cloudflare.AiGateway("DatasetGateway", { + id: GATEWAY_ID, + }); + const dataset = yield* Cloudflare.AiGatewayDataset("Dataset", { + gatewayId: gateway.gatewayId, + name: "alchemy-test-dataset-v2", + enable: false, + filters: [ + { key: "provider", operator: "eq", value: ["workers-ai"] }, + { key: "cached", operator: "eq", value: [false] }, + ], + }); + return { gateway, dataset }; + }), + ); + + expect(updated.dataset.datasetId).toEqual(initial.dataset.datasetId); + expect(updated.dataset.name).toEqual("alchemy-test-dataset-v2"); + expect(updated.dataset.enable).toBe(false); + expect(updated.dataset.filters).toHaveLength(2); + + const liveUpdated = yield* aiGateway.getDataset({ + accountId, + gatewayId: GATEWAY_ID, + id: initial.dataset.datasetId, + }); + expect(liveUpdated.name).toEqual("alchemy-test-dataset-v2"); + expect(liveUpdated.enable).toBe(false); + expect(liveUpdated.filters).toHaveLength(2); + + // Redeploying identical props is a no-op (still the same dataset). + const noop = yield* stack.deploy( + Effect.gen(function* () { + const gateway = yield* Cloudflare.AiGateway("DatasetGateway", { + id: GATEWAY_ID, + }); + const dataset = yield* Cloudflare.AiGatewayDataset("Dataset", { + gatewayId: gateway.gatewayId, + name: "alchemy-test-dataset-v2", + enable: false, + filters: [ + { key: "provider", operator: "eq", value: ["workers-ai"] }, + { key: "cached", operator: "eq", value: [false] }, + ], + }); + return { gateway, dataset }; + }), + ); + expect(noop.dataset.datasetId).toEqual(initial.dataset.datasetId); + + yield* stack.destroy(); + + yield* expectGone(accountId, GATEWAY_ID, initial.dataset.datasetId); + }).pipe(logLevel), ); - test.provider("replaces dataset when the gateway changes", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const initial = yield* stack.deploy( - Effect.gen(function* () { - const gatewayA = yield* Cloudflare.AiGateway("ReplaceGatewayA", { - id: GATEWAY_ID, - }); - yield* Cloudflare.AiGateway("ReplaceGatewayB", { - id: GATEWAY_ID_B, - }); - const dataset = yield* Cloudflare.AiGatewayDataset("ReplaceDataset", { - gatewayId: gatewayA.gatewayId, - name: "alchemy-test-dataset-replace", - filters: [], - }); - return { dataset }; - }), - ); - expect(initial.dataset.gatewayId).toEqual(GATEWAY_ID); - - // Moving the dataset to another gateway is a replacement: new id, - // new parent, and the old dataset is removed from gateway A. - const moved = yield* stack.deploy( - Effect.gen(function* () { - yield* Cloudflare.AiGateway("ReplaceGatewayA", { - id: GATEWAY_ID, - }); - const gatewayB = yield* Cloudflare.AiGateway("ReplaceGatewayB", { - id: GATEWAY_ID_B, - }); - const dataset = yield* Cloudflare.AiGatewayDataset("ReplaceDataset", { - gatewayId: gatewayB.gatewayId, - name: "alchemy-test-dataset-replace", - filters: [], - }); - return { dataset }; - }), - ); - - expect(moved.dataset.gatewayId).toEqual(GATEWAY_ID_B); - expect(moved.dataset.datasetId).not.toEqual(initial.dataset.datasetId); - - // The replaced dataset is gone from gateway A. - yield* expectGone(accountId, GATEWAY_ID, initial.dataset.datasetId); - - yield* stack.destroy(); - - yield* expectGone(accountId, GATEWAY_ID_B, moved.dataset.datasetId); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "replaces dataset when the gateway changes", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const initial = yield* stack.deploy( + Effect.gen(function* () { + const gatewayA = yield* Cloudflare.AiGateway("ReplaceGatewayA", { + id: GATEWAY_ID, + }); + yield* Cloudflare.AiGateway("ReplaceGatewayB", { + id: GATEWAY_ID_B, + }); + const dataset = yield* Cloudflare.AiGatewayDataset( + "ReplaceDataset", + { + gatewayId: gatewayA.gatewayId, + name: "alchemy-test-dataset-replace", + filters: [], + }, + ); + return { dataset }; + }), + ); + expect(initial.dataset.gatewayId).toEqual(GATEWAY_ID); + + // Moving the dataset to another gateway is a replacement: new id, + // new parent, and the old dataset is removed from gateway A. + const moved = yield* stack.deploy( + Effect.gen(function* () { + yield* Cloudflare.AiGateway("ReplaceGatewayA", { + id: GATEWAY_ID, + }); + const gatewayB = yield* Cloudflare.AiGateway("ReplaceGatewayB", { + id: GATEWAY_ID_B, + }); + const dataset = yield* Cloudflare.AiGatewayDataset( + "ReplaceDataset", + { + gatewayId: gatewayB.gatewayId, + name: "alchemy-test-dataset-replace", + filters: [], + }, + ); + return { dataset }; + }), + ); + + expect(moved.dataset.gatewayId).toEqual(GATEWAY_ID_B); + expect(moved.dataset.datasetId).not.toEqual(initial.dataset.datasetId); + + // The replaced dataset is gone from gateway A. + yield* expectGone(accountId, GATEWAY_ID, initial.dataset.datasetId); + + yield* stack.destroy(); + + yield* expectGone(accountId, GATEWAY_ID_B, moved.dataset.datasetId); + }).pipe(logLevel), ); - test.provider("recreates a dataset after out-of-band delete", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const initial = yield* stack.deploy( - Effect.gen(function* () { - const gateway = yield* Cloudflare.AiGateway("HealGateway", { - id: GATEWAY_ID, - }); - const dataset = yield* Cloudflare.AiGatewayDataset("HealDataset", { - gatewayId: gateway.gatewayId, - name: "alchemy-test-dataset-heal", - filters: [{ key: "success", operator: "eq", value: [true] }], - }); - return { dataset }; - }), - ); - - // Delete the dataset out-of-band. A redeploy with identical props is a - // planner no-op, so change a prop to force reconcile — it must observe - // the dataset as missing and recreate it instead of failing on a 404. - yield* aiGateway.deleteDataset({ - accountId, - gatewayId: GATEWAY_ID, - id: initial.dataset.datasetId, - }); - - const healed = yield* stack.deploy( - Effect.gen(function* () { - const gateway = yield* Cloudflare.AiGateway("HealGateway", { - id: GATEWAY_ID, - }); - const dataset = yield* Cloudflare.AiGatewayDataset("HealDataset", { - gatewayId: gateway.gatewayId, - name: "alchemy-test-dataset-heal", - enable: false, - filters: [{ key: "success", operator: "eq", value: [true] }], - }); - return { dataset }; - }), - ); - - expect(healed.dataset.datasetId).not.toEqual(initial.dataset.datasetId); - expect(healed.dataset.enable).toBe(false); - - const live = yield* aiGateway.getDataset({ - accountId, - gatewayId: GATEWAY_ID, - id: healed.dataset.datasetId, - }); - expect(live.name).toEqual("alchemy-test-dataset-heal"); - - yield* stack.destroy(); - - yield* expectGone(accountId, GATEWAY_ID, healed.dataset.datasetId); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "recreates a dataset after out-of-band delete", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const initial = yield* stack.deploy( + Effect.gen(function* () { + const gateway = yield* Cloudflare.AiGateway("HealGateway", { + id: GATEWAY_ID, + }); + const dataset = yield* Cloudflare.AiGatewayDataset("HealDataset", { + gatewayId: gateway.gatewayId, + name: "alchemy-test-dataset-heal", + filters: [{ key: "success", operator: "eq", value: [true] }], + }); + return { dataset }; + }), + ); + + // Delete the dataset out-of-band. A redeploy with identical props is a + // planner no-op, so change a prop to force reconcile — it must observe + // the dataset as missing and recreate it instead of failing on a 404. + yield* aiGateway.deleteDataset({ + accountId, + gatewayId: GATEWAY_ID, + id: initial.dataset.datasetId, + }); + + const healed = yield* stack.deploy( + Effect.gen(function* () { + const gateway = yield* Cloudflare.AiGateway("HealGateway", { + id: GATEWAY_ID, + }); + const dataset = yield* Cloudflare.AiGatewayDataset("HealDataset", { + gatewayId: gateway.gatewayId, + name: "alchemy-test-dataset-heal", + enable: false, + filters: [{ key: "success", operator: "eq", value: [true] }], + }); + return { dataset }; + }), + ); + + expect(healed.dataset.datasetId).not.toEqual(initial.dataset.datasetId); + expect(healed.dataset.enable).toBe(false); + + const live = yield* aiGateway.getDataset({ + accountId, + gatewayId: GATEWAY_ID, + id: healed.dataset.datasetId, + }); + expect(live.name).toEqual("alchemy-test-dataset-heal"); + + yield* stack.destroy(); + + yield* expectGone(accountId, GATEWAY_ID, healed.dataset.datasetId); + }).pipe(logLevel), ); - test.provider("list enumerates datasets across gateways", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const deployed = yield* stack.deploy( - Effect.gen(function* () { - const gateway = yield* Cloudflare.AiGateway("ListGateway", { - id: GATEWAY_ID, - }); - const dataset = yield* Cloudflare.AiGatewayDataset("ListDataset", { - gatewayId: gateway.gatewayId, - name: "alchemy-test-dataset-list", - filters: [{ key: "success", operator: "eq", value: [true] }], - }); - return { dataset }; - }), - ); - - const provider = yield* Provider.findProvider( - Cloudflare.AiGatewayDataset, - ); - const all = yield* provider.list(); - - expect(all.some((d) => d.datasetId === deployed.dataset.datasetId)).toBe( - true, - ); - - yield* stack.destroy(); - - yield* expectGone(accountId, GATEWAY_ID, deployed.dataset.datasetId); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates datasets across gateways", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const deployed = yield* stack.deploy( + Effect.gen(function* () { + const gateway = yield* Cloudflare.AiGateway("ListGateway", { + id: GATEWAY_ID, + }); + const dataset = yield* Cloudflare.AiGatewayDataset("ListDataset", { + gatewayId: gateway.gatewayId, + name: "alchemy-test-dataset-list", + filters: [{ key: "success", operator: "eq", value: [true] }], + }); + return { dataset }; + }), + ); + + const provider = yield* Provider.findProvider( + Cloudflare.AiGatewayDataset, + ); + const all = yield* provider.list(); + + expect( + all.some((d) => d.datasetId === deployed.dataset.datasetId), + ).toBe(true); + + yield* stack.destroy(); + + yield* expectGone(accountId, GATEWAY_ID, deployed.dataset.datasetId); + }).pipe(logLevel), ); }); diff --git a/packages/alchemy/test/Cloudflare/AiGateway/DynamicRouting.test.ts b/packages/alchemy/test/Cloudflare/AiGateway/DynamicRouting.test.ts index c7ef34fe1..15a594190 100644 --- a/packages/alchemy/test/Cloudflare/AiGateway/DynamicRouting.test.ts +++ b/packages/alchemy/test/Cloudflare/AiGateway/DynamicRouting.test.ts @@ -10,6 +10,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -64,7 +67,7 @@ const expectGone = (accountId: string, gatewayId: string, routeId: string) => Effect.catchTag("GatewayNotFound", () => Effect.void), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create, update elements (new deployed version), rename, delete", (stack) => Effect.gen(function* () { @@ -167,165 +170,174 @@ test.provider( }).pipe(logLevel), ); -test.provider("replaces route when the gateway changes", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const initial = yield* stack.deploy( - Effect.gen(function* () { - const gatewayA = yield* Cloudflare.AiGateway("RouteGatewayA", { - id: GATEWAY_ID, - }); - yield* Cloudflare.AiGateway("RouteGatewayB", { - id: GATEWAY_ID_B, - }); - const route = yield* Cloudflare.AiGatewayDynamicRouting( - "ReplaceRoute", - { - gatewayId: gatewayA.gatewayId, - name: "alchemy-test-route-replace", - elements: graph("@cf/meta/llama-3.1-8b-instruct", 1), - }, - ); - return { route }; - }), - ); - expect(initial.route.gatewayId).toEqual(GATEWAY_ID); - - // Moving the route to another gateway is a replacement: new id, new - // parent, and the old route is removed from gateway A. - const moved = yield* stack.deploy( - Effect.gen(function* () { - yield* Cloudflare.AiGateway("RouteGatewayA", { - id: GATEWAY_ID, - }); - const gatewayB = yield* Cloudflare.AiGateway("RouteGatewayB", { - id: GATEWAY_ID_B, - }); - const route = yield* Cloudflare.AiGatewayDynamicRouting( - "ReplaceRoute", - { - gatewayId: gatewayB.gatewayId, - name: "alchemy-test-route-replace", - elements: graph("@cf/meta/llama-3.1-8b-instruct", 1), - }, - ); - return { route }; - }), - ); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "replaces route when the gateway changes", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const initial = yield* stack.deploy( + Effect.gen(function* () { + const gatewayA = yield* Cloudflare.AiGateway("RouteGatewayA", { + id: GATEWAY_ID, + }); + yield* Cloudflare.AiGateway("RouteGatewayB", { + id: GATEWAY_ID_B, + }); + const route = yield* Cloudflare.AiGatewayDynamicRouting( + "ReplaceRoute", + { + gatewayId: gatewayA.gatewayId, + name: "alchemy-test-route-replace", + elements: graph("@cf/meta/llama-3.1-8b-instruct", 1), + }, + ); + return { route }; + }), + ); + expect(initial.route.gatewayId).toEqual(GATEWAY_ID); + + // Moving the route to another gateway is a replacement: new id, new + // parent, and the old route is removed from gateway A. + const moved = yield* stack.deploy( + Effect.gen(function* () { + yield* Cloudflare.AiGateway("RouteGatewayA", { + id: GATEWAY_ID, + }); + const gatewayB = yield* Cloudflare.AiGateway("RouteGatewayB", { + id: GATEWAY_ID_B, + }); + const route = yield* Cloudflare.AiGatewayDynamicRouting( + "ReplaceRoute", + { + gatewayId: gatewayB.gatewayId, + name: "alchemy-test-route-replace", + elements: graph("@cf/meta/llama-3.1-8b-instruct", 1), + }, + ); + return { route }; + }), + ); - expect(moved.route.gatewayId).toEqual(GATEWAY_ID_B); - expect(moved.route.routeId).not.toEqual(initial.route.routeId); + expect(moved.route.gatewayId).toEqual(GATEWAY_ID_B); + expect(moved.route.routeId).not.toEqual(initial.route.routeId); - // The replaced route is gone from gateway A. - yield* expectGone(accountId, GATEWAY_ID, initial.route.routeId); + // The replaced route is gone from gateway A. + yield* expectGone(accountId, GATEWAY_ID, initial.route.routeId); - yield* stack.destroy(); + yield* stack.destroy(); - yield* expectGone(accountId, GATEWAY_ID_B, moved.route.routeId); - }).pipe(logLevel), + yield* expectGone(accountId, GATEWAY_ID_B, moved.route.routeId); + }).pipe(logLevel), ); -test.provider("recreates a route after out-of-band delete", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const initial = yield* stack.deploy( - Effect.gen(function* () { - const gateway = yield* Cloudflare.AiGateway("HealRouteGateway", { - id: GATEWAY_ID, - }); - const route = yield* Cloudflare.AiGatewayDynamicRouting("HealRoute", { - gatewayId: gateway.gatewayId, - name: "alchemy-test-route-heal", - elements: graph("@cf/meta/llama-3.1-8b-instruct", 1), - }); - return { route }; - }), - ); - - // Delete the route out-of-band. A redeploy with identical props is a - // planner no-op, so change a prop to force reconcile — it must observe - // the route as missing and recreate it instead of failing on a 404. - yield* aiGateway.deleteDynamicRouting({ - accountId, - gatewayId: GATEWAY_ID, - id: initial.route.routeId, - }); - - const healed = yield* stack.deploy( - Effect.gen(function* () { - const gateway = yield* Cloudflare.AiGateway("HealRouteGateway", { - id: GATEWAY_ID, - }); - const route = yield* Cloudflare.AiGatewayDynamicRouting("HealRoute", { - gatewayId: gateway.gatewayId, - name: "alchemy-test-route-heal", - elements: graph("@cf/meta/llama-3.1-8b-instruct", 3), - }); - return { route }; - }), - ); - - expect(healed.route.routeId).not.toEqual(initial.route.routeId); - expect(healed.route.elements).toEqual( - graph("@cf/meta/llama-3.1-8b-instruct", 3), - ); - - yield* stack.destroy(); - - yield* expectGone(accountId, GATEWAY_ID, healed.route.routeId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "recreates a route after out-of-band delete", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const initial = yield* stack.deploy( + Effect.gen(function* () { + const gateway = yield* Cloudflare.AiGateway("HealRouteGateway", { + id: GATEWAY_ID, + }); + const route = yield* Cloudflare.AiGatewayDynamicRouting("HealRoute", { + gatewayId: gateway.gatewayId, + name: "alchemy-test-route-heal", + elements: graph("@cf/meta/llama-3.1-8b-instruct", 1), + }); + return { route }; + }), + ); + + // Delete the route out-of-band. A redeploy with identical props is a + // planner no-op, so change a prop to force reconcile — it must observe + // the route as missing and recreate it instead of failing on a 404. + yield* aiGateway.deleteDynamicRouting({ + accountId, + gatewayId: GATEWAY_ID, + id: initial.route.routeId, + }); + + const healed = yield* stack.deploy( + Effect.gen(function* () { + const gateway = yield* Cloudflare.AiGateway("HealRouteGateway", { + id: GATEWAY_ID, + }); + const route = yield* Cloudflare.AiGatewayDynamicRouting("HealRoute", { + gatewayId: gateway.gatewayId, + name: "alchemy-test-route-heal", + elements: graph("@cf/meta/llama-3.1-8b-instruct", 3), + }); + return { route }; + }), + ); + + expect(healed.route.routeId).not.toEqual(initial.route.routeId); + expect(healed.route.elements).toEqual( + graph("@cf/meta/llama-3.1-8b-instruct", 3), + ); + + yield* stack.destroy(); + + yield* expectGone(accountId, GATEWAY_ID, healed.route.routeId); + }).pipe(logLevel), ); // Canonical `list()` test (parent fan-out): routes are scoped under a gateway // with no account-wide route API, so `list()` enumerates every account gateway // and exhaustively lists each gateway's routes. Deploy a gateway + route, then // assert the deployed route appears in the exhaustively-paginated result. -test.provider("list enumerates routes across all gateways", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const deployed = yield* stack.deploy( - Effect.gen(function* () { - const gateway = yield* Cloudflare.AiGateway("ListRouteGateway", { - id: GATEWAY_ID, - }); - const route = yield* Cloudflare.AiGatewayDynamicRouting("ListRoute", { - gatewayId: gateway.gatewayId, - name: "alchemy-test-route-list", - elements: graph("@cf/meta/llama-3.1-8b-instruct", 1), - }); - return { route }; - }), - ); - - const provider = yield* Provider.findProvider( - Cloudflare.AiGatewayDynamicRouting, - ); - - // The route appears in list() shortly after deploy, but its element graph - // is materialized from a separate deployed-version lookup that propagates - // with its own eventual-consistency lag. Poll until the route is present - // AND its graph has propagated before asserting. - const all = yield* poll({ - description: "list() includes the deployed route with its element graph", - effect: provider.list(), - predicate: (all) => - (all.find((r) => r.routeId === deployed.route.routeId)?.elements - ?.length ?? 0) > 0, - }); - - const found = all.find((r) => r.routeId === deployed.route.routeId); - expect(found).toBeDefined(); - expect(found?.gatewayId).toEqual(GATEWAY_ID); - expect(found?.name).toEqual("alchemy-test-route-list"); - expect(found?.elements).toEqual(graph("@cf/meta/llama-3.1-8b-instruct", 1)); - - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates routes across all gateways", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const deployed = yield* stack.deploy( + Effect.gen(function* () { + const gateway = yield* Cloudflare.AiGateway("ListRouteGateway", { + id: GATEWAY_ID, + }); + const route = yield* Cloudflare.AiGatewayDynamicRouting("ListRoute", { + gatewayId: gateway.gatewayId, + name: "alchemy-test-route-list", + elements: graph("@cf/meta/llama-3.1-8b-instruct", 1), + }); + return { route }; + }), + ); + + const provider = yield* Provider.findProvider( + Cloudflare.AiGatewayDynamicRouting, + ); + + // The route appears in list() shortly after deploy, but its element graph + // is materialized from a separate deployed-version lookup that propagates + // with its own eventual-consistency lag. Poll until the route is present + // AND its graph has propagated before asserting. + const all = yield* poll({ + description: + "list() includes the deployed route with its element graph", + effect: provider.list(), + predicate: (all) => + (all.find((r) => r.routeId === deployed.route.routeId)?.elements + ?.length ?? 0) > 0, + }); + + const found = all.find((r) => r.routeId === deployed.route.routeId); + expect(found).toBeDefined(); + expect(found?.gatewayId).toEqual(GATEWAY_ID); + expect(found?.name).toEqual("alchemy-test-route-list"); + expect(found?.elements).toEqual( + graph("@cf/meta/llama-3.1-8b-instruct", 1), + ); + + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/AiGateway/Evaluation.test.ts b/packages/alchemy/test/Cloudflare/AiGateway/Evaluation.test.ts index 72bbadd16..87f14ed70 100644 --- a/packages/alchemy/test/Cloudflare/AiGateway/Evaluation.test.ts +++ b/packages/alchemy/test/Cloudflare/AiGateway/Evaluation.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -40,127 +43,141 @@ const expectGone = ( Effect.catchTag("EvaluationNotFound", () => Effect.void), ); -test.provider("create, noop, replace, delete an evaluation", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - // The mandatory evaluation types (speed, cost) are account-global - // constants — discover their ids out-of-band. - const types = yield* Cloudflare.listEvaluationTypes(accountId); - const typeIds = types - .filter((t) => t.mandatory) - .map((t) => t.id) - .sort(); - expect(typeIds.length).toBeGreaterThan(0); - - const program = (name: string) => - Effect.gen(function* () { - const gateway = yield* Cloudflare.AiGateway("EvalGateway", { - id: GATEWAY_ID, - }); - const dataset = yield* Cloudflare.AiGatewayDataset("EvalDataset", { - gatewayId: gateway.gatewayId, - name: "alchemy-test-eval-dataset", - filters: [{ key: "success", operator: "eq", value: [true] }], - }); - const evaluation = yield* Cloudflare.AiGatewayEvaluation("Evaluation", { - gatewayId: gateway.gatewayId, - name, - datasetIds: [dataset.datasetId], - evaluationTypeIds: typeIds, +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, noop, replace, delete an evaluation", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + // The mandatory evaluation types (speed, cost) are account-global + // constants — discover their ids out-of-band. + const types = yield* Cloudflare.listEvaluationTypes(accountId); + const typeIds = types + .filter((t) => t.mandatory) + .map((t) => t.id) + .sort(); + expect(typeIds.length).toBeGreaterThan(0); + + const program = (name: string) => + Effect.gen(function* () { + const gateway = yield* Cloudflare.AiGateway("EvalGateway", { + id: GATEWAY_ID, + }); + const dataset = yield* Cloudflare.AiGatewayDataset("EvalDataset", { + gatewayId: gateway.gatewayId, + name: "alchemy-test-eval-dataset", + filters: [{ key: "success", operator: "eq", value: [true] }], + }); + const evaluation = yield* Cloudflare.AiGatewayEvaluation( + "Evaluation", + { + gatewayId: gateway.gatewayId, + name, + datasetIds: [dataset.datasetId], + evaluationTypeIds: typeIds, + }, + ); + return { dataset, evaluation }; }); - return { dataset, evaluation }; - }); - const initial = yield* stack.deploy(program("alchemy-test-eval")); - - expect(initial.evaluation.evaluationId).toBeDefined(); - expect(initial.evaluation.accountId).toEqual(accountId); - expect(initial.evaluation.gatewayId).toEqual(GATEWAY_ID); - expect(initial.evaluation.name).toEqual("alchemy-test-eval"); - expect(initial.evaluation.datasetIds).toEqual([initial.dataset.datasetId]); - expect([...initial.evaluation.evaluationTypeIds].sort()).toEqual(typeIds); - expect(initial.evaluation.totalLogs).toEqual(0); - - // Verify out-of-band via the API. - const live = yield* aiGateway.getEvaluation({ - accountId, - gatewayId: GATEWAY_ID, - id: initial.evaluation.evaluationId, - }); - expect(live.name).toEqual("alchemy-test-eval"); - expect(live.datasets.map((d) => d.id)).toEqual([initial.dataset.datasetId]); - - // Redeploying identical props is a no-op (still the same evaluation). - const noop = yield* stack.deploy(program("alchemy-test-eval")); - expect(noop.evaluation.evaluationId).toEqual( - initial.evaluation.evaluationId, - ); - - // Evaluations are create-only — renaming is a replacement. - const renamed = yield* stack.deploy(program("alchemy-test-eval-v2")); - expect(renamed.evaluation.name).toEqual("alchemy-test-eval-v2"); - expect(renamed.evaluation.evaluationId).not.toEqual( - initial.evaluation.evaluationId, - ); - - // The replaced evaluation is gone. - yield* expectGone(accountId, GATEWAY_ID, initial.evaluation.evaluationId); - - yield* stack.destroy(); - - yield* expectGone(accountId, GATEWAY_ID, renamed.evaluation.evaluationId); - }).pipe(logLevel), + const initial = yield* stack.deploy(program("alchemy-test-eval")); + + expect(initial.evaluation.evaluationId).toBeDefined(); + expect(initial.evaluation.accountId).toEqual(accountId); + expect(initial.evaluation.gatewayId).toEqual(GATEWAY_ID); + expect(initial.evaluation.name).toEqual("alchemy-test-eval"); + expect(initial.evaluation.datasetIds).toEqual([ + initial.dataset.datasetId, + ]); + expect([...initial.evaluation.evaluationTypeIds].sort()).toEqual(typeIds); + expect(initial.evaluation.totalLogs).toEqual(0); + + // Verify out-of-band via the API. + const live = yield* aiGateway.getEvaluation({ + accountId, + gatewayId: GATEWAY_ID, + id: initial.evaluation.evaluationId, + }); + expect(live.name).toEqual("alchemy-test-eval"); + expect(live.datasets.map((d) => d.id)).toEqual([ + initial.dataset.datasetId, + ]); + + // Redeploying identical props is a no-op (still the same evaluation). + const noop = yield* stack.deploy(program("alchemy-test-eval")); + expect(noop.evaluation.evaluationId).toEqual( + initial.evaluation.evaluationId, + ); + + // Evaluations are create-only — renaming is a replacement. + const renamed = yield* stack.deploy(program("alchemy-test-eval-v2")); + expect(renamed.evaluation.name).toEqual("alchemy-test-eval-v2"); + expect(renamed.evaluation.evaluationId).not.toEqual( + initial.evaluation.evaluationId, + ); + + // The replaced evaluation is gone. + yield* expectGone(accountId, GATEWAY_ID, initial.evaluation.evaluationId); + + yield* stack.destroy(); + + yield* expectGone(accountId, GATEWAY_ID, renamed.evaluation.evaluationId); + }).pipe(logLevel), ); -test.provider("list enumerates the deployed evaluation", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const types = yield* Cloudflare.listEvaluationTypes(accountId); - const typeIds = types - .filter((t) => t.mandatory) - .map((t) => t.id) - .sort(); - expect(typeIds.length).toBeGreaterThan(0); - - const deployed = yield* stack.deploy( - Effect.gen(function* () { - const gateway = yield* Cloudflare.AiGateway("EvalGateway", { - id: GATEWAY_ID, - }); - const dataset = yield* Cloudflare.AiGatewayDataset("EvalDataset", { - gatewayId: gateway.gatewayId, - name: "alchemy-test-eval-list-dataset", - filters: [{ key: "success", operator: "eq", value: [true] }], - }); - const evaluation = yield* Cloudflare.AiGatewayEvaluation("Evaluation", { - gatewayId: gateway.gatewayId, - name: "alchemy-test-eval-list", - datasetIds: [dataset.datasetId], - evaluationTypeIds: typeIds, - }); - return { evaluation }; - }), - ); - - const provider = yield* Provider.findProvider( - Cloudflare.AiGatewayEvaluation, - ); - const all = yield* provider.list(); - - const found = all.find( - (e) => e.evaluationId === deployed.evaluation.evaluationId, - ); - expect(found).toBeDefined(); - expect(found?.accountId).toEqual(accountId); - expect(found?.gatewayId).toEqual(GATEWAY_ID); - expect(found?.name).toEqual("alchemy-test-eval-list"); - - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed evaluation", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const types = yield* Cloudflare.listEvaluationTypes(accountId); + const typeIds = types + .filter((t) => t.mandatory) + .map((t) => t.id) + .sort(); + expect(typeIds.length).toBeGreaterThan(0); + + const deployed = yield* stack.deploy( + Effect.gen(function* () { + const gateway = yield* Cloudflare.AiGateway("EvalGateway", { + id: GATEWAY_ID, + }); + const dataset = yield* Cloudflare.AiGatewayDataset("EvalDataset", { + gatewayId: gateway.gatewayId, + name: "alchemy-test-eval-list-dataset", + filters: [{ key: "success", operator: "eq", value: [true] }], + }); + const evaluation = yield* Cloudflare.AiGatewayEvaluation( + "Evaluation", + { + gatewayId: gateway.gatewayId, + name: "alchemy-test-eval-list", + datasetIds: [dataset.datasetId], + evaluationTypeIds: typeIds, + }, + ); + return { evaluation }; + }), + ); + + const provider = yield* Provider.findProvider( + Cloudflare.AiGatewayEvaluation, + ); + const all = yield* provider.list(); + + const found = all.find( + (e) => e.evaluationId === deployed.evaluation.evaluationId, + ); + expect(found).toBeDefined(); + expect(found?.accountId).toEqual(accountId); + expect(found?.gatewayId).toEqual(GATEWAY_ID); + expect(found?.name).toEqual("alchemy-test-eval-list"); + + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/AiGateway/LanguageModel.test.ts b/packages/alchemy/test/Cloudflare/AiGateway/LanguageModel.test.ts index bae7bf549..1d86bfe06 100644 --- a/packages/alchemy/test/Cloudflare/AiGateway/LanguageModel.test.ts +++ b/packages/alchemy/test/Cloudflare/AiGateway/LanguageModel.test.ts @@ -11,6 +11,9 @@ import * as HttpClient from "effect/unstable/http/HttpClient"; import { Gateway } from "./fixtures/Gateway.ts"; import LanguageModelTestWorker from "./fixtures/LanguageModelWorker.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + // Fresh `workers.dev` URLs return non-200 (404 / 500 "Script not // found") for a few seconds while the edge propagates. Each test uses // `HttpClient.filterStatusOk(yield* HttpClient.HttpClient)` so the @@ -64,7 +67,7 @@ const parseSse = (sse: string): ReadonlyArray => .filter((line) => line.length > 0) .map((line) => JSON.parse(line) as StreamPart); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "deployed worker generates text via AiGateway-backed LanguageModel", Effect.gen(function* () { const out = yield* stack; @@ -102,7 +105,7 @@ test( { timeout: 180_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "deployed worker streams text via AiGateway-backed LanguageModel", Effect.gen(function* () { const out = yield* stack; @@ -132,7 +135,7 @@ test( { timeout: 180_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "streamed parts respect ordering: text-start → text-delta+ → text-end → finish", Effect.gen(function* () { const out = yield* stack; @@ -179,7 +182,7 @@ test( { timeout: 180_000 }, ); -test.skipIf(!process.env.DEBUG_RAW_STREAM)( +test.skipIf(!process.env.DEBUG_RAW_STREAM || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "DEBUG: dump raw Workers AI SSE stream", Effect.gen(function* () { const out = yield* stack; @@ -220,7 +223,7 @@ test.skipIf(!process.env.DEBUG_RAW_STREAM)( { timeout: 180_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "stream finish part reports the real token counts and a `stop` reason", Effect.gen(function* () { const out = yield* stack; @@ -253,7 +256,7 @@ test( { timeout: 180_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "stream emits multiple text-delta chunks for a long-form response", Effect.gen(function* () { const out = yield* stack; @@ -289,7 +292,7 @@ test( // /chat?id=... route which is wired by `ChatAgent` from the Agent // slice. The fixture in this slice (`LanguageModelWorker`) does not // include that route — the test re-activates in the Agent PR. -test.skip( +test.skip.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "persisted chat survives across DO invocations", Effect.gen(function* () { const out = yield* stack; @@ -332,7 +335,7 @@ test.skip( { timeout: 240_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "deployed worker invokes a tool via AiGateway-backed LanguageModel", Effect.gen(function* () { const out = yield* stack; @@ -387,7 +390,7 @@ test( { timeout: 180_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "streams tool-call parts via AiGateway-backed LanguageModel", Effect.gen(function* () { const out = yield* stack; @@ -446,7 +449,7 @@ test( { timeout: 180_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "concatenated tool-params-delta payloads parse back into the requested arguments", Effect.gen(function* () { const out = yield* stack; @@ -485,7 +488,7 @@ test( { timeout: 180_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "streams Effect-native parts and prints them live", Effect.gen(function* () { const out = yield* stack; diff --git a/packages/alchemy/test/Cloudflare/AiGateway/ProviderConfig.test.ts b/packages/alchemy/test/Cloudflare/AiGateway/ProviderConfig.test.ts index 512c9beb0..be497a8ec 100644 --- a/packages/alchemy/test/Cloudflare/AiGateway/ProviderConfig.test.ts +++ b/packages/alchemy/test/Cloudflare/AiGateway/ProviderConfig.test.ts @@ -10,6 +10,9 @@ import * as Redacted from "effect/Redacted"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -57,141 +60,145 @@ const expectGone = ( }), ); -test.provider("create, noop, replace, delete a BYOK provider config", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const program = (rateLimit?: number) => - Effect.gen(function* () { - // Cloudflare allows a single Secrets Store per account — the - // resource adopts the existing one and never deletes it. - const store = yield* Cloudflare.SecretsStore("PcStore"); - const gateway = yield* Cloudflare.AiGateway("PcGateway", { - id: LIFECYCLE_GATEWAY_ID, - // BYOK resolution requires the gateway to reference the store. - storeId: store.storeId, - }); - const secret = yield* Cloudflare.Secret("PcSecret", { - store, - name: secretName(LIFECYCLE_GATEWAY_ID), - value: Redacted.make(SECRET_VALUE), - scopes: ["ai_gateway"], - }); - const config = yield* Cloudflare.AiGatewayProviderConfig("Byok", { - gatewayId: gateway.gatewayId, - providerSlug: PROVIDER_SLUG, - alias: ALIAS, - secretId: secret.secretId, - defaultConfig: true, - ...(rateLimit !== undefined && { - rateLimit, - rateLimitPeriod: 60, - }), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, noop, replace, delete a BYOK provider config", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const program = (rateLimit?: number) => + Effect.gen(function* () { + // Cloudflare allows a single Secrets Store per account — the + // resource adopts the existing one and never deletes it. + const store = yield* Cloudflare.SecretsStore("PcStore"); + const gateway = yield* Cloudflare.AiGateway("PcGateway", { + id: LIFECYCLE_GATEWAY_ID, + // BYOK resolution requires the gateway to reference the store. + storeId: store.storeId, + }); + const secret = yield* Cloudflare.Secret("PcSecret", { + store, + name: secretName(LIFECYCLE_GATEWAY_ID), + value: Redacted.make(SECRET_VALUE), + scopes: ["ai_gateway"], + }); + const config = yield* Cloudflare.AiGatewayProviderConfig("Byok", { + gatewayId: gateway.gatewayId, + providerSlug: PROVIDER_SLUG, + alias: ALIAS, + secretId: secret.secretId, + defaultConfig: true, + ...(rateLimit !== undefined && { + rateLimit, + rateLimitPeriod: 60, + }), + }); + return { secret, config }; }); - return { secret, config }; - }); - const initial = yield* stack.deploy(program()); - - expect(initial.config.providerConfigId).toBeDefined(); - expect(initial.config.accountId).toEqual(accountId); - expect(initial.config.gatewayId).toEqual(LIFECYCLE_GATEWAY_ID); - expect(initial.config.providerSlug).toEqual(PROVIDER_SLUG); - expect(initial.config.alias).toEqual(ALIAS); - expect(initial.config.secretId).toEqual(initial.secret.secretId); - expect(initial.config.defaultConfig).toBe(true); - expect(initial.config.rateLimit).toBeUndefined(); - - // Verify out-of-band via the API. - const live = yield* aiGateway.listProviderConfigs({ - accountId, - gatewayId: LIFECYCLE_GATEWAY_ID, - perPage: 50, - }); - const liveConfig = live.result.find( - (c) => c.id === initial.config.providerConfigId, - ); - expect(liveConfig).toBeDefined(); - expect(liveConfig!.alias).toEqual(ALIAS); - expect(liveConfig!.providerSlug).toEqual(PROVIDER_SLUG); - expect(liveConfig!.secretId).toEqual(initial.secret.secretId); - - // Redeploying identical props is a no-op (still the same config). - const noop = yield* stack.deploy(program()); - expect(noop.config.providerConfigId).toEqual( - initial.config.providerConfigId, - ); - - // Provider configs have no update API — adding a rate limit is a - // delete-first replacement (same provider slug + alias). - const limited = yield* stack.deploy(program(100)); - expect(limited.config.providerConfigId).not.toEqual( - initial.config.providerConfigId, - ); - expect(limited.config.rateLimit).toEqual(100); - expect(limited.config.rateLimitPeriod).toEqual(60); - - // The replaced config is gone. - yield* expectGone( - accountId, - LIFECYCLE_GATEWAY_ID, - initial.config.providerConfigId, - ); - - yield* stack.destroy(); - - yield* expectGone( - accountId, - LIFECYCLE_GATEWAY_ID, - limited.config.providerConfigId, - ); - }).pipe(logLevel), + const initial = yield* stack.deploy(program()); + + expect(initial.config.providerConfigId).toBeDefined(); + expect(initial.config.accountId).toEqual(accountId); + expect(initial.config.gatewayId).toEqual(LIFECYCLE_GATEWAY_ID); + expect(initial.config.providerSlug).toEqual(PROVIDER_SLUG); + expect(initial.config.alias).toEqual(ALIAS); + expect(initial.config.secretId).toEqual(initial.secret.secretId); + expect(initial.config.defaultConfig).toBe(true); + expect(initial.config.rateLimit).toBeUndefined(); + + // Verify out-of-band via the API. + const live = yield* aiGateway.listProviderConfigs({ + accountId, + gatewayId: LIFECYCLE_GATEWAY_ID, + perPage: 50, + }); + const liveConfig = live.result.find( + (c) => c.id === initial.config.providerConfigId, + ); + expect(liveConfig).toBeDefined(); + expect(liveConfig!.alias).toEqual(ALIAS); + expect(liveConfig!.providerSlug).toEqual(PROVIDER_SLUG); + expect(liveConfig!.secretId).toEqual(initial.secret.secretId); + + // Redeploying identical props is a no-op (still the same config). + const noop = yield* stack.deploy(program()); + expect(noop.config.providerConfigId).toEqual( + initial.config.providerConfigId, + ); + + // Provider configs have no update API — adding a rate limit is a + // delete-first replacement (same provider slug + alias). + const limited = yield* stack.deploy(program(100)); + expect(limited.config.providerConfigId).not.toEqual( + initial.config.providerConfigId, + ); + expect(limited.config.rateLimit).toEqual(100); + expect(limited.config.rateLimitPeriod).toEqual(60); + + // The replaced config is gone. + yield* expectGone( + accountId, + LIFECYCLE_GATEWAY_ID, + initial.config.providerConfigId, + ); + + yield* stack.destroy(); + + yield* expectGone( + accountId, + LIFECYCLE_GATEWAY_ID, + limited.config.providerConfigId, + ); + }).pipe(logLevel), ); -test.provider("list enumerates the deployed provider config", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const deployed = yield* stack.deploy( - Effect.gen(function* () { - const store = yield* Cloudflare.SecretsStore("PcListStore"); - const gateway = yield* Cloudflare.AiGateway("PcListGateway", { - id: LIST_GATEWAY_ID, - storeId: store.storeId, - }); - const secret = yield* Cloudflare.Secret("PcListSecret", { - store, - name: secretName(LIST_GATEWAY_ID), - value: Redacted.make(SECRET_VALUE), - scopes: ["ai_gateway"], - }); - return yield* Cloudflare.AiGatewayProviderConfig("ByokList", { - gatewayId: gateway.gatewayId, - providerSlug: PROVIDER_SLUG, - alias: ALIAS, - secretId: secret.secretId, - defaultConfig: true, - }); - }), - ); - - const provider = yield* Provider.findProvider( - Cloudflare.AiGatewayProviderConfig, - ); - const all = yield* provider.list(); - - expect( - all.some((c) => c.providerConfigId === deployed.providerConfigId), - ).toBe(true); - const found = all.find( - (c) => c.providerConfigId === deployed.providerConfigId, - )!; - expect(found.gatewayId).toEqual(LIST_GATEWAY_ID); - expect(found.providerSlug).toEqual(PROVIDER_SLUG); - expect(found.alias).toEqual(ALIAS); - - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed provider config", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const deployed = yield* stack.deploy( + Effect.gen(function* () { + const store = yield* Cloudflare.SecretsStore("PcListStore"); + const gateway = yield* Cloudflare.AiGateway("PcListGateway", { + id: LIST_GATEWAY_ID, + storeId: store.storeId, + }); + const secret = yield* Cloudflare.Secret("PcListSecret", { + store, + name: secretName(LIST_GATEWAY_ID), + value: Redacted.make(SECRET_VALUE), + scopes: ["ai_gateway"], + }); + return yield* Cloudflare.AiGatewayProviderConfig("ByokList", { + gatewayId: gateway.gatewayId, + providerSlug: PROVIDER_SLUG, + alias: ALIAS, + secretId: secret.secretId, + defaultConfig: true, + }); + }), + ); + + const provider = yield* Provider.findProvider( + Cloudflare.AiGatewayProviderConfig, + ); + const all = yield* provider.list(); + + expect( + all.some((c) => c.providerConfigId === deployed.providerConfigId), + ).toBe(true); + const found = all.find( + (c) => c.providerConfigId === deployed.providerConfigId, + )!; + expect(found.gatewayId).toEqual(LIST_GATEWAY_ID); + expect(found.providerSlug).toEqual(PROVIDER_SLUG); + expect(found.alias).toEqual(ALIAS); + + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/AiGateway/fixtures/ChatBackend.ts b/packages/alchemy/test/Cloudflare/AiGateway/fixtures/ChatBackend.ts index a2a570377..e97ced7f4 100644 --- a/packages/alchemy/test/Cloudflare/AiGateway/fixtures/ChatBackend.ts +++ b/packages/alchemy/test/Cloudflare/AiGateway/fixtures/ChatBackend.ts @@ -1,4 +1,5 @@ import * as Cloudflare from "@/Cloudflare"; +import { Layer } from "effect"; import * as Effect from "effect/Effect"; import * as Ref from "effect/Ref"; import { Chat } from "effect/unstable/ai"; @@ -41,5 +42,5 @@ export default class ChatBackend extends Cloudflare.DurableObjectNamespace return { bucket, search, serviceToken: search.serviceToken }; }); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "construct auto-creates a managed token and wires it into the instance", (stack) => Effect.gen(function* () { @@ -126,7 +129,7 @@ const crawlerProgram = () => return { target, search, serviceToken: search.serviceToken }; }); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "web-crawler source skips token minting", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/AiSearch/Bindings.test.ts b/packages/alchemy/test/Cloudflare/AiSearch/Bindings.test.ts index ba4839314..44050ac1d 100644 --- a/packages/alchemy/test/Cloudflare/AiSearch/Bindings.test.ts +++ b/packages/alchemy/test/Cloudflare/AiSearch/Bindings.test.ts @@ -7,6 +7,9 @@ import * as Schedule from "effect/Schedule"; import * as HttpClient from "effect/unstable/http/HttpClient"; import Stack from "./fixtures/bindings-stack.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test, beforeAll, afterAll, deploy, destroy } = Test.make({ providers: Cloudflare.providers(), }); @@ -30,7 +33,7 @@ afterAll.skipIf(!!process.env.NO_DESTROY)(destroy(Stack), { timeout: 420_000 }); // Deploying the Worker succeeding at all proves Cloudflare accepted both the // `ai_search` and `ai_search_namespace` bindings. The `/bindings` route then // confirms they are injected and shaped correctly at runtime. -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "worker deploys with ai_search + ai_search_namespace bindings injected", Effect.gen(function* () { const { url } = yield* stack; @@ -80,7 +83,7 @@ test( // and reads them through the Effect-native client. Resolving each client's // `raw` runtime handle proves the Effect-first path wires through to the // live runtime bindings. -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "effect worker resolves ai_search + ai_search_namespace via Effect clients", Effect.gen(function* () { const { effectUrl } = yield* stack; diff --git a/packages/alchemy/test/Cloudflare/AiSearch/Instance.test.ts b/packages/alchemy/test/Cloudflare/AiSearch/Instance.test.ts index b85cb9092..763173fe7 100644 --- a/packages/alchemy/test/Cloudflare/AiSearch/Instance.test.ts +++ b/packages/alchemy/test/Cloudflare/AiSearch/Instance.test.ts @@ -9,6 +9,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import AiSearchCrawlTargetWorker from "./fixtures/crawl-target-worker.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -62,7 +65,7 @@ const program = (props?: Partial) => return { bucket, instance }; }); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create, update mutable props, and delete an r2-backed instance", (stack) => Effect.gen(function* () { @@ -145,7 +148,7 @@ test.provider( { timeout: 240_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "changing the embedding model triggers a replacement", (stack) => Effect.gen(function* () { @@ -184,7 +187,7 @@ test.provider( { timeout: 240_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "recreates after out-of-band delete", (stack) => Effect.gen(function* () { @@ -230,7 +233,7 @@ test.provider( // enumerates every namespace (including the account-provided `default`) and // fans out a paginated instance list per namespace, hydrating each into the // `read` Attributes shape. Deploy an instance and assert its id appears. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed instance", (stack) => Effect.gen(function* () { @@ -261,7 +264,7 @@ test.provider( // an R2 source). Cloudflare only crawls a domain the account owns, so the // crawl is seeded at a Worker we deploy (its `workers.dev` URL is owned by the // account); `parseType: "crawl"` walks pages instead of requiring a sitemap. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "creates a web-crawler instance (no service token)", (stack) => Effect.gen(function* () { @@ -315,7 +318,7 @@ const nsProgram = (props?: Partial) => return { namespace, bucket, instance }; }); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "creates an instance in a custom namespace and moving namespaces replaces", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/AiSearch/Namespace.test.ts b/packages/alchemy/test/Cloudflare/AiSearch/Namespace.test.ts index c28060ca3..7efcf419d 100644 --- a/packages/alchemy/test/Cloudflare/AiSearch/Namespace.test.ts +++ b/packages/alchemy/test/Cloudflare/AiSearch/Namespace.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -78,7 +81,7 @@ const program = (props?: Cloudflare.AiSearchNamespaceProps) => return { namespace }; }); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create, update description in place, and delete a namespace", (stack) => Effect.gen(function* () { @@ -136,7 +139,7 @@ test.provider( { timeout: 240_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "changing the name triggers a replacement", (stack) => Effect.gen(function* () { @@ -172,7 +175,7 @@ test.provider( // namespace, resolve the provider from context via the typed // `findProvider`, call `list()`, and assert the deployed namespace // appears in the exhaustively-paginated result. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed namespace", (stack) => Effect.gen(function* () { @@ -192,7 +195,7 @@ test.provider( { timeout: 240_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "adopts the reserved default namespace without deleting it on teardown", (stack) => Effect.gen(function* () { @@ -217,7 +220,7 @@ test.provider( { timeout: 120_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "recreates after out-of-band delete", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/AiSearch/Token.test.ts b/packages/alchemy/test/Cloudflare/AiSearch/Token.test.ts index bbcf3cda8..0c778aca4 100644 --- a/packages/alchemy/test/Cloudflare/AiSearch/Token.test.ts +++ b/packages/alchemy/test/Cloudflare/AiSearch/Token.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -68,7 +71,7 @@ const program = ( return { apiToken, token }; }); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create, rename in place, and delete a service token", (stack) => Effect.gen(function* () { @@ -116,7 +119,7 @@ test.provider( { timeout: 240_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "recreates after out-of-band delete", (stack) => Effect.gen(function* () { @@ -156,7 +159,7 @@ test.provider( { timeout: 240_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed service token", (stack) => Effect.gen(function* () { @@ -183,7 +186,7 @@ test.provider( { timeout: 240_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "an AI Search instance syncs with a stack-minted service token", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/AiSecurity/CustomTopics.test.ts b/packages/alchemy/test/Cloudflare/AiSecurity/CustomTopics.test.ts index a55b0ad84..14d12a593 100644 --- a/packages/alchemy/test/Cloudflare/AiSecurity/CustomTopics.test.ts +++ b/packages/alchemy/test/Cloudflare/AiSecurity/CustomTopics.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -64,7 +67,7 @@ const setBaseline = ( }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed AiSecurityNotEntitled error on unentitled accounts", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/AiSecurity/Settings.test.ts b/packages/alchemy/test/Cloudflare/AiSecurity/Settings.test.ts index af6efc70d..2d598486e 100644 --- a/packages/alchemy/test/Cloudflare/AiSecurity/Settings.test.ts +++ b/packages/alchemy/test/Cloudflare/AiSecurity/Settings.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -61,7 +64,7 @@ const setBaseline = (zoneId: string, enabled: boolean) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed AiSecurityNotEntitled error on unentitled accounts", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Alerting/NotificationPolicy.test.ts b/packages/alchemy/test/Cloudflare/Alerting/NotificationPolicy.test.ts index a9e28400d..cd236077b 100644 --- a/packages/alchemy/test/Cloudflare/Alerting/NotificationPolicy.test.ts +++ b/packages/alchemy/test/Cloudflare/Alerting/NotificationPolicy.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -17,118 +20,124 @@ const logLevel = Effect.provideService( const EMAIL = "test@alchemy.run"; -test.provider("create, update, delete notification policy", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const policy = yield* stack.deploy( - Cloudflare.NotificationPolicy("SslPolicy", { - alertType: "universal_ssl_event_type", - mechanisms: { email: [{ id: EMAIL }] }, - }), - ); - - expect(policy.policyId).toBeDefined(); - expect(policy.accountId).toEqual(accountId); - expect(policy.alertType).toEqual("universal_ssl_event_type"); - expect(policy.enabled).toBe(true); - - // Verify out-of-band via the API. - const actual = yield* alerting.getPolicy({ - accountId, - policyId: policy.policyId, - }); - expect(actual.name).toEqual(policy.name); - expect(actual.alertType).toEqual("universal_ssl_event_type"); - expect(actual.mechanisms?.email?.[0]?.id).toEqual(EMAIL); - - // Update mutable props in place — same id. - const updated = yield* stack.deploy( - Cloudflare.NotificationPolicy("SslPolicy", { - alertType: "universal_ssl_event_type", - enabled: false, - description: "paused during migration", - mechanisms: { email: [{ id: EMAIL }] }, - }), - ); - expect(updated.policyId).toEqual(policy.policyId); - expect(updated.enabled).toBe(false); - - const afterUpdate = yield* alerting.getPolicy({ - accountId, - policyId: policy.policyId, - }); - expect(afterUpdate.enabled).toBe(false); - expect(afterUpdate.description).toEqual("paused during migration"); - - yield* stack.destroy(); - - yield* waitForPolicyDeleted(accountId, policy.policyId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, update, delete notification policy", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const policy = yield* stack.deploy( + Cloudflare.NotificationPolicy("SslPolicy", { + alertType: "universal_ssl_event_type", + mechanisms: { email: [{ id: EMAIL }] }, + }), + ); + + expect(policy.policyId).toBeDefined(); + expect(policy.accountId).toEqual(accountId); + expect(policy.alertType).toEqual("universal_ssl_event_type"); + expect(policy.enabled).toBe(true); + + // Verify out-of-band via the API. + const actual = yield* alerting.getPolicy({ + accountId, + policyId: policy.policyId, + }); + expect(actual.name).toEqual(policy.name); + expect(actual.alertType).toEqual("universal_ssl_event_type"); + expect(actual.mechanisms?.email?.[0]?.id).toEqual(EMAIL); + + // Update mutable props in place — same id. + const updated = yield* stack.deploy( + Cloudflare.NotificationPolicy("SslPolicy", { + alertType: "universal_ssl_event_type", + enabled: false, + description: "paused during migration", + mechanisms: { email: [{ id: EMAIL }] }, + }), + ); + expect(updated.policyId).toEqual(policy.policyId); + expect(updated.enabled).toBe(false); + + const afterUpdate = yield* alerting.getPolicy({ + accountId, + policyId: policy.policyId, + }); + expect(afterUpdate.enabled).toBe(false); + expect(afterUpdate.description).toEqual("paused during migration"); + + yield* stack.destroy(); + + yield* waitForPolicyDeleted(accountId, policy.policyId); + }).pipe(logLevel), ); -test.provider("replaces policy when alertType changes", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const policy = yield* stack.deploy( - Cloudflare.NotificationPolicy("ReplacePolicy", { - alertType: "universal_ssl_event_type", - mechanisms: { email: [{ id: EMAIL }] }, - }), - ); - expect(policy.alertType).toEqual("universal_ssl_event_type"); - - const replaced = yield* stack.deploy( - Cloudflare.NotificationPolicy("ReplacePolicy", { - alertType: "incident_alert", - mechanisms: { email: [{ id: EMAIL }] }, - }), - ); - expect(replaced.alertType).toEqual("incident_alert"); - expect(replaced.policyId).not.toEqual(policy.policyId); - - // The replaced (old) policy must be gone. - yield* waitForPolicyDeleted(accountId, policy.policyId); - - const actual = yield* alerting.getPolicy({ - accountId, - policyId: replaced.policyId, - }); - expect(actual.alertType).toEqual("incident_alert"); - - yield* stack.destroy(); - - yield* waitForPolicyDeleted(accountId, replaced.policyId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "replaces policy when alertType changes", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const policy = yield* stack.deploy( + Cloudflare.NotificationPolicy("ReplacePolicy", { + alertType: "universal_ssl_event_type", + mechanisms: { email: [{ id: EMAIL }] }, + }), + ); + expect(policy.alertType).toEqual("universal_ssl_event_type"); + + const replaced = yield* stack.deploy( + Cloudflare.NotificationPolicy("ReplacePolicy", { + alertType: "incident_alert", + mechanisms: { email: [{ id: EMAIL }] }, + }), + ); + expect(replaced.alertType).toEqual("incident_alert"); + expect(replaced.policyId).not.toEqual(policy.policyId); + + // The replaced (old) policy must be gone. + yield* waitForPolicyDeleted(accountId, policy.policyId); + + const actual = yield* alerting.getPolicy({ + accountId, + policyId: replaced.policyId, + }); + expect(actual.alertType).toEqual("incident_alert"); + + yield* stack.destroy(); + + yield* waitForPolicyDeleted(accountId, replaced.policyId); + }).pipe(logLevel), ); // Canonical `list()` test (account-scoped collection): deploy a real policy, // then assert it appears in the exhaustively-paginated account-wide result. -test.provider("list enumerates the deployed notification policy", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const deployed = yield* stack.deploy( - Cloudflare.NotificationPolicy("ListPolicy", { - alertType: "universal_ssl_event_type", - mechanisms: { email: [{ id: EMAIL }] }, - }), - ); - - const provider = yield* Provider.findProvider( - Cloudflare.NotificationPolicy, - ); - const all = yield* provider.list(); - - expect(all.some((p) => p.policyId === deployed.policyId)).toBe(true); - - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed notification policy", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const deployed = yield* stack.deploy( + Cloudflare.NotificationPolicy("ListPolicy", { + alertType: "universal_ssl_event_type", + mechanisms: { email: [{ id: EMAIL }] }, + }), + ); + + const provider = yield* Provider.findProvider( + Cloudflare.NotificationPolicy, + ); + const all = yield* provider.list(); + + expect(all.some((p) => p.policyId === deployed.policyId)).toBe(true); + + yield* stack.destroy(); + }).pipe(logLevel), ); const waitForPolicyDeleted = (accountId: string, policyId: string) => diff --git a/packages/alchemy/test/Cloudflare/Alerting/Silence.test.ts b/packages/alchemy/test/Cloudflare/Alerting/Silence.test.ts index 5a8a5702f..ef6c348b1 100644 --- a/packages/alchemy/test/Cloudflare/Alerting/Silence.test.ts +++ b/packages/alchemy/test/Cloudflare/Alerting/Silence.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -39,154 +42,166 @@ const silenceWindow = Effect.gen(function* () { const sameInstant = (a: string | undefined | null, b: string) => a != null && Date.parse(a) === Date.parse(b); -test.provider("create, update window in place, delete silence", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - const { start, end, extendedEnd } = yield* silenceWindow; - - yield* stack.destroy(); - - const initial = yield* stack.deploy( - Effect.gen(function* () { - const policy = yield* Cloudflare.NotificationPolicy("SilencedPolicy", { - alertType: "universal_ssl_event_type", - mechanisms: { email: [{ id: EMAIL }] }, - }); - return yield* Cloudflare.Silence("Maintenance", { - policyId: policy.policyId, - startTime: start, - endTime: end, - }); - }), - ); - - expect(initial.silenceId).toBeDefined(); - expect(initial.accountId).toEqual(accountId); - expect(initial.policyId).toBeDefined(); - expect(sameInstant(initial.startTime, start)).toBe(true); - expect(sameInstant(initial.endTime, end)).toBe(true); - - // Verify out-of-band via the API. - const actual = yield* alerting.getSilence({ - accountId, - silenceId: initial.silenceId, - }); - expect(actual.policyId).toEqual(initial.policyId); - expect(sameInstant(actual.startTime, start)).toBe(true); - expect(sameInstant(actual.endTime, end)).toBe(true); - - // Extend the window in place — same silence id. - const updated = yield* stack.deploy( - Effect.gen(function* () { - const policy = yield* Cloudflare.NotificationPolicy("SilencedPolicy", { - alertType: "universal_ssl_event_type", - mechanisms: { email: [{ id: EMAIL }] }, - }); - return yield* Cloudflare.Silence("Maintenance", { - policyId: policy.policyId, - startTime: start, - endTime: extendedEnd, - }); - }), - ); - - expect(updated.silenceId).toEqual(initial.silenceId); - expect(sameInstant(updated.endTime, extendedEnd)).toBe(true); - - const afterUpdate = yield* alerting.getSilence({ - accountId, - silenceId: initial.silenceId, - }); - expect(sameInstant(afterUpdate.endTime, extendedEnd)).toBe(true); - - yield* stack.destroy(); - - yield* waitForSilenceDeleted(accountId, initial.silenceId); - }).pipe(logLevel), -); - -test.provider("replaces silence when the policy changes", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - const { start, end } = yield* silenceWindow; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, update window in place, delete silence", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + const { start, end, extendedEnd } = yield* silenceWindow; - yield* stack.destroy(); + yield* stack.destroy(); - const deploySilence = (policyResourceId: "PolicyA" | "PolicyB") => - stack.deploy( + const initial = yield* stack.deploy( Effect.gen(function* () { - const policyA = yield* Cloudflare.NotificationPolicy("PolicyA", { - alertType: "universal_ssl_event_type", - mechanisms: { email: [{ id: EMAIL }] }, - }); - const policyB = yield* Cloudflare.NotificationPolicy("PolicyB", { - alertType: "universal_ssl_event_type", - mechanisms: { email: [{ id: EMAIL }] }, - }); - const target = policyResourceId === "PolicyA" ? policyA : policyB; - const silence = yield* Cloudflare.Silence("ReplaceMe", { - policyId: target.policyId, + const policy = yield* Cloudflare.NotificationPolicy( + "SilencedPolicy", + { + alertType: "universal_ssl_event_type", + mechanisms: { email: [{ id: EMAIL }] }, + }, + ); + return yield* Cloudflare.Silence("Maintenance", { + policyId: policy.policyId, startTime: start, endTime: end, }); - return { policyA, policyB, silence }; }), ); - const initial = yield* deploySilence("PolicyA"); - expect(initial.silence.policyId).toEqual(initial.policyA.policyId); + expect(initial.silenceId).toBeDefined(); + expect(initial.accountId).toEqual(accountId); + expect(initial.policyId).toBeDefined(); + expect(sameInstant(initial.startTime, start)).toBe(true); + expect(sameInstant(initial.endTime, end)).toBe(true); + + // Verify out-of-band via the API. + const actual = yield* alerting.getSilence({ + accountId, + silenceId: initial.silenceId, + }); + expect(actual.policyId).toEqual(initial.policyId); + expect(sameInstant(actual.startTime, start)).toBe(true); + expect(sameInstant(actual.endTime, end)).toBe(true); + + // Extend the window in place — same silence id. + const updated = yield* stack.deploy( + Effect.gen(function* () { + const policy = yield* Cloudflare.NotificationPolicy( + "SilencedPolicy", + { + alertType: "universal_ssl_event_type", + mechanisms: { email: [{ id: EMAIL }] }, + }, + ); + return yield* Cloudflare.Silence("Maintenance", { + policyId: policy.policyId, + startTime: start, + endTime: extendedEnd, + }); + }), + ); - // Pointing the silence at a different policy is a replacement — the - // silence update API cannot move a silence between policies. - const replaced = yield* deploySilence("PolicyB"); - expect(replaced.silence.policyId).toEqual(replaced.policyB.policyId); - expect(replaced.silence.silenceId).not.toEqual(initial.silence.silenceId); + expect(updated.silenceId).toEqual(initial.silenceId); + expect(sameInstant(updated.endTime, extendedEnd)).toBe(true); - // The replaced (old) silence must be gone. - yield* waitForSilenceDeleted(accountId, initial.silence.silenceId); + const afterUpdate = yield* alerting.getSilence({ + accountId, + silenceId: initial.silenceId, + }); + expect(sameInstant(afterUpdate.endTime, extendedEnd)).toBe(true); - const actual = yield* alerting.getSilence({ - accountId, - silenceId: replaced.silence.silenceId, - }); - expect(actual.policyId).toEqual(replaced.policyB.policyId); + yield* stack.destroy(); - yield* stack.destroy(); + yield* waitForSilenceDeleted(accountId, initial.silenceId); + }).pipe(logLevel), +); - yield* waitForSilenceDeleted(accountId, replaced.silence.silenceId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "replaces silence when the policy changes", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + const { start, end } = yield* silenceWindow; + + yield* stack.destroy(); + + const deploySilence = (policyResourceId: "PolicyA" | "PolicyB") => + stack.deploy( + Effect.gen(function* () { + const policyA = yield* Cloudflare.NotificationPolicy("PolicyA", { + alertType: "universal_ssl_event_type", + mechanisms: { email: [{ id: EMAIL }] }, + }); + const policyB = yield* Cloudflare.NotificationPolicy("PolicyB", { + alertType: "universal_ssl_event_type", + mechanisms: { email: [{ id: EMAIL }] }, + }); + const target = policyResourceId === "PolicyA" ? policyA : policyB; + const silence = yield* Cloudflare.Silence("ReplaceMe", { + policyId: target.policyId, + startTime: start, + endTime: end, + }); + return { policyA, policyB, silence }; + }), + ); + + const initial = yield* deploySilence("PolicyA"); + expect(initial.silence.policyId).toEqual(initial.policyA.policyId); + + // Pointing the silence at a different policy is a replacement — the + // silence update API cannot move a silence between policies. + const replaced = yield* deploySilence("PolicyB"); + expect(replaced.silence.policyId).toEqual(replaced.policyB.policyId); + expect(replaced.silence.silenceId).not.toEqual(initial.silence.silenceId); + + // The replaced (old) silence must be gone. + yield* waitForSilenceDeleted(accountId, initial.silence.silenceId); + + const actual = yield* alerting.getSilence({ + accountId, + silenceId: replaced.silence.silenceId, + }); + expect(actual.policyId).toEqual(replaced.policyB.policyId); + + yield* stack.destroy(); + + yield* waitForSilenceDeleted(accountId, replaced.silence.silenceId); + }).pipe(logLevel), ); -test.provider("list enumerates the deployed silence", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - const { start, end } = yield* silenceWindow; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed silence", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + const { start, end } = yield* silenceWindow; - yield* stack.destroy(); + yield* stack.destroy(); - const deployed = yield* stack.deploy( - Effect.gen(function* () { - const policy = yield* Cloudflare.NotificationPolicy("ListedPolicy", { - alertType: "universal_ssl_event_type", - mechanisms: { email: [{ id: EMAIL }] }, - }); - return yield* Cloudflare.Silence("ListedSilence", { - policyId: policy.policyId, - startTime: start, - endTime: end, - }); - }), - ); + const deployed = yield* stack.deploy( + Effect.gen(function* () { + const policy = yield* Cloudflare.NotificationPolicy("ListedPolicy", { + alertType: "universal_ssl_event_type", + mechanisms: { email: [{ id: EMAIL }] }, + }); + return yield* Cloudflare.Silence("ListedSilence", { + policyId: policy.policyId, + startTime: start, + endTime: end, + }); + }), + ); - const provider = yield* Provider.findProvider(Cloudflare.Silence); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider(Cloudflare.Silence); + const all = yield* provider.list(); - expect(all.some((s) => s.silenceId === deployed.silenceId)).toBe(true); + expect(all.some((s) => s.silenceId === deployed.silenceId)).toBe(true); - yield* stack.destroy(); + yield* stack.destroy(); - yield* waitForSilenceDeleted(accountId, deployed.silenceId); - }).pipe(logLevel), + yield* waitForSilenceDeleted(accountId, deployed.silenceId); + }).pipe(logLevel), ); const waitForSilenceDeleted = (accountId: string, silenceId: string) => diff --git a/packages/alchemy/test/Cloudflare/Alerting/Webhook.test.ts b/packages/alchemy/test/Cloudflare/Alerting/Webhook.test.ts index f8e43a3ca..891b0ad4f 100644 --- a/packages/alchemy/test/Cloudflare/Alerting/Webhook.test.ts +++ b/packages/alchemy/test/Cloudflare/Alerting/Webhook.test.ts @@ -10,6 +10,9 @@ import * as Schedule from "effect/Schedule"; import * as pathe from "pathe"; import { expectUrlContains } from "../Utils/Http.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -28,104 +31,108 @@ const Receiver = () => subdomain: { enabled: true }, }); -test.provider("create, update, delete webhook destination", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - // Phase 1: deploy the receiving worker alone and wait until the - // workers.dev URL actually serves (fresh subdomains take a few - // seconds to propagate; the webhook test POST must hit a live 200). - const receiver = yield* stack.deploy(Receiver()); - expect(receiver.url).toBeDefined(); - yield* expectUrlContains(receiver.url!, "webhook-ok", { - label: "webhook receiver", - }); - - // Phase 2: create the webhook destination pointing at the worker. - const webhook = yield* stack.deploy( - Effect.gen(function* () { - const worker = yield* Receiver(); - return yield* Cloudflare.NotificationWebhook("AlertWebhook", { - url: worker.url.as(), - }); - }), - ); - - expect(webhook.webhookId).toBeDefined(); - expect(webhook.accountId).toEqual(accountId); - expect(webhook.name).toBeDefined(); - expect(webhook.url).toEqual(receiver.url); - - // Verify out-of-band via the API. - const actual = yield* alerting.getDestinationWebhook({ - accountId, - webhookId: webhook.webhookId, - }); - expect(actual.name).toEqual(webhook.name); - expect(actual.url).toEqual(receiver.url); - - // Phase 3: rename (mutable prop) — same id, new name. - const renamed = yield* stack.deploy( - Effect.gen(function* () { - const worker = yield* Receiver(); - return yield* Cloudflare.NotificationWebhook("AlertWebhook", { - name: "alchemy-test-alerting-webhook-renamed", - url: worker.url.as(), - }); - }), - ); - expect(renamed.webhookId).toEqual(webhook.webhookId); - expect(renamed.name).toEqual("alchemy-test-alerting-webhook-renamed"); - - const afterRename = yield* alerting.getDestinationWebhook({ - accountId, - webhookId: webhook.webhookId, - }); - expect(afterRename.name).toEqual("alchemy-test-alerting-webhook-renamed"); - - yield* stack.destroy(); - - yield* waitForWebhookDeleted(accountId, webhook.webhookId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, update, delete webhook destination", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + // Phase 1: deploy the receiving worker alone and wait until the + // workers.dev URL actually serves (fresh subdomains take a few + // seconds to propagate; the webhook test POST must hit a live 200). + const receiver = yield* stack.deploy(Receiver()); + expect(receiver.url).toBeDefined(); + yield* expectUrlContains(receiver.url!, "webhook-ok", { + label: "webhook receiver", + }); + + // Phase 2: create the webhook destination pointing at the worker. + const webhook = yield* stack.deploy( + Effect.gen(function* () { + const worker = yield* Receiver(); + return yield* Cloudflare.NotificationWebhook("AlertWebhook", { + url: worker.url.as(), + }); + }), + ); + + expect(webhook.webhookId).toBeDefined(); + expect(webhook.accountId).toEqual(accountId); + expect(webhook.name).toBeDefined(); + expect(webhook.url).toEqual(receiver.url); + + // Verify out-of-band via the API. + const actual = yield* alerting.getDestinationWebhook({ + accountId, + webhookId: webhook.webhookId, + }); + expect(actual.name).toEqual(webhook.name); + expect(actual.url).toEqual(receiver.url); + + // Phase 3: rename (mutable prop) — same id, new name. + const renamed = yield* stack.deploy( + Effect.gen(function* () { + const worker = yield* Receiver(); + return yield* Cloudflare.NotificationWebhook("AlertWebhook", { + name: "alchemy-test-alerting-webhook-renamed", + url: worker.url.as(), + }); + }), + ); + expect(renamed.webhookId).toEqual(webhook.webhookId); + expect(renamed.name).toEqual("alchemy-test-alerting-webhook-renamed"); + + const afterRename = yield* alerting.getDestinationWebhook({ + accountId, + webhookId: webhook.webhookId, + }); + expect(afterRename.name).toEqual("alchemy-test-alerting-webhook-renamed"); + + yield* stack.destroy(); + + yield* waitForWebhookDeleted(accountId, webhook.webhookId); + }).pipe(logLevel), ); -test.provider("list enumerates the deployed webhook destination", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - // Deploy the receiving worker, wait for it to serve, then create the - // webhook destination pointing at it. - const receiver = yield* stack.deploy(Receiver()); - expect(receiver.url).toBeDefined(); - yield* expectUrlContains(receiver.url!, "webhook-ok", { - label: "webhook receiver", - }); - - const webhook = yield* stack.deploy( - Effect.gen(function* () { - const worker = yield* Receiver(); - return yield* Cloudflare.NotificationWebhook("ListWebhook", { - url: worker.url.as(), - }); - }), - ); - - const provider = yield* Provider.findProvider( - Cloudflare.NotificationWebhook, - ); - const all = yield* provider.list(); - - const match = all.find((w) => w.webhookId === webhook.webhookId); - expect(match).toBeDefined(); - expect(match!.name).toEqual(webhook.name); - expect(match!.url).toEqual(webhook.url); - - yield* stack.destroy(); - - yield* waitForWebhookDeleted(webhook.accountId, webhook.webhookId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed webhook destination", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + // Deploy the receiving worker, wait for it to serve, then create the + // webhook destination pointing at it. + const receiver = yield* stack.deploy(Receiver()); + expect(receiver.url).toBeDefined(); + yield* expectUrlContains(receiver.url!, "webhook-ok", { + label: "webhook receiver", + }); + + const webhook = yield* stack.deploy( + Effect.gen(function* () { + const worker = yield* Receiver(); + return yield* Cloudflare.NotificationWebhook("ListWebhook", { + url: worker.url.as(), + }); + }), + ); + + const provider = yield* Provider.findProvider( + Cloudflare.NotificationWebhook, + ); + const all = yield* provider.list(); + + const match = all.find((w) => w.webhookId === webhook.webhookId); + expect(match).toBeDefined(); + expect(match!.name).toEqual(webhook.name); + expect(match!.url).toEqual(webhook.url); + + yield* stack.destroy(); + + yield* waitForWebhookDeleted(webhook.accountId, webhook.webhookId); + }).pipe(logLevel), ); const waitForWebhookDeleted = (accountId: string, webhookId: string) => diff --git a/packages/alchemy/test/Cloudflare/AnalyticsEngine/AnalyticsEngineDataset.test.ts b/packages/alchemy/test/Cloudflare/AnalyticsEngine/AnalyticsEngineDataset.test.ts index 101635db4..7c6ad4e83 100644 --- a/packages/alchemy/test/Cloudflare/AnalyticsEngine/AnalyticsEngineDataset.test.ts +++ b/packages/alchemy/test/Cloudflare/AnalyticsEngine/AnalyticsEngineDataset.test.ts @@ -9,6 +9,9 @@ import * as HttpClient from "effect/unstable/http/HttpClient"; import { Dataset } from "./fixtures/dataset.ts"; import AnalyticsEngineTestWorker from "./fixtures/worker.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test, beforeAll, afterAll, deploy, destroy } = Test.make({ providers: Cloudflare.providers(), }); @@ -37,7 +40,7 @@ const Stack = Alchemy.Stack( const stack = beforeAll(deploy(Stack)); afterAll.skipIf(!!process.env.NO_DESTROY)(destroy(Stack)); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "deployed worker can write data points through the Analytics Engine binding", Effect.gen(function* () { const { url } = yield* stack; diff --git a/packages/alchemy/test/Cloudflare/ApiShield/Configuration.test.ts b/packages/alchemy/test/Cloudflare/ApiShield/Configuration.test.ts index 5b6792597..5ed15cf79 100644 --- a/packages/alchemy/test/Cloudflare/ApiShield/Configuration.test.ts +++ b/packages/alchemy/test/Cloudflare/ApiShield/Configuration.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -63,7 +66,7 @@ const setBaseline = ( }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed NotEntitled error on unentitled zones", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/ApiShield/Label.test.ts b/packages/alchemy/test/Cloudflare/ApiShield/Label.test.ts index 2b00503a6..3790b6ed0 100644 --- a/packages/alchemy/test/Cloudflare/ApiShield/Label.test.ts +++ b/packages/alchemy/test/Cloudflare/ApiShield/Label.test.ts @@ -10,6 +10,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -65,104 +68,108 @@ const purgeLabel = (zoneId: string, name: string) => }), ); -test.provider("create, update description in place, destroy a label", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - yield* stack.destroy(); - yield* purgeLabel(zoneId, NAME_DEFAULT); - - const label = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.ApiShieldLabel("DefaultLabel", { - zoneId, - name: NAME_DEFAULT, - description: "v1", - }).pipe(adopt(true)); - }), - ); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, update description in place, destroy a label", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - expect(label.zoneId).toEqual(zoneId); - expect(label.name).toEqual(NAME_DEFAULT); - expect(label.description).toEqual("v1"); - expect(label.source).toEqual("user"); - - const live = yield* getLabel(zoneId, NAME_DEFAULT); - expect(live?.name).toEqual(NAME_DEFAULT); - expect(live?.description).toEqual("v1"); - - // Update the mutable description — same identity, patched in place. - const updated = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.ApiShieldLabel("DefaultLabel", { - zoneId, - name: NAME_DEFAULT, - description: "v2", - }).pipe(adopt(true)); - }), - ); - expect(updated.name).toEqual(NAME_DEFAULT); - expect(updated.createdAt).toEqual(label.createdAt); - expect(updated.description).toEqual("v2"); + yield* stack.destroy(); + yield* purgeLabel(zoneId, NAME_DEFAULT); + + const label = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.ApiShieldLabel("DefaultLabel", { + zoneId, + name: NAME_DEFAULT, + description: "v1", + }).pipe(adopt(true)); + }), + ); - const patched = yield* getLabel(zoneId, NAME_DEFAULT); - expect(patched?.description).toEqual("v2"); + expect(label.zoneId).toEqual(zoneId); + expect(label.name).toEqual(NAME_DEFAULT); + expect(label.description).toEqual("v1"); + expect(label.source).toEqual("user"); - yield* stack.destroy(); + const live = yield* getLabel(zoneId, NAME_DEFAULT); + expect(live?.name).toEqual(NAME_DEFAULT); + expect(live?.description).toEqual("v1"); - const gone = yield* getLabel(zoneId, NAME_DEFAULT); - expect(gone).toBeUndefined(); - }).pipe(logLevel), + // Update the mutable description — same identity, patched in place. + const updated = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.ApiShieldLabel("DefaultLabel", { + zoneId, + name: NAME_DEFAULT, + description: "v2", + }).pipe(adopt(true)); + }), + ); + expect(updated.name).toEqual(NAME_DEFAULT); + expect(updated.createdAt).toEqual(label.createdAt); + expect(updated.description).toEqual("v2"); + + const patched = yield* getLabel(zoneId, NAME_DEFAULT); + expect(patched?.description).toEqual("v2"); + + yield* stack.destroy(); + + const gone = yield* getLabel(zoneId, NAME_DEFAULT); + expect(gone).toBeUndefined(); + }).pipe(logLevel), ); -test.provider("renaming a label triggers replacement", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - yield* stack.destroy(); - yield* purgeLabel(zoneId, NAME_RENAME_A); - yield* purgeLabel(zoneId, NAME_RENAME_B); - - const initial = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.ApiShieldLabel("RenameLabel", { - zoneId, - name: NAME_RENAME_A, - description: "before rename", - }).pipe(adopt(true)); - }), - ); - expect(initial.name).toEqual(NAME_RENAME_A); - - const replaced = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.ApiShieldLabel("RenameLabel", { - zoneId, - name: NAME_RENAME_B, - description: "after rename", - }).pipe(adopt(true)); - }), - ); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "renaming a label triggers replacement", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - // The name is the label's identity — a new physical label exists. - expect(replaced.name).toEqual(NAME_RENAME_B); - expect(replaced.description).toEqual("after rename"); + yield* stack.destroy(); + yield* purgeLabel(zoneId, NAME_RENAME_A); + yield* purgeLabel(zoneId, NAME_RENAME_B); - // The old label was deleted as part of the replacement. - const oldLabel = yield* getLabel(zoneId, NAME_RENAME_A); - expect(oldLabel).toBeUndefined(); + const initial = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.ApiShieldLabel("RenameLabel", { + zoneId, + name: NAME_RENAME_A, + description: "before rename", + }).pipe(adopt(true)); + }), + ); + expect(initial.name).toEqual(NAME_RENAME_A); - const live = yield* getLabel(zoneId, NAME_RENAME_B); - expect(live?.name).toEqual(NAME_RENAME_B); + const replaced = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.ApiShieldLabel("RenameLabel", { + zoneId, + name: NAME_RENAME_B, + description: "after rename", + }).pipe(adopt(true)); + }), + ); + + // The name is the label's identity — a new physical label exists. + expect(replaced.name).toEqual(NAME_RENAME_B); + expect(replaced.description).toEqual("after rename"); + + // The old label was deleted as part of the replacement. + const oldLabel = yield* getLabel(zoneId, NAME_RENAME_A); + expect(oldLabel).toBeUndefined(); - yield* stack.destroy(); + const live = yield* getLabel(zoneId, NAME_RENAME_B); + expect(live?.name).toEqual(NAME_RENAME_B); - const gone = yield* getLabel(zoneId, NAME_RENAME_B); - expect(gone).toBeUndefined(); - }).pipe(logLevel), + yield* stack.destroy(); + + const gone = yield* getLabel(zoneId, NAME_RENAME_B); + expect(gone).toBeUndefined(); + }).pipe(logLevel), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "generated name respects Cloudflare's 24-character limit", (stack) => Effect.gen(function* () { @@ -192,32 +199,34 @@ test.provider( }).pipe(logLevel), ); -test.provider("list enumerates the deployed label", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - yield* stack.destroy(); - yield* purgeLabel(zoneId, NAME_LIST); - - const deployed = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.ApiShieldLabel("ListLabel", { - zoneId, - name: NAME_LIST, - description: "listed", - }).pipe(adopt(true)); - }), - ); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed label", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - const provider = yield* Provider.findProvider(Cloudflare.ApiShieldLabel); - const all = yield* provider.list(); + yield* stack.destroy(); + yield* purgeLabel(zoneId, NAME_LIST); - expect( - all.some( - (label) => label.zoneId === zoneId && label.name === deployed.name, - ), - ).toBe(true); + const deployed = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.ApiShieldLabel("ListLabel", { + zoneId, + name: NAME_LIST, + description: "listed", + }).pipe(adopt(true)); + }), + ); + + const provider = yield* Provider.findProvider(Cloudflare.ApiShieldLabel); + const all = yield* provider.list(); - yield* stack.destroy(); - }).pipe(logLevel), + expect( + all.some( + (label) => label.zoneId === zoneId && label.name === deployed.name, + ), + ).toBe(true); + + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/ApiShield/Operation.test.ts b/packages/alchemy/test/Cloudflare/ApiShield/Operation.test.ts index 9f7d78a5f..02edb38bc 100644 --- a/packages/alchemy/test/Cloudflare/ApiShield/Operation.test.ts +++ b/packages/alchemy/test/Cloudflare/ApiShield/Operation.test.ts @@ -12,6 +12,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -97,7 +100,7 @@ const purgeOperation = ( ), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create, no-op redeploy, destroy an API Shield operation", (stack) => Effect.gen(function* () { @@ -154,99 +157,103 @@ test.provider( }).pipe(logLevel), ); -test.provider("changing the method triggers replacement", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - const getTuple = { - method: "GET", - host: zoneName, - endpoint: ENDPOINT_REPLACE, - }; - const postTuple = { ...getTuple, method: "POST" }; - - yield* stack.destroy(); - yield* purgeOperation(zoneId, getTuple); - yield* purgeOperation(zoneId, postTuple); - - const initial = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.ApiShieldOperation("ReplaceOp", { - zoneId, - method: "GET", - host: zoneName, - endpoint: ENDPOINT_REPLACE, - }).pipe(adopt(true)); - }), - ); - expect(initial.method).toEqual("GET"); - - const replaced = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.ApiShieldOperation("ReplaceOp", { - zoneId, - method: "POST", - host: zoneName, - endpoint: ENDPOINT_REPLACE, - }).pipe(adopt(true)); - }), - ); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "changing the method triggers replacement", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; + const getTuple = { + method: "GET", + host: zoneName, + endpoint: ENDPOINT_REPLACE, + }; + const postTuple = { ...getTuple, method: "POST" }; + + yield* stack.destroy(); + yield* purgeOperation(zoneId, getTuple); + yield* purgeOperation(zoneId, postTuple); + + const initial = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.ApiShieldOperation("ReplaceOp", { + zoneId, + method: "GET", + host: zoneName, + endpoint: ENDPOINT_REPLACE, + }).pipe(adopt(true)); + }), + ); + expect(initial.method).toEqual("GET"); - // The tuple is the operation's identity — a new physical operation. - expect(replaced.operationId).not.toEqual(initial.operationId); - expect(replaced.method).toEqual("POST"); + const replaced = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.ApiShieldOperation("ReplaceOp", { + zoneId, + method: "POST", + host: zoneName, + endpoint: ENDPOINT_REPLACE, + }).pipe(adopt(true)); + }), + ); - // The old GET operation was deleted as part of the replacement. - const oldOp = yield* findOperation(zoneId, getTuple); - expect(oldOp).toBeUndefined(); + // The tuple is the operation's identity — a new physical operation. + expect(replaced.operationId).not.toEqual(initial.operationId); + expect(replaced.method).toEqual("POST"); - yield* stack.destroy(); + // The old GET operation was deleted as part of the replacement. + const oldOp = yield* findOperation(zoneId, getTuple); + expect(oldOp).toBeUndefined(); - const gone = yield* findOperation(zoneId, postTuple); - expect(gone).toBeUndefined(); - }).pipe(logLevel), + yield* stack.destroy(); + + const gone = yield* findOperation(zoneId, postTuple); + expect(gone).toBeUndefined(); + }).pipe(logLevel), ); -test.provider("list enumerates the deployed API Shield operation", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - const tuple = { - method: "GET", - host: zoneName, - endpoint: ENDPOINT_LIST, - }; - - yield* stack.destroy(); - yield* purgeOperation(zoneId, tuple); - - const op = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.ApiShieldOperation("ListOp", { - zoneId, - method: "GET", - host: zoneName, - endpoint: ENDPOINT_LIST, - }).pipe(adopt(true)); - }), - ); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed API Shield operation", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; + const tuple = { + method: "GET", + host: zoneName, + endpoint: ENDPOINT_LIST, + }; - const provider = yield* Provider.findProvider( - Cloudflare.ApiShieldOperation, - ); - const all = yield* provider.list(); - - // The deployed operation appears in the exhaustively-paginated, - // all-zones result with the exact `read` shape. - const found = all.find((x) => x.operationId === op.operationId); - expect(found).toBeDefined(); - expect(found?.zoneId).toEqual(zoneId); - expect(found?.method).toEqual("GET"); - expect(found?.host).toEqual(zoneName); - expect(found?.endpoint).toEqual("/alchemy-apishield/list"); - expect(found?.lastUpdated).toBeDefined(); - - yield* stack.destroy(); - - const gone = yield* findOperation(zoneId, tuple); - expect(gone).toBeUndefined(); - }).pipe(logLevel), + yield* stack.destroy(); + yield* purgeOperation(zoneId, tuple); + + const op = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.ApiShieldOperation("ListOp", { + zoneId, + method: "GET", + host: zoneName, + endpoint: ENDPOINT_LIST, + }).pipe(adopt(true)); + }), + ); + + const provider = yield* Provider.findProvider( + Cloudflare.ApiShieldOperation, + ); + const all = yield* provider.list(); + + // The deployed operation appears in the exhaustively-paginated, + // all-zones result with the exact `read` shape. + const found = all.find((x) => x.operationId === op.operationId); + expect(found).toBeDefined(); + expect(found?.zoneId).toEqual(zoneId); + expect(found?.method).toEqual("GET"); + expect(found?.host).toEqual(zoneName); + expect(found?.endpoint).toEqual("/alchemy-apishield/list"); + expect(found?.lastUpdated).toBeDefined(); + + yield* stack.destroy(); + + const gone = yield* findOperation(zoneId, tuple); + expect(gone).toBeUndefined(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/ApiShield/UserSchema.test.ts b/packages/alchemy/test/Cloudflare/ApiShield/UserSchema.test.ts index 58abb3600..02a45fad5 100644 --- a/packages/alchemy/test/Cloudflare/ApiShield/UserSchema.test.ts +++ b/packages/alchemy/test/Cloudflare/ApiShield/UserSchema.test.ts @@ -13,6 +13,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -87,7 +90,7 @@ const purgeSchemasNamed = (zoneId: string, name: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create, enable validation in place, destroy a user schema", (stack) => Effect.gen(function* () { @@ -143,7 +146,7 @@ test.provider( { timeout: 120_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "changing the schema source triggers replacement", (stack) => Effect.gen(function* () { @@ -197,7 +200,7 @@ test.provider( // Canonical `list()` test (zone-scoped collection): `list()` fans out over // every zone via `listAllZones` and exhaustively paginates each zone's // schemas. Deploy a schema, then assert its id appears in the result. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed user schema", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/ApiToken/AccountApiToken.test.ts b/packages/alchemy/test/Cloudflare/ApiToken/AccountApiToken.test.ts index f9a2fa300..23b6d109f 100644 --- a/packages/alchemy/test/Cloudflare/ApiToken/AccountApiToken.test.ts +++ b/packages/alchemy/test/Cloudflare/ApiToken/AccountApiToken.test.ts @@ -11,6 +11,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "node:test"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -185,38 +188,42 @@ describe.skip("AccountApiToken", () => { class TokenStillExists extends Data.TaggedError("TokenStillExists") {} describe("AccountApiToken list", () => { - test.provider("list enumerates the deployed account token", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const token = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.AccountApiToken("ListToken", { - name: "alchemy-test-acct-list", - policies: [ - { - effect: "allow", - permissionGroups: ["Workers Scripts Read"], - resources: { - [`com.cloudflare.api.account.${accountId}`]: "*", + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed account token", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const token = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.AccountApiToken("ListToken", { + name: "alchemy-test-acct-list", + policies: [ + { + effect: "allow", + permissionGroups: ["Workers Scripts Read"], + resources: { + [`com.cloudflare.api.account.${accountId}`]: "*", + }, }, - }, - ], - }); - }), - ); - - const provider = yield* Provider.findProvider(Cloudflare.AccountApiToken); - const all = yield* provider.list(); - - expect(all.some((t) => t.tokenId === token.tokenId)).toBe(true); - const found = all.find((t) => t.tokenId === token.tokenId)!; - expect(found.name).toEqual(token.name); - expect(found.accountId).toEqual(accountId); - - yield* stack.destroy(); - }).pipe(logLevel), + ], + }); + }), + ); + + const provider = yield* Provider.findProvider( + Cloudflare.AccountApiToken, + ); + const all = yield* provider.list(); + + expect(all.some((t) => t.tokenId === token.tokenId)).toBe(true); + const found = all.find((t) => t.tokenId === token.tokenId)!; + expect(found.name).toEqual(token.name); + expect(found.accountId).toEqual(accountId); + + yield* stack.destroy(); + }).pipe(logLevel), ); }); diff --git a/packages/alchemy/test/Cloudflare/Argo/SmartRouting.test.ts b/packages/alchemy/test/Cloudflare/Argo/SmartRouting.test.ts index 5c7f28d92..882bbd2d5 100644 --- a/packages/alchemy/test/Cloudflare/Argo/SmartRouting.test.ts +++ b/packages/alchemy/test/Cloudflare/Argo/SmartRouting.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -67,7 +70,7 @@ const setBaseline = (zoneId: string, value: "on" | "off") => ); describe.sequential("SmartRouting", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed NotAuthorized error on zones without the Argo add-on", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Argo/TieredCaching.test.ts b/packages/alchemy/test/Cloudflare/Argo/TieredCaching.test.ts index c4d20ccf3..6ea2892a7 100644 --- a/packages/alchemy/test/Cloudflare/Argo/TieredCaching.test.ts +++ b/packages/alchemy/test/Cloudflare/Argo/TieredCaching.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -63,7 +66,7 @@ const setBaseline = (zoneId: string, value: "on" | "off") => // opposite baselines; run them serially so they don't corrupt each other's // captured `initialValue` under the global concurrent test config. describe.sequential("TieredCaching", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "enables Tiered Caching and restores the original value on destroy", (stack) => Effect.gen(function* () { @@ -97,7 +100,7 @@ describe.sequential("TieredCaching", () => { }).pipe(logLevel), ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "updates enabled in place and keeps the captured initial value", (stack) => Effect.gen(function* () { @@ -150,19 +153,21 @@ describe.sequential("TieredCaching", () => { // API for this per-zone setting, so `list()` enumerates every zone via // `listAllZones` and reads the singleton in each. Assert the result is // non-empty and contains the standing test zone. - test.provider("list enumerates the setting across all zones", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the setting across all zones", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - const provider = yield* Provider.findProvider(Cloudflare.TieredCaching); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider(Cloudflare.TieredCaching); + const all = yield* provider.list(); - expect(all.length).toBeGreaterThan(0); - expect(all.some((s) => s.zoneId === zoneId)).toBe(true); + expect(all.length).toBeGreaterThan(0); + expect(all.some((s) => s.zoneId === zoneId)).toBe(true); - // `stack` is unused here (the singleton always exists on every zone), - // but keep the destroy bookend so the harness state stays clean. - yield* stack.destroy(); - }).pipe(logLevel), + // `stack` is unused here (the singleton always exists on every zone), + // but keep the destroy bookend so the harness state stays clean. + yield* stack.destroy(); + }).pipe(logLevel), ); }); diff --git a/packages/alchemy/test/Cloudflare/BotManagement/BotManagement.test.ts b/packages/alchemy/test/Cloudflare/BotManagement/BotManagement.test.ts index f8d9fc78f..2620d6c46 100644 --- a/packages/alchemy/test/Cloudflare/BotManagement/BotManagement.test.ts +++ b/packages/alchemy/test/Cloudflare/BotManagement/BotManagement.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -79,7 +82,7 @@ const restoreSbfm = (zoneId: string, original: ObservedConfig) => }).pipe(Effect.ignore); describe.sequential("BotManagement", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "manages SBFM settings on the zone singleton and restores them on destroy", (stack) => Effect.gen(function* () { @@ -153,7 +156,7 @@ describe.sequential("BotManagement", () => { { timeout: 240_000 }, ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "deploy with no settings set adopts the singleton without writing", (stack) => Effect.gen(function* () { @@ -195,7 +198,7 @@ describe.sequential("BotManagement", () => { { timeout: 240_000 }, ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "toggles a boolean SBFM field and restores it on destroy", (stack) => Effect.gen(function* () { @@ -241,7 +244,7 @@ describe.sequential("BotManagement", () => { // result is non-empty and contains the standing test zone. This is a // read-only assertion (no mutation), so it runs regardless of whether the // zone has the paid Bot Management add-on. - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates bot management across all zones", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Browser/Browser.test.ts b/packages/alchemy/test/Cloudflare/Browser/Browser.test.ts index b924f03d6..4802f9578 100644 --- a/packages/alchemy/test/Cloudflare/Browser/Browser.test.ts +++ b/packages/alchemy/test/Cloudflare/Browser/Browser.test.ts @@ -7,6 +7,9 @@ import * as Schedule from "effect/Schedule"; import * as HttpClient from "effect/unstable/http/HttpClient"; import Stack from "./fixtures/stack.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test, beforeAll, afterAll, deploy, destroy } = Test.make({ providers: Cloudflare.providers(), }); @@ -40,7 +43,7 @@ const readJson = (url: string) => const stack = beforeAll(deploy(Stack)); afterAll.skipIf(!!process.env.NO_DESTROY)(destroy(Stack)); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "async worker renders a page title through Browser Rendering", Effect.gen(function* () { const { asyncWorkerUrl } = yield* stack; @@ -55,7 +58,7 @@ test( { timeout: 180_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "effect worker exercises content via quickAction wrapper", Effect.gen(function* () { const { effectWorkerUrl } = yield* stack; @@ -70,7 +73,7 @@ test( { timeout: 180_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "effect worker converts a page to markdown", Effect.gen(function* () { const { effectWorkerUrl } = yield* stack; @@ -83,7 +86,7 @@ test( { timeout: 180_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "effect worker extracts links", Effect.gen(function* () { const { effectWorkerUrl } = yield* stack; @@ -96,7 +99,7 @@ test( { timeout: 180_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "effect worker scrapes elements by selector", Effect.gen(function* () { const { effectWorkerUrl } = yield* stack; @@ -109,7 +112,7 @@ test( { timeout: 180_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "effect worker takes a page snapshot", Effect.gen(function* () { const { effectWorkerUrl } = yield* stack; @@ -124,7 +127,7 @@ test( { timeout: 180_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "effect worker streams a screenshot", Effect.gen(function* () { const { effectWorkerUrl } = yield* stack; @@ -137,7 +140,7 @@ test( { timeout: 180_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "effect worker streams a PDF", Effect.gen(function* () { const { effectWorkerUrl } = yield* stack; @@ -150,7 +153,7 @@ test( { timeout: 180_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "effect worker extracts JSON with AI", Effect.gen(function* () { const { effectWorkerUrl } = yield* stack; @@ -163,7 +166,7 @@ test( { timeout: 180_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "effect worker calls the generic quickAction", Effect.gen(function* () { const { effectWorkerUrl } = yield* stack; @@ -176,7 +179,7 @@ test( { timeout: 180_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "effect worker exposes the raw BrowserRun binding", Effect.gen(function* () { const { effectWorkerUrl } = yield* stack; diff --git a/packages/alchemy/test/Cloudflare/Cache/CacheReserve.test.ts b/packages/alchemy/test/Cloudflare/Cache/CacheReserve.test.ts index 52a19b72d..5efa5c316 100644 --- a/packages/alchemy/test/Cloudflare/Cache/CacheReserve.test.ts +++ b/packages/alchemy/test/Cloudflare/Cache/CacheReserve.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -60,7 +63,7 @@ const setBaseline = (zoneId: string, value: "on" | "off") => // Both cases mutate the same zone-level Cache Reserve singleton; run them serially so they don't corrupt each other's captured `initialValue` under the global concurrent test config. describe.sequential("CacheReserve", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed SettingUnavailableForPlan error on unentitled zones", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Cache/OriginCloudRegion.test.ts b/packages/alchemy/test/Cloudflare/Cache/OriginCloudRegion.test.ts index eb969ccb0..07137f3d7 100644 --- a/packages/alchemy/test/Cloudflare/Cache/OriginCloudRegion.test.ts +++ b/packages/alchemy/test/Cloudflare/Cache/OriginCloudRegion.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -67,7 +70,7 @@ const getMapping = (zoneId: string, ip: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "creates, updates in place, replaces on ip change, and deletes the mapping", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Cache/RegionalTieredCache.test.ts b/packages/alchemy/test/Cloudflare/Cache/RegionalTieredCache.test.ts index 662636ec2..38956100a 100644 --- a/packages/alchemy/test/Cloudflare/Cache/RegionalTieredCache.test.ts +++ b/packages/alchemy/test/Cloudflare/Cache/RegionalTieredCache.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -60,7 +63,7 @@ const setBaseline = (zoneId: string, value: "on" | "off") => // Both cases mutate the same zone-level Regional Tiered Cache singleton; run them serially so they don't corrupt each other's captured `initialValue` under the global concurrent test config. describe.sequential("RegionalTieredCache", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed SettingUnavailableForPlan error on non-Enterprise zones", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Cache/SmartTieredCache.test.ts b/packages/alchemy/test/Cloudflare/Cache/SmartTieredCache.test.ts index 26667e67e..6126e6f78 100644 --- a/packages/alchemy/test/Cloudflare/Cache/SmartTieredCache.test.ts +++ b/packages/alchemy/test/Cloudflare/Cache/SmartTieredCache.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -63,7 +66,7 @@ const setBaseline = (zoneId: string, value: "on" | "off") => // opposite baselines; run them serially so they don't corrupt each other's // captured `initialValue` under the global concurrent test config. describe.sequential("SmartTieredCache", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "enables Smart Tiered Cache and restores the original value on destroy", (stack) => Effect.gen(function* () { @@ -99,7 +102,7 @@ describe.sequential("SmartTieredCache", () => { }).pipe(logLevel), ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "updates the setting in place and keeps the captured initial value", (stack) => Effect.gen(function* () { @@ -153,21 +156,23 @@ describe.sequential("SmartTieredCache", () => { // API for this per-zone setting, so `list()` enumerates every zone via // `listAllZones` and reads the singleton in each. Assert the result is // non-empty and contains the standing test zone. - test.provider("list enumerates the setting across all zones", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - const provider = yield* Provider.findProvider( - Cloudflare.SmartTieredCache, - ); - const all = yield* provider.list(); - - expect(all.length).toBeGreaterThan(0); - expect(all.some((s) => s.zoneId === zoneId)).toBe(true); - - // `stack` is unused here (the singleton always exists on every zone), - // but keep the destroy bookends so the harness state stays clean. - yield* stack.destroy(); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the setting across all zones", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; + + const provider = yield* Provider.findProvider( + Cloudflare.SmartTieredCache, + ); + const all = yield* provider.list(); + + expect(all.length).toBeGreaterThan(0); + expect(all.some((s) => s.zoneId === zoneId)).toBe(true); + + // `stack` is unused here (the singleton always exists on every zone), + // but keep the destroy bookends so the harness state stays clean. + yield* stack.destroy(); + }).pipe(logLevel), ); }); diff --git a/packages/alchemy/test/Cloudflare/Cache/Variants.test.ts b/packages/alchemy/test/Cloudflare/Cache/Variants.test.ts index 873b4e1b7..edfe5e61f 100644 --- a/packages/alchemy/test/Cloudflare/Cache/Variants.test.ts +++ b/packages/alchemy/test/Cloudflare/Cache/Variants.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -64,7 +67,7 @@ const resetBaseline = (zoneId: string) => ); describe.sequential("Variants", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "creates, updates in place, and deletes the variants setting", (stack) => Effect.gen(function* () { @@ -136,7 +139,7 @@ describe.sequential("Variants", () => { }).pipe(logLevel), ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "deploy is idempotent and converges out-of-band drift", (stack) => Effect.gen(function* () { @@ -199,31 +202,33 @@ describe.sequential("Variants", () => { // zone via `listAllZones` and reads the setting in each, skipping zones // where it was never configured. Deploy the setting on the standing test // zone first so it appears in the enumeration, then assert it is present. - test.provider("list enumerates the configured variants settings", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - yield* stack.destroy(); - yield* resetBaseline(zoneId); - - yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.Variants("ImageVariants", { - zoneId, - jpeg: ["image/webp"], - }); - }), - ); - - const provider = yield* Provider.findProvider(Cloudflare.Variants); - const all = yield* provider.list(); - - expect(all.length).toBeGreaterThan(0); - const entry = all.find((v) => v.zoneId === zoneId); - expect(entry).toBeDefined(); - expect(entry!.value.jpeg).toEqual(["image/webp"]); - - yield* stack.destroy(); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the configured variants settings", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; + + yield* stack.destroy(); + yield* resetBaseline(zoneId); + + yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.Variants("ImageVariants", { + zoneId, + jpeg: ["image/webp"], + }); + }), + ); + + const provider = yield* Provider.findProvider(Cloudflare.Variants); + const all = yield* provider.list(); + + expect(all.length).toBeGreaterThan(0); + const entry = all.find((v) => v.zoneId === zoneId); + expect(entry).toBeDefined(); + expect(entry!.value.jpeg).toEqual(["image/webp"]); + + yield* stack.destroy(); + }).pipe(logLevel), ); }); diff --git a/packages/alchemy/test/Cloudflare/Calls/App.test.ts b/packages/alchemy/test/Cloudflare/Calls/App.test.ts index b840a07fe..a89c81d7a 100644 --- a/packages/alchemy/test/Cloudflare/Calls/App.test.ts +++ b/packages/alchemy/test/Cloudflare/Calls/App.test.ts @@ -9,6 +9,9 @@ import * as Redacted from "effect/Redacted"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -43,141 +46,149 @@ const expectGone = (accountId: string, appId: string) => }), ); -test.provider("create and delete an app with default name", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create and delete an app with default name", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - yield* stack.destroy(); + yield* stack.destroy(); - const app = yield* stack.deploy(Cloudflare.CallsApp("DefaultApp", {})); + const app = yield* stack.deploy(Cloudflare.CallsApp("DefaultApp", {})); - expect(app.appId).toBeTruthy(); - expect(Redacted.value(app.secret)).toBeTruthy(); - expect(app.accountId).toEqual(accountId); - expect(app.name).toBeTruthy(); + expect(app.appId).toBeTruthy(); + expect(Redacted.value(app.secret)).toBeTruthy(); + expect(app.accountId).toEqual(accountId); + expect(app.name).toBeTruthy(); - const live = yield* getApp(accountId, app.appId); - expect(live.uid).toEqual(app.appId); - expect(live.name).toEqual(app.name); + const live = yield* getApp(accountId, app.appId); + expect(live.uid).toEqual(app.appId); + expect(live.name).toEqual(app.name); - yield* stack.destroy(); + yield* stack.destroy(); - yield* expectGone(accountId, app.appId); - }).pipe(logLevel), + yield* expectGone(accountId, app.appId); + }).pipe(logLevel), ); -test.provider("update name in place (same appId, secret preserved)", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const initial = yield* stack.deploy( - Cloudflare.CallsApp("UpdateApp", { - name: "alchemy-calls-app-update", - }), - ); - - expect(initial.name).toEqual("alchemy-calls-app-update"); - const initialSecret = Redacted.value(initial.secret); - expect(initialSecret).toBeTruthy(); - - const updated = yield* stack.deploy( - Cloudflare.CallsApp("UpdateApp", { - name: "alchemy-calls-app-update-v2", - }), - ); - - // Same app mutated in place — not a replacement — and the - // create-only secret is carried forward across the update. - expect(updated.appId).toEqual(initial.appId); - expect(updated.name).toEqual("alchemy-calls-app-update-v2"); - expect(Redacted.value(updated.secret)).toEqual(initialSecret); - - const live = yield* getApp(accountId, updated.appId); - expect(live.name).toEqual("alchemy-calls-app-update-v2"); - - // Redeploying identical props is a no-op (still the same app). - const noop = yield* stack.deploy( - Cloudflare.CallsApp("UpdateApp", { - name: "alchemy-calls-app-update-v2", - }), - ); - expect(noop.appId).toEqual(initial.appId); - expect(Redacted.value(noop.secret)).toEqual(initialSecret); - - yield* stack.destroy(); - - yield* expectGone(accountId, initial.appId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "update name in place (same appId, secret preserved)", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const initial = yield* stack.deploy( + Cloudflare.CallsApp("UpdateApp", { + name: "alchemy-calls-app-update", + }), + ); + + expect(initial.name).toEqual("alchemy-calls-app-update"); + const initialSecret = Redacted.value(initial.secret); + expect(initialSecret).toBeTruthy(); + + const updated = yield* stack.deploy( + Cloudflare.CallsApp("UpdateApp", { + name: "alchemy-calls-app-update-v2", + }), + ); + + // Same app mutated in place — not a replacement — and the + // create-only secret is carried forward across the update. + expect(updated.appId).toEqual(initial.appId); + expect(updated.name).toEqual("alchemy-calls-app-update-v2"); + expect(Redacted.value(updated.secret)).toEqual(initialSecret); + + const live = yield* getApp(accountId, updated.appId); + expect(live.name).toEqual("alchemy-calls-app-update-v2"); + + // Redeploying identical props is a no-op (still the same app). + const noop = yield* stack.deploy( + Cloudflare.CallsApp("UpdateApp", { + name: "alchemy-calls-app-update-v2", + }), + ); + expect(noop.appId).toEqual(initial.appId); + expect(Redacted.value(noop.secret)).toEqual(initialSecret); + + yield* stack.destroy(); + + yield* expectGone(accountId, initial.appId); + }).pipe(logLevel), ); -test.provider("list enumerates the deployed app", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed app", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - yield* stack.destroy(); + yield* stack.destroy(); - const app = yield* stack.deploy( - Cloudflare.CallsApp("ListApp", { - name: "alchemy-calls-app-list", - }), - ); + const app = yield* stack.deploy( + Cloudflare.CallsApp("ListApp", { + name: "alchemy-calls-app-list", + }), + ); - const provider = yield* Provider.findProvider(Cloudflare.CallsApp); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider(Cloudflare.CallsApp); + const all = yield* provider.list(); - // The account-scoped enumeration must contain the just-deployed app. - const found = all.find((a) => a.appId === app.appId); - expect(found).toBeDefined(); - expect(found?.accountId).toEqual(accountId); - expect(found?.name).toEqual("alchemy-calls-app-list"); + // The account-scoped enumeration must contain the just-deployed app. + const found = all.find((a) => a.appId === app.appId); + expect(found).toBeDefined(); + expect(found?.accountId).toEqual(accountId); + expect(found?.name).toEqual("alchemy-calls-app-list"); - yield* stack.destroy(); + yield* stack.destroy(); - yield* expectGone(accountId, app.appId); - }).pipe(logLevel), + yield* expectGone(accountId, app.appId); + }).pipe(logLevel), ); -test.provider("recreates after out-of-band delete", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const app = yield* stack.deploy( - Cloudflare.CallsApp("HealApp", { - name: "alchemy-calls-app-heal", - }), - ); - - // Delete the app out-of-band. A redeploy with identical props is a - // planner no-op, so change a prop to force reconcile — it must observe - // the app as missing and recreate it instead of failing on the 20007. - yield* calls.deleteSfu({ accountId, appId: app.appId }).pipe( - Effect.retry({ - while: (e) => e._tag === "Forbidden", - schedule: Schedule.exponential("500 millis"), - times: 8, - }), - ); - - const healed = yield* stack.deploy( - Cloudflare.CallsApp("HealApp", { - name: "alchemy-calls-app-heal-v2", - }), - ); - - expect(healed.appId).not.toEqual(app.appId); - expect(Redacted.value(healed.secret)).toBeTruthy(); - expect(Redacted.value(healed.secret)).not.toEqual( - Redacted.value(app.secret), - ); - const live = yield* getApp(accountId, healed.appId); - expect(live.name).toEqual("alchemy-calls-app-heal-v2"); - - yield* stack.destroy(); - - yield* expectGone(accountId, healed.appId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "recreates after out-of-band delete", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const app = yield* stack.deploy( + Cloudflare.CallsApp("HealApp", { + name: "alchemy-calls-app-heal", + }), + ); + + // Delete the app out-of-band. A redeploy with identical props is a + // planner no-op, so change a prop to force reconcile — it must observe + // the app as missing and recreate it instead of failing on the 20007. + yield* calls.deleteSfu({ accountId, appId: app.appId }).pipe( + Effect.retry({ + while: (e) => e._tag === "Forbidden", + schedule: Schedule.exponential("500 millis"), + times: 8, + }), + ); + + const healed = yield* stack.deploy( + Cloudflare.CallsApp("HealApp", { + name: "alchemy-calls-app-heal-v2", + }), + ); + + expect(healed.appId).not.toEqual(app.appId); + expect(Redacted.value(healed.secret)).toBeTruthy(); + expect(Redacted.value(healed.secret)).not.toEqual( + Redacted.value(app.secret), + ); + const live = yield* getApp(accountId, healed.appId); + expect(live.name).toEqual("alchemy-calls-app-heal-v2"); + + yield* stack.destroy(); + + yield* expectGone(accountId, healed.appId); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Calls/TurnKey.test.ts b/packages/alchemy/test/Cloudflare/Calls/TurnKey.test.ts index 182b63f68..59439c474 100644 --- a/packages/alchemy/test/Cloudflare/Calls/TurnKey.test.ts +++ b/packages/alchemy/test/Cloudflare/Calls/TurnKey.test.ts @@ -9,6 +9,9 @@ import * as Redacted from "effect/Redacted"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -43,137 +46,147 @@ const expectGone = (accountId: string, keyId: string) => }), ); -test.provider("create and delete a TURN key with default name", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create and delete a TURN key with default name", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - yield* stack.destroy(); + yield* stack.destroy(); - const turnKey = yield* stack.deploy( - Cloudflare.CallsTurnKey("DefaultTurnKey", {}), - ); + const turnKey = yield* stack.deploy( + Cloudflare.CallsTurnKey("DefaultTurnKey", {}), + ); - expect(turnKey.keyId).toBeTruthy(); - expect(Redacted.value(turnKey.key)).toBeTruthy(); - expect(turnKey.accountId).toEqual(accountId); - expect(turnKey.name).toBeTruthy(); + expect(turnKey.keyId).toBeTruthy(); + expect(Redacted.value(turnKey.key)).toBeTruthy(); + expect(turnKey.accountId).toEqual(accountId); + expect(turnKey.name).toBeTruthy(); - const live = yield* getTurnKey(accountId, turnKey.keyId); - expect(live.uid).toEqual(turnKey.keyId); - expect(live.name).toEqual(turnKey.name); + const live = yield* getTurnKey(accountId, turnKey.keyId); + expect(live.uid).toEqual(turnKey.keyId); + expect(live.name).toEqual(turnKey.name); - yield* stack.destroy(); + yield* stack.destroy(); - yield* expectGone(accountId, turnKey.keyId); - }).pipe(logLevel), + yield* expectGone(accountId, turnKey.keyId); + }).pipe(logLevel), ); -test.provider("update name in place (same keyId, key preserved)", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const initial = yield* stack.deploy( - Cloudflare.CallsTurnKey("UpdateTurnKey", { - name: "alchemy-calls-turn-update", - }), - ); - - expect(initial.name).toEqual("alchemy-calls-turn-update"); - const initialKey = Redacted.value(initial.key); - expect(initialKey).toBeTruthy(); - - const updated = yield* stack.deploy( - Cloudflare.CallsTurnKey("UpdateTurnKey", { - name: "alchemy-calls-turn-update-v2", - }), - ); - - // Same TURN key mutated in place — not a replacement — and the - // create-only key is carried forward across the update. - expect(updated.keyId).toEqual(initial.keyId); - expect(updated.name).toEqual("alchemy-calls-turn-update-v2"); - expect(Redacted.value(updated.key)).toEqual(initialKey); - - const live = yield* getTurnKey(accountId, updated.keyId); - expect(live.name).toEqual("alchemy-calls-turn-update-v2"); - - // Redeploying identical props is a no-op (still the same key). - const noop = yield* stack.deploy( - Cloudflare.CallsTurnKey("UpdateTurnKey", { - name: "alchemy-calls-turn-update-v2", - }), - ); - expect(noop.keyId).toEqual(initial.keyId); - expect(Redacted.value(noop.key)).toEqual(initialKey); - - yield* stack.destroy(); - - yield* expectGone(accountId, initial.keyId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "update name in place (same keyId, key preserved)", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const initial = yield* stack.deploy( + Cloudflare.CallsTurnKey("UpdateTurnKey", { + name: "alchemy-calls-turn-update", + }), + ); + + expect(initial.name).toEqual("alchemy-calls-turn-update"); + const initialKey = Redacted.value(initial.key); + expect(initialKey).toBeTruthy(); + + const updated = yield* stack.deploy( + Cloudflare.CallsTurnKey("UpdateTurnKey", { + name: "alchemy-calls-turn-update-v2", + }), + ); + + // Same TURN key mutated in place — not a replacement — and the + // create-only key is carried forward across the update. + expect(updated.keyId).toEqual(initial.keyId); + expect(updated.name).toEqual("alchemy-calls-turn-update-v2"); + expect(Redacted.value(updated.key)).toEqual(initialKey); + + const live = yield* getTurnKey(accountId, updated.keyId); + expect(live.name).toEqual("alchemy-calls-turn-update-v2"); + + // Redeploying identical props is a no-op (still the same key). + const noop = yield* stack.deploy( + Cloudflare.CallsTurnKey("UpdateTurnKey", { + name: "alchemy-calls-turn-update-v2", + }), + ); + expect(noop.keyId).toEqual(initial.keyId); + expect(Redacted.value(noop.key)).toEqual(initialKey); + + yield* stack.destroy(); + + yield* expectGone(accountId, initial.keyId); + }).pipe(logLevel), ); -test.provider("recreates after out-of-band delete", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const turnKey = yield* stack.deploy( - Cloudflare.CallsTurnKey("HealTurnKey", { - name: "alchemy-calls-turn-heal", - }), - ); - - // Delete the TURN key out-of-band. A redeploy with identical props is - // a planner no-op, so change a prop to force reconcile — it must - // observe the key as missing and recreate it instead of failing on - // the 20008. - yield* calls.deleteTurn({ accountId, keyId: turnKey.keyId }).pipe( - Effect.retry({ - while: (e) => e._tag === "Forbidden", - schedule: Schedule.exponential("500 millis"), - times: 8, - }), - ); - - const healed = yield* stack.deploy( - Cloudflare.CallsTurnKey("HealTurnKey", { - name: "alchemy-calls-turn-heal-v2", - }), - ); - - expect(healed.keyId).not.toEqual(turnKey.keyId); - expect(Redacted.value(healed.key)).toBeTruthy(); - expect(Redacted.value(healed.key)).not.toEqual(Redacted.value(turnKey.key)); - const live = yield* getTurnKey(accountId, healed.keyId); - expect(live.name).toEqual("alchemy-calls-turn-heal-v2"); - - yield* stack.destroy(); - - yield* expectGone(accountId, healed.keyId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "recreates after out-of-band delete", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const turnKey = yield* stack.deploy( + Cloudflare.CallsTurnKey("HealTurnKey", { + name: "alchemy-calls-turn-heal", + }), + ); + + // Delete the TURN key out-of-band. A redeploy with identical props is + // a planner no-op, so change a prop to force reconcile — it must + // observe the key as missing and recreate it instead of failing on + // the 20008. + yield* calls.deleteTurn({ accountId, keyId: turnKey.keyId }).pipe( + Effect.retry({ + while: (e) => e._tag === "Forbidden", + schedule: Schedule.exponential("500 millis"), + times: 8, + }), + ); + + const healed = yield* stack.deploy( + Cloudflare.CallsTurnKey("HealTurnKey", { + name: "alchemy-calls-turn-heal-v2", + }), + ); + + expect(healed.keyId).not.toEqual(turnKey.keyId); + expect(Redacted.value(healed.key)).toBeTruthy(); + expect(Redacted.value(healed.key)).not.toEqual( + Redacted.value(turnKey.key), + ); + const live = yield* getTurnKey(accountId, healed.keyId); + expect(live.name).toEqual("alchemy-calls-turn-heal-v2"); + + yield* stack.destroy(); + + yield* expectGone(accountId, healed.keyId); + }).pipe(logLevel), ); // Canonical `list()` test (account collection): deploy a TURN key, then // enumerate every TURN key in the account via the provider's `list()` and // assert the deployed key is present in the exhaustively-paginated result. -test.provider("list enumerates the deployed TURN key", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed TURN key", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); - const deployed = yield* stack.deploy( - Cloudflare.CallsTurnKey("ListTurnKey", { - name: "alchemy-calls-turn-list", - }), - ); + const deployed = yield* stack.deploy( + Cloudflare.CallsTurnKey("ListTurnKey", { + name: "alchemy-calls-turn-list", + }), + ); - const provider = yield* Provider.findProvider(Cloudflare.CallsTurnKey); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider(Cloudflare.CallsTurnKey); + const all = yield* provider.list(); - expect(all.some((x) => x.keyId === deployed.keyId)).toBe(true); + expect(all.some((x) => x.keyId === deployed.keyId)).toBe(true); - yield* stack.destroy(); - }).pipe(logLevel), + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/CertificateAuthorities/HostnameAssociation.test.ts b/packages/alchemy/test/Cloudflare/CertificateAuthorities/HostnameAssociation.test.ts index ef75648f4..f9bb655c5 100644 --- a/packages/alchemy/test/Cloudflare/CertificateAuthorities/HostnameAssociation.test.ts +++ b/packages/alchemy/test/Cloudflare/CertificateAuthorities/HostnameAssociation.test.ts @@ -12,6 +12,9 @@ import * as Schedule from "effect/Schedule"; import { CA_CERT_1, CA_CERT_2 } from "./fixtures/certs.ts"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -109,7 +112,7 @@ const waitForCertDelete = (accountId: string, mtlsCertificateId: string) => ); describe.sequential("HostnameAssociation", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "pins Managed CA hostnames, updates in place, and clears on destroy", (stack) => Effect.gen(function* () { @@ -157,7 +160,7 @@ describe.sequential("HostnameAssociation", () => { }).pipe(logLevel), ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "associates hostnames with an uploaded CA and destroys before the cert", (stack) => Effect.gen(function* () { @@ -199,7 +202,7 @@ describe.sequential("HostnameAssociation", () => { }).pipe(logLevel), ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "changing the certificate key replaces the association", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/ClientCertificate/ClientCertificate.test.ts b/packages/alchemy/test/Cloudflare/ClientCertificate/ClientCertificate.test.ts index 28d69f55c..503e02569 100644 --- a/packages/alchemy/test/Cloudflare/ClientCertificate/ClientCertificate.test.ts +++ b/packages/alchemy/test/Cloudflare/ClientCertificate/ClientCertificate.test.ts @@ -11,6 +11,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { CSR_A, CSR_B } from "./fixtures/csr.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -66,7 +69,7 @@ const waitUntilRevoked = (zoneId: string, clientCertificateId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create signs the CSR, destroy revokes the certificate", (stack) => Effect.gen(function* () { @@ -134,7 +137,7 @@ test.provider( { timeout: 240_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "changing validityDays replaces — new id issued, old certificate revoked", (stack) => Effect.gen(function* () { @@ -200,7 +203,7 @@ test.provider( { timeout: 300_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates client certificates across zones", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/CloudConnector/Rules.test.ts b/packages/alchemy/test/Cloudflare/CloudConnector/Rules.test.ts index 431d1edde..828b7cac9 100644 --- a/packages/alchemy/test/Cloudflare/CloudConnector/Rules.test.ts +++ b/packages/alchemy/test/Cloudflare/CloudConnector/Rules.test.ts @@ -11,6 +11,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -69,7 +72,7 @@ const purgeRules = (zoneId: string) => ); describe.sequential("Rules", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "cloud connector rules — create, update in place, destroy clears the list", (stack) => Effect.gen(function* () { @@ -156,7 +159,7 @@ describe.sequential("Rules", () => { { timeout: 180_000 }, ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "no-op redeploy leaves the rule list untouched and ids stable", (stack) => Effect.gen(function* () { @@ -205,7 +208,7 @@ describe.sequential("Rules", () => { // every zone via `listAllZones` and reads its rules. Deploy a rule on the // standing test zone, then assert the test zone appears in the result with // the rule we created. - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates rule lists across all zones", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/CloudforceOne/ScanConfig.test.ts b/packages/alchemy/test/Cloudflare/CloudforceOne/ScanConfig.test.ts index f65c0271e..6c319f17c 100644 --- a/packages/alchemy/test/Cloudflare/CloudforceOne/ScanConfig.test.ts +++ b/packages/alchemy/test/Cloudflare/CloudforceOne/ScanConfig.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -32,7 +35,7 @@ const probeEntitlement = (accountId: string) => // Unentitlement probe — pins the typed Unauthorized rejection ("needs cfone.port_scan // entitlement") and skips on entitled accounts, where the API would accept the calls. -test.provider.skipIf(entitled)( +test.provider.skipIf(entitled || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "unentitled accounts surface the typed Unauthorized error", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Connectivity/DirectoryService.test.ts b/packages/alchemy/test/Cloudflare/Connectivity/DirectoryService.test.ts index 3c1d889a6..ed690b093 100644 --- a/packages/alchemy/test/Cloudflare/Connectivity/DirectoryService.test.ts +++ b/packages/alchemy/test/Cloudflare/Connectivity/DirectoryService.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -44,299 +47,309 @@ const expectGone = (accountId: string, serviceId: string) => }), ); -test.provider("tcp service lifecycle: create, update, host switch", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - // Create — tcp database service on a tunnel-backed IPv4 host. - const { tunnel, service } = yield* stack.deploy( - Effect.gen(function* () { - const tunnel = yield* Cloudflare.Tunnel("DirSvcTunnel", { - ingress: [{ service: "http://localhost:8080" }], - adopt: true, - }); - const service = yield* Cloudflare.DirectoryService("PgService", { - name: "alchemy-connectivity-dirsvc-tcp", - type: "tcp", - tcpPort: 5432, - appProtocol: "postgresql", - host: { - ipv4: "10.10.0.21", - network: { tunnelId: tunnel.tunnelId }, - }, - }); - return { tunnel, service }; - }), - ); - - expect(service.serviceId).toBeDefined(); - expect(service.accountId).toEqual(accountId); - expect(service.name).toEqual("alchemy-connectivity-dirsvc-tcp"); - expect(service.type).toEqual("tcp"); - expect(service.tcpPort).toEqual(5432); - expect(service.appProtocol).toEqual("postgresql"); - expect(service.host).toMatchObject({ - ipv4: "10.10.0.21", - network: { tunnelId: tunnel.tunnelId }, - }); - - // Out-of-band verify against the live API. - const live = yield* getService(accountId, service.serviceId); - expect(live.serviceId).toEqual(service.serviceId); - expect(live.name).toEqual("alchemy-connectivity-dirsvc-tcp"); - - // Update in place — new name and port, same serviceId. - const updated = yield* stack.deploy( - Effect.gen(function* () { - const tunnel = yield* Cloudflare.Tunnel("DirSvcTunnel", { - ingress: [{ service: "http://localhost:8080" }], - adopt: true, - }); - return yield* Cloudflare.DirectoryService("PgService", { - name: "alchemy-connectivity-dirsvc-tcp-v2", - type: "tcp", - tcpPort: 5433, - appProtocol: "postgresql", - host: { - ipv4: "10.10.0.21", - network: { tunnelId: tunnel.tunnelId }, - }, - }); - }), - ); - - expect(updated.serviceId).toEqual(service.serviceId); - expect(updated.name).toEqual("alchemy-connectivity-dirsvc-tcp-v2"); - expect(updated.tcpPort).toEqual(5433); - - const liveUpdated = yield* getService(accountId, service.serviceId); - expect(liveUpdated.name).toEqual("alchemy-connectivity-dirsvc-tcp-v2"); - - // Switch the host variant (ipv4 -> hostname) — still an in-place - // update, same serviceId. Keep the tunnel deployed across steps. - const rehosted = yield* stack.deploy( - Effect.gen(function* () { - const tunnel = yield* Cloudflare.Tunnel("DirSvcTunnel", { - ingress: [{ service: "http://localhost:8080" }], - adopt: true, - }); - return yield* Cloudflare.DirectoryService("PgService", { - name: "alchemy-connectivity-dirsvc-tcp-v2", - type: "tcp", - tcpPort: 5433, - appProtocol: "postgresql", - host: { - hostname: "db.internal", - resolverNetwork: { tunnelId: tunnel.tunnelId }, - }, - }); - }), - ); - - expect(rehosted.serviceId).toEqual(service.serviceId); - expect(rehosted.host).toMatchObject({ - hostname: "db.internal", - resolverNetwork: { tunnelId: tunnel.tunnelId }, - }); - - // Redeploying identical props is a no-op (same serviceId, no drift). - const noop = yield* stack.deploy( - Effect.gen(function* () { - const tunnel = yield* Cloudflare.Tunnel("DirSvcTunnel", { - ingress: [{ service: "http://localhost:8080" }], - adopt: true, - }); - return yield* Cloudflare.DirectoryService("PgService", { - name: "alchemy-connectivity-dirsvc-tcp-v2", - type: "tcp", - tcpPort: 5433, - appProtocol: "postgresql", - host: { - hostname: "db.internal", - resolverNetwork: { tunnelId: tunnel.tunnelId }, - }, - }); - }), - ); - expect(noop.serviceId).toEqual(service.serviceId); - - yield* stack.destroy(); - yield* expectGone(accountId, service.serviceId); - - // Destroy is idempotent — a second destroy of an empty stack is a no-op. - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "tcp service lifecycle: create, update, host switch", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + // Create — tcp database service on a tunnel-backed IPv4 host. + const { tunnel, service } = yield* stack.deploy( + Effect.gen(function* () { + const tunnel = yield* Cloudflare.Tunnel("DirSvcTunnel", { + ingress: [{ service: "http://localhost:8080" }], + adopt: true, + }); + const service = yield* Cloudflare.DirectoryService("PgService", { + name: "alchemy-connectivity-dirsvc-tcp", + type: "tcp", + tcpPort: 5432, + appProtocol: "postgresql", + host: { + ipv4: "10.10.0.21", + network: { tunnelId: tunnel.tunnelId }, + }, + }); + return { tunnel, service }; + }), + ); + + expect(service.serviceId).toBeDefined(); + expect(service.accountId).toEqual(accountId); + expect(service.name).toEqual("alchemy-connectivity-dirsvc-tcp"); + expect(service.type).toEqual("tcp"); + expect(service.tcpPort).toEqual(5432); + expect(service.appProtocol).toEqual("postgresql"); + expect(service.host).toMatchObject({ + ipv4: "10.10.0.21", + network: { tunnelId: tunnel.tunnelId }, + }); + + // Out-of-band verify against the live API. + const live = yield* getService(accountId, service.serviceId); + expect(live.serviceId).toEqual(service.serviceId); + expect(live.name).toEqual("alchemy-connectivity-dirsvc-tcp"); + + // Update in place — new name and port, same serviceId. + const updated = yield* stack.deploy( + Effect.gen(function* () { + const tunnel = yield* Cloudflare.Tunnel("DirSvcTunnel", { + ingress: [{ service: "http://localhost:8080" }], + adopt: true, + }); + return yield* Cloudflare.DirectoryService("PgService", { + name: "alchemy-connectivity-dirsvc-tcp-v2", + type: "tcp", + tcpPort: 5433, + appProtocol: "postgresql", + host: { + ipv4: "10.10.0.21", + network: { tunnelId: tunnel.tunnelId }, + }, + }); + }), + ); + + expect(updated.serviceId).toEqual(service.serviceId); + expect(updated.name).toEqual("alchemy-connectivity-dirsvc-tcp-v2"); + expect(updated.tcpPort).toEqual(5433); + + const liveUpdated = yield* getService(accountId, service.serviceId); + expect(liveUpdated.name).toEqual("alchemy-connectivity-dirsvc-tcp-v2"); + + // Switch the host variant (ipv4 -> hostname) — still an in-place + // update, same serviceId. Keep the tunnel deployed across steps. + const rehosted = yield* stack.deploy( + Effect.gen(function* () { + const tunnel = yield* Cloudflare.Tunnel("DirSvcTunnel", { + ingress: [{ service: "http://localhost:8080" }], + adopt: true, + }); + return yield* Cloudflare.DirectoryService("PgService", { + name: "alchemy-connectivity-dirsvc-tcp-v2", + type: "tcp", + tcpPort: 5433, + appProtocol: "postgresql", + host: { + hostname: "db.internal", + resolverNetwork: { tunnelId: tunnel.tunnelId }, + }, + }); + }), + ); + + expect(rehosted.serviceId).toEqual(service.serviceId); + expect(rehosted.host).toMatchObject({ + hostname: "db.internal", + resolverNetwork: { tunnelId: tunnel.tunnelId }, + }); + + // Redeploying identical props is a no-op (same serviceId, no drift). + const noop = yield* stack.deploy( + Effect.gen(function* () { + const tunnel = yield* Cloudflare.Tunnel("DirSvcTunnel", { + ingress: [{ service: "http://localhost:8080" }], + adopt: true, + }); + return yield* Cloudflare.DirectoryService("PgService", { + name: "alchemy-connectivity-dirsvc-tcp-v2", + type: "tcp", + tcpPort: 5433, + appProtocol: "postgresql", + host: { + hostname: "db.internal", + resolverNetwork: { tunnelId: tunnel.tunnelId }, + }, + }); + }), + ); + expect(noop.serviceId).toEqual(service.serviceId); + + yield* stack.destroy(); + yield* expectGone(accountId, service.serviceId); + + // Destroy is idempotent — a second destroy of an empty stack is a no-op. + yield* stack.destroy(); + }).pipe(logLevel), ); -test.provider("http service with explicit ports and default name", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const service = yield* stack.deploy( - Effect.gen(function* () { - const tunnel = yield* Cloudflare.Tunnel("DirSvcHttpTunnel", { - ingress: [{ service: "http://localhost:8080" }], - adopt: true, - }); - return yield* Cloudflare.DirectoryService("HttpService", { - type: "http", - httpPort: 8080, - httpsPort: 8443, - host: { - hostname: "api.internal", - resolverNetwork: { - tunnelId: tunnel.tunnelId, - resolverIps: ["10.0.0.53"], +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "http service with explicit ports and default name", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const service = yield* stack.deploy( + Effect.gen(function* () { + const tunnel = yield* Cloudflare.Tunnel("DirSvcHttpTunnel", { + ingress: [{ service: "http://localhost:8080" }], + adopt: true, + }); + return yield* Cloudflare.DirectoryService("HttpService", { + type: "http", + httpPort: 8080, + httpsPort: 8443, + host: { + hostname: "api.internal", + resolverNetwork: { + tunnelId: tunnel.tunnelId, + resolverIps: ["10.0.0.53"], + }, }, - }, - }); - }), - ); - - expect(service.serviceId).toBeDefined(); - // Name was omitted — the provider generates one from app/stage/id. - expect(service.name).toBeTruthy(); - expect(service.type).toEqual("http"); - expect(service.httpPort).toEqual(8080); - expect(service.httpsPort).toEqual(8443); - expect(service.host).toMatchObject({ - hostname: "api.internal", - resolverNetwork: { resolverIps: ["10.0.0.53"] }, - }); - - const live = yield* getService(accountId, service.serviceId); - expect(live.serviceId).toEqual(service.serviceId); - - // Update ports in place. - const updated = yield* stack.deploy( - Effect.gen(function* () { - const tunnel = yield* Cloudflare.Tunnel("DirSvcHttpTunnel", { - ingress: [{ service: "http://localhost:8080" }], - adopt: true, - }); - return yield* Cloudflare.DirectoryService("HttpService", { - type: "http", - httpPort: 3000, - httpsPort: 3001, - host: { - hostname: "api.internal", - resolverNetwork: { - tunnelId: tunnel.tunnelId, - resolverIps: ["10.0.0.53"], + }); + }), + ); + + expect(service.serviceId).toBeDefined(); + // Name was omitted — the provider generates one from app/stage/id. + expect(service.name).toBeTruthy(); + expect(service.type).toEqual("http"); + expect(service.httpPort).toEqual(8080); + expect(service.httpsPort).toEqual(8443); + expect(service.host).toMatchObject({ + hostname: "api.internal", + resolverNetwork: { resolverIps: ["10.0.0.53"] }, + }); + + const live = yield* getService(accountId, service.serviceId); + expect(live.serviceId).toEqual(service.serviceId); + + // Update ports in place. + const updated = yield* stack.deploy( + Effect.gen(function* () { + const tunnel = yield* Cloudflare.Tunnel("DirSvcHttpTunnel", { + ingress: [{ service: "http://localhost:8080" }], + adopt: true, + }); + return yield* Cloudflare.DirectoryService("HttpService", { + type: "http", + httpPort: 3000, + httpsPort: 3001, + host: { + hostname: "api.internal", + resolverNetwork: { + tunnelId: tunnel.tunnelId, + resolverIps: ["10.0.0.53"], + }, }, - }, - }); - }), - ); - - expect(updated.serviceId).toEqual(service.serviceId); - expect(updated.httpPort).toEqual(3000); - expect(updated.httpsPort).toEqual(3001); - - yield* stack.destroy(); - yield* expectGone(accountId, service.serviceId); - }).pipe(logLevel), + }); + }), + ); + + expect(updated.serviceId).toEqual(service.serviceId); + expect(updated.httpPort).toEqual(3000); + expect(updated.httpsPort).toEqual(3001); + + yield* stack.destroy(); + yield* expectGone(accountId, service.serviceId); + }).pipe(logLevel), ); -test.provider("list enumerates the deployed directory service", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const service = yield* stack.deploy( - Effect.gen(function* () { - const tunnel = yield* Cloudflare.Tunnel("DirSvcListTunnel", { - ingress: [{ service: "http://localhost:8080" }], - adopt: true, - }); - return yield* Cloudflare.DirectoryService("ListService", { - name: "alchemy-connectivity-dirsvc-list", - type: "tcp", - tcpPort: 5432, - host: { - ipv4: "10.20.0.21", - network: { tunnelId: tunnel.tunnelId }, - }, - }); - }), - ); - - const provider = yield* Provider.findProvider(Cloudflare.DirectoryService); - const all = yield* provider.list(); - - expect(all.some((s) => s.serviceId === service.serviceId)).toBe(true); - const found = all.find((s) => s.serviceId === service.serviceId)!; - expect(found.name).toEqual("alchemy-connectivity-dirsvc-list"); - expect(found.type).toEqual("tcp"); - - yield* stack.destroy(); - yield* expectGone(service.accountId, service.serviceId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed directory service", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const service = yield* stack.deploy( + Effect.gen(function* () { + const tunnel = yield* Cloudflare.Tunnel("DirSvcListTunnel", { + ingress: [{ service: "http://localhost:8080" }], + adopt: true, + }); + return yield* Cloudflare.DirectoryService("ListService", { + name: "alchemy-connectivity-dirsvc-list", + type: "tcp", + tcpPort: 5432, + host: { + ipv4: "10.20.0.21", + network: { tunnelId: tunnel.tunnelId }, + }, + }); + }), + ); + + const provider = yield* Provider.findProvider( + Cloudflare.DirectoryService, + ); + const all = yield* provider.list(); + + expect(all.some((s) => s.serviceId === service.serviceId)).toBe(true); + const found = all.find((s) => s.serviceId === service.serviceId)!; + expect(found.name).toEqual("alchemy-connectivity-dirsvc-list"); + expect(found.type).toEqual("tcp"); + + yield* stack.destroy(); + yield* expectGone(service.accountId, service.serviceId); + }).pipe(logLevel), ); -test.provider("recreates after out-of-band delete", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const service = yield* stack.deploy( - Effect.gen(function* () { - const tunnel = yield* Cloudflare.Tunnel("DirSvcHealTunnel", { - ingress: [{ service: "http://localhost:8080" }], - adopt: true, - }); - return yield* Cloudflare.DirectoryService("HealService", { - name: "alchemy-connectivity-dirsvc-heal", - type: "http", - httpPort: 8080, - host: { - hostname: "heal.internal", - resolverNetwork: { tunnelId: tunnel.tunnelId }, - }, - }); - }), - ); - - // Delete the service out-of-band; a redeploy with changed props must - // observe it as missing and recreate instead of failing on a 404. - yield* connectivity - .deleteDirectoryService({ accountId, serviceId: service.serviceId }) - .pipe( - Effect.retry({ - while: (e) => e._tag === "Forbidden", - schedule: Schedule.exponential("500 millis"), - times: 8, +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "recreates after out-of-band delete", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const service = yield* stack.deploy( + Effect.gen(function* () { + const tunnel = yield* Cloudflare.Tunnel("DirSvcHealTunnel", { + ingress: [{ service: "http://localhost:8080" }], + adopt: true, + }); + return yield* Cloudflare.DirectoryService("HealService", { + name: "alchemy-connectivity-dirsvc-heal", + type: "http", + httpPort: 8080, + host: { + hostname: "heal.internal", + resolverNetwork: { tunnelId: tunnel.tunnelId }, + }, + }); }), ); - const healed = yield* stack.deploy( - Effect.gen(function* () { - const tunnel = yield* Cloudflare.Tunnel("DirSvcHealTunnel", { - ingress: [{ service: "http://localhost:8080" }], - adopt: true, - }); - return yield* Cloudflare.DirectoryService("HealService", { - name: "alchemy-connectivity-dirsvc-heal", - type: "http", - httpPort: 9090, - host: { - hostname: "heal.internal", - resolverNetwork: { tunnelId: tunnel.tunnelId }, - }, - }); - }), - ); - - expect(healed.serviceId).not.toEqual(service.serviceId); - expect(healed.httpPort).toEqual(9090); - const live = yield* getService(accountId, healed.serviceId); - expect(live.name).toEqual("alchemy-connectivity-dirsvc-heal"); - - yield* stack.destroy(); - yield* expectGone(accountId, healed.serviceId); - }).pipe(logLevel), + // Delete the service out-of-band; a redeploy with changed props must + // observe it as missing and recreate instead of failing on a 404. + yield* connectivity + .deleteDirectoryService({ accountId, serviceId: service.serviceId }) + .pipe( + Effect.retry({ + while: (e) => e._tag === "Forbidden", + schedule: Schedule.exponential("500 millis"), + times: 8, + }), + ); + + const healed = yield* stack.deploy( + Effect.gen(function* () { + const tunnel = yield* Cloudflare.Tunnel("DirSvcHealTunnel", { + ingress: [{ service: "http://localhost:8080" }], + adopt: true, + }); + return yield* Cloudflare.DirectoryService("HealService", { + name: "alchemy-connectivity-dirsvc-heal", + type: "http", + httpPort: 9090, + host: { + hostname: "heal.internal", + resolverNetwork: { tunnelId: tunnel.tunnelId }, + }, + }); + }), + ); + + expect(healed.serviceId).not.toEqual(service.serviceId); + expect(healed.httpPort).toEqual(9090); + const live = yield* getService(accountId, healed.serviceId); + expect(live.name).toEqual("alchemy-connectivity-dirsvc-heal"); + + yield* stack.destroy(); + yield* expectGone(accountId, healed.serviceId); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Container/Container.test.ts b/packages/alchemy/test/Cloudflare/Container/Container.test.ts new file mode 100644 index 000000000..b8e407724 --- /dev/null +++ b/packages/alchemy/test/Cloudflare/Container/Container.test.ts @@ -0,0 +1,146 @@ +import * as Cloudflare from "@/Cloudflare"; +import * as Test from "@/Test/Vitest"; +import { describe, expect } from "@effect/vitest"; +import * as Effect from "effect/Effect"; +import { MinimumLogLevel } from "effect/References"; +import * as Schedule from "effect/Schedule"; +import * as HttpClient from "effect/unstable/http/HttpClient"; +import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"; +import EffectfulStack from "./fixtures/effectful/stack.ts"; +import ExternalStack from "./fixtures/external/stack.ts"; +import RemoteStack from "./fixtures/remote/stack.ts"; + +const { test, beforeAll, afterAll, deploy, destroy } = Test.make({ + providers: Cloudflare.providers(), + state: Cloudflare.state(), +}); + +const logLevel = Effect.provideService( + MinimumLogLevel, + process.env.DEBUG ? "Debug" : "Info", +); + +// Container image build + push + worker/DO deploy comfortably exceeds the +// default 120s hook budget, so give every deploy/destroy plenty of room. +const HOOK_TIMEOUT = 600_000; +const TEST_TIMEOUT = 300_000; + +// Note on image choice for the non-Effect (`image`/`dockerfile`) variants: +// stock nginx images crash-loop inside Cloudflare's container sandbox because +// they symlink /var/log/nginx/{access,error}.log to /dev/stdout and +// /dev/stderr, and opening those device paths fails with ENXIO (errno 6) — the +// nginx master aborts with `[emerg] could not open error log file` before it +// ever binds the port, so getTcpPort() can never connect. The fixtures work +// around this two ways: the `external` Dockerfile (which we own) replaces the +// log symlinks with real files, and the `remote` variant uses a server image +// (`mendhak/http-https-echo`) that writes to its inherited stdout fd directly +// instead of opening the /dev/std* paths as files. + +// Cap exponential backoff at 3s — keeps the fast path snappy but stops the +// geometric blow-up from dominating wall time when CF edge is slow. +const readinessSchedule = Schedule.exponential("500 millis").pipe( + Schedule.either(Schedule.spaced("3 seconds")), +); +const readinessRetries = 30; + +// While a freshly pre-created worker propagates, Cloudflare's edge serves +// Alchemy's pre-create stub (200 with this body); any poll that sees it retries. +const DEPLOY_PLACEHOLDER = "Alchemy worker is being deployed..."; + +// Force `Connection: close` so each readiness attempt opens a fresh connection +// and can land on an edge that already has the new deploy (a pooled keep-alive +// socket stays pinned to one edge metal and can keep reading the stale body). +const freshConn = HttpClient.mapRequest( + HttpClientRequest.setHeader("connection", "close"), +); + +// Retry a freshly-deployed worker route until it answers 200 with a body that +// contains `expected` — rejecting both transient non-200s and the deploy stub. +// Each attempt is bounded so a worker that is hung waiting on its container +// surfaces as a retryable failure rather than blocking the whole test budget. +const fetchReady = (url: string, expected: string) => + Effect.gen(function* () { + const client = freshConn(yield* HttpClient.HttpClient); + return yield* client.get(url).pipe( + Effect.flatMap((r) => + r.status !== 200 + ? Effect.fail(new Error(`Worker not ready: ${r.status}`)) + : Effect.flatMap(r.text, (body) => + body.includes(DEPLOY_PLACEHOLDER) || !body.includes(expected) + ? Effect.fail(new Error(`not ready: got ${body}`)) + : Effect.succeed(body), + ), + ), + Effect.timeout("30 seconds"), + Effect.retry({ schedule: readinessSchedule, times: readinessRetries }), + ); + }); + +/** + * Effect-native container (`main`): the entrypoint Effect is bundled into a + * generated image. Exercises an HTTP round-trip to its port-3000 server + * (`/hello`) via the Durable Object's `getTcpPort` proxy. + */ +describe("effectful container (main)", () => { + const stack = beforeAll(deploy(EffectfulStack), { timeout: HOOK_TIMEOUT }); + afterAll.skipIf(!!process.env.NO_DESTROY)(destroy(EffectfulStack), { + timeout: HOOK_TIMEOUT, + }); + + test.skipIf(!!process.env.FAST)( + "deploys and serves over its TCP port", + Effect.gen(function* () { + const { url } = yield* stack; + + const hello = yield* fetchReady(`${url}/hello`, "effectful container"); + expect(hello).toContain("effectful container"); + }).pipe(logLevel), + { timeout: TEST_TIMEOUT }, + ); +}); + +/** + * External container (`context` / `dockerfile`): Alchemy builds the user's + * Dockerfile against the context directory (nginx serving a static page on + * port 8080) and the DO proxies a request to it. + */ +describe("external container (context/dockerfile)", () => { + const stack = beforeAll(deploy(ExternalStack), { timeout: HOOK_TIMEOUT }); + afterAll.skipIf(!!process.env.NO_DESTROY)(destroy(ExternalStack), { + timeout: HOOK_TIMEOUT, + }); + + test.skipIf(!!process.env.FAST)( + "builds the user Dockerfile and serves it over its TCP port", + Effect.gen(function* () { + const { url } = yield* stack; + + const hello = yield* fetchReady(`${url}/hello`, "external container"); + expect(hello).toContain("external container"); + }).pipe(logLevel), + { timeout: TEST_TIMEOUT }, + ); +}); + +/** + * Remote container (`image`): Alchemy pulls a pre-built public image and + * re-pushes it to Cloudflare's registry without building anything; the DO + * proxies a request to it. + */ +describe("remote container (image)", () => { + const stack = beforeAll(deploy(RemoteStack), { timeout: HOOK_TIMEOUT }); + afterAll.skipIf(!!process.env.NO_DESTROY)(destroy(RemoteStack), { + timeout: HOOK_TIMEOUT, + }); + + test.skipIf(!!process.env.FAST)( + "pulls and re-pushes the remote image and serves it over its TCP port", + Effect.gen(function* () { + const { url } = yield* stack; + + const hello = yield* fetchReady(`${url}/hello`, "method"); + expect(hello).toContain("method"); + }).pipe(logLevel), + { timeout: TEST_TIMEOUT }, + ); +}); diff --git a/packages/alchemy/test/Cloudflare/Container/fixtures/effectful/container.ts b/packages/alchemy/test/Cloudflare/Container/fixtures/effectful/container.ts new file mode 100644 index 000000000..503acd70e --- /dev/null +++ b/packages/alchemy/test/Cloudflare/Container/fixtures/effectful/container.ts @@ -0,0 +1,35 @@ +import * as Cloudflare from "@/Cloudflare"; +import * as Effect from "effect/Effect"; +import { HttpServerRequest } from "effect/unstable/http/HttpServerRequest"; +import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse"; +import { Storage } from "./storage.ts"; + +export class MyContainer extends Cloudflare.Container< + MyContainer, + { + ping: () => Effect.Effect; + } +>()("EffectfulContainer") {} + +export default MyContainer.make( + { + main: import.meta.filename, + dockerfile: "FROM oven/bun:latest", + }, + Effect.gen(function* () { + const bucket = yield* Cloudflare.R2.ReadWriteBucket(Storage); + + return { + ping: () => Effect.succeed("pong"), + fetch: Effect.gen(function* () { + yield* bucket.get("test.txt").pipe(Effect.orDie); + const request = yield* HttpServerRequest; + const url = new URL(request.url, "http://container"); + if (url.pathname === "/health") { + return yield* HttpServerResponse.json({ ok: true }); + } + return HttpServerResponse.text("hello from effectful container"); + }), + }; + }).pipe(Effect.provide(Cloudflare.R2.ReadWriteBucketHttp)), +); diff --git a/packages/alchemy/test/Cloudflare/Container/fixtures/effectful/object.ts b/packages/alchemy/test/Cloudflare/Container/fixtures/effectful/object.ts new file mode 100644 index 000000000..db721b9e9 --- /dev/null +++ b/packages/alchemy/test/Cloudflare/Container/fixtures/effectful/object.ts @@ -0,0 +1,44 @@ +import * as Cloudflare from "@/Cloudflare"; +import * as Effect from "effect/Effect"; +import * as Layer from "effect/Layer"; +import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"; +import { MyContainer } from "./container.ts"; +import { Storage } from "./storage.ts"; + +export class Object extends Cloudflare.DurableObjectNamespace()( + "Object", + Effect.gen(function* () { + const bucket = yield* Cloudflare.R2.ReadWriteBucket(Storage); + const container = yield* MyContainer; + const state = yield* Cloudflare.DurableObjectState; + + return Effect.gen(function* () { + yield* state.storage.sql.exec( + "CREATE TABLE IF NOT EXISTS test (id INTEGER PRIMARY KEY, name TEXT)", + ); + + const conn = yield* container.getTcpPort(3000); + + return { + get: (key: string) => bucket.get(key), + ping: () => container.ping(), + hello: () => + Effect.gen(function* () { + const response = yield* conn.fetch( + HttpClientRequest.get("http://container/"), + ); + return yield* response.text; + }).pipe(Effect.orDie), + }; + }); + }).pipe( + Effect.provide( + Layer.mergeAll( + Cloudflare.R2.ReadWriteBucketBinding, + Cloudflare.layerContainer(MyContainer, { + enableInternet: true, + }), + ), + ), + ), +) {} diff --git a/packages/alchemy/test/Cloudflare/Container/fixtures/effectful/stack.ts b/packages/alchemy/test/Cloudflare/Container/fixtures/effectful/stack.ts new file mode 100644 index 000000000..0e85ee58b --- /dev/null +++ b/packages/alchemy/test/Cloudflare/Container/fixtures/effectful/stack.ts @@ -0,0 +1,21 @@ +import * as Cloudflare from "@/Cloudflare"; +import * as Alchemy from "@/index.ts"; +import * as Effect from "effect/Effect"; +import MyContainerLive from "./container.ts"; +import Worker from "./worker.ts"; + +export default Alchemy.Stack( + "EffectfulContainerStack", + { + providers: Cloudflare.providers(), + state: Cloudflare.state(), + }, + Effect.gen(function* () { + const worker = yield* Worker; + // yield* MyContainer.Application + + return { + url: worker.url.as(), + }; + }).pipe(Effect.provide(MyContainerLive)), +); diff --git a/packages/alchemy/test/Cloudflare/Container/fixtures/effectful/storage.ts b/packages/alchemy/test/Cloudflare/Container/fixtures/effectful/storage.ts new file mode 100644 index 000000000..dea5e71b4 --- /dev/null +++ b/packages/alchemy/test/Cloudflare/Container/fixtures/effectful/storage.ts @@ -0,0 +1,3 @@ +import * as Cloudflare from "@/Cloudflare"; + +export const Storage = Cloudflare.R2Bucket("Storage"); diff --git a/packages/alchemy/test/Cloudflare/Container/fixtures/effectful/worker.ts b/packages/alchemy/test/Cloudflare/Container/fixtures/effectful/worker.ts new file mode 100644 index 000000000..4e8da649b --- /dev/null +++ b/packages/alchemy/test/Cloudflare/Container/fixtures/effectful/worker.ts @@ -0,0 +1,37 @@ +import * as Cloudflare from "@/Cloudflare"; +import * as Effect from "effect/Effect"; +import { HttpServerRequest } from "effect/unstable/http/HttpServerRequest"; +import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse"; +import { Object } from "./object.ts"; + +export default Cloudflare.Worker( + "Worker", + { + main: import.meta.filename, + }, + Effect.gen(function* () { + const objects = yield* Object; + + return { + fetch: Effect.gen(function* () { + const request = yield* HttpServerRequest; + const url = new URL(request.url, "http://x"); + + if (url.pathname === "/ping") { + const pong = yield* objects.getByName("default").ping(); + return HttpServerResponse.text(pong); + } + + if (url.pathname === "/hello") { + const text = yield* objects + .getByName("default") + .hello() + .pipe(Effect.orDie); + return HttpServerResponse.text(text); + } + + return HttpServerResponse.text("ok"); + }), + }; + }), +); diff --git a/packages/alchemy/test/Cloudflare/Container/fixtures/external/context/Dockerfile b/packages/alchemy/test/Cloudflare/Container/fixtures/external/context/Dockerfile new file mode 100644 index 000000000..c285f4515 --- /dev/null +++ b/packages/alchemy/test/Cloudflare/Container/fixtures/external/context/Dockerfile @@ -0,0 +1,16 @@ +FROM nginxinc/nginx-unprivileged:alpine + +# The stock nginx image symlinks /var/log/nginx/{access,error}.log to +# /dev/stdout and /dev/stderr. Inside Cloudflare's container sandbox those +# device paths cannot be opened (open() fails with ENXIO, errno 6), so nginx +# aborts at startup with `[emerg] could not open error log file` and the +# container crash-loops — the port is never bound and getTcpPort() hangs. +# Replace the symlinks with real, nginx-writable files so the master can boot. +USER root +RUN rm -f /var/log/nginx/access.log /var/log/nginx/error.log \ + && touch /var/log/nginx/access.log /var/log/nginx/error.log \ + && chown nginx:nginx /var/log/nginx/access.log /var/log/nginx/error.log +USER nginx + +COPY index.html /usr/share/nginx/html/index.html +EXPOSE 8080 diff --git a/packages/alchemy/test/Cloudflare/Container/fixtures/external/context/index.html b/packages/alchemy/test/Cloudflare/Container/fixtures/external/context/index.html new file mode 100644 index 000000000..7ef2cd98a --- /dev/null +++ b/packages/alchemy/test/Cloudflare/Container/fixtures/external/context/index.html @@ -0,0 +1 @@ +hello from external container diff --git a/packages/alchemy/test/Cloudflare/Container/fixtures/external/object.ts b/packages/alchemy/test/Cloudflare/Container/fixtures/external/object.ts new file mode 100644 index 000000000..621084588 --- /dev/null +++ b/packages/alchemy/test/Cloudflare/Container/fixtures/external/object.ts @@ -0,0 +1,45 @@ +import * as Cloudflare from "@/Cloudflare"; +import { Layer } from "effect"; +import * as Effect from "effect/Effect"; +import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"; +import path from "node:path"; + +class ExternalContainer extends Cloudflare.Container()( + "ExternalContainer", + { + context: path.join(import.meta.dirname, "context"), + observability: { logs: { enabled: true } }, + }, +) {} + +/** + * Durable Object that binds and starts the {@link ExternalContainer} and + * proxies an HTTP request to the nginx server running on port 8080 inside it. + */ +export class ExternalContainerObject extends Cloudflare.DurableObjectNamespace()( + "ExternalContainerObject", + Effect.gen(function* () { + const container = yield* ExternalContainer; + + return Effect.gen(function* () { + const { fetch } = yield* container.getTcpPort(8080); + + return { + hello: Effect.fn("hello")(function* () { + const response = yield* fetch( + HttpClientRequest.get("http://container/"), + ); + return yield* response.text; + }), + }; + }); + }).pipe( + Effect.provide( + Layer.mergeAll( + Cloudflare.layerContainer(ExternalContainer, { + enableInternet: true, + }), + ), + ), + ), +) {} diff --git a/packages/alchemy/test/Cloudflare/Container/fixtures/external/stack.ts b/packages/alchemy/test/Cloudflare/Container/fixtures/external/stack.ts new file mode 100644 index 000000000..ff10d7570 --- /dev/null +++ b/packages/alchemy/test/Cloudflare/Container/fixtures/external/stack.ts @@ -0,0 +1,13 @@ +import * as Cloudflare from "@/Cloudflare"; +import * as Alchemy from "@/index.ts"; +import * as Effect from "effect/Effect"; +import ExternalContainerWorker from "./worker.ts"; + +export default Alchemy.Stack( + "ExternalContainerStack", + { providers: Cloudflare.providers(), state: Cloudflare.state() }, + Effect.gen(function* () { + const worker = yield* ExternalContainerWorker; + return { url: worker.url.as() }; + }), +); diff --git a/packages/alchemy/test/Cloudflare/Container/fixtures/external/worker.ts b/packages/alchemy/test/Cloudflare/Container/fixtures/external/worker.ts new file mode 100644 index 000000000..c0781d55d --- /dev/null +++ b/packages/alchemy/test/Cloudflare/Container/fixtures/external/worker.ts @@ -0,0 +1,32 @@ +import * as Cloudflare from "@/Cloudflare"; +import * as Effect from "effect/Effect"; +import { HttpServerRequest } from "effect/unstable/http/HttpServerRequest"; +import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse"; +import { ExternalContainerObject } from "./object.ts"; + +export default class ExternalContainerWorker extends Cloudflare.Worker()( + "ExternalContainerWorker", + { + main: import.meta.filename, + }, + Effect.gen(function* () { + const objects = yield* ExternalContainerObject; + + return { + fetch: Effect.gen(function* () { + const request = yield* HttpServerRequest; + const url = new URL(request.url, "http://x"); + + if (url.pathname === "/hello") { + const text = yield* objects + .getByName("default") + .hello() + .pipe(Effect.orDie); + return HttpServerResponse.text(text); + } + + return HttpServerResponse.text("ok"); + }), + }; + }), +) {} diff --git a/packages/alchemy/test/Cloudflare/Container/fixtures/remote/object.ts b/packages/alchemy/test/Cloudflare/Container/fixtures/remote/object.ts new file mode 100644 index 000000000..382464c98 --- /dev/null +++ b/packages/alchemy/test/Cloudflare/Container/fixtures/remote/object.ts @@ -0,0 +1,42 @@ +import * as Cloudflare from "@/Cloudflare"; +import * as Effect from "effect/Effect"; +import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"; + +export class RemoteContainer extends Cloudflare.Container()( + "RemoteContainer", + { + image: "mendhak/http-https-echo:latest", + observability: { logs: { enabled: true } }, + }, +) {} + +/** + * Durable Object that binds and starts the {@link RemoteContainer} and + * proxies an HTTP request to the echo server running on port 8080 inside it. + */ +export class RemoteContainerObject extends Cloudflare.DurableObjectNamespace()( + "RemoteContainerObject", + Effect.gen(function* () { + const container = yield* RemoteContainer; + + return Effect.gen(function* () { + const { fetch } = yield* container.getTcpPort(8080); + + return { + hello: () => + Effect.gen(function* () { + const response = yield* fetch( + HttpClientRequest.get("http://container/"), + ); + return yield* response.text; + }), + }; + }); + }).pipe( + Effect.provide( + Cloudflare.layerContainer(RemoteContainer, { + enableInternet: true, + }), + ), + ), +) {} diff --git a/packages/alchemy/test/Cloudflare/Container/fixtures/remote/stack.ts b/packages/alchemy/test/Cloudflare/Container/fixtures/remote/stack.ts new file mode 100644 index 000000000..ad995e980 --- /dev/null +++ b/packages/alchemy/test/Cloudflare/Container/fixtures/remote/stack.ts @@ -0,0 +1,13 @@ +import * as Cloudflare from "@/Cloudflare"; +import * as Alchemy from "@/index.ts"; +import * as Effect from "effect/Effect"; +import RemoteContainerWorker from "./worker.ts"; + +export default Alchemy.Stack( + "RemoteContainerStack", + { providers: Cloudflare.providers(), state: Cloudflare.state() }, + Effect.gen(function* () { + const worker = yield* RemoteContainerWorker; + return { url: worker.url.as() }; + }), +); diff --git a/packages/alchemy/test/Cloudflare/Container/fixtures/remote/worker.ts b/packages/alchemy/test/Cloudflare/Container/fixtures/remote/worker.ts new file mode 100644 index 000000000..b3e5f59fa --- /dev/null +++ b/packages/alchemy/test/Cloudflare/Container/fixtures/remote/worker.ts @@ -0,0 +1,39 @@ +import * as Cloudflare from "@/Cloudflare"; +import * as Effect from "effect/Effect"; +import { HttpServerRequest } from "effect/unstable/http/HttpServerRequest"; +import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse"; +import { RemoteContainerObject } from "./object.ts"; + +export default class RemoteContainerWorker extends Cloudflare.Worker()( + "RemoteContainerWorker", + { + main: import.meta.filename, + }, + Effect.gen(function* () { + const objects = yield* RemoteContainerObject; + + return { + fetch: Effect.gen(function* () { + const request = yield* HttpServerRequest; + const url = new URL(request.url, "http://x"); + + if (url.pathname === "/hello") { + const text = yield* objects.getByName("default").hello(); + return HttpServerResponse.text(text); + } + + return HttpServerResponse.text("ok"); + }).pipe( + Effect.catchTag("HttpClientError", (err) => + Effect.succeed( + err.response + ? HttpServerResponse.fromClientResponse(err.response) + : HttpServerResponse.text(err.message, { + status: 500, + }), + ), + ), + ), + }; + }), +) {} diff --git a/packages/alchemy/test/Cloudflare/ContentScanning/ContentScanning.test.ts b/packages/alchemy/test/Cloudflare/ContentScanning/ContentScanning.test.ts index 016b93072..fd35e3bc4 100644 --- a/packages/alchemy/test/Cloudflare/ContentScanning/ContentScanning.test.ts +++ b/packages/alchemy/test/Cloudflare/ContentScanning/ContentScanning.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -64,7 +67,7 @@ const setBaseline = (zoneId: string, value: "enabled" | "disabled") => // Both cases mutate the same zone-level content-scanning enablement singleton; run them serially so they don't corrupt each other's captured `initialValue` under the global concurrent test config. describe.sequential("ContentScanning", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed ContentScanningNotEntitled error on unentitled zones", (stack) => Effect.gen(function* () { @@ -90,7 +93,7 @@ describe.sequential("ContentScanning", () => { }).pipe(logLevel), ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "pins Content Scanning off on an unentitled zone and destroys cleanly", (stack) => Effect.gen(function* () { @@ -147,30 +150,34 @@ describe.sequential("ContentScanning", () => { // on every plan (only enabling is entitlement-gated), so this stays an // ungated read-only assertion. Assert the result is non-empty and contains // the standing test zone. - test.provider("list enumerates the status across all zones", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - const provider = yield* Provider.findProvider(Cloudflare.ContentScanning); - // The freshly-minted scoped token propagates eventually-consistently, so - // the account-wide enumeration intermittently 403s (`Forbidden`) or 401s - // (`Unauthorized`). Both are transient here — ride out the blip like - // every other out-of-band call in this suite. - const all = yield* provider.list().pipe( - Effect.retry({ - while: (e) => e._tag === "Forbidden" || e._tag === "Unauthorized", - schedule: forbiddenRetrySchedule, - times: 8, - }), - ); - - expect(all.length).toBeGreaterThan(0); - expect(all.some((s) => s.zoneId === zoneId)).toBe(true); - - // `stack` is unused here (the singleton always exists on every zone), - // but keep the destroy bookend so the harness state stays clean. - yield* stack.destroy(); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the status across all zones", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; + + const provider = yield* Provider.findProvider( + Cloudflare.ContentScanning, + ); + // The freshly-minted scoped token propagates eventually-consistently, so + // the account-wide enumeration intermittently 403s (`Forbidden`) or 401s + // (`Unauthorized`). Both are transient here — ride out the blip like + // every other out-of-band call in this suite. + const all = yield* provider.list().pipe( + Effect.retry({ + while: (e) => e._tag === "Forbidden" || e._tag === "Unauthorized", + schedule: forbiddenRetrySchedule, + times: 8, + }), + ); + + expect(all.length).toBeGreaterThan(0); + expect(all.some((s) => s.zoneId === zoneId)).toBe(true); + + // `stack` is unused here (the singleton always exists on every zone), + // but keep the destroy bookend so the harness state stays clean. + yield* stack.destroy(); + }).pipe(logLevel), ); test.provider.skipIf(!entitledZoneId)( diff --git a/packages/alchemy/test/Cloudflare/ContentScanning/Expression.test.ts b/packages/alchemy/test/Cloudflare/ContentScanning/Expression.test.ts index f27517450..9b3cbb7b3 100644 --- a/packages/alchemy/test/Cloudflare/ContentScanning/Expression.test.ts +++ b/packages/alchemy/test/Cloudflare/ContentScanning/Expression.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -54,7 +57,7 @@ const listExpressions = (zoneId: string) => // Both cases enable the same zone-level content-scanning singleton as a prerequisite; run them serially so they don't fight over that singleton under the global concurrent test config. describe.sequential("Expression", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed ContentScanningNotEnabled error when scanning is disabled", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/CustomCertificate/CustomCertificate.test.ts b/packages/alchemy/test/Cloudflare/CustomCertificate/CustomCertificate.test.ts index 11adacdb8..a7620c338 100644 --- a/packages/alchemy/test/Cloudflare/CustomCertificate/CustomCertificate.test.ts +++ b/packages/alchemy/test/Cloudflare/CustomCertificate/CustomCertificate.test.ts @@ -12,6 +12,9 @@ import * as Schedule from "effect/Schedule"; import * as FileSystem from "effect/FileSystem"; import * as Path from "effect/Path"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -65,7 +68,7 @@ const getCertificate = (zoneId: string, customCertificateId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed PlanLevelNotAllowed error on unentitled zones", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/D1/D1Binding.test.ts b/packages/alchemy/test/Cloudflare/D1/D1Binding.test.ts index 02a5c15ee..cd765164d 100644 --- a/packages/alchemy/test/Cloudflare/D1/D1Binding.test.ts +++ b/packages/alchemy/test/Cloudflare/D1/D1Binding.test.ts @@ -10,6 +10,9 @@ import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"; import type * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"; import D1Worker from "./d1-worker.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -69,7 +72,7 @@ const retryUntilOk = ( * upon by the deploy-time policy and the runtime lookup match, and * the Cloudflare runtime actually injected the binding into `env`. */ -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "D1Connection.bind exercises the full client surface", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/D1/Database.test.ts b/packages/alchemy/test/Cloudflare/D1/Database.test.ts index 434801097..d8c80ae2a 100644 --- a/packages/alchemy/test/Cloudflare/D1/Database.test.ts +++ b/packages/alchemy/test/Cloudflare/D1/Database.test.ts @@ -12,6 +12,9 @@ import * as Path from "effect/Path"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -350,108 +353,112 @@ test.provider("imports SQL files via importFiles", (stack) => }).pipe(logLevel), ); -test.provider("clones a database by databaseId", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - const fs = yield* FileSystem.FileSystem; - const path = yield* Path.Path; - const dir = yield* fs.makeTempDirectory({ - prefix: "alchemy-d1-clone-id-", - }); - const seedPath = path.join(dir, "seed.sql"); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "clones a database by databaseId", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + const fs = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + const dir = yield* fs.makeTempDirectory({ + prefix: "alchemy-d1-clone-id-", + }); + const seedPath = path.join(dir, "seed.sql"); - yield* fs.writeFileString( - seedPath, - [ - "CREATE TABLE colors (id INTEGER PRIMARY KEY, name TEXT NOT NULL);", - "INSERT INTO colors (id, name) VALUES (1, 'red'), (2, 'green'), (3, 'blue');", - ].join("\n"), - ); + yield* fs.writeFileString( + seedPath, + [ + "CREATE TABLE colors (id INTEGER PRIMARY KEY, name TEXT NOT NULL);", + "INSERT INTO colors (id, name) VALUES (1, 'red'), (2, 'green'), (3, 'blue');", + ].join("\n"), + ); - yield* stack.destroy(); + yield* stack.destroy(); - const { source, target } = yield* stack.deploy( - Effect.gen(function* () { - const source = yield* Cloudflare.D1Database("CloneByIdSource", { - importFiles: [seedPath], - }); - const target = yield* Cloudflare.D1Database("CloneByIdTarget", { - clone: { databaseId: source.databaseId }, - }); - return { source, target }; - }), - ); + const { source, target } = yield* stack.deploy( + Effect.gen(function* () { + const source = yield* Cloudflare.D1Database("CloneByIdSource", { + importFiles: [seedPath], + }); + const target = yield* Cloudflare.D1Database("CloneByIdTarget", { + clone: { databaseId: source.databaseId }, + }); + return { source, target }; + }), + ); - expect(target.databaseId).not.toEqual(source.databaseId); + expect(target.databaseId).not.toEqual(source.databaseId); - const targetColors = yield* getResults<{ id: number; name: string }>( - accountId, - target.databaseId, - "SELECT id, name FROM colors ORDER BY id;", - ); - expect(targetColors).toEqual([ - { id: 1, name: "red" }, - { id: 2, name: "green" }, - { id: 3, name: "blue" }, - ]); + const targetColors = yield* getResults<{ id: number; name: string }>( + accountId, + target.databaseId, + "SELECT id, name FROM colors ORDER BY id;", + ); + expect(targetColors).toEqual([ + { id: 1, name: "red" }, + { id: 2, name: "green" }, + { id: 3, name: "blue" }, + ]); - yield* stack.destroy(); - yield* waitForDatabaseToBeDeleted(source.databaseId, accountId); - yield* waitForDatabaseToBeDeleted(target.databaseId, accountId); - }).pipe(logLevel), + yield* stack.destroy(); + yield* waitForDatabaseToBeDeleted(source.databaseId, accountId); + yield* waitForDatabaseToBeDeleted(target.databaseId, accountId); + }).pipe(logLevel), ); -test.provider("clones a database by name lookup", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - const fs = yield* FileSystem.FileSystem; - const path = yield* Path.Path; - const dir = yield* fs.makeTempDirectory({ - prefix: "alchemy-d1-clone-name-", - }); - const seedPath = path.join(dir, "seed.sql"); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "clones a database by name lookup", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + const fs = yield* FileSystem.FileSystem; + const path = yield* Path.Path; + const dir = yield* fs.makeTempDirectory({ + prefix: "alchemy-d1-clone-name-", + }); + const seedPath = path.join(dir, "seed.sql"); - yield* fs.writeFileString( - seedPath, - [ - "CREATE TABLE animals (id INTEGER PRIMARY KEY, kind TEXT NOT NULL);", - "INSERT INTO animals (id, kind) VALUES (1, 'cat'), (2, 'dog');", - ].join("\n"), - ); + yield* fs.writeFileString( + seedPath, + [ + "CREATE TABLE animals (id INTEGER PRIMARY KEY, kind TEXT NOT NULL);", + "INSERT INTO animals (id, kind) VALUES (1, 'cat'), (2, 'dog');", + ].join("\n"), + ); - yield* stack.destroy(); + yield* stack.destroy(); - const { source, target } = yield* stack.deploy( - Effect.gen(function* () { - const source = yield* Cloudflare.D1Database("CloneByNameSource", { - importFiles: [seedPath], - }); - const target = yield* Cloudflare.D1Database("CloneByNameTarget", { - clone: { name: source.databaseName }, - }); - return { source, target }; - }), - ); + const { source, target } = yield* stack.deploy( + Effect.gen(function* () { + const source = yield* Cloudflare.D1Database("CloneByNameSource", { + importFiles: [seedPath], + }); + const target = yield* Cloudflare.D1Database("CloneByNameTarget", { + clone: { name: source.databaseName }, + }); + return { source, target }; + }), + ); - expect(target.databaseId).not.toEqual(source.databaseId); + expect(target.databaseId).not.toEqual(source.databaseId); - const animals = yield* getResults<{ id: number; kind: string }>( - accountId, - target.databaseId, - "SELECT id, kind FROM animals ORDER BY id;", - ); - expect(animals).toEqual([ - { id: 1, kind: "cat" }, - { id: 2, kind: "dog" }, - ]); + const animals = yield* getResults<{ id: number; kind: string }>( + accountId, + target.databaseId, + "SELECT id, kind FROM animals ORDER BY id;", + ); + expect(animals).toEqual([ + { id: 1, kind: "cat" }, + { id: 2, kind: "dog" }, + ]); - yield* stack.destroy(); - yield* waitForDatabaseToBeDeleted(source.databaseId, accountId); - yield* waitForDatabaseToBeDeleted(target.databaseId, accountId); - }).pipe(logLevel), + yield* stack.destroy(); + yield* waitForDatabaseToBeDeleted(source.databaseId, accountId); + yield* waitForDatabaseToBeDeleted(target.databaseId, accountId); + }).pipe(logLevel), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "clones a database by passing the source resource directly", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/DdosProtection/AllowlistEntry.test.ts b/packages/alchemy/test/Cloudflare/DdosProtection/AllowlistEntry.test.ts index c0de3b847..53ed50862 100644 --- a/packages/alchemy/test/Cloudflare/DdosProtection/AllowlistEntry.test.ts +++ b/packages/alchemy/test/Cloudflare/DdosProtection/AllowlistEntry.test.ts @@ -7,6 +7,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -34,7 +37,7 @@ const accountId = Effect.gen(function* () { // Inverse probe: pins the typed `AdvancedTcpProtectionNotEntitled` rejection, // so it skips on entitled accounts (CLOUDFLARE_TEST_MAGIC_TRANSIT set), where // the API would accept the call. -test.provider.skipIf(!!magicTransit)( +test.provider.skipIf(!!magicTransit || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed AdvancedTcpProtectionNotEntitled error without Magic Transit", (stack) => Effect.gen(function* () { @@ -138,7 +141,7 @@ test.provider.skipIf(!magicTransit)( // Advanced TCP Protection entitlement the enumeration API rejects with the // typed `AdvancedTcpProtectionNotEntitled` error (Cloudflare code 8888), which // `list()` maps to a well-typed empty array — assert that here, ungated. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list returns a well-typed empty array without Magic Transit", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Devices/CustomProfile.test.ts b/packages/alchemy/test/Cloudflare/Devices/CustomProfile.test.ts index 6821eba62..1c7a8a599 100644 --- a/packages/alchemy/test/Cloudflare/Devices/CustomProfile.test.ts +++ b/packages/alchemy/test/Cloudflare/Devices/CustomProfile.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -42,7 +45,7 @@ const expectGone = (accountId: string, policyId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create, update in place, and delete a custom device profile", (stack) => Effect.gen(function* () { @@ -118,33 +121,37 @@ test.provider( }).pipe(logLevel), ); -test.provider("list enumerates the deployed custom device profile", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const deployed = yield* stack.deploy( - Cloudflare.DeviceCustomProfile("ListProfile", { - name: "alchemy-test-custom-profile-list", - match: 'identity.email == "list@alchemy-test-2.us"', - precedence: 12011, - description: "Alchemy list() test profile", - }), - ); - - const provider = yield* Provider.findProvider( - Cloudflare.DeviceCustomProfile, - ); - const all = yield* provider.list(); - - // Exhaustively paginated account collection must contain the profile we - // just deployed, hydrated into the exact `read` Attributes shape. - const found = all.find((p) => p.policyId === deployed.policyId); - expect(found).toBeDefined(); - expect(found?.name).toEqual("alchemy-test-custom-profile-list"); - expect(found?.match).toEqual('identity.email == "list@alchemy-test-2.us"'); - expect(found?.accountId).toEqual(deployed.accountId); - expect(found?.default).toEqual(false); - - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed custom device profile", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const deployed = yield* stack.deploy( + Cloudflare.DeviceCustomProfile("ListProfile", { + name: "alchemy-test-custom-profile-list", + match: 'identity.email == "list@alchemy-test-2.us"', + precedence: 12011, + description: "Alchemy list() test profile", + }), + ); + + const provider = yield* Provider.findProvider( + Cloudflare.DeviceCustomProfile, + ); + const all = yield* provider.list(); + + // Exhaustively paginated account collection must contain the profile we + // just deployed, hydrated into the exact `read` Attributes shape. + const found = all.find((p) => p.policyId === deployed.policyId); + expect(found).toBeDefined(); + expect(found?.name).toEqual("alchemy-test-custom-profile-list"); + expect(found?.match).toEqual( + 'identity.email == "list@alchemy-test-2.us"', + ); + expect(found?.accountId).toEqual(deployed.accountId); + expect(found?.default).toEqual(false); + + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Devices/DefaultProfile.test.ts b/packages/alchemy/test/Cloudflare/Devices/DefaultProfile.test.ts index 58126fa3b..c56ceb962 100644 --- a/packages/alchemy/test/Cloudflare/Devices/DefaultProfile.test.ts +++ b/packages/alchemy/test/Cloudflare/Devices/DefaultProfile.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -111,23 +114,25 @@ describe.sequential("DefaultProfile", () => { // reads the single profile and returns it as a one-element array — exactly // mirroring `read`. Read-only, so it is NOT gated behind the mutation env // var; it only requires the account to have Zero Trust / WARP entitlement. - test.provider("list returns the singleton default device profile", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list returns the singleton default device profile", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - yield* stack.destroy(); + yield* stack.destroy(); - const provider = yield* Provider.findProvider( - Cloudflare.DeviceDefaultProfile, - ); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider( + Cloudflare.DeviceDefaultProfile, + ); + const all = yield* provider.list(); - // Account singleton: exactly one element, well-typed Attributes. - expect(all.length).toEqual(1); - expect(all[0].accountId).toEqual(accountId); - expect(["include", "exclude"]).toContain(all[0].mode); + // Account singleton: exactly one element, well-typed Attributes. + expect(all.length).toEqual(1); + expect(all[0].accountId).toEqual(accountId); + expect(["include", "exclude"]).toContain(all[0].mode); - yield* stack.destroy(); - }).pipe(logLevel), + yield* stack.destroy(); + }).pipe(logLevel), ); }); diff --git a/packages/alchemy/test/Cloudflare/Devices/DexTest.test.ts b/packages/alchemy/test/Cloudflare/Devices/DexTest.test.ts index b5e5cdce8..603b3d79b 100644 --- a/packages/alchemy/test/Cloudflare/Devices/DexTest.test.ts +++ b/packages/alchemy/test/Cloudflare/Devices/DexTest.test.ts @@ -7,6 +7,9 @@ import { expect } from "@effect/vitest"; import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -25,7 +28,7 @@ const entitled = !!process.env.CLOUDFLARE_TEST_DEX; const TEST_NAME = "alchemy-test-dex"; -test.provider.skipIf(entitled)( +test.provider.skipIf(entitled || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "unentitled accounts surface the typed Forbidden entitlement error", (stack) => Effect.gen(function* () { @@ -155,7 +158,7 @@ test.provider.skipIf(!entitled)( { timeout: 90_000 }, ); -test.provider.skipIf(entitled)( +test.provider.skipIf(entitled || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list returns a well-typed empty array on unentitled accounts", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Devices/ManagedNetwork.test.ts b/packages/alchemy/test/Cloudflare/Devices/ManagedNetwork.test.ts index 26aa1cfce..e80cac520 100644 --- a/packages/alchemy/test/Cloudflare/Devices/ManagedNetwork.test.ts +++ b/packages/alchemy/test/Cloudflare/Devices/ManagedNetwork.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -51,7 +54,7 @@ const SHA_A = const SHA_B = "7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730" as const; -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create, update in place, and delete a managed network", (stack) => Effect.gen(function* () { @@ -106,25 +109,27 @@ test.provider( }).pipe(logLevel), ); -test.provider("list enumerates the deployed managed network", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed managed network", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); - const deployed = yield* stack.deploy( - Cloudflare.DeviceManagedNetwork("ListResource", { - name: "alchemy-test-managed-network-list", - config: { tlsSockaddr: "192.0.2.3:443", sha256: SHA_A }, - }), - ); + const deployed = yield* stack.deploy( + Cloudflare.DeviceManagedNetwork("ListResource", { + name: "alchemy-test-managed-network-list", + config: { tlsSockaddr: "192.0.2.3:443", sha256: SHA_A }, + }), + ); - const provider = yield* Provider.findProvider( - Cloudflare.DeviceManagedNetwork, - ); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider( + Cloudflare.DeviceManagedNetwork, + ); + const all = yield* provider.list(); - expect(all.some((n) => n.networkId === deployed.networkId)).toBe(true); + expect(all.some((n) => n.networkId === deployed.networkId)).toBe(true); - yield* stack.destroy(); - yield* expectGone(deployed.accountId, deployed.networkId); - }).pipe(logLevel), + yield* stack.destroy(); + yield* expectGone(deployed.accountId, deployed.networkId); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Devices/PostureIntegration.test.ts b/packages/alchemy/test/Cloudflare/Devices/PostureIntegration.test.ts index ec8e8bc15..8a4f3b587 100644 --- a/packages/alchemy/test/Cloudflare/Devices/PostureIntegration.test.ts +++ b/packages/alchemy/test/Cloudflare/Devices/PostureIntegration.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import * as Redacted from "effect/Redacted"; import { MinimumLogLevel } from "effect/References"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -28,7 +31,7 @@ const logLevel = Effect.provideService( // always runs and pins the typed tag. const external = !!process.env.CLOUDFLARE_TEST_POSTURE_INTEGRATION; -test.provider.skipIf(external)( +test.provider.skipIf(external || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "creates without a real provider surface the typed InvalidPostureIntegrationConfig error", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Devices/PostureRule.test.ts b/packages/alchemy/test/Cloudflare/Devices/PostureRule.test.ts index 39ec4037f..5a6ea162f 100644 --- a/packages/alchemy/test/Cloudflare/Devices/PostureRule.test.ts +++ b/packages/alchemy/test/Cloudflare/Devices/PostureRule.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -42,133 +45,141 @@ const expectGone = (accountId: string, ruleId: string) => }), ); -test.provider("create, update in place, and delete a posture rule", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const rule = yield* stack.deploy( - Cloudflare.DevicePostureRule("WindowsOsVersion", { - name: "alchemy-test-posture-os", - type: "os_version", - description: "Require Windows 10.0.19045+", - match: [{ platform: "windows" }], - schedule: "5m", - input: { - operatingSystem: "windows", - operator: ">=", - version: "10.0.19045", - }, - }), - ); - - expect(rule.postureRuleId).toBeTruthy(); - expect(rule.accountId).toEqual(accountId); - expect(rule.name).toEqual("alchemy-test-posture-os"); - expect(rule.type).toEqual("os_version"); - expect(rule.description).toEqual("Require Windows 10.0.19045+"); - - // Out-of-band verify against the live API. - const live = yield* getRule(accountId, rule.postureRuleId); - expect(live.name).toEqual("alchemy-test-posture-os"); - expect(live.type).toEqual("os_version"); - - // Update mutable props in place — same rule id. - const updated = yield* stack.deploy( - Cloudflare.DevicePostureRule("WindowsOsVersion", { - name: "alchemy-test-posture-os", - type: "os_version", - description: "Require Windows 10.0.22631+", - match: [{ platform: "windows" }], - schedule: "10m", - input: { - operatingSystem: "windows", - operator: ">=", - version: "10.0.22631", - }, - }), - ); - expect(updated.postureRuleId).toEqual(rule.postureRuleId); - expect(updated.description).toEqual("Require Windows 10.0.22631+"); - expect(updated.schedule).toEqual("10m"); - - const live2 = yield* getRule(accountId, rule.postureRuleId); - expect(live2.description).toEqual("Require Windows 10.0.22631+"); - expect(live2.schedule).toEqual("10m"); - - yield* stack.destroy(); - yield* expectGone(accountId, rule.postureRuleId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, update in place, and delete a posture rule", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const rule = yield* stack.deploy( + Cloudflare.DevicePostureRule("WindowsOsVersion", { + name: "alchemy-test-posture-os", + type: "os_version", + description: "Require Windows 10.0.19045+", + match: [{ platform: "windows" }], + schedule: "5m", + input: { + operatingSystem: "windows", + operator: ">=", + version: "10.0.19045", + }, + }), + ); + + expect(rule.postureRuleId).toBeTruthy(); + expect(rule.accountId).toEqual(accountId); + expect(rule.name).toEqual("alchemy-test-posture-os"); + expect(rule.type).toEqual("os_version"); + expect(rule.description).toEqual("Require Windows 10.0.19045+"); + + // Out-of-band verify against the live API. + const live = yield* getRule(accountId, rule.postureRuleId); + expect(live.name).toEqual("alchemy-test-posture-os"); + expect(live.type).toEqual("os_version"); + + // Update mutable props in place — same rule id. + const updated = yield* stack.deploy( + Cloudflare.DevicePostureRule("WindowsOsVersion", { + name: "alchemy-test-posture-os", + type: "os_version", + description: "Require Windows 10.0.22631+", + match: [{ platform: "windows" }], + schedule: "10m", + input: { + operatingSystem: "windows", + operator: ">=", + version: "10.0.22631", + }, + }), + ); + expect(updated.postureRuleId).toEqual(rule.postureRuleId); + expect(updated.description).toEqual("Require Windows 10.0.22631+"); + expect(updated.schedule).toEqual("10m"); + + const live2 = yield* getRule(accountId, rule.postureRuleId); + expect(live2.description).toEqual("Require Windows 10.0.22631+"); + expect(live2.schedule).toEqual("10m"); + + yield* stack.destroy(); + yield* expectGone(accountId, rule.postureRuleId); + }).pipe(logLevel), ); -test.provider("changing the rule type triggers a replacement", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const original = yield* stack.deploy( - Cloudflare.DevicePostureRule("Replace", { - name: "alchemy-test-posture-replace", - type: "firewall", - match: [{ platform: "mac" }], - schedule: "5m", - input: { enabled: true, operatingSystem: "mac" }, - }), - ); - expect(original.type).toEqual("firewall"); - - // `type` is immutable — diff must request a replacement. - const replaced = yield* stack.deploy( - Cloudflare.DevicePostureRule("Replace", { - name: "alchemy-test-posture-replace", - type: "disk_encryption", - match: [{ platform: "mac" }], - schedule: "5m", - input: { requireAll: true }, - }), - ); - expect(replaced.type).toEqual("disk_encryption"); - expect(replaced.postureRuleId).not.toEqual(original.postureRuleId); - - // The old rule must be gone after the replacement completes. - yield* expectGone(accountId, original.postureRuleId); - - yield* stack.destroy(); - yield* expectGone(accountId, replaced.postureRuleId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "changing the rule type triggers a replacement", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const original = yield* stack.deploy( + Cloudflare.DevicePostureRule("Replace", { + name: "alchemy-test-posture-replace", + type: "firewall", + match: [{ platform: "mac" }], + schedule: "5m", + input: { enabled: true, operatingSystem: "mac" }, + }), + ); + expect(original.type).toEqual("firewall"); + + // `type` is immutable — diff must request a replacement. + const replaced = yield* stack.deploy( + Cloudflare.DevicePostureRule("Replace", { + name: "alchemy-test-posture-replace", + type: "disk_encryption", + match: [{ platform: "mac" }], + schedule: "5m", + input: { requireAll: true }, + }), + ); + expect(replaced.type).toEqual("disk_encryption"); + expect(replaced.postureRuleId).not.toEqual(original.postureRuleId); + + // The old rule must be gone after the replacement completes. + yield* expectGone(accountId, original.postureRuleId); + + yield* stack.destroy(); + yield* expectGone(accountId, replaced.postureRuleId); + }).pipe(logLevel), ); -test.provider("list enumerates the deployed posture rule", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const deployed = yield* stack.deploy( - Cloudflare.DevicePostureRule("ListRule", { - name: "alchemy-test-posture-list", - type: "os_version", - match: [{ platform: "windows" }], - schedule: "5m", - input: { - operatingSystem: "windows", - operator: ">=", - version: "10.0.19045", - }, - }), - ); - - const provider = yield* Provider.findProvider(Cloudflare.DevicePostureRule); - const all = yield* provider.list(); - - // Exhaustive pagination must include the rule we just deployed. - expect( - all.some((rule) => rule.postureRuleId === deployed.postureRuleId), - ).toBe(true); - - yield* stack.destroy(); - yield* expectGone(accountId, deployed.postureRuleId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed posture rule", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const deployed = yield* stack.deploy( + Cloudflare.DevicePostureRule("ListRule", { + name: "alchemy-test-posture-list", + type: "os_version", + match: [{ platform: "windows" }], + schedule: "5m", + input: { + operatingSystem: "windows", + operator: ">=", + version: "10.0.19045", + }, + }), + ); + + const provider = yield* Provider.findProvider( + Cloudflare.DevicePostureRule, + ); + const all = yield* provider.list(); + + // Exhaustive pagination must include the rule we just deployed. + expect( + all.some((rule) => rule.postureRuleId === deployed.postureRuleId), + ).toBe(true); + + yield* stack.destroy(); + yield* expectGone(accountId, deployed.postureRuleId); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Devices/Settings.test.ts b/packages/alchemy/test/Cloudflare/Devices/Settings.test.ts index d444c825a..0ee2ca0b1 100644 --- a/packages/alchemy/test/Cloudflare/Devices/Settings.test.ts +++ b/packages/alchemy/test/Cloudflare/Devices/Settings.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -28,7 +31,7 @@ const getSettings = (accountId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "patches disableForTime and restores the original value on destroy", (stack) => Effect.gen(function* () { @@ -82,21 +85,23 @@ test.provider( // Canonical `list()` test (account-scoped singleton): there is exactly one // device-settings object per account and no enumeration API, so `list()` // reads the single singleton and returns a one-element Attributes array. -test.provider("list returns the account's device settings singleton", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list returns the account's device settings singleton", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - yield* stack.destroy(); + yield* stack.destroy(); - const provider = yield* Provider.findProvider(Cloudflare.DeviceSettings); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider(Cloudflare.DeviceSettings); + const all = yield* provider.list(); - // Exactly one element — the account-wide singleton — well-typed as - // DeviceSettings["Attributes"]. - expect(all.length).toEqual(1); - expect(all[0].accountId).toEqual(accountId); - expect(all[0].initialSettings).toBeDefined(); + // Exactly one element — the account-wide singleton — well-typed as + // DeviceSettings["Attributes"]. + expect(all.length).toEqual(1); + expect(all[0].accountId).toEqual(accountId); + expect(all[0].initialSettings).toBeDefined(); - yield* stack.destroy(); - }).pipe(logLevel), + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Diagnostics/EndpointHealthcheck.test.ts b/packages/alchemy/test/Cloudflare/Diagnostics/EndpointHealthcheck.test.ts index 8b87ffdfb..5fd825054 100644 --- a/packages/alchemy/test/Cloudflare/Diagnostics/EndpointHealthcheck.test.ts +++ b/packages/alchemy/test/Cloudflare/Diagnostics/EndpointHealthcheck.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -49,7 +52,7 @@ const expectGone = (accountId: string, id: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "rejects public IPs with the typed InvalidHealthcheckEndpoint error", (stack) => Effect.gen(function* () { @@ -98,7 +101,7 @@ const cleanLeftovers = (accountId: string) => ), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "creates an endpoint healthcheck, updates in place, and destroys", (stack) => Effect.gen(function* () { @@ -181,7 +184,7 @@ test.provider( // Canonical `list()` test (account collection): deploy a real healthcheck, // then assert its UUID appears in the exhaustively-enumerated result. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed endpoint healthcheck", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Dlp/Dlp.test.ts b/packages/alchemy/test/Cloudflare/Dlp/Dlp.test.ts index 987868b57..3779d3479 100644 --- a/packages/alchemy/test/Cloudflare/Dlp/Dlp.test.ts +++ b/packages/alchemy/test/Cloudflare/Dlp/Dlp.test.ts @@ -6,6 +6,9 @@ import { expect } from "@effect/vitest"; import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -20,7 +23,7 @@ const logLevel = Effect.provideService( // the probe test always runs and pins the typed tag. const entitled = !!process.env.CLOUDFLARE_TEST_DLP; -test.provider.skipIf(entitled)( +test.provider.skipIf(entitled || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "unentitled accounts surface the typed Forbidden error", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Dlp/Entry.test.ts b/packages/alchemy/test/Cloudflare/Dlp/Entry.test.ts index 2b390d241..859a27872 100644 --- a/packages/alchemy/test/Cloudflare/Dlp/Entry.test.ts +++ b/packages/alchemy/test/Cloudflare/Dlp/Entry.test.ts @@ -5,6 +5,9 @@ import { expect } from "@effect/vitest"; import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -24,7 +27,7 @@ const entitled = !!process.env.CLOUDFLARE_TEST_DLP; // exhaustively paginated, hydrated into the exact `read` Attributes shape. // On an unentitled account this is the empty array (well-typed []); the // assertion proves the op is callable and returns the correct shape. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list returns the account's custom DLP entries", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Dlp/Profile.test.ts b/packages/alchemy/test/Cloudflare/Dlp/Profile.test.ts index cb2d561f5..8b9b5f014 100644 --- a/packages/alchemy/test/Cloudflare/Dlp/Profile.test.ts +++ b/packages/alchemy/test/Cloudflare/Dlp/Profile.test.ts @@ -5,6 +5,9 @@ import { expect } from "@effect/vitest"; import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -24,7 +27,7 @@ const entitled = !!process.env.CLOUDFLARE_TEST_DLP; // custom DLP profile. The result is the exact `read` Attributes shape. On an // unentitled account there may be zero custom profiles, so we only assert the // result is a well-typed array whose elements have the Attributes shape. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates custom DLP profiles (read-only)", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Dns/AccountSettings.test.ts b/packages/alchemy/test/Cloudflare/Dns/AccountSettings.test.ts index 0fe223f00..9f441b5f5 100644 --- a/packages/alchemy/test/Cloudflare/Dns/AccountSettings.test.ts +++ b/packages/alchemy/test/Cloudflare/Dns/AccountSettings.test.ts @@ -9,6 +9,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -66,7 +69,7 @@ const normalizeBaseline = (accountId: string) => }); describe.sequential("AccountSettings", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list returns the account's DNS settings singleton", (stack) => Effect.gen(function* () { @@ -97,7 +100,7 @@ describe.sequential("AccountSettings", () => { { timeout: 120_000 }, ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "pins a zone default and restores the pre-management value on destroy", (stack) => Effect.gen(function* () { @@ -142,7 +145,7 @@ describe.sequential("AccountSettings", () => { { timeout: 300_000 }, ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "updates in place, unions managedKeys, restores all managed fields", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Dns/DnsRecord.test.ts b/packages/alchemy/test/Cloudflare/Dns/DnsRecord.test.ts index f2fd4376b..a69e0e7d0 100644 --- a/packages/alchemy/test/Cloudflare/Dns/DnsRecord.test.ts +++ b/packages/alchemy/test/Cloudflare/Dns/DnsRecord.test.ts @@ -12,6 +12,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -94,140 +97,146 @@ const purgeRecords = (zoneId: string, name: string, type: string) => ), ); -test.provider("create and delete an A record with default props", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create and delete an A record with default props", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - yield* stack.destroy(); + yield* stack.destroy(); - const record = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.DnsRecord("DefaultA", { - zoneId, - name: NAME_DEFAULT, - type: "A", - content: "203.0.113.10", - }).pipe(adopt(true)); - }), - ); + const record = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.DnsRecord("DefaultA", { + zoneId, + name: NAME_DEFAULT, + type: "A", + content: "203.0.113.10", + }).pipe(adopt(true)); + }), + ); + + expect(record.recordId).toBeDefined(); + expect(record.zoneId).toEqual(zoneId); + expect(record.name).toEqual(NAME_DEFAULT); + expect(record.type).toEqual("A"); + expect(record.content).toEqual("203.0.113.10"); + // Cloudflare echoes ttl=1 for "automatic" (the default). + expect(record.ttl).toEqual(1); + expect(record.proxied).toEqual(false); + + const live = yield* getRecord(zoneId, record.recordId); + expect(live.id).toEqual(record.recordId); + expect(live.name).toEqual(NAME_DEFAULT); + expect(live.type).toEqual("A"); + expect(live.content).toEqual("203.0.113.10"); - expect(record.recordId).toBeDefined(); - expect(record.zoneId).toEqual(zoneId); - expect(record.name).toEqual(NAME_DEFAULT); - expect(record.type).toEqual("A"); - expect(record.content).toEqual("203.0.113.10"); - // Cloudflare echoes ttl=1 for "automatic" (the default). - expect(record.ttl).toEqual(1); - expect(record.proxied).toEqual(false); - - const live = yield* getRecord(zoneId, record.recordId); - expect(live.id).toEqual(record.recordId); - expect(live.name).toEqual(NAME_DEFAULT); - expect(live.type).toEqual("A"); - expect(live.content).toEqual("203.0.113.10"); - - yield* stack.destroy(); - - const gone = yield* findRecord(zoneId, NAME_DEFAULT, "A"); - expect(gone).toBeUndefined(); - }).pipe(logLevel), + yield* stack.destroy(); + + const gone = yield* findRecord(zoneId, NAME_DEFAULT, "A"); + expect(gone).toBeUndefined(); + }).pipe(logLevel), ); -test.provider("updating mutable fields patches in place", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "updating mutable fields patches in place", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - yield* stack.destroy(); + yield* stack.destroy(); - const initial = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.DnsRecord("UpdateA", { - zoneId, - name: NAME_UPDATE, - type: "A", - content: "203.0.113.20", - ttl: 300, - comment: "v1", - }).pipe(adopt(true)); - }), - ); + const initial = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.DnsRecord("UpdateA", { + zoneId, + name: NAME_UPDATE, + type: "A", + content: "203.0.113.20", + ttl: 300, + comment: "v1", + }).pipe(adopt(true)); + }), + ); - expect(initial.content).toEqual("203.0.113.20"); - expect(initial.ttl).toEqual(300); + expect(initial.content).toEqual("203.0.113.20"); + expect(initial.ttl).toEqual(300); - const updated = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.DnsRecord("UpdateA", { - zoneId, - name: NAME_UPDATE, - type: "A", - content: "203.0.113.21", - ttl: 600, - comment: "v2", - }).pipe(adopt(true)); - }), - ); + const updated = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.DnsRecord("UpdateA", { + zoneId, + name: NAME_UPDATE, + type: "A", + content: "203.0.113.21", + ttl: 600, + comment: "v2", + }).pipe(adopt(true)); + }), + ); - // Same record patched in place — not a replacement. - expect(updated.recordId).toEqual(initial.recordId); - expect(updated.content).toEqual("203.0.113.21"); - expect(updated.ttl).toEqual(600); + // Same record patched in place — not a replacement. + expect(updated.recordId).toEqual(initial.recordId); + expect(updated.content).toEqual("203.0.113.21"); + expect(updated.ttl).toEqual(600); - const live = yield* getRecord(zoneId, updated.recordId); - expect(live.content).toEqual("203.0.113.21"); - expect(live.ttl).toEqual(600); - expect(live.comment).toEqual("v2"); + const live = yield* getRecord(zoneId, updated.recordId); + expect(live.content).toEqual("203.0.113.21"); + expect(live.ttl).toEqual(600); + expect(live.comment).toEqual("v2"); - yield* stack.destroy(); - }).pipe(logLevel), + yield* stack.destroy(); + }).pipe(logLevel), ); -test.provider("changing the record type triggers replacement", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "changing the record type triggers replacement", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - yield* stack.destroy(); + yield* stack.destroy(); - const initial = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.DnsRecord("ReplaceRecord", { - zoneId, - name: NAME_REPLACE, - type: "A", - content: "203.0.113.30", - }).pipe(adopt(true)); - }), - ); + const initial = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.DnsRecord("ReplaceRecord", { + zoneId, + name: NAME_REPLACE, + type: "A", + content: "203.0.113.30", + }).pipe(adopt(true)); + }), + ); - expect(initial.type).toEqual("A"); + expect(initial.type).toEqual("A"); - const replaced = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.DnsRecord("ReplaceRecord", { - zoneId, - name: NAME_REPLACE, - type: "TXT", - content: '"alchemy-replace-test"', - }).pipe(adopt(true)); - }), - ); + const replaced = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.DnsRecord("ReplaceRecord", { + zoneId, + name: NAME_REPLACE, + type: "TXT", + content: '"alchemy-replace-test"', + }).pipe(adopt(true)); + }), + ); - // (name, type) is the record's identity — a new physical record exists. - expect(replaced.recordId).not.toEqual(initial.recordId); - expect(replaced.type).toEqual("TXT"); + // (name, type) is the record's identity — a new physical record exists. + expect(replaced.recordId).not.toEqual(initial.recordId); + expect(replaced.type).toEqual("TXT"); - // The old A record was deleted as part of the replacement. - const oldRecord = yield* findRecord(zoneId, NAME_REPLACE, "A"); - expect(oldRecord).toBeUndefined(); + // The old A record was deleted as part of the replacement. + const oldRecord = yield* findRecord(zoneId, NAME_REPLACE, "A"); + expect(oldRecord).toBeUndefined(); - const live = yield* getRecord(zoneId, replaced.recordId); - expect(live.type).toEqual("TXT"); + const live = yield* getRecord(zoneId, replaced.recordId); + expect(live.type).toEqual("TXT"); - yield* stack.destroy(); - }).pipe(logLevel), + yield* stack.destroy(); + }).pipe(logLevel), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "adoption — existing record errors without adopt, takes over with adopt(true)", (stack) => Effect.gen(function* () { @@ -308,36 +317,38 @@ test.provider( // zone via `listAllZones`, exhaustively paginates each zone's DNS records, and // hydrates them into the `read` Attributes shape. Deploy a record and assert it // appears in the exhaustively-paginated result. -test.provider("list enumerates the deployed DNS record", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed DNS record", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - yield* stack.destroy(); + yield* stack.destroy(); - const record = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.DnsRecord("ListedA", { - zoneId, - name: NAME_LIST, - type: "A", - content: "203.0.113.50", - }).pipe(adopt(true)); - }), - ); + const record = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.DnsRecord("ListedA", { + zoneId, + name: NAME_LIST, + type: "A", + content: "203.0.113.50", + }).pipe(adopt(true)); + }), + ); - const provider = yield* Provider.findProvider(Cloudflare.DnsRecord); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider(Cloudflare.DnsRecord); + const all = yield* provider.list(); - expect(all.length).toBeGreaterThan(0); - expect(all.some((r) => r.recordId === record.recordId)).toBe(true); - const found = all.find((r) => r.recordId === record.recordId); - expect(found?.zoneId).toEqual(zoneId); - expect(found?.name).toEqual(NAME_LIST); - expect(found?.type).toEqual("A"); - expect(found?.content).toEqual("203.0.113.50"); + expect(all.length).toBeGreaterThan(0); + expect(all.some((r) => r.recordId === record.recordId)).toBe(true); + const found = all.find((r) => r.recordId === record.recordId); + expect(found?.zoneId).toEqual(zoneId); + expect(found?.name).toEqual(NAME_LIST); + expect(found?.type).toEqual("A"); + expect(found?.content).toEqual("203.0.113.50"); - yield* stack.destroy(); - }).pipe(logLevel), + yield* stack.destroy(); + }).pipe(logLevel), ); /** diff --git a/packages/alchemy/test/Cloudflare/Dns/Dnssec.test.ts b/packages/alchemy/test/Cloudflare/Dns/Dnssec.test.ts index 4cf876b02..45db122ba 100644 --- a/packages/alchemy/test/Cloudflare/Dns/Dnssec.test.ts +++ b/packages/alchemy/test/Cloudflare/Dns/Dnssec.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -81,7 +84,7 @@ const normalizeDisabled = (zoneId: string) => // Both cases mutate the same zone-level DNSSEC singleton with opposite desired states; run them serially so they don't corrupt each other's captured baseline under the global concurrent test config. describe.sequential("Dnssec", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "enables DNSSEC, captures the disabled baseline, destroy deactivates", (stack) => Effect.gen(function* () { @@ -131,7 +134,7 @@ describe.sequential("Dnssec", () => { { timeout: 300_000 }, ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "updates status in place — same zone singleton, no replacement", (stack) => Effect.gen(function* () { @@ -183,7 +186,7 @@ describe.sequential("Dnssec", () => { // `listAllZones` and reads each one's config, skipping disabled zones // (which `read` treats as "not created"). Enable DNSSEC on the test zone // so it appears in the result, assert it's present, then restore. - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates active DNSSEC across zones", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Dns/View.test.ts b/packages/alchemy/test/Cloudflare/Dns/View.test.ts index 71515ab72..faf2b46b8 100644 --- a/packages/alchemy/test/Cloudflare/Dns/View.test.ts +++ b/packages/alchemy/test/Cloudflare/Dns/View.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -49,7 +52,7 @@ const retryForbidden = ( }), ); -test.provider.skipIf(internalDnsEntitled)( +test.provider.skipIf(internalDnsEntitled || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed InternalDnsNotAvailable error on unentitled accounts", (stack) => Effect.gen(function* () { @@ -76,7 +79,7 @@ test.provider.skipIf(internalDnsEntitled)( // accounts it simply returns no views. We can therefore always assert a // well-typed array, and only assert presence of a deployed view when the // account carries the Internal DNS entitlement. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list returns a well-typed array of internal DNS views", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Dns/ZoneSettings.test.ts b/packages/alchemy/test/Cloudflare/Dns/ZoneSettings.test.ts index 4b854247f..086e69302 100644 --- a/packages/alchemy/test/Cloudflare/Dns/ZoneSettings.test.ts +++ b/packages/alchemy/test/Cloudflare/Dns/ZoneSettings.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -79,7 +82,7 @@ const normalizeBaseline = (zoneId: string) => }); describe.sequential("ZoneSettings", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "pins flattenAllCnames and restores the pre-management value on destroy", (stack) => Effect.gen(function* () { @@ -123,7 +126,7 @@ describe.sequential("ZoneSettings", () => { { timeout: 300_000 }, ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "updates in place, unions managedKeys, restores all managed fields", (stack) => Effect.gen(function* () { @@ -200,19 +203,23 @@ describe.sequential("ZoneSettings", () => { // API for this per-zone settings object, so `list()` enumerates every zone // via `listAllZones` and reads the singleton in each. Assert the result is // non-empty and contains the standing test zone. - test.provider("list enumerates DNS settings across all zones", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates DNS settings across all zones", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - const provider = yield* Provider.findProvider(Cloudflare.ZoneDnsSettings); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider( + Cloudflare.ZoneDnsSettings, + ); + const all = yield* provider.list(); - expect(all.length).toBeGreaterThan(0); - expect(all.some((s) => s.zoneId === zoneId)).toBe(true); + expect(all.length).toBeGreaterThan(0); + expect(all.some((s) => s.zoneId === zoneId)).toBe(true); - // `stack` is unused here (the singleton always exists on every zone), - // but keep the destroy bookend so the harness state stays clean. - yield* stack.destroy(); - }).pipe(logLevel), + // `stack` is unused here (the singleton always exists on every zone), + // but keep the destroy bookend so the harness state stays clean. + yield* stack.destroy(); + }).pipe(logLevel), ); }); diff --git a/packages/alchemy/test/Cloudflare/Dns/ZoneTransferAcl.test.ts b/packages/alchemy/test/Cloudflare/Dns/ZoneTransferAcl.test.ts index 3072952a5..dea0f594a 100644 --- a/packages/alchemy/test/Cloudflare/Dns/ZoneTransferAcl.test.ts +++ b/packages/alchemy/test/Cloudflare/Dns/ZoneTransferAcl.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -44,7 +47,7 @@ const expectGone = (accountId: string, aclId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create, update in place, and delete a zone transfer ACL", (stack) => Effect.gen(function* () { @@ -94,7 +97,7 @@ test.provider( { timeout: 120_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "generates a deterministic name when none is provided", (stack) => Effect.gen(function* () { @@ -117,7 +120,7 @@ test.provider( { timeout: 120_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed zone transfer ACLs", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Dns/ZoneTransferIncoming.test.ts b/packages/alchemy/test/Cloudflare/Dns/ZoneTransferIncoming.test.ts index 5427038f5..c4b4e75e8 100644 --- a/packages/alchemy/test/Cloudflare/Dns/ZoneTransferIncoming.test.ts +++ b/packages/alchemy/test/Cloudflare/Dns/ZoneTransferIncoming.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -81,7 +84,7 @@ test.provider( { timeout: 120_000 }, ); -test.provider.skipIf(!!secondaryZoneId)( +test.provider.skipIf(!!secondaryZoneId || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "incoming config does not persist on non-secondary zones (typed not-found)", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Dns/ZoneTransferOutgoing.test.ts b/packages/alchemy/test/Cloudflare/Dns/ZoneTransferOutgoing.test.ts index d605af599..898945cab 100644 --- a/packages/alchemy/test/Cloudflare/Dns/ZoneTransferOutgoing.test.ts +++ b/packages/alchemy/test/Cloudflare/Dns/ZoneTransferOutgoing.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -49,7 +52,7 @@ const retryForbidden = ( }), ); -test.provider.skipIf(outgoingEntitled)( +test.provider.skipIf(outgoingEntitled || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed OutgoingZoneTransfersNotAllowed error on unentitled zones", (stack) => Effect.gen(function* () { @@ -156,7 +159,7 @@ test.provider.skipIf(!outgoingEntitled)( // read-only assertion (well-typed Attributes[]) always runs; on the // unentitled testing account no zone returns a config, so the deployed- // presence assertion is gated behind an entitled zone. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the outgoing transfer config per zone", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Dns/ZoneTransferPeer.test.ts b/packages/alchemy/test/Cloudflare/Dns/ZoneTransferPeer.test.ts index 97634bbf9..394ab9fef 100644 --- a/packages/alchemy/test/Cloudflare/Dns/ZoneTransferPeer.test.ts +++ b/packages/alchemy/test/Cloudflare/Dns/ZoneTransferPeer.test.ts @@ -9,6 +9,9 @@ import * as Redacted from "effect/Redacted"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -48,7 +51,7 @@ const expectGone = (accountId: string, peerId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create with connection settings, update in place, and delete a peer", (stack) => Effect.gen(function* () { @@ -103,7 +106,7 @@ test.provider( { timeout: 120_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "wires a TSIG reference through to the peer", (stack) => Effect.gen(function* () { @@ -137,7 +140,7 @@ test.provider( { timeout: 120_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed peer", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Dns/ZoneTransferTsig.test.ts b/packages/alchemy/test/Cloudflare/Dns/ZoneTransferTsig.test.ts index 4298cbd01..a3a8113dd 100644 --- a/packages/alchemy/test/Cloudflare/Dns/ZoneTransferTsig.test.ts +++ b/packages/alchemy/test/Cloudflare/Dns/ZoneTransferTsig.test.ts @@ -9,6 +9,9 @@ import * as Redacted from "effect/Redacted"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -50,7 +53,7 @@ const expectGone = (accountId: string, tsigId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create, rotate the secret in place, and delete a TSIG", (stack) => Effect.gen(function* () { @@ -103,7 +106,7 @@ test.provider( // resolve the provider from context via the typed `findProvider`, call // `list()`, and assert the deployed TSIG appears in the exhaustively-paginated // result. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed TSIG", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/DnsFirewall/DnsFirewall.test.ts b/packages/alchemy/test/Cloudflare/DnsFirewall/DnsFirewall.test.ts index ca758d410..04af88899 100644 --- a/packages/alchemy/test/Cloudflare/DnsFirewall/DnsFirewall.test.ts +++ b/packages/alchemy/test/Cloudflare/DnsFirewall/DnsFirewall.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -47,7 +50,7 @@ const expectGone = (accountId: string, dnsFirewallId: string) => }), ); -test.provider.skipIf(entitled)( +test.provider.skipIf(entitled || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed DnsFirewallNotEntitled error on unentitled accounts", (stack) => Effect.gen(function* () { @@ -197,7 +200,7 @@ test.provider.skipIf(!entitled)( // account and returns the (empty) cluster Attributes array. This read-only // assertion proves `list()` exhaustively paginates and produces the exact // `read` Attributes shape. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list returns the DNS firewall cluster Attributes array", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Email/EmailAddress.test.ts b/packages/alchemy/test/Cloudflare/Email/EmailAddress.test.ts index 5305f2ec6..6f041ed07 100644 --- a/packages/alchemy/test/Cloudflare/Email/EmailAddress.test.ts +++ b/packages/alchemy/test/Cloudflare/Email/EmailAddress.test.ts @@ -7,6 +7,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -24,37 +27,39 @@ const testEmail = "alchemy-list-test@alchemy-test-2.us"; // destination address, resolve the provider from context via the typed // `findProvider`, call `list()`, and assert the deployed address appears in // the exhaustively-paginated result. -test.provider("list enumerates the deployed email address", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const address = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.EmailAddress("ListAddress", { - email: testEmail, - }); - }), - ); - - expect(address.email).toEqual(testEmail); - - const provider = yield* Provider.findProvider(Cloudflare.EmailAddress); - - // A freshly-deployed address is eventually consistent in the account-wide - // list(); poll until it appears before asserting. - const all = yield* poll({ - description: "list() includes the deployed email address", - effect: provider.list(), - predicate: (all) => all.some((a) => a.email === testEmail), - schedule: Schedule.both( - Schedule.spaced("3 seconds"), - Schedule.recurs(20), - ), - }); - - expect(all.some((a) => a.addressId === address.addressId)).toBe(true); - expect(all.some((a) => a.email === testEmail)).toBe(true); - - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed email address", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const address = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.EmailAddress("ListAddress", { + email: testEmail, + }); + }), + ); + + expect(address.email).toEqual(testEmail); + + const provider = yield* Provider.findProvider(Cloudflare.EmailAddress); + + // A freshly-deployed address is eventually consistent in the account-wide + // list(); poll until it appears before asserting. + const all = yield* poll({ + description: "list() includes the deployed email address", + effect: provider.list(), + predicate: (all) => all.some((a) => a.email === testEmail), + schedule: Schedule.both( + Schedule.spaced("3 seconds"), + Schedule.recurs(20), + ), + }); + + expect(all.some((a) => a.addressId === address.addressId)).toBe(true); + expect(all.some((a) => a.email === testEmail)).toBe(true); + + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Email/EmailCatchAll.test.ts b/packages/alchemy/test/Cloudflare/Email/EmailCatchAll.test.ts index da61fb803..eebb7c567 100644 --- a/packages/alchemy/test/Cloudflare/Email/EmailCatchAll.test.ts +++ b/packages/alchemy/test/Cloudflare/Email/EmailCatchAll.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -70,7 +73,7 @@ const setBaseline = (zoneId: string) => ); describe.sequential("EmailCatchAll", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "configures the catch-all rule and restores the baseline on destroy", (stack) => Effect.gen(function* () { @@ -119,7 +122,7 @@ describe.sequential("EmailCatchAll", () => { }).pipe(logLevel), ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "updates the catch-all rule in place and keeps the captured baseline", (stack) => Effect.gen(function* () { @@ -188,7 +191,7 @@ describe.sequential("EmailCatchAll", () => { // Email Routing enabled). Ensure Email Routing is enabled on the standing // test zone, then assert the result is non-empty, well-typed, and contains // the test zone. - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the catch-all rule across all zones", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Email/EmailRouting.test.ts b/packages/alchemy/test/Cloudflare/Email/EmailRouting.test.ts index 836e370ec..92f758c2a 100644 --- a/packages/alchemy/test/Cloudflare/Email/EmailRouting.test.ts +++ b/packages/alchemy/test/Cloudflare/Email/EmailRouting.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -65,27 +68,29 @@ describe.sequential("EmailRouting", () => { // `listAllZones` and reads the singleton in each. Assert the result is // non-empty and contains the standing test zone. Capture-and-restore the // zone's `enabled` state so the run leaves no residue. - test.provider("list enumerates email routing across all zones", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates email routing across all zones", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - yield* stack.destroy(); + yield* stack.destroy(); - // Capture the pre-test enabled state to restore at the end. - const before = yield* getEmailRouting(zoneId); + // Capture the pre-test enabled state to restore at the end. + const before = yield* getEmailRouting(zoneId); - const provider = yield* Provider.findProvider(Cloudflare.EmailRouting); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider(Cloudflare.EmailRouting); + const all = yield* provider.list(); - expect(all.length).toBeGreaterThan(0); - const entry = all.find((r) => r.zoneId === zoneId); - expect(entry).toBeDefined(); - expect(entry!.name).toEqual(zoneName); + expect(all.length).toBeGreaterThan(0); + const entry = all.find((r) => r.zoneId === zoneId); + expect(entry).toBeDefined(); + expect(entry!.name).toEqual(zoneName); - // Restore the captured state. - yield* setEnabled(zoneId, before.enabled); + // Restore the captured state. + yield* setEnabled(zoneId, before.enabled); - yield* stack.destroy(); - }).pipe(logLevel), + yield* stack.destroy(); + }).pipe(logLevel), ); }); diff --git a/packages/alchemy/test/Cloudflare/Email/EmailRule.test.ts b/packages/alchemy/test/Cloudflare/Email/EmailRule.test.ts index bdf04a0b3..266a8d405 100644 --- a/packages/alchemy/test/Cloudflare/Email/EmailRule.test.ts +++ b/packages/alchemy/test/Cloudflare/Email/EmailRule.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -56,7 +59,7 @@ describe.sequential("EmailRule", () => { // paginates each zone's rules (skipping zones without Email Routing enabled). // Deploy a rule on the standing test zone, then assert it appears in the // exhaustively-paginated result. - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed email rule across all zones", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Email/SendEmail.test.ts b/packages/alchemy/test/Cloudflare/Email/SendEmail.test.ts index 62449e728..6b3e3c1fd 100644 --- a/packages/alchemy/test/Cloudflare/Email/SendEmail.test.ts +++ b/packages/alchemy/test/Cloudflare/Email/SendEmail.test.ts @@ -8,6 +8,9 @@ import * as Schedule from "effect/Schedule"; import * as HttpClient from "effect/unstable/http/HttpClient"; import SendEmailWorker from "./fixtures/worker.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test, beforeAll, afterAll, deploy, destroy } = Test.make({ providers: Cloudflare.providers(), }); @@ -39,7 +42,7 @@ afterAll.skipIf(!!process.env.NO_DESTROY || skip)(destroy(Stack)); // The send_email binding only delivers from a domain with Email Routing // enabled to a verified destination address — supply such a pair via // CLOUDFLARE_TEST_EMAIL_FROM / CLOUDFLARE_TEST_EMAIL_TO to run this test. -test.skipIf(skip)( +test.skipIf(skip || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "sends an email through the Worker send_email binding", Effect.gen(function* () { const { url } = yield* stack; diff --git a/packages/alchemy/test/Cloudflare/Email/SendingSubdomain.test.ts b/packages/alchemy/test/Cloudflare/Email/SendingSubdomain.test.ts index 743e24435..d18579a6f 100644 --- a/packages/alchemy/test/Cloudflare/Email/SendingSubdomain.test.ts +++ b/packages/alchemy/test/Cloudflare/Email/SendingSubdomain.test.ts @@ -13,6 +13,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -92,99 +95,103 @@ const purgeSubdomain = (zoneId: string, name: string) => ), ); -test.provider("create and destroy a sending subdomain", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create and destroy a sending subdomain", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - yield* stack.destroy(); - yield* purgeSubdomain(zoneId, NAME_DEFAULT); + yield* stack.destroy(); + yield* purgeSubdomain(zoneId, NAME_DEFAULT); - const sending = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.EmailSendingSubdomain("Sending", { - zoneId, - name: NAME_DEFAULT, - }); - }), - ); + const sending = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.EmailSendingSubdomain("Sending", { + zoneId, + name: NAME_DEFAULT, + }); + }), + ); - expect(sending.subdomainId).toBeDefined(); - expect(sending.zoneId).toEqual(zoneId); - expect(sending.name).toEqual(NAME_DEFAULT); - // CF-hosted zone — DNS records are auto-created and validate - // immediately; the reconciler polls briefly for `enabled`. - expect(sending.enabled).toEqual(true); - expect(sending.dkimSelector).toBeDefined(); - expect(sending.returnPathDomain).toContain(NAME_DEFAULT); - - // Out-of-band verification against the live API. - const live = yield* getSubdomain(zoneId, sending.subdomainId); - expect(live.tag).toEqual(sending.subdomainId); - expect(live.name).toEqual(NAME_DEFAULT); - - yield* stack.destroy(); - - // Gone after destroy — the typed not-found is the success signal. - const gone = yield* getSubdomain(zoneId, sending.subdomainId).pipe( - Effect.map(() => "still-there" as const), - Effect.catchTag("SendingSubdomainNotFound", () => - Effect.succeed("gone" as const), - ), - ); - expect(gone).toEqual("gone"); + expect(sending.subdomainId).toBeDefined(); + expect(sending.zoneId).toEqual(zoneId); + expect(sending.name).toEqual(NAME_DEFAULT); + // CF-hosted zone — DNS records are auto-created and validate + // immediately; the reconciler polls briefly for `enabled`. + expect(sending.enabled).toEqual(true); + expect(sending.dkimSelector).toBeDefined(); + expect(sending.returnPathDomain).toContain(NAME_DEFAULT); + + // Out-of-band verification against the live API. + const live = yield* getSubdomain(zoneId, sending.subdomainId); + expect(live.tag).toEqual(sending.subdomainId); + expect(live.name).toEqual(NAME_DEFAULT); + + yield* stack.destroy(); - // Destroy again — delete is idempotent. - yield* stack.destroy(); - }).pipe(logLevel), + // Gone after destroy — the typed not-found is the success signal. + const gone = yield* getSubdomain(zoneId, sending.subdomainId).pipe( + Effect.map(() => "still-there" as const), + Effect.catchTag("SendingSubdomainNotFound", () => + Effect.succeed("gone" as const), + ), + ); + expect(gone).toEqual("gone"); + + // Destroy again — delete is idempotent. + yield* stack.destroy(); + }).pipe(logLevel), ); -test.provider("changing the name triggers replacement", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - yield* stack.destroy(); - yield* purgeSubdomain(zoneId, NAME_REPLACE_A); - yield* purgeSubdomain(zoneId, NAME_REPLACE_B); - - const initial = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.EmailSendingSubdomain("ReplaceSending", { - zoneId, - name: NAME_REPLACE_A, - }); - }), - ); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "changing the name triggers replacement", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - expect(initial.name).toEqual(NAME_REPLACE_A); + yield* stack.destroy(); + yield* purgeSubdomain(zoneId, NAME_REPLACE_A); + yield* purgeSubdomain(zoneId, NAME_REPLACE_B); - const replaced = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.EmailSendingSubdomain("ReplaceSending", { - zoneId, - name: NAME_REPLACE_B, - }); - }), - ); + const initial = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.EmailSendingSubdomain("ReplaceSending", { + zoneId, + name: NAME_REPLACE_A, + }); + }), + ); - // The name is the subdomain's identity — a new physical resource. - expect(replaced.subdomainId).not.toEqual(initial.subdomainId); - expect(replaced.name).toEqual(NAME_REPLACE_B); + expect(initial.name).toEqual(NAME_REPLACE_A); - // The old subdomain was deleted as part of the replacement. - const old = yield* findByName(zoneId, NAME_REPLACE_A); - expect(old).toBeUndefined(); + const replaced = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.EmailSendingSubdomain("ReplaceSending", { + zoneId, + name: NAME_REPLACE_B, + }); + }), + ); - const live = yield* findByName(zoneId, NAME_REPLACE_B); - expect(live?.tag).toEqual(replaced.subdomainId); + // The name is the subdomain's identity — a new physical resource. + expect(replaced.subdomainId).not.toEqual(initial.subdomainId); + expect(replaced.name).toEqual(NAME_REPLACE_B); - yield* stack.destroy(); + // The old subdomain was deleted as part of the replacement. + const old = yield* findByName(zoneId, NAME_REPLACE_A); + expect(old).toBeUndefined(); - const gone = yield* findByName(zoneId, NAME_REPLACE_B); - expect(gone).toBeUndefined(); - }).pipe(logLevel), + const live = yield* findByName(zoneId, NAME_REPLACE_B); + expect(live?.tag).toEqual(replaced.subdomainId); + + yield* stack.destroy(); + + const gone = yield* findByName(zoneId, NAME_REPLACE_B); + expect(gone).toBeUndefined(); + }).pipe(logLevel), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "adoption — existing subdomain errors without adopt, takes over with adopt(true)", (stack) => Effect.gen(function* () { @@ -244,7 +251,7 @@ test.provider( { timeout: 180_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed sending subdomain", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/EmailSecurity/AllowPolicy.test.ts b/packages/alchemy/test/Cloudflare/EmailSecurity/AllowPolicy.test.ts index ce43242a5..545806c7b 100644 --- a/packages/alchemy/test/Cloudflare/EmailSecurity/AllowPolicy.test.ts +++ b/packages/alchemy/test/Cloudflare/EmailSecurity/AllowPolicy.test.ts @@ -9,6 +9,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -42,7 +45,7 @@ const findByPattern = (accountId: string) => // Unentitlement probe — pins the typed EmailSecurityNotEntitled rejection and // skips on entitled accounts, where the list call would succeed. -test.provider.skipIf(entitled)( +test.provider.skipIf(entitled || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed EmailSecurityNotEntitled error on unentitled accounts", (stack) => Effect.gen(function* () { @@ -73,7 +76,7 @@ test.provider.skipIf(entitled)( // Read-only list assertion that runs on every account. On unentitled // accounts the provider's list() maps the typed EmailSecurityNotEntitled // rejection to a well-typed empty array; entitled accounts return real rows. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates allow policies (read-only)", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/EmailSecurity/Domain.test.ts b/packages/alchemy/test/Cloudflare/EmailSecurity/Domain.test.ts index 4f582c7be..edc90dae9 100644 --- a/packages/alchemy/test/Cloudflare/EmailSecurity/Domain.test.ts +++ b/packages/alchemy/test/Cloudflare/EmailSecurity/Domain.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -101,7 +104,7 @@ test.provider.skipIf(!sacrificialDomain)( // list op rejects with EmailSecurityNotEntitled / Forbidden, which the // provider's `list()` maps to a well-typed []. On entitled accounts every // onboarded domain is returned in the exact `read` Attributes shape. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates Email Security domains (or [] when unentitled)", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/EmailSecurity/ImpersonationRegistryEntry.test.ts b/packages/alchemy/test/Cloudflare/EmailSecurity/ImpersonationRegistryEntry.test.ts index 2946c7574..aaa9a9144 100644 --- a/packages/alchemy/test/Cloudflare/EmailSecurity/ImpersonationRegistryEntry.test.ts +++ b/packages/alchemy/test/Cloudflare/EmailSecurity/ImpersonationRegistryEntry.test.ts @@ -9,6 +9,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -103,7 +106,7 @@ test.provider.skipIf(!entitled)( // `EmailSecurityNotEntitled` error and returns a well-typed empty array, so // this runs everywhere and proves the wiring (typed `findProvider`, exact // Attributes shape). -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list returns the account impersonation registry", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/EmailSecurity/TrustedDomain.test.ts b/packages/alchemy/test/Cloudflare/EmailSecurity/TrustedDomain.test.ts index 3bf6b074e..b7c6fd2b8 100644 --- a/packages/alchemy/test/Cloudflare/EmailSecurity/TrustedDomain.test.ts +++ b/packages/alchemy/test/Cloudflare/EmailSecurity/TrustedDomain.test.ts @@ -9,6 +9,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -101,7 +104,7 @@ test.provider.skipIf(!entitled)( // and the result is an empty array — so this assertion is meaningful without // the add-on. On an entitled account it also asserts the deployed entry is // present. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates trusted domains", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Firewall/AccessRule.test.ts b/packages/alchemy/test/Cloudflare/Firewall/AccessRule.test.ts index a3c88c601..b977b5fa5 100644 --- a/packages/alchemy/test/Cloudflare/Firewall/AccessRule.test.ts +++ b/packages/alchemy/test/Cloudflare/Firewall/AccessRule.test.ts @@ -11,6 +11,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -131,186 +134,194 @@ const expectAccountRuleGone = (accountId: string, ruleId: string) => }), ); -test.provider("create and delete a zone-scoped ip rule", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - const zoneId = yield* resolveZoneId; - - yield* stack.destroy(); - yield* purgeRules({ zoneId }, IP_DEFAULT); - - const rule = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.FirewallAccessRule("DefaultRule", { - zoneId, - configuration: { target: "ip", value: IP_DEFAULT }, - mode: "challenge", - notes: "alchemy firewall test (default)", - }).pipe(adopt(true)); - }), - ); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create and delete a zone-scoped ip rule", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + const zoneId = yield* resolveZoneId; + + yield* stack.destroy(); + yield* purgeRules({ zoneId }, IP_DEFAULT); + + const rule = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.FirewallAccessRule("DefaultRule", { + zoneId, + configuration: { target: "ip", value: IP_DEFAULT }, + mode: "challenge", + notes: "alchemy firewall test (default)", + }).pipe(adopt(true)); + }), + ); - expect(rule.ruleId).toBeDefined(); - expect(rule.zoneId).toEqual(zoneId); - expect(rule.accountId).toEqual(accountId); - expect(rule.configuration).toEqual({ target: "ip", value: IP_DEFAULT }); - expect(rule.mode).toEqual("challenge"); - expect(rule.notes).toEqual("alchemy firewall test (default)"); - expect(rule.allowedModes).toContain("block"); + expect(rule.ruleId).toBeDefined(); + expect(rule.zoneId).toEqual(zoneId); + expect(rule.accountId).toEqual(accountId); + expect(rule.configuration).toEqual({ target: "ip", value: IP_DEFAULT }); + expect(rule.mode).toEqual("challenge"); + expect(rule.notes).toEqual("alchemy firewall test (default)"); + expect(rule.allowedModes).toContain("block"); - const live = yield* getZoneRule(zoneId, rule.ruleId); - expect(live.id).toEqual(rule.ruleId); - expect(live.configuration.value).toEqual(IP_DEFAULT); - expect(live.mode).toEqual("challenge"); + const live = yield* getZoneRule(zoneId, rule.ruleId); + expect(live.id).toEqual(rule.ruleId); + expect(live.configuration.value).toEqual(IP_DEFAULT); + expect(live.mode).toEqual("challenge"); - yield* stack.destroy(); + yield* stack.destroy(); - yield* expectZoneRuleGone(zoneId, rule.ruleId); - }).pipe(logLevel), + yield* expectZoneRuleGone(zoneId, rule.ruleId); + }).pipe(logLevel), ); -test.provider("update mode and notes in place (same ruleId)", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - yield* stack.destroy(); - yield* purgeRules({ zoneId }, IP_UPDATE); - - const initial = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.FirewallAccessRule("UpdateRule", { - zoneId, - configuration: { target: "ip", value: IP_UPDATE }, - mode: "challenge", - notes: "v1", - }).pipe(adopt(true)); - }), - ); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "update mode and notes in place (same ruleId)", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - expect(initial.mode).toEqual("challenge"); - expect(initial.notes).toEqual("v1"); - - const updated = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.FirewallAccessRule("UpdateRule", { - zoneId, - configuration: { target: "ip", value: IP_UPDATE }, - mode: "managed_challenge", - notes: "v2", - }).pipe(adopt(true)); - }), - ); + yield* stack.destroy(); + yield* purgeRules({ zoneId }, IP_UPDATE); - // Same rule patched in place — not a replacement. - expect(updated.ruleId).toEqual(initial.ruleId); - expect(updated.mode).toEqual("managed_challenge"); - expect(updated.notes).toEqual("v2"); - - const live = yield* getZoneRule(zoneId, updated.ruleId); - expect(live.mode).toEqual("managed_challenge"); - expect(live.notes).toEqual("v2"); - - // Redeploying identical props is a no-op (still the same rule). - const noop = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.FirewallAccessRule("UpdateRule", { - zoneId, - configuration: { target: "ip", value: IP_UPDATE }, - mode: "managed_challenge", - notes: "v2", - }).pipe(adopt(true)); - }), - ); - expect(noop.ruleId).toEqual(initial.ruleId); + const initial = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.FirewallAccessRule("UpdateRule", { + zoneId, + configuration: { target: "ip", value: IP_UPDATE }, + mode: "challenge", + notes: "v1", + }).pipe(adopt(true)); + }), + ); - yield* stack.destroy(); + expect(initial.mode).toEqual("challenge"); + expect(initial.notes).toEqual("v1"); - yield* expectZoneRuleGone(zoneId, initial.ruleId); - }).pipe(logLevel), + const updated = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.FirewallAccessRule("UpdateRule", { + zoneId, + configuration: { target: "ip", value: IP_UPDATE }, + mode: "managed_challenge", + notes: "v2", + }).pipe(adopt(true)); + }), + ); + + // Same rule patched in place — not a replacement. + expect(updated.ruleId).toEqual(initial.ruleId); + expect(updated.mode).toEqual("managed_challenge"); + expect(updated.notes).toEqual("v2"); + + const live = yield* getZoneRule(zoneId, updated.ruleId); + expect(live.mode).toEqual("managed_challenge"); + expect(live.notes).toEqual("v2"); + + // Redeploying identical props is a no-op (still the same rule). + const noop = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.FirewallAccessRule("UpdateRule", { + zoneId, + configuration: { target: "ip", value: IP_UPDATE }, + mode: "managed_challenge", + notes: "v2", + }).pipe(adopt(true)); + }), + ); + expect(noop.ruleId).toEqual(initial.ruleId); + + yield* stack.destroy(); + + yield* expectZoneRuleGone(zoneId, initial.ruleId); + }).pipe(logLevel), ); -test.provider("changing the configuration triggers replacement", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - yield* stack.destroy(); - yield* purgeRules({ zoneId }, IP_REPLACE_OLD); - yield* purgeRules({ zoneId }, IP_REPLACE_NEW); - - const initial = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.FirewallAccessRule("ReplaceRule", { - zoneId, - configuration: { target: "ip", value: IP_REPLACE_OLD }, - mode: "challenge", - }).pipe(adopt(true)); - }), - ); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "changing the configuration triggers replacement", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - expect(initial.configuration.value).toEqual(IP_REPLACE_OLD); + yield* stack.destroy(); + yield* purgeRules({ zoneId }, IP_REPLACE_OLD); + yield* purgeRules({ zoneId }, IP_REPLACE_NEW); - const replaced = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.FirewallAccessRule("ReplaceRule", { - zoneId, - configuration: { target: "ip", value: IP_REPLACE_NEW }, - mode: "challenge", - }).pipe(adopt(true)); - }), - ); + const initial = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.FirewallAccessRule("ReplaceRule", { + zoneId, + configuration: { target: "ip", value: IP_REPLACE_OLD }, + mode: "challenge", + }).pipe(adopt(true)); + }), + ); + + expect(initial.configuration.value).toEqual(IP_REPLACE_OLD); + + const replaced = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.FirewallAccessRule("ReplaceRule", { + zoneId, + configuration: { target: "ip", value: IP_REPLACE_NEW }, + mode: "challenge", + }).pipe(adopt(true)); + }), + ); - // The configuration is the rule's identity — a new physical rule exists. - expect(replaced.ruleId).not.toEqual(initial.ruleId); - expect(replaced.configuration.value).toEqual(IP_REPLACE_NEW); + // The configuration is the rule's identity — a new physical rule exists. + expect(replaced.ruleId).not.toEqual(initial.ruleId); + expect(replaced.configuration.value).toEqual(IP_REPLACE_NEW); - // The old rule was deleted as part of the replacement. - yield* expectZoneRuleGone(zoneId, initial.ruleId); + // The old rule was deleted as part of the replacement. + yield* expectZoneRuleGone(zoneId, initial.ruleId); - const live = yield* getZoneRule(zoneId, replaced.ruleId); - expect(live.configuration.value).toEqual(IP_REPLACE_NEW); + const live = yield* getZoneRule(zoneId, replaced.ruleId); + expect(live.configuration.value).toEqual(IP_REPLACE_NEW); - yield* stack.destroy(); + yield* stack.destroy(); - yield* expectZoneRuleGone(zoneId, replaced.ruleId); - }).pipe(logLevel), + yield* expectZoneRuleGone(zoneId, replaced.ruleId); + }).pipe(logLevel), ); -test.provider("list enumerates the deployed zone-scoped rule", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - yield* stack.destroy(); - yield* purgeRules({ zoneId }, IP_LIST); - - const rule = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.FirewallAccessRule("ListRule", { - zoneId, - configuration: { target: "ip", value: IP_LIST }, - mode: "challenge", - notes: "alchemy firewall test (list)", - }).pipe(adopt(true)); - }), - ); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed zone-scoped rule", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - const provider = yield* Provider.findProvider( - Cloudflare.FirewallAccessRule, - ); - const all = yield* provider.list(); + yield* stack.destroy(); + yield* purgeRules({ zoneId }, IP_LIST); - const found = all.find((r) => r.ruleId === rule.ruleId); - expect(found).toBeDefined(); - expect(found?.zoneId).toEqual(zoneId); - expect(found?.configuration).toEqual({ target: "ip", value: IP_LIST }); - expect(found?.mode).toEqual("challenge"); + const rule = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.FirewallAccessRule("ListRule", { + zoneId, + configuration: { target: "ip", value: IP_LIST }, + mode: "challenge", + notes: "alchemy firewall test (list)", + }).pipe(adopt(true)); + }), + ); - yield* stack.destroy(); + const provider = yield* Provider.findProvider( + Cloudflare.FirewallAccessRule, + ); + const all = yield* provider.list(); + + const found = all.find((r) => r.ruleId === rule.ruleId); + expect(found).toBeDefined(); + expect(found?.zoneId).toEqual(zoneId); + expect(found?.configuration).toEqual({ target: "ip", value: IP_LIST }); + expect(found?.mode).toEqual("challenge"); - yield* expectZoneRuleGone(zoneId, rule.ruleId); - }).pipe(logLevel), + yield* stack.destroy(); + + yield* expectZoneRuleGone(zoneId, rule.ruleId); + }).pipe(logLevel), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "account-scoped rule (no zoneId) create, update, delete", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Firewall/Lockdown.test.ts b/packages/alchemy/test/Cloudflare/Firewall/Lockdown.test.ts index d9611d6c9..daa1264a1 100644 --- a/packages/alchemy/test/Cloudflare/Firewall/Lockdown.test.ts +++ b/packages/alchemy/test/Cloudflare/Firewall/Lockdown.test.ts @@ -11,6 +11,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -98,7 +101,7 @@ const expectLockdownGone = (zoneId: string, lockdownId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create, update urls/configurations/description/paused in place, destroy", (stack) => Effect.gen(function* () { @@ -191,34 +194,36 @@ test.provider( // every zone via `listAllZones`, exhaustively paginates each zone's lockdown // rules, and hydrates them into the `read` Attributes shape. Deploy a rule and // assert it appears in the enumerated result. -test.provider("list enumerates the deployed lockdown rule", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - yield* stack.destroy(); - yield* purgeLockdowns(zoneId, [URL_LIST]); - - const deployed = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.Lockdown("ListLockdown", { - zoneId, - urls: [URL_LIST], - configurations: [{ target: "ip", value: IP_LIST }], - description: "alchemy lockdown list test", - }).pipe(adopt(true)); - }), - ); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed lockdown rule", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - const provider = yield* Provider.findProvider(Cloudflare.Lockdown); - const all = yield* provider.list(); + yield* stack.destroy(); + yield* purgeLockdowns(zoneId, [URL_LIST]); - expect(all.some((r) => r.lockdownId === deployed.lockdownId)).toBe(true); - const found = all.find((r) => r.lockdownId === deployed.lockdownId); - expect(found?.zoneId).toEqual(zoneId); - expect(found?.urls).toEqual([URL_LIST]); + const deployed = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.Lockdown("ListLockdown", { + zoneId, + urls: [URL_LIST], + configurations: [{ target: "ip", value: IP_LIST }], + description: "alchemy lockdown list test", + }).pipe(adopt(true)); + }), + ); + + const provider = yield* Provider.findProvider(Cloudflare.Lockdown); + const all = yield* provider.list(); - yield* stack.destroy(); + expect(all.some((r) => r.lockdownId === deployed.lockdownId)).toBe(true); + const found = all.find((r) => r.lockdownId === deployed.lockdownId); + expect(found?.zoneId).toEqual(zoneId); + expect(found?.urls).toEqual([URL_LIST]); - yield* expectLockdownGone(zoneId, deployed.lockdownId); - }).pipe(logLevel), + yield* stack.destroy(); + + yield* expectLockdownGone(zoneId, deployed.lockdownId); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Firewall/UaRule.test.ts b/packages/alchemy/test/Cloudflare/Firewall/UaRule.test.ts index f21f0aaf0..781ccc32e 100644 --- a/packages/alchemy/test/Cloudflare/Firewall/UaRule.test.ts +++ b/packages/alchemy/test/Cloudflare/Firewall/UaRule.test.ts @@ -11,6 +11,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -91,7 +94,7 @@ const expectUaRuleGone = (zoneId: string, uaRuleId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create, update mode/paused/description/userAgent in place, destroy", (stack) => Effect.gen(function* () { @@ -174,34 +177,36 @@ test.provider( // zone with no account-wide list, so `list()` enumerates every zone via // `listAllZones` and exhaustively paginates each. Deploy one rule and assert // it appears in the result, hydrated into the full `read` Attributes shape. -test.provider("list enumerates UA rules across all zones", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - yield* stack.destroy(); - yield* purgeUaRules(zoneId, [UA_LIST]); - - const deployed = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.UaRule("ListUaRule", { - zoneId, - userAgent: UA_LIST, - mode: "block", - description: "alchemy ua rule list test", - }).pipe(adopt(true)); - }), - ); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates UA rules across all zones", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; + + yield* stack.destroy(); + yield* purgeUaRules(zoneId, [UA_LIST]); + + const deployed = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.UaRule("ListUaRule", { + zoneId, + userAgent: UA_LIST, + mode: "block", + description: "alchemy ua rule list test", + }).pipe(adopt(true)); + }), + ); - const provider = yield* Provider.findProvider(Cloudflare.UaRule); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider(Cloudflare.UaRule); + const all = yield* provider.list(); - const found = all.find((r) => r.uaRuleId === deployed.uaRuleId); - expect(found).toBeDefined(); - expect(found?.zoneId).toEqual(zoneId); - expect(found?.userAgent).toEqual(UA_LIST); - expect(found?.mode).toEqual("block"); + const found = all.find((r) => r.uaRuleId === deployed.uaRuleId); + expect(found).toBeDefined(); + expect(found?.zoneId).toEqual(zoneId); + expect(found?.userAgent).toEqual(UA_LIST); + expect(found?.mode).toEqual("block"); - yield* stack.destroy(); - yield* purgeUaRules(zoneId, [UA_LIST]); - }).pipe(logLevel), + yield* stack.destroy(); + yield* purgeUaRules(zoneId, [UA_LIST]); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Flagship/App.test.ts b/packages/alchemy/test/Cloudflare/Flagship/App.test.ts index 3042c3eac..a20a68117 100644 --- a/packages/alchemy/test/Cloudflare/Flagship/App.test.ts +++ b/packages/alchemy/test/Cloudflare/Flagship/App.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -34,148 +37,154 @@ const expectAppGone = (accountId: string, appId: string) => Effect.catchTag("FlagshipAppNotFound", () => Effect.void), ); -test.provider("create, update, delete a Flagship app", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const initial = yield* stack.deploy( - Effect.gen(function* () { - const app = yield* Cloudflare.FlagshipApp("App", { - name: "alchemy-test-flagship-app", - }); - return { app }; - }), - ); - - expect(initial.app.appId).toBeDefined(); - expect(initial.app.accountId).toEqual(accountId); - expect(initial.app.name).toEqual("alchemy-test-flagship-app"); - - // Verify out-of-band via the API. - const live = yield* flagship.getApp({ - accountId, - appId: initial.app.appId, - }); - expect(live.name).toEqual("alchemy-test-flagship-app"); - - // Rename in place — same app id. - const renamed = yield* stack.deploy( - Effect.gen(function* () { - const app = yield* Cloudflare.FlagshipApp("App", { - name: "alchemy-test-flagship-app-v2", - }); - return { app }; - }), - ); - expect(renamed.app.appId).toEqual(initial.app.appId); - expect(renamed.app.name).toEqual("alchemy-test-flagship-app-v2"); - - const liveRenamed = yield* flagship.getApp({ - accountId, - appId: initial.app.appId, - }); - expect(liveRenamed.name).toEqual("alchemy-test-flagship-app-v2"); - - // Redeploying identical props is a no-op (still the same app). - const noop = yield* stack.deploy( - Effect.gen(function* () { - const app = yield* Cloudflare.FlagshipApp("App", { - name: "alchemy-test-flagship-app-v2", - }); - return { app }; - }), - ); - expect(noop.app.appId).toEqual(initial.app.appId); - - yield* stack.destroy(); - - yield* expectAppGone(accountId, initial.app.appId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, update, delete a Flagship app", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const initial = yield* stack.deploy( + Effect.gen(function* () { + const app = yield* Cloudflare.FlagshipApp("App", { + name: "alchemy-test-flagship-app", + }); + return { app }; + }), + ); + + expect(initial.app.appId).toBeDefined(); + expect(initial.app.accountId).toEqual(accountId); + expect(initial.app.name).toEqual("alchemy-test-flagship-app"); + + // Verify out-of-band via the API. + const live = yield* flagship.getApp({ + accountId, + appId: initial.app.appId, + }); + expect(live.name).toEqual("alchemy-test-flagship-app"); + + // Rename in place — same app id. + const renamed = yield* stack.deploy( + Effect.gen(function* () { + const app = yield* Cloudflare.FlagshipApp("App", { + name: "alchemy-test-flagship-app-v2", + }); + return { app }; + }), + ); + expect(renamed.app.appId).toEqual(initial.app.appId); + expect(renamed.app.name).toEqual("alchemy-test-flagship-app-v2"); + + const liveRenamed = yield* flagship.getApp({ + accountId, + appId: initial.app.appId, + }); + expect(liveRenamed.name).toEqual("alchemy-test-flagship-app-v2"); + + // Redeploying identical props is a no-op (still the same app). + const noop = yield* stack.deploy( + Effect.gen(function* () { + const app = yield* Cloudflare.FlagshipApp("App", { + name: "alchemy-test-flagship-app-v2", + }); + return { app }; + }), + ); + expect(noop.app.appId).toEqual(initial.app.appId); + + yield* stack.destroy(); + + yield* expectAppGone(accountId, initial.app.appId); + }).pipe(logLevel), ); -test.provider("recreates an app after out-of-band delete", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const initial = yield* stack.deploy( - Effect.gen(function* () { - const app = yield* Cloudflare.FlagshipApp("HealApp", { - name: "alchemy-test-flagship-heal", - }); - return { app }; - }), - ); - - // Delete the app out-of-band. A redeploy with identical props is a - // planner no-op, so change a prop to force reconcile — it must observe - // the app as missing and recreate it instead of failing on a 404. - yield* flagship.deleteApp({ accountId, appId: initial.app.appId }); - - const healed = yield* stack.deploy( - Effect.gen(function* () { - const app = yield* Cloudflare.FlagshipApp("HealApp", { - name: "alchemy-test-flagship-heal-v2", - }); - return { app }; - }), - ); - - expect(healed.app.appId).not.toEqual(initial.app.appId); - expect(healed.app.name).toEqual("alchemy-test-flagship-heal-v2"); - - const live = yield* flagship.getApp({ - accountId, - appId: healed.app.appId, - }); - expect(live.name).toEqual("alchemy-test-flagship-heal-v2"); - - yield* stack.destroy(); - - yield* expectAppGone(accountId, healed.app.appId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "recreates an app after out-of-band delete", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const initial = yield* stack.deploy( + Effect.gen(function* () { + const app = yield* Cloudflare.FlagshipApp("HealApp", { + name: "alchemy-test-flagship-heal", + }); + return { app }; + }), + ); + + // Delete the app out-of-band. A redeploy with identical props is a + // planner no-op, so change a prop to force reconcile — it must observe + // the app as missing and recreate it instead of failing on a 404. + yield* flagship.deleteApp({ accountId, appId: initial.app.appId }); + + const healed = yield* stack.deploy( + Effect.gen(function* () { + const app = yield* Cloudflare.FlagshipApp("HealApp", { + name: "alchemy-test-flagship-heal-v2", + }); + return { app }; + }), + ); + + expect(healed.app.appId).not.toEqual(initial.app.appId); + expect(healed.app.name).toEqual("alchemy-test-flagship-heal-v2"); + + const live = yield* flagship.getApp({ + accountId, + appId: healed.app.appId, + }); + expect(live.name).toEqual("alchemy-test-flagship-heal-v2"); + + yield* stack.destroy(); + + yield* expectAppGone(accountId, healed.app.appId); + }).pipe(logLevel), ); -test.provider("list enumerates the deployed Flagship app", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const deployed = yield* stack.deploy( - Effect.gen(function* () { - const app = yield* Cloudflare.FlagshipApp("ListApp", { - name: "alchemy-test-flagship-list", - }); - return { app }; - }), - ); - - const provider = yield* Provider.findProvider(Cloudflare.FlagshipApp); - - // The flagship apps list endpoint is eventually consistent: a freshly - // created app is strongly readable by id via getApp but takes a few - // seconds to surface in the account-wide list. Retry until it appears. - const all = yield* provider.list().pipe( - Effect.flatMap((apps) => - apps.some((a) => a.appId === deployed.app.appId) - ? Effect.succeed(apps) - : Effect.fail(new AppNotListedYet()), - ), - Effect.retry({ - while: (e): e is AppNotListedYet => e instanceof AppNotListedYet, - schedule: Schedule.exponential("500 millis").pipe( - Schedule.both(Schedule.recurs(8)), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed Flagship app", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const deployed = yield* stack.deploy( + Effect.gen(function* () { + const app = yield* Cloudflare.FlagshipApp("ListApp", { + name: "alchemy-test-flagship-list", + }); + return { app }; + }), + ); + + const provider = yield* Provider.findProvider(Cloudflare.FlagshipApp); + + // The flagship apps list endpoint is eventually consistent: a freshly + // created app is strongly readable by id via getApp but takes a few + // seconds to surface in the account-wide list. Retry until it appears. + const all = yield* provider.list().pipe( + Effect.flatMap((apps) => + apps.some((a) => a.appId === deployed.app.appId) + ? Effect.succeed(apps) + : Effect.fail(new AppNotListedYet()), ), - }), - ); - - expect(all.some((a) => a.appId === deployed.app.appId)).toBe(true); - const found = all.find((a) => a.appId === deployed.app.appId); - expect(found?.name).toEqual("alchemy-test-flagship-list"); - expect(found?.accountId).toEqual(deployed.app.accountId); - - yield* stack.destroy(); - }).pipe(logLevel), + Effect.retry({ + while: (e): e is AppNotListedYet => e instanceof AppNotListedYet, + schedule: Schedule.exponential("500 millis").pipe( + Schedule.both(Schedule.recurs(8)), + ), + }), + ); + + expect(all.some((a) => a.appId === deployed.app.appId)).toBe(true); + const found = all.find((a) => a.appId === deployed.app.appId); + expect(found?.name).toEqual("alchemy-test-flagship-list"); + expect(found?.accountId).toEqual(deployed.app.accountId); + + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Flagship/Flag.test.ts b/packages/alchemy/test/Cloudflare/Flagship/Flag.test.ts index f52b96d25..ce740831d 100644 --- a/packages/alchemy/test/Cloudflare/Flagship/Flag.test.ts +++ b/packages/alchemy/test/Cloudflare/Flagship/Flag.test.ts @@ -10,6 +10,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -36,246 +39,262 @@ const expectFlagGone = (accountId: string, appId: string, flagKey: string) => ), ); -test.provider("create, update, delete a flag in an app", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const initial = yield* stack.deploy( - Effect.gen(function* () { - const app = yield* Cloudflare.FlagshipApp("FlagApp", { - name: "alchemy-test-flagship-flags", - }); - const flag = yield* Cloudflare.FlagshipFlag("Flag", { - appId: app.appId, - key: "alchemy-test-flag", - defaultVariation: "off", - variations: { off: false, on: true }, - }); - return { app, flag }; - }), - ); - - expect(initial.flag.accountId).toEqual(accountId); - expect(initial.flag.appId).toEqual(initial.app.appId); - expect(initial.flag.key).toEqual("alchemy-test-flag"); - expect(initial.flag.enabled).toBe(true); - expect(initial.flag.defaultVariation).toEqual("off"); - expect(initial.flag.variations).toEqual({ off: false, on: true }); - expect(initial.flag.rules).toEqual([]); - - // Verify out-of-band via the API. - const live = yield* flagship.getAppFlag({ - accountId, - appId: initial.app.appId, - flagKey: "alchemy-test-flag", - }); - expect(live.enabled).toBe(true); - expect(live.defaultVariation).toEqual("off"); - expect(live.type).toEqual("boolean"); - - // Update mutable props in place — same key, same app. - const updated = yield* stack.deploy( - Effect.gen(function* () { - const app = yield* Cloudflare.FlagshipApp("FlagApp", { - name: "alchemy-test-flagship-flags", - }); - const flag = yield* Cloudflare.FlagshipFlag("Flag", { - appId: app.appId, - key: "alchemy-test-flag", - enabled: false, - defaultVariation: "on", - variations: { off: false, on: true }, - description: "managed by alchemy", - rules: [ - { - priority: 1, - conditions: [ - { attribute: "country", operator: "equals", value: "US" }, - ], - serveVariation: "on", - rollout: { percentage: 50 }, - }, - ], - }); - return { app, flag }; - }), - ); - - expect(updated.flag.key).toEqual("alchemy-test-flag"); - expect(updated.flag.appId).toEqual(initial.app.appId); - expect(updated.flag.enabled).toBe(false); - expect(updated.flag.defaultVariation).toEqual("on"); - expect(updated.flag.description).toEqual("managed by alchemy"); - expect(updated.flag.rules).toEqual([ - { - priority: 1, - conditions: [{ attribute: "country", operator: "equals", value: "US" }], - serveVariation: "on", - rollout: { percentage: 50 }, - }, - ]); - - const liveUpdated = yield* flagship.getAppFlag({ - accountId, - appId: initial.app.appId, - flagKey: "alchemy-test-flag", - }); - expect(liveUpdated.enabled).toBe(false); - expect(liveUpdated.defaultVariation).toEqual("on"); - expect(liveUpdated.rules).toHaveLength(1); - - // Redeploying identical props is a no-op. - const noop = yield* stack.deploy( - Effect.gen(function* () { - const app = yield* Cloudflare.FlagshipApp("FlagApp", { - name: "alchemy-test-flagship-flags", - }); - const flag = yield* Cloudflare.FlagshipFlag("Flag", { - appId: app.appId, - key: "alchemy-test-flag", - enabled: false, - defaultVariation: "on", - variations: { off: false, on: true }, - description: "managed by alchemy", - rules: [ - { - priority: 1, - conditions: [ - { attribute: "country", operator: "equals", value: "US" }, - ], - serveVariation: "on", - rollout: { percentage: 50 }, - }, +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, update, delete a flag in an app", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const initial = yield* stack.deploy( + Effect.gen(function* () { + const app = yield* Cloudflare.FlagshipApp("FlagApp", { + name: "alchemy-test-flagship-flags", + }); + const flag = yield* Cloudflare.FlagshipFlag("Flag", { + appId: app.appId, + key: "alchemy-test-flag", + defaultVariation: "off", + variations: { off: false, on: true }, + }); + return { app, flag }; + }), + ); + + expect(initial.flag.accountId).toEqual(accountId); + expect(initial.flag.appId).toEqual(initial.app.appId); + expect(initial.flag.key).toEqual("alchemy-test-flag"); + expect(initial.flag.enabled).toBe(true); + expect(initial.flag.defaultVariation).toEqual("off"); + expect(initial.flag.variations).toEqual({ off: false, on: true }); + expect(initial.flag.rules).toEqual([]); + + // Verify out-of-band via the API. + const live = yield* flagship.getAppFlag({ + accountId, + appId: initial.app.appId, + flagKey: "alchemy-test-flag", + }); + expect(live.enabled).toBe(true); + expect(live.defaultVariation).toEqual("off"); + expect(live.type).toEqual("boolean"); + + // Update mutable props in place — same key, same app. + const updated = yield* stack.deploy( + Effect.gen(function* () { + const app = yield* Cloudflare.FlagshipApp("FlagApp", { + name: "alchemy-test-flagship-flags", + }); + const flag = yield* Cloudflare.FlagshipFlag("Flag", { + appId: app.appId, + key: "alchemy-test-flag", + enabled: false, + defaultVariation: "on", + variations: { off: false, on: true }, + description: "managed by alchemy", + rules: [ + { + priority: 1, + conditions: [ + { attribute: "country", operator: "equals", value: "US" }, + ], + serveVariation: "on", + rollout: { percentage: 50 }, + }, + ], + }); + return { app, flag }; + }), + ); + + expect(updated.flag.key).toEqual("alchemy-test-flag"); + expect(updated.flag.appId).toEqual(initial.app.appId); + expect(updated.flag.enabled).toBe(false); + expect(updated.flag.defaultVariation).toEqual("on"); + expect(updated.flag.description).toEqual("managed by alchemy"); + expect(updated.flag.rules).toEqual([ + { + priority: 1, + conditions: [ + { attribute: "country", operator: "equals", value: "US" }, ], - }); - return { app, flag }; - }), - ); - expect(noop.flag.updatedAt).toEqual(updated.flag.updatedAt); + serveVariation: "on", + rollout: { percentage: 50 }, + }, + ]); + + const liveUpdated = yield* flagship.getAppFlag({ + accountId, + appId: initial.app.appId, + flagKey: "alchemy-test-flag", + }); + expect(liveUpdated.enabled).toBe(false); + expect(liveUpdated.defaultVariation).toEqual("on"); + expect(liveUpdated.rules).toHaveLength(1); - yield* stack.destroy(); + // Redeploying identical props is a no-op. + const noop = yield* stack.deploy( + Effect.gen(function* () { + const app = yield* Cloudflare.FlagshipApp("FlagApp", { + name: "alchemy-test-flagship-flags", + }); + const flag = yield* Cloudflare.FlagshipFlag("Flag", { + appId: app.appId, + key: "alchemy-test-flag", + enabled: false, + defaultVariation: "on", + variations: { off: false, on: true }, + description: "managed by alchemy", + rules: [ + { + priority: 1, + conditions: [ + { attribute: "country", operator: "equals", value: "US" }, + ], + serveVariation: "on", + rollout: { percentage: 50 }, + }, + ], + }); + return { app, flag }; + }), + ); + expect(noop.flag.updatedAt).toEqual(updated.flag.updatedAt); - yield* expectFlagGone(accountId, initial.app.appId, "alchemy-test-flag"); - }).pipe(logLevel), + yield* stack.destroy(); + + yield* expectFlagGone(accountId, initial.app.appId, "alchemy-test-flag"); + }).pipe(logLevel), ); -test.provider("replaces the flag when the key changes", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const initial = yield* stack.deploy( - Effect.gen(function* () { - const app = yield* Cloudflare.FlagshipApp("ReplaceApp", { - name: "alchemy-test-flagship-replace", - }); - const flag = yield* Cloudflare.FlagshipFlag("ReplaceFlag", { - appId: app.appId, - key: "alchemy-test-flag-a", - defaultVariation: "off", - variations: { off: false, on: true }, - }); - return { app, flag }; - }), - ); - expect(initial.flag.key).toEqual("alchemy-test-flag-a"); - - // Changing the key is a replacement: a new flag is created and the old - // one is deleted. - const replaced = yield* stack.deploy( - Effect.gen(function* () { - const app = yield* Cloudflare.FlagshipApp("ReplaceApp", { - name: "alchemy-test-flagship-replace", - }); - const flag = yield* Cloudflare.FlagshipFlag("ReplaceFlag", { - appId: app.appId, - key: "alchemy-test-flag-b", - defaultVariation: "off", - variations: { off: false, on: true }, - }); - return { app, flag }; - }), - ); - - expect(replaced.flag.key).toEqual("alchemy-test-flag-b"); - yield* expectFlagGone(accountId, initial.app.appId, "alchemy-test-flag-a"); - - yield* stack.destroy(); - - yield* expectFlagGone(accountId, initial.app.appId, "alchemy-test-flag-b"); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "replaces the flag when the key changes", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const initial = yield* stack.deploy( + Effect.gen(function* () { + const app = yield* Cloudflare.FlagshipApp("ReplaceApp", { + name: "alchemy-test-flagship-replace", + }); + const flag = yield* Cloudflare.FlagshipFlag("ReplaceFlag", { + appId: app.appId, + key: "alchemy-test-flag-a", + defaultVariation: "off", + variations: { off: false, on: true }, + }); + return { app, flag }; + }), + ); + expect(initial.flag.key).toEqual("alchemy-test-flag-a"); + + // Changing the key is a replacement: a new flag is created and the old + // one is deleted. + const replaced = yield* stack.deploy( + Effect.gen(function* () { + const app = yield* Cloudflare.FlagshipApp("ReplaceApp", { + name: "alchemy-test-flagship-replace", + }); + const flag = yield* Cloudflare.FlagshipFlag("ReplaceFlag", { + appId: app.appId, + key: "alchemy-test-flag-b", + defaultVariation: "off", + variations: { off: false, on: true }, + }); + return { app, flag }; + }), + ); + + expect(replaced.flag.key).toEqual("alchemy-test-flag-b"); + yield* expectFlagGone( + accountId, + initial.app.appId, + "alchemy-test-flag-a", + ); + + yield* stack.destroy(); + + yield* expectFlagGone( + accountId, + initial.app.appId, + "alchemy-test-flag-b", + ); + }).pipe(logLevel), ); -test.provider("recreates a flag after out-of-band delete", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const initial = yield* stack.deploy( - Effect.gen(function* () { - const app = yield* Cloudflare.FlagshipApp("HealApp", { - name: "alchemy-test-flagship-heal-flag", - }); - const flag = yield* Cloudflare.FlagshipFlag("HealFlag", { - appId: app.appId, - key: "alchemy-test-flag-heal", - defaultVariation: "off", - variations: { off: false, on: true }, - }); - return { app, flag }; - }), - ); - - // Delete the flag out-of-band. A redeploy with identical props is a - // planner no-op, so change a prop to force reconcile — it must observe - // the flag as missing and recreate it instead of failing on a 404. - yield* flagship.deleteAppFlag({ - accountId, - appId: initial.app.appId, - flagKey: "alchemy-test-flag-heal", - }); - - const healed = yield* stack.deploy( - Effect.gen(function* () { - const app = yield* Cloudflare.FlagshipApp("HealApp", { - name: "alchemy-test-flagship-heal-flag", - }); - const flag = yield* Cloudflare.FlagshipFlag("HealFlag", { - appId: app.appId, - key: "alchemy-test-flag-heal", - enabled: false, - defaultVariation: "off", - variations: { off: false, on: true }, - }); - return { app, flag }; - }), - ); - - expect(healed.flag.key).toEqual("alchemy-test-flag-heal"); - expect(healed.flag.enabled).toBe(false); - - const live = yield* flagship.getAppFlag({ - accountId, - appId: initial.app.appId, - flagKey: "alchemy-test-flag-heal", - }); - expect(live.enabled).toBe(false); - - yield* stack.destroy(); - - yield* expectFlagGone( - accountId, - initial.app.appId, - "alchemy-test-flag-heal", - ); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "recreates a flag after out-of-band delete", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const initial = yield* stack.deploy( + Effect.gen(function* () { + const app = yield* Cloudflare.FlagshipApp("HealApp", { + name: "alchemy-test-flagship-heal-flag", + }); + const flag = yield* Cloudflare.FlagshipFlag("HealFlag", { + appId: app.appId, + key: "alchemy-test-flag-heal", + defaultVariation: "off", + variations: { off: false, on: true }, + }); + return { app, flag }; + }), + ); + + // Delete the flag out-of-band. A redeploy with identical props is a + // planner no-op, so change a prop to force reconcile — it must observe + // the flag as missing and recreate it instead of failing on a 404. + yield* flagship.deleteAppFlag({ + accountId, + appId: initial.app.appId, + flagKey: "alchemy-test-flag-heal", + }); + + const healed = yield* stack.deploy( + Effect.gen(function* () { + const app = yield* Cloudflare.FlagshipApp("HealApp", { + name: "alchemy-test-flagship-heal-flag", + }); + const flag = yield* Cloudflare.FlagshipFlag("HealFlag", { + appId: app.appId, + key: "alchemy-test-flag-heal", + enabled: false, + defaultVariation: "off", + variations: { off: false, on: true }, + }); + return { app, flag }; + }), + ); + + expect(healed.flag.key).toEqual("alchemy-test-flag-heal"); + expect(healed.flag.enabled).toBe(false); + + const live = yield* flagship.getAppFlag({ + accountId, + appId: initial.app.appId, + flagKey: "alchemy-test-flag-heal", + }); + expect(live.enabled).toBe(false); + + yield* stack.destroy(); + + yield* expectFlagGone( + accountId, + initial.app.appId, + "alchemy-test-flag-heal", + ); + }).pipe(logLevel), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed flag", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Flagship/Flagship.test.ts b/packages/alchemy/test/Cloudflare/Flagship/Flagship.test.ts index 638af53f2..cce3ff126 100644 --- a/packages/alchemy/test/Cloudflare/Flagship/Flagship.test.ts +++ b/packages/alchemy/test/Cloudflare/Flagship/Flagship.test.ts @@ -7,6 +7,9 @@ import * as Schedule from "effect/Schedule"; import * as HttpClient from "effect/unstable/http/HttpClient"; import Stack from "./fixtures/stack.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + // The fixture stack provisions its own Flagship app via the FlagshipApp // resource and binds it to both workers. Each fixture worker evaluates a // flag and returns the result; with no matching flag configured, Flagship @@ -51,7 +54,7 @@ const getJson = (url: string) => const stack = beforeAll(deploy(Stack)); afterAll.skipIf(!!process.env.NO_DESTROY)(destroy(Stack)); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "async worker evaluates a boolean flag via env Flagship binding", Effect.gen(function* () { const { asyncWorkerUrl } = yield* stack; @@ -61,7 +64,7 @@ test( { timeout: 240_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "async worker returns fallback details for a nonexistent flag", Effect.gen(function* () { const { asyncWorkerUrl } = yield* stack; @@ -76,7 +79,7 @@ test( { timeout: 240_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "effect worker evaluates a boolean flag via FlagshipApp.bind", Effect.gen(function* () { const { effectWorkerUrl } = yield* stack; @@ -86,7 +89,7 @@ test( { timeout: 240_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "effect worker returns fallback details for a nonexistent flag", Effect.gen(function* () { const { effectWorkerUrl } = yield* stack; diff --git a/packages/alchemy/test/Cloudflare/Fraud/FraudDetectionSettings.test.ts b/packages/alchemy/test/Cloudflare/Fraud/FraudDetectionSettings.test.ts index a59119d83..22061b62c 100644 --- a/packages/alchemy/test/Cloudflare/Fraud/FraudDetectionSettings.test.ts +++ b/packages/alchemy/test/Cloudflare/Fraud/FraudDetectionSettings.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -53,7 +56,7 @@ const getSettings = (zoneId: string) => // Both cases mutate the same zone-level fraud-detection settings singleton; run them serially so they don't corrupt each other's captured baseline under the global concurrent test config. describe.sequential("FraudDetectionSettings", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "adopts the zone singleton without writing and restores nothing on destroy", (stack) => Effect.gen(function* () { @@ -126,7 +129,7 @@ describe.sequential("FraudDetectionSettings", () => { // Unentitlement probe — pins the typed FraudDetectionNotEntitled rejection (code 10400) // and skips on entitled zones, where the PUT would succeed and mutate live settings. - test.provider.skipIf(entitled)( + test.provider.skipIf(entitled || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed FraudDetectionNotEntitled error on unentitled zones", (stack) => Effect.gen(function* () { @@ -262,21 +265,23 @@ describe.sequential("FraudDetectionSettings", () => { // never trips the entitlement gate (which lives on `putFraud`), so this // read-only assertion always runs. Assert the result is non-empty and // contains the standing test zone. - test.provider("list enumerates the settings across all zones", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - const provider = yield* Provider.findProvider( - Cloudflare.FraudDetectionSettings, - ); - const all = yield* provider.list(); - - expect(all.length).toBeGreaterThan(0); - expect(all.some((s) => s.zoneId === zoneId)).toBe(true); - - // `stack` is unused here (the singleton always exists on every zone), - // but keep the destroy bookend so the harness state stays clean. - yield* stack.destroy(); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the settings across all zones", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; + + const provider = yield* Provider.findProvider( + Cloudflare.FraudDetectionSettings, + ); + const all = yield* provider.list(); + + expect(all.length).toBeGreaterThan(0); + expect(all.some((s) => s.zoneId === zoneId)).toBe(true); + + // `stack` is unused here (the singleton always exists on every zone), + // but keep the destroy bookend so the harness state stays clean. + yield* stack.destroy(); + }).pipe(logLevel), ); }); diff --git a/packages/alchemy/test/Cloudflare/Gateway/Certificate.test.ts b/packages/alchemy/test/Cloudflare/Gateway/Certificate.test.ts index 3721749e0..61f2bdd85 100644 --- a/packages/alchemy/test/Cloudflare/Gateway/Certificate.test.ts +++ b/packages/alchemy/test/Cloudflare/Gateway/Certificate.test.ts @@ -10,6 +10,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); // Cloudflare caps Zero Trust certificate *generation* at 3 per 24h per account @@ -104,7 +107,7 @@ const logQuotaSkip = (what: string) => `skipping ${what}: Cloudflare's 3-per-24h gateway certificate creation quota is exhausted (GatewayCertificateQuotaReached)`, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create an activated certificate, deactivate in place, destroy", (stack) => Effect.gen(function* () { @@ -199,7 +202,7 @@ test.provider.skipIf(!runGatewayCertTests)( { timeout: 120_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed gateway certificate", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Gateway/Configuration.test.ts b/packages/alchemy/test/Cloudflare/Gateway/Configuration.test.ts index 1ac48d233..ab92e036d 100644 --- a/packages/alchemy/test/Cloudflare/Gateway/Configuration.test.ts +++ b/packages/alchemy/test/Cloudflare/Gateway/Configuration.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -47,7 +50,7 @@ const seedBaseline = (accountId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "manage activityLog and protocolDetection, then restore on destroy", (stack) => Effect.gen(function* () { @@ -121,7 +124,7 @@ test.provider( { timeout: 120_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list returns the account Gateway configuration singleton", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Gateway/List.test.ts b/packages/alchemy/test/Cloudflare/Gateway/List.test.ts index 58e6b2594..aecc65ea6 100644 --- a/packages/alchemy/test/Cloudflare/Gateway/List.test.ts +++ b/packages/alchemy/test/Cloudflare/Gateway/List.test.ts @@ -7,6 +7,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -41,144 +44,150 @@ const expectGone = (accountId: string, listId: string) => const itemValues = (items: ReadonlyArray<{ value: string }>) => items.map((i) => i.value).sort(); -test.provider("create, verify, and destroy a DOMAIN list", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const list = yield* stack.deploy( - Cloudflare.GatewayList("BasicList", { - name: "alchemy-zt-list-basic", - type: "DOMAIN", - description: "alchemy test list", - items: [ - { value: "a.alchemy-test.example" }, - { value: "b.alchemy-test.example" }, - ], - }), - ); - - expect(list.listId).toBeTruthy(); - expect(list.accountId).toEqual(accountId); - expect(list.type).toEqual("DOMAIN"); - expect(itemValues(list.items)).toEqual([ - "a.alchemy-test.example", - "b.alchemy-test.example", - ]); - - const live = yield* getList(accountId, list.listId); - expect(live.name).toEqual("alchemy-zt-list-basic"); - expect(live.description).toEqual("alchemy test list"); - expect(itemValues((live.items ?? []) as { value: string }[])).toEqual([ - "a.alchemy-test.example", - "b.alchemy-test.example", - ]); - - yield* stack.destroy(); - yield* expectGone(accountId, list.listId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, verify, and destroy a DOMAIN list", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const list = yield* stack.deploy( + Cloudflare.GatewayList("BasicList", { + name: "alchemy-zt-list-basic", + type: "DOMAIN", + description: "alchemy test list", + items: [ + { value: "a.alchemy-test.example" }, + { value: "b.alchemy-test.example" }, + ], + }), + ); + + expect(list.listId).toBeTruthy(); + expect(list.accountId).toEqual(accountId); + expect(list.type).toEqual("DOMAIN"); + expect(itemValues(list.items)).toEqual([ + "a.alchemy-test.example", + "b.alchemy-test.example", + ]); + + const live = yield* getList(accountId, list.listId); + expect(live.name).toEqual("alchemy-zt-list-basic"); + expect(live.description).toEqual("alchemy test list"); + expect(itemValues((live.items ?? []) as { value: string }[])).toEqual([ + "a.alchemy-test.example", + "b.alchemy-test.example", + ]); + + yield* stack.destroy(); + yield* expectGone(accountId, list.listId); + }).pipe(logLevel), ); -test.provider("update items, description, and name in place", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const initial = yield* stack.deploy( - Cloudflare.GatewayList("UpdateList", { - name: "alchemy-zt-list-update", - type: "DOMAIN", - items: [ - { value: "keep.alchemy-test.example" }, - { value: "remove.alchemy-test.example" }, - ], - }), - ); - - const updated = yield* stack.deploy( - Cloudflare.GatewayList("UpdateList", { - name: "alchemy-zt-list-update-v2", - type: "DOMAIN", - description: "now with a description", - items: [ - { value: "keep.alchemy-test.example" }, - { value: "add.alchemy-test.example" }, - ], - }), - ); - - // Same list mutated in place — not a replacement. - expect(updated.listId).toEqual(initial.listId); - expect(updated.name).toEqual("alchemy-zt-list-update-v2"); - expect(itemValues(updated.items)).toEqual([ - "add.alchemy-test.example", - "keep.alchemy-test.example", - ]); - - const live = yield* getList(accountId, updated.listId); - expect(live.name).toEqual("alchemy-zt-list-update-v2"); - expect(live.description).toEqual("now with a description"); - expect(itemValues((live.items ?? []) as { value: string }[])).toEqual([ - "add.alchemy-test.example", - "keep.alchemy-test.example", - ]); - - // Redeploying identical props is a no-op (still the same list). - const noop = yield* stack.deploy( - Cloudflare.GatewayList("UpdateList", { - name: "alchemy-zt-list-update-v2", - type: "DOMAIN", - description: "now with a description", - items: [ - { value: "keep.alchemy-test.example" }, - { value: "add.alchemy-test.example" }, - ], - }), - ); - expect(noop.listId).toEqual(initial.listId); - - yield* stack.destroy(); - yield* expectGone(accountId, initial.listId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "update items, description, and name in place", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const initial = yield* stack.deploy( + Cloudflare.GatewayList("UpdateList", { + name: "alchemy-zt-list-update", + type: "DOMAIN", + items: [ + { value: "keep.alchemy-test.example" }, + { value: "remove.alchemy-test.example" }, + ], + }), + ); + + const updated = yield* stack.deploy( + Cloudflare.GatewayList("UpdateList", { + name: "alchemy-zt-list-update-v2", + type: "DOMAIN", + description: "now with a description", + items: [ + { value: "keep.alchemy-test.example" }, + { value: "add.alchemy-test.example" }, + ], + }), + ); + + // Same list mutated in place — not a replacement. + expect(updated.listId).toEqual(initial.listId); + expect(updated.name).toEqual("alchemy-zt-list-update-v2"); + expect(itemValues(updated.items)).toEqual([ + "add.alchemy-test.example", + "keep.alchemy-test.example", + ]); + + const live = yield* getList(accountId, updated.listId); + expect(live.name).toEqual("alchemy-zt-list-update-v2"); + expect(live.description).toEqual("now with a description"); + expect(itemValues((live.items ?? []) as { value: string }[])).toEqual([ + "add.alchemy-test.example", + "keep.alchemy-test.example", + ]); + + // Redeploying identical props is a no-op (still the same list). + const noop = yield* stack.deploy( + Cloudflare.GatewayList("UpdateList", { + name: "alchemy-zt-list-update-v2", + type: "DOMAIN", + description: "now with a description", + items: [ + { value: "keep.alchemy-test.example" }, + { value: "add.alchemy-test.example" }, + ], + }), + ); + expect(noop.listId).toEqual(initial.listId); + + yield* stack.destroy(); + yield* expectGone(accountId, initial.listId); + }).pipe(logLevel), ); -test.provider("changing the type replaces the list", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const domainList = yield* stack.deploy( - Cloudflare.GatewayList("ReplaceList", { - name: "alchemy-zt-list-replace", - type: "DOMAIN", - items: [{ value: "x.alchemy-test.example" }], - }), - ); - - // The name is the resource's cold-read identity, so a replacement - // (type change) pairs with a rename — keeping the old name would make - // the engine find the doomed sibling and refuse to adopt it. - const ipList = yield* stack.deploy( - Cloudflare.GatewayList("ReplaceList", { - name: "alchemy-zt-list-replace-ip", - type: "IP", - items: [{ value: "203.0.113.1" }], - }), - ); - - // Type is immutable — the engine must have created a new list. - expect(ipList.listId).not.toEqual(domainList.listId); - expect(ipList.type).toEqual("IP"); - - const live = yield* getList(accountId, ipList.listId); - expect(live.type).toEqual("IP"); - // The old list was deleted by the replacement. - yield* expectGone(accountId, domainList.listId); - - yield* stack.destroy(); - yield* expectGone(accountId, ipList.listId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "changing the type replaces the list", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const domainList = yield* stack.deploy( + Cloudflare.GatewayList("ReplaceList", { + name: "alchemy-zt-list-replace", + type: "DOMAIN", + items: [{ value: "x.alchemy-test.example" }], + }), + ); + + // The name is the resource's cold-read identity, so a replacement + // (type change) pairs with a rename — keeping the old name would make + // the engine find the doomed sibling and refuse to adopt it. + const ipList = yield* stack.deploy( + Cloudflare.GatewayList("ReplaceList", { + name: "alchemy-zt-list-replace-ip", + type: "IP", + items: [{ value: "203.0.113.1" }], + }), + ); + + // Type is immutable — the engine must have created a new list. + expect(ipList.listId).not.toEqual(domainList.listId); + expect(ipList.type).toEqual("IP"); + + const live = yield* getList(accountId, ipList.listId); + expect(live.type).toEqual("IP"); + // The old list was deleted by the replacement. + yield* expectGone(accountId, domainList.listId); + + yield* stack.destroy(); + yield* expectGone(accountId, ipList.listId); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Gateway/Location.test.ts b/packages/alchemy/test/Cloudflare/Gateway/Location.test.ts index 8c8d2a826..8286d5137 100644 --- a/packages/alchemy/test/Cloudflare/Gateway/Location.test.ts +++ b/packages/alchemy/test/Cloudflare/Gateway/Location.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -39,148 +42,156 @@ const expectGone = (accountId: string, locationId: string) => }), ); -test.provider("create, verify, and destroy a location", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const location = yield* stack.deploy( - Cloudflare.GatewayLocation("BasicLocation", { - name: "alchemy-zt-location-basic", - ecsSupport: false, - }), - ); - - expect(location.locationId).toBeTruthy(); - expect(location.accountId).toEqual(accountId); - expect(location.name).toEqual("alchemy-zt-location-basic"); - expect(location.clientDefault).toBe(false); - // Cloudflare assigns a stable DoH subdomain to every location. - expect(location.dohSubdomain).toBeTruthy(); - - const live = yield* getLocation(accountId, location.locationId); - expect(live.name).toEqual("alchemy-zt-location-basic"); - expect(live.dohSubdomain).toEqual(location.dohSubdomain); - - yield* stack.destroy(); - yield* expectGone(accountId, location.locationId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, verify, and destroy a location", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const location = yield* stack.deploy( + Cloudflare.GatewayLocation("BasicLocation", { + name: "alchemy-zt-location-basic", + ecsSupport: false, + }), + ); + + expect(location.locationId).toBeTruthy(); + expect(location.accountId).toEqual(accountId); + expect(location.name).toEqual("alchemy-zt-location-basic"); + expect(location.clientDefault).toBe(false); + // Cloudflare assigns a stable DoH subdomain to every location. + expect(location.dohSubdomain).toBeTruthy(); + + const live = yield* getLocation(accountId, location.locationId); + expect(live.name).toEqual("alchemy-zt-location-basic"); + expect(live.dohSubdomain).toEqual(location.dohSubdomain); + + yield* stack.destroy(); + yield* expectGone(accountId, location.locationId); + }).pipe(logLevel), ); -test.provider("update name and ecsSupport in place (same id)", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const initial = yield* stack.deploy( - Cloudflare.GatewayLocation("UpdateLocation", { - name: "alchemy-zt-location-update", - ecsSupport: false, - }), - ); - expect(initial.ecsSupport).toBe(false); - - const updated = yield* stack.deploy( - Cloudflare.GatewayLocation("UpdateLocation", { - name: "alchemy-zt-location-update-v2", - ecsSupport: true, - }), - ); - - // Same location mutated in place — not a replacement; the - // server-assigned DoH subdomain is stable across updates. - expect(updated.locationId).toEqual(initial.locationId); - expect(updated.dohSubdomain).toEqual(initial.dohSubdomain); - expect(updated.name).toEqual("alchemy-zt-location-update-v2"); - expect(updated.ecsSupport).toBe(true); - - const live = yield* getLocation(accountId, updated.locationId); - expect(live.name).toEqual("alchemy-zt-location-update-v2"); - expect(live.ecsSupport).toBe(true); - - // Redeploying identical props is a no-op (still the same location). - const noop = yield* stack.deploy( - Cloudflare.GatewayLocation("UpdateLocation", { - name: "alchemy-zt-location-update-v2", - ecsSupport: true, - }), - ); - expect(noop.locationId).toEqual(initial.locationId); - - yield* stack.destroy(); - yield* expectGone(accountId, initial.locationId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "update name and ecsSupport in place (same id)", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const initial = yield* stack.deploy( + Cloudflare.GatewayLocation("UpdateLocation", { + name: "alchemy-zt-location-update", + ecsSupport: false, + }), + ); + expect(initial.ecsSupport).toBe(false); + + const updated = yield* stack.deploy( + Cloudflare.GatewayLocation("UpdateLocation", { + name: "alchemy-zt-location-update-v2", + ecsSupport: true, + }), + ); + + // Same location mutated in place — not a replacement; the + // server-assigned DoH subdomain is stable across updates. + expect(updated.locationId).toEqual(initial.locationId); + expect(updated.dohSubdomain).toEqual(initial.dohSubdomain); + expect(updated.name).toEqual("alchemy-zt-location-update-v2"); + expect(updated.ecsSupport).toBe(true); + + const live = yield* getLocation(accountId, updated.locationId); + expect(live.name).toEqual("alchemy-zt-location-update-v2"); + expect(live.ecsSupport).toBe(true); + + // Redeploying identical props is a no-op (still the same location). + const noop = yield* stack.deploy( + Cloudflare.GatewayLocation("UpdateLocation", { + name: "alchemy-zt-location-update-v2", + ecsSupport: true, + }), + ); + expect(noop.locationId).toEqual(initial.locationId); + + yield* stack.destroy(); + yield* expectGone(accountId, initial.locationId); + }).pipe(logLevel), ); -test.provider("recreates after out-of-band delete", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const location = yield* stack.deploy( - Cloudflare.GatewayLocation("HealLocation", { - name: "alchemy-zt-location-heal", - ecsSupport: false, - }), - ); - - yield* zeroTrust - .deleteGatewayLocation({ accountId, locationId: location.locationId }) - .pipe( - Effect.retry({ - while: (e) => e._tag === "Forbidden", - schedule: Schedule.exponential("500 millis"), - times: 8, +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "recreates after out-of-band delete", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const location = yield* stack.deploy( + Cloudflare.GatewayLocation("HealLocation", { + name: "alchemy-zt-location-heal", + ecsSupport: false, }), ); - // Change a prop to force reconcile — it must observe the location as - // missing and recreate it instead of failing on a 404. - const healed = yield* stack.deploy( - Cloudflare.GatewayLocation("HealLocation", { - name: "alchemy-zt-location-heal", - ecsSupport: true, - }), - ); - - expect(healed.locationId).not.toEqual(location.locationId); - expect(healed.ecsSupport).toBe(true); - const live = yield* getLocation(accountId, healed.locationId); - expect(live.ecsSupport).toBe(true); - - yield* stack.destroy(); - yield* expectGone(accountId, healed.locationId); - }).pipe(logLevel), + yield* zeroTrust + .deleteGatewayLocation({ accountId, locationId: location.locationId }) + .pipe( + Effect.retry({ + while: (e) => e._tag === "Forbidden", + schedule: Schedule.exponential("500 millis"), + times: 8, + }), + ); + + // Change a prop to force reconcile — it must observe the location as + // missing and recreate it instead of failing on a 404. + const healed = yield* stack.deploy( + Cloudflare.GatewayLocation("HealLocation", { + name: "alchemy-zt-location-heal", + ecsSupport: true, + }), + ); + + expect(healed.locationId).not.toEqual(location.locationId); + expect(healed.ecsSupport).toBe(true); + const live = yield* getLocation(accountId, healed.locationId); + expect(live.ecsSupport).toBe(true); + + yield* stack.destroy(); + yield* expectGone(accountId, healed.locationId); + }).pipe(logLevel), ); // Canonical `list()` test (account-scoped collection): deploy a location, // then enumerate every Gateway location in the account via the typed // provider and assert the deployed one is present in the exhaustively // paginated result. -test.provider("list enumerates deployed gateway locations", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const location = yield* stack.deploy( - Cloudflare.GatewayLocation("ListLocation", { - name: "alchemy-zt-location-list", - ecsSupport: false, - }), - ); - - const provider = yield* Provider.findProvider(Cloudflare.GatewayLocation); - const all = yield* provider.list(); - - // The deployed location appears with the exact `read` Attributes shape. - const found = all.find((l) => l.locationId === location.locationId); - expect(found).toBeDefined(); - expect(found?.name).toEqual("alchemy-zt-location-list"); - expect(found?.accountId).toEqual(location.accountId); - - yield* stack.destroy(); - yield* expectGone(location.accountId, location.locationId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates deployed gateway locations", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const location = yield* stack.deploy( + Cloudflare.GatewayLocation("ListLocation", { + name: "alchemy-zt-location-list", + ecsSupport: false, + }), + ); + + const provider = yield* Provider.findProvider(Cloudflare.GatewayLocation); + const all = yield* provider.list(); + + // The deployed location appears with the exact `read` Attributes shape. + const found = all.find((l) => l.locationId === location.locationId); + expect(found).toBeDefined(); + expect(found?.name).toEqual("alchemy-zt-location-list"); + expect(found?.accountId).toEqual(location.accountId); + + yield* stack.destroy(); + yield* expectGone(location.accountId, location.locationId); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Gateway/Logging.test.ts b/packages/alchemy/test/Cloudflare/Gateway/Logging.test.ts index 87793db3e..36304f7b8 100644 --- a/packages/alchemy/test/Cloudflare/Gateway/Logging.test.ts +++ b/packages/alchemy/test/Cloudflare/Gateway/Logging.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -26,7 +29,7 @@ const getLogging = (accountId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "manage logging settings, update in place, restore on destroy", (stack) => Effect.gen(function* () { @@ -96,7 +99,7 @@ test.provider( // account. `list()` reads the single instance and returns it as a // one-element array. Assert exactly one well-typed Attributes for the // ambient account. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list returns the account's Gateway logging singleton", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Gateway/ProxyEndpoint.test.ts b/packages/alchemy/test/Cloudflare/Gateway/ProxyEndpoint.test.ts index 5753fe505..ad8cf41f5 100644 --- a/packages/alchemy/test/Cloudflare/Gateway/ProxyEndpoint.test.ts +++ b/packages/alchemy/test/Cloudflare/Gateway/ProxyEndpoint.test.ts @@ -9,6 +9,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -40,7 +43,7 @@ const expectGone = (accountId: string, proxyEndpointId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create, update, and destroy an identity-kind proxy endpoint", (stack) => Effect.gen(function* () { @@ -89,89 +92,96 @@ test.provider( // error (Cloudflare code 2009: "IP based proxy endpoints are limited to // enterprise accounts."). Exercise the typed error surface directly; if the // account ever becomes Enterprise the create succeeds and we clean up. -test.provider("ip-kind endpoints surface the typed entitlement error", () => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - const outcome = yield* zeroTrust - .createGatewayProxyEndpoint({ - accountId, - name: "alchemy-zt-proxy-ip", - kind: "ip", - // RFC 5737 documentation range. - ips: ["203.0.113.1/32"], - }) - .pipe( - Effect.retry({ - while: (e) => e._tag === "Forbidden", - schedule: Schedule.exponential("500 millis"), - times: 8, - }), - Effect.map((created) => ({ _tag: "Created" as const, created })), - Effect.catchTag("IpProxyEndpointsRequireEnterprise", (e) => - Effect.succeed({ _tag: "Entitlement" as const, error: e }), - ), - ); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "ip-kind endpoints surface the typed entitlement error", + () => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - if (outcome._tag === "Created") { - // Enterprise account — clean up the endpoint we just made. - yield* zeroTrust - .deleteGatewayProxyEndpoint({ + const outcome = yield* zeroTrust + .createGatewayProxyEndpoint({ accountId, - proxyEndpointId: outcome.created.id ?? "", + name: "alchemy-zt-proxy-ip", + kind: "ip", + // RFC 5737 documentation range. + ips: ["203.0.113.1/32"], }) - .pipe(Effect.catchTag("ProxyEndpointNotFound", () => Effect.void)); - expect(outcome.created.name).toEqual("alchemy-zt-proxy-ip"); - } else { - expect(outcome.error._tag).toEqual("IpProxyEndpointsRequireEnterprise"); - } - }).pipe(logLevel), + .pipe( + Effect.retry({ + while: (e) => e._tag === "Forbidden", + schedule: Schedule.exponential("500 millis"), + times: 8, + }), + Effect.map((created) => ({ _tag: "Created" as const, created })), + Effect.catchTag("IpProxyEndpointsRequireEnterprise", (e) => + Effect.succeed({ _tag: "Entitlement" as const, error: e }), + ), + ); + + if (outcome._tag === "Created") { + // Enterprise account — clean up the endpoint we just made. + yield* zeroTrust + .deleteGatewayProxyEndpoint({ + accountId, + proxyEndpointId: outcome.created.id ?? "", + }) + .pipe(Effect.catchTag("ProxyEndpointNotFound", () => Effect.void)); + expect(outcome.created.name).toEqual("alchemy-zt-proxy-ip"); + } else { + expect(outcome.error._tag).toEqual("IpProxyEndpointsRequireEnterprise"); + } + }).pipe(logLevel), ); -test.provider("list enumerates the deployed proxy endpoint", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - // Self-heal: remove any same-named endpoint left orphaned in the cloud by a - // previously-interrupted run so the deploy below doesn't trip the - // "OwnedBySomeoneElse" adoption guard. - const orphans = yield* zeroTrust.listGatewayProxyEndpoints - .items({ accountId }) - .pipe( - Stream.filter((e) => e.name === "alchemy-zt-proxy-list"), - Stream.runCollect, - Effect.map((chunk) => Array.from(chunk)), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed proxy endpoint", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + // Self-heal: remove any same-named endpoint left orphaned in the cloud by a + // previously-interrupted run so the deploy below doesn't trip the + // "OwnedBySomeoneElse" adoption guard. + const orphans = yield* zeroTrust.listGatewayProxyEndpoints + .items({ accountId }) + .pipe( + Stream.filter((e) => e.name === "alchemy-zt-proxy-list"), + Stream.runCollect, + Effect.map((chunk) => Array.from(chunk)), + ); + yield* Effect.forEach(orphans, (o) => + zeroTrust + .deleteGatewayProxyEndpoint({ + accountId, + proxyEndpointId: o.id ?? "", + }) + .pipe(Effect.catchTag("ProxyEndpointNotFound", () => Effect.void)), ); - yield* Effect.forEach(orphans, (o) => - zeroTrust - .deleteGatewayProxyEndpoint({ accountId, proxyEndpointId: o.id ?? "" }) - .pipe(Effect.catchTag("ProxyEndpointNotFound", () => Effect.void)), - ); - - // identity-kind endpoints work on all Zero Trust plans, so this deploy is - // not entitlement-gated (unlike ip-kind, which needs Enterprise). - const endpoint = yield* stack.deploy( - Cloudflare.GatewayProxyEndpoint("ListProxy", { - name: "alchemy-zt-proxy-list", - kind: "identity", - }), - ); - - const provider = yield* Provider.findProvider( - Cloudflare.GatewayProxyEndpoint, - ); - const all = yield* provider.list(); - - const match = all.find( - (x) => x.proxyEndpointId === endpoint.proxyEndpointId, - ); - expect(match).toBeDefined(); - expect(match?.accountId).toEqual(accountId); - expect(match?.name).toEqual("alchemy-zt-proxy-list"); - expect(match?.kind).toEqual("identity"); - - yield* stack.destroy(); - }).pipe(logLevel), + + // identity-kind endpoints work on all Zero Trust plans, so this deploy is + // not entitlement-gated (unlike ip-kind, which needs Enterprise). + const endpoint = yield* stack.deploy( + Cloudflare.GatewayProxyEndpoint("ListProxy", { + name: "alchemy-zt-proxy-list", + kind: "identity", + }), + ); + + const provider = yield* Provider.findProvider( + Cloudflare.GatewayProxyEndpoint, + ); + const all = yield* provider.list(); + + const match = all.find( + (x) => x.proxyEndpointId === endpoint.proxyEndpointId, + ); + expect(match).toBeDefined(); + expect(match?.accountId).toEqual(accountId); + expect(match?.name).toEqual("alchemy-zt-proxy-list"); + expect(match?.kind).toEqual("identity"); + + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Gateway/Rule.test.ts b/packages/alchemy/test/Cloudflare/Gateway/Rule.test.ts index 3033654c7..e388f1161 100644 --- a/packages/alchemy/test/Cloudflare/Gateway/Rule.test.ts +++ b/packages/alchemy/test/Cloudflare/Gateway/Rule.test.ts @@ -6,6 +6,9 @@ import { expect } from "@effect/vitest"; import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -18,36 +21,38 @@ const logLevel = Effect.provideService( // with the typed `Provider.findProvider` helper and assert `list()` returns // the exhaustively-paginated set hydrated into the exact `read` Attributes // shape (so the deployed rule's id is present). -test.provider("list enumerates the deployed Gateway rule", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const rule = yield* stack.deploy( - Cloudflare.GatewayRule("ListRule", { - name: "alchemy-zt-rule-list", - action: "block", - filters: ["dns"], - traffic: 'any(dns.domains[*] == "list-test.alchemy-test.example")', - enabled: true, - }), - ); - - expect(rule.ruleId).toBeTruthy(); - expect(rule.accountId).toEqual(accountId); - - const provider = yield* Provider.findProvider(Cloudflare.GatewayRule); - const all = yield* provider.list(); - - // The deployed rule appears in the exhaustively-paginated result, and the - // hydrated element matches the `read` Attributes shape exactly. - const found = all.find((r) => r.ruleId === rule.ruleId); - expect(found).toBeDefined(); - expect(found?.accountId).toEqual(accountId); - expect(found?.action).toEqual("block"); - expect(found?.name).toEqual("alchemy-zt-rule-list"); - - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed Gateway rule", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const rule = yield* stack.deploy( + Cloudflare.GatewayRule("ListRule", { + name: "alchemy-zt-rule-list", + action: "block", + filters: ["dns"], + traffic: 'any(dns.domains[*] == "list-test.alchemy-test.example")', + enabled: true, + }), + ); + + expect(rule.ruleId).toBeTruthy(); + expect(rule.accountId).toEqual(accountId); + + const provider = yield* Provider.findProvider(Cloudflare.GatewayRule); + const all = yield* provider.list(); + + // The deployed rule appears in the exhaustively-paginated result, and the + // hydrated element matches the `read` Attributes shape exactly. + const found = all.find((r) => r.ruleId === rule.ruleId); + expect(found).toBeDefined(); + expect(found?.accountId).toEqual(accountId); + expect(found?.action).toEqual("block"); + expect(found?.name).toEqual("alchemy-zt-rule-list"); + + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/GoogleTagGateway/GoogleTagGateway.test.ts b/packages/alchemy/test/Cloudflare/GoogleTagGateway/GoogleTagGateway.test.ts index 65bc35a13..67c0a2f3f 100644 --- a/packages/alchemy/test/Cloudflare/GoogleTagGateway/GoogleTagGateway.test.ts +++ b/packages/alchemy/test/Cloudflare/GoogleTagGateway/GoogleTagGateway.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -65,7 +68,7 @@ const setBaseline = (zoneId: string) => ); describe.sequential("GoogleTagGateway", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "configures the gateway, updates in place, and restores the baseline on destroy", (stack) => Effect.gen(function* () { @@ -142,7 +145,7 @@ describe.sequential("GoogleTagGateway", () => { { timeout: 120_000 }, ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "no-op redeploy skips the PUT and destroy is idempotent", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Healthcheck/Healthcheck.test.ts b/packages/alchemy/test/Cloudflare/Healthcheck/Healthcheck.test.ts index 234633015..220d44333 100644 --- a/packages/alchemy/test/Cloudflare/Healthcheck/Healthcheck.test.ts +++ b/packages/alchemy/test/Cloudflare/Healthcheck/Healthcheck.test.ts @@ -13,6 +13,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -105,7 +108,7 @@ const purgeByName = (zoneId: string, name: string) => ), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create and delete an HTTP health check with default name", (stack) => Effect.gen(function* () { @@ -147,7 +150,7 @@ test.provider( }).pipe(logLevel), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "update mutable props in place (same id), then no-op redeploy", (stack) => Effect.gen(function* () { @@ -220,7 +223,7 @@ test.provider( }).pipe(logLevel), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "changing type HTTP→TCP updates in place; delete is idempotent", (stack) => Effect.gen(function* () { @@ -279,7 +282,7 @@ test.provider( }).pipe(logLevel), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "adoption — existing check errors without adopt, takes over with adopt(true)", (stack) => Effect.gen(function* () { @@ -349,7 +352,7 @@ test.provider( }).pipe(logLevel), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed health check across zones", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/HostnameTlsSetting/HostnameTlsSetting.test.ts b/packages/alchemy/test/Cloudflare/HostnameTlsSetting/HostnameTlsSetting.test.ts index 89059555f..02784a500 100644 --- a/packages/alchemy/test/Cloudflare/HostnameTlsSetting/HostnameTlsSetting.test.ts +++ b/packages/alchemy/test/Cloudflare/HostnameTlsSetting/HostnameTlsSetting.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -55,7 +58,7 @@ const findSetting = (zoneId: string, settingId: string, hostname: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "lists overrides and surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Iam/ResourceGroup.test.ts b/packages/alchemy/test/Cloudflare/Iam/ResourceGroup.test.ts index 5778581fe..6c22e2824 100644 --- a/packages/alchemy/test/Cloudflare/Iam/ResourceGroup.test.ts +++ b/packages/alchemy/test/Cloudflare/Iam/ResourceGroup.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -49,7 +52,7 @@ const expectGone = (accountId: string, resourceGroupId: string) => Effect.map((g) => expect(g._tag).toEqual("None")), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create, verify out-of-band, update name+scope in place, destroy", (stack) => Effect.gen(function* () { @@ -131,7 +134,7 @@ test.provider( { timeout: 120_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed resource group", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Iam/UserGroup.test.ts b/packages/alchemy/test/Cloudflare/Iam/UserGroup.test.ts index 0b1bea580..9585366ba 100644 --- a/packages/alchemy/test/Cloudflare/Iam/UserGroup.test.ts +++ b/packages/alchemy/test/Cloudflare/Iam/UserGroup.test.ts @@ -9,6 +9,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -89,7 +92,7 @@ const program = (opts: { return { rg, group }; }); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create with a policy, verify out-of-band, update policies in place, destroy", (stack) => Effect.gen(function* () { @@ -177,7 +180,7 @@ test.provider( const UG_LIST_NAME = "alchemy-iam-ug-list"; -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed user group", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Iam/UserGroupMembership.test.ts b/packages/alchemy/test/Cloudflare/Iam/UserGroupMembership.test.ts index dcd892e6c..c287504f3 100644 --- a/packages/alchemy/test/Cloudflare/Iam/UserGroupMembership.test.ts +++ b/packages/alchemy/test/Cloudflare/Iam/UserGroupMembership.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -87,7 +90,7 @@ const program = (opts: { memberId: string; target: "A" | "B" }) => return { groupA, groupB, membership }; }); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create, verify out-of-band, replace when the user group changes, destroy", (stack) => Effect.gen(function* () { @@ -138,7 +141,7 @@ test.provider( { timeout: 120_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates memberships across all user groups in the account", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Images/Images.test.ts b/packages/alchemy/test/Cloudflare/Images/Images.test.ts index b4d4a29cf..2c90f3acf 100644 --- a/packages/alchemy/test/Cloudflare/Images/Images.test.ts +++ b/packages/alchemy/test/Cloudflare/Images/Images.test.ts @@ -8,6 +8,9 @@ import * as HttpClient from "effect/unstable/http/HttpClient"; import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"; import Stack from "./fixtures/stack.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test, beforeAll, afterAll, deploy, destroy } = Test.make({ providers: Cloudflare.providers(), }); @@ -77,7 +80,7 @@ const postImage = (url: string) => const stack = beforeAll(deploy(Stack)); afterAll.skipIf(!!process.env.NO_DESTROY)(destroy(Stack)); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "async worker reads image info via env Images binding", Effect.gen(function* () { const { asyncWorkerUrl } = yield* stack; @@ -92,7 +95,7 @@ test( { timeout: 240_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "effect worker reads image info via yield* Images", Effect.gen(function* () { const { effectWorkerUrl } = yield* stack; diff --git a/packages/alchemy/test/Cloudflare/Images/SigningKey.test.ts b/packages/alchemy/test/Cloudflare/Images/SigningKey.test.ts index 560d34f65..9fde40ba9 100644 --- a/packages/alchemy/test/Cloudflare/Images/SigningKey.test.ts +++ b/packages/alchemy/test/Cloudflare/Images/SigningKey.test.ts @@ -9,6 +9,9 @@ import * as Redacted from "effect/Redacted"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -51,27 +54,31 @@ const expectGone = (accountId: string, name: string) => // `list()` swallows the `ImagesAccessNotEnabled` 5403 and returns `[]` on // unentitled accounts, so the result is a well-typed Attributes[] in either // case. On an entitled account it additionally contains the deployed key. -test.provider("list enumerates the account's signing keys", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the account's signing keys", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - yield* stack.destroy(); + yield* stack.destroy(); - const provider = yield* Provider.findProvider(Cloudflare.ImagesSigningKey); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider( + Cloudflare.ImagesSigningKey, + ); + const all = yield* provider.list(); - expect(Array.isArray(all)).toBe(true); - for (const key of all) { - expect(typeof key.keyName).toBe("string"); - expect(key.accountId).toEqual(accountId); - expect(typeof Redacted.value(key.value)).toBe("string"); - } + expect(Array.isArray(all)).toBe(true); + for (const key of all) { + expect(typeof key.keyName).toBe("string"); + expect(key.accountId).toEqual(accountId); + expect(typeof Redacted.value(key.value)).toBe("string"); + } - yield* stack.destroy(); - }).pipe(logLevel), + yield* stack.destroy(); + }).pipe(logLevel), ); -test.provider.skipIf(keysEntitled)( +test.provider.skipIf(keysEntitled || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed ImagesAccessNotEnabled error on unentitled accounts", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Images/Variant.test.ts b/packages/alchemy/test/Cloudflare/Images/Variant.test.ts index aaf6ffd3a..5f0f16c5a 100644 --- a/packages/alchemy/test/Cloudflare/Images/Variant.test.ts +++ b/packages/alchemy/test/Cloudflare/Images/Variant.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -47,118 +50,122 @@ const expectGone = (accountId: string, variantId: string) => }), ); -test.provider("create, update in place, and delete a variant", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - // Variant names are alphanumeric only (no hyphens/underscores) — use - // the logical ID verbatim as the deterministic name. - const created = yield* stack.deploy( - Cloudflare.ImagesVariant("alchemytestvariant", { - fit: "cover", - width: 100, - height: 100, - }), - ); - - expect(created.variantName).toEqual("alchemytestvariant"); - expect(created.accountId).toEqual(accountId); - expect(created.fit).toEqual("cover"); - expect(created.width).toEqual(100); - expect(created.height).toEqual(100); - expect(created.metadata).toEqual("none"); - expect(created.neverRequireSignedURLs).toEqual(false); - - // Out-of-band verification against the live API. - const live = yield* getVariant(accountId, created.variantName); - expect(live.variant?.id).toEqual("alchemytestvariant"); - expect(live.variant?.options.fit).toEqual("cover"); - expect(live.variant?.options.width).toEqual(100); - expect(live.variant?.options.height).toEqual(100); - - // Update mutable options in place — same variant name, PATCHed options. - const updated = yield* stack.deploy( - Cloudflare.ImagesVariant("alchemytestvariant", { - fit: "contain", - width: 200, - height: 100, - metadata: "copyright", - neverRequireSignedURLs: true, - }), - ); - - expect(updated.variantName).toEqual("alchemytestvariant"); - expect(updated.fit).toEqual("contain"); - expect(updated.width).toEqual(200); - expect(updated.height).toEqual(100); - expect(updated.metadata).toEqual("copyright"); - expect(updated.neverRequireSignedURLs).toEqual(true); - - const patched = yield* getVariant(accountId, updated.variantName); - expect(patched.variant?.options.fit).toEqual("contain"); - expect(patched.variant?.options.width).toEqual(200); - expect(patched.variant?.options.metadata).toEqual("copyright"); - expect(patched.variant?.neverRequireSignedURLs).toEqual(true); - - // No-op redeploy — identical props, still the same variant. - const noop = yield* stack.deploy( - Cloudflare.ImagesVariant("alchemytestvariant", { - fit: "contain", - width: 200, - height: 100, - metadata: "copyright", - neverRequireSignedURLs: true, - }), - ); - expect(noop.variantName).toEqual("alchemytestvariant"); - - yield* stack.destroy(); - - yield* expectGone(accountId, "alchemytestvariant"); - - // Destroy again — delete is idempotent. - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, update in place, and delete a variant", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + // Variant names are alphanumeric only (no hyphens/underscores) — use + // the logical ID verbatim as the deterministic name. + const created = yield* stack.deploy( + Cloudflare.ImagesVariant("alchemytestvariant", { + fit: "cover", + width: 100, + height: 100, + }), + ); + + expect(created.variantName).toEqual("alchemytestvariant"); + expect(created.accountId).toEqual(accountId); + expect(created.fit).toEqual("cover"); + expect(created.width).toEqual(100); + expect(created.height).toEqual(100); + expect(created.metadata).toEqual("none"); + expect(created.neverRequireSignedURLs).toEqual(false); + + // Out-of-band verification against the live API. + const live = yield* getVariant(accountId, created.variantName); + expect(live.variant?.id).toEqual("alchemytestvariant"); + expect(live.variant?.options.fit).toEqual("cover"); + expect(live.variant?.options.width).toEqual(100); + expect(live.variant?.options.height).toEqual(100); + + // Update mutable options in place — same variant name, PATCHed options. + const updated = yield* stack.deploy( + Cloudflare.ImagesVariant("alchemytestvariant", { + fit: "contain", + width: 200, + height: 100, + metadata: "copyright", + neverRequireSignedURLs: true, + }), + ); + + expect(updated.variantName).toEqual("alchemytestvariant"); + expect(updated.fit).toEqual("contain"); + expect(updated.width).toEqual(200); + expect(updated.height).toEqual(100); + expect(updated.metadata).toEqual("copyright"); + expect(updated.neverRequireSignedURLs).toEqual(true); + + const patched = yield* getVariant(accountId, updated.variantName); + expect(patched.variant?.options.fit).toEqual("contain"); + expect(patched.variant?.options.width).toEqual(200); + expect(patched.variant?.options.metadata).toEqual("copyright"); + expect(patched.variant?.neverRequireSignedURLs).toEqual(true); + + // No-op redeploy — identical props, still the same variant. + const noop = yield* stack.deploy( + Cloudflare.ImagesVariant("alchemytestvariant", { + fit: "contain", + width: 200, + height: 100, + metadata: "copyright", + neverRequireSignedURLs: true, + }), + ); + expect(noop.variantName).toEqual("alchemytestvariant"); + + yield* stack.destroy(); + + yield* expectGone(accountId, "alchemytestvariant"); + + // Destroy again — delete is idempotent. + yield* stack.destroy(); + }).pipe(logLevel), ); -test.provider("replace a variant when the name changes", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const initial = yield* stack.deploy( - Cloudflare.ImagesVariant("ReplaceVariant", { - name: "alchemyreplacea", - fit: "cover", - width: 64, - height: 64, - }), - ); - expect(initial.variantName).toEqual("alchemyreplacea"); - - // Renaming the variant replaces it — the name is the API path id. - const replaced = yield* stack.deploy( - Cloudflare.ImagesVariant("ReplaceVariant", { - name: "alchemyreplaceb", - fit: "cover", - width: 64, - height: 64, - }), - ); - expect(replaced.variantName).toEqual("alchemyreplaceb"); - - const live = yield* getVariant(accountId, "alchemyreplaceb"); - expect(live.variant?.id).toEqual("alchemyreplaceb"); - - // The old variant is gone after the replacement completes. - yield* expectGone(accountId, "alchemyreplacea"); - - yield* stack.destroy(); - yield* expectGone(accountId, "alchemyreplaceb"); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "replace a variant when the name changes", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const initial = yield* stack.deploy( + Cloudflare.ImagesVariant("ReplaceVariant", { + name: "alchemyreplacea", + fit: "cover", + width: 64, + height: 64, + }), + ); + expect(initial.variantName).toEqual("alchemyreplacea"); + + // Renaming the variant replaces it — the name is the API path id. + const replaced = yield* stack.deploy( + Cloudflare.ImagesVariant("ReplaceVariant", { + name: "alchemyreplaceb", + fit: "cover", + width: 64, + height: 64, + }), + ); + expect(replaced.variantName).toEqual("alchemyreplaceb"); + + const live = yield* getVariant(accountId, "alchemyreplaceb"); + expect(live.variant?.id).toEqual("alchemyreplaceb"); + + // The old variant is gone after the replacement completes. + yield* expectGone(accountId, "alchemyreplacea"); + + yield* stack.destroy(); + yield* expectGone(accountId, "alchemyreplaceb"); + }).pipe(logLevel), ); // Canonical `list()` test (account collection): deploy a variant, resolve the diff --git a/packages/alchemy/test/Cloudflare/Intel/IndicatorFeed.test.ts b/packages/alchemy/test/Cloudflare/Intel/IndicatorFeed.test.ts index f9381368f..6720b1553 100644 --- a/packages/alchemy/test/Cloudflare/Intel/IndicatorFeed.test.ts +++ b/packages/alchemy/test/Cloudflare/Intel/IndicatorFeed.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -46,7 +49,7 @@ const getFeed = (accountId: string, feedId: number) => // adopts the same feed instead of leaking a new one. const FEED_NAME = "alchemy-intel-test-feed"; -test.provider.skipIf(entitled)( +test.provider.skipIf(entitled || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "unentitled accounts surface the typed IndicatorFeedsNotEntitled error", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/KeylessCertificate/KeylessCertificate.test.ts b/packages/alchemy/test/Cloudflare/KeylessCertificate/KeylessCertificate.test.ts index 5c8f440db..b7a5e00f7 100644 --- a/packages/alchemy/test/Cloudflare/KeylessCertificate/KeylessCertificate.test.ts +++ b/packages/alchemy/test/Cloudflare/KeylessCertificate/KeylessCertificate.test.ts @@ -11,6 +11,9 @@ import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; import { CERT_1, CERT_2 } from "./fixtures/certs.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -74,7 +77,7 @@ const expectGone = (zoneId: string, keylessCertificateId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed KeylessSslNotAvailable error on non-Enterprise zones", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/LeakedCredentialCheck/Detection.test.ts b/packages/alchemy/test/Cloudflare/LeakedCredentialCheck/Detection.test.ts index 1629b79d1..8aaa3fcdb 100644 --- a/packages/alchemy/test/Cloudflare/LeakedCredentialCheck/Detection.test.ts +++ b/packages/alchemy/test/Cloudflare/LeakedCredentialCheck/Detection.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -47,53 +50,55 @@ describe.sequential("LeakedCredentialDetection", () => { // On the standard testing account no zone has detections (quota is zero), // so the result is a well-typed empty array. When an entitled zone is // supplied, deploy a detection and assert it appears in the result. - test.provider("list enumerates custom detections across all zones", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates custom detections across all zones", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - yield* stack.destroy(); + yield* stack.destroy(); - const provider = yield* Provider.findProvider( - Cloudflare.LeakedCredentialDetection, - ); + const provider = yield* Provider.findProvider( + Cloudflare.LeakedCredentialDetection, + ); - if (detectionZoneId) { - const usernameExpr = - 'lookup_json_string(http.request.body.raw, "user")'; - const passwordExpr = - 'lookup_json_string(http.request.body.raw, "pass")'; + if (detectionZoneId) { + const usernameExpr = + 'lookup_json_string(http.request.body.raw, "user")'; + const passwordExpr = + 'lookup_json_string(http.request.body.raw, "pass")'; - const detection = yield* stack.deploy( - Effect.gen(function* () { - const check = yield* Cloudflare.LeakedCredentialCheck("Lcc", { - zoneId: detectionZoneId, - enabled: true, - }); - return yield* Cloudflare.LeakedCredentialDetection( - "ListDetection", - { - zoneId: check.zoneId, - username: usernameExpr, - password: passwordExpr, - }, - ); - }), - ); + const detection = yield* stack.deploy( + Effect.gen(function* () { + const check = yield* Cloudflare.LeakedCredentialCheck("Lcc", { + zoneId: detectionZoneId, + enabled: true, + }); + return yield* Cloudflare.LeakedCredentialDetection( + "ListDetection", + { + zoneId: check.zoneId, + username: usernameExpr, + password: passwordExpr, + }, + ); + }), + ); - const all = yield* provider.list(); - expect(all.some((d) => d.detectionId === detection.detectionId)).toBe( - true, - ); - } else { - // Read-only assertion: the result is a well-typed array (empty on the - // unentitled standard account). `zoneId` is resolved to prove the - // standing test zone exists in the enumeration scope. - expect(zoneId).toBeTruthy(); - const all = yield* provider.list(); - expect(Array.isArray(all)).toBe(true); - } + const all = yield* provider.list(); + expect(all.some((d) => d.detectionId === detection.detectionId)).toBe( + true, + ); + } else { + // Read-only assertion: the result is a well-typed array (empty on the + // unentitled standard account). `zoneId` is resolved to prove the + // standing test zone exists in the enumeration scope. + expect(zoneId).toBeTruthy(); + const all = yield* provider.list(); + expect(Array.isArray(all)).toBe(true); + } - yield* stack.destroy(); - }).pipe(logLevel), + yield* stack.destroy(); + }).pipe(logLevel), ); }); diff --git a/packages/alchemy/test/Cloudflare/LeakedCredentialCheck/LeakedCredentialCheck.test.ts b/packages/alchemy/test/Cloudflare/LeakedCredentialCheck/LeakedCredentialCheck.test.ts index c791bd909..695e44bd2 100644 --- a/packages/alchemy/test/Cloudflare/LeakedCredentialCheck/LeakedCredentialCheck.test.ts +++ b/packages/alchemy/test/Cloudflare/LeakedCredentialCheck/LeakedCredentialCheck.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -65,7 +68,7 @@ const setBaseline = (zoneId: string, enabled: boolean) => // Both cases mutate the same zone-level Leaked Credential Check singleton; run them serially so they don't corrupt each other's captured `initialEnabled` under the global concurrent test config. describe.sequential("LeakedCredentialCheck", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "enables leaked credential checks and restores the baseline on destroy", (stack) => Effect.gen(function* () { @@ -127,7 +130,7 @@ describe.sequential("LeakedCredentialCheck", () => { }).pipe(logLevel), ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed DetectionQuotaExceeded error on unentitled zones", (stack) => Effect.gen(function* () { @@ -167,22 +170,24 @@ describe.sequential("LeakedCredentialCheck", () => { // API for this per-zone setting, so `list()` enumerates every zone via // `listAllZones` and reads the singleton in each. Assert the result is // non-empty and contains the standing test zone. - test.provider("list enumerates the check across all zones", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - const provider = yield* Provider.findProvider( - Cloudflare.LeakedCredentialCheck, - ); - const all = yield* provider.list(); - - expect(all.length).toBeGreaterThan(0); - expect(all.some((s) => s.zoneId === zoneId)).toBe(true); - - // `stack` is unused here (the singleton always exists on every zone), - // but keep the destroy bookends so the harness state stays clean. - yield* stack.destroy(); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the check across all zones", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; + + const provider = yield* Provider.findProvider( + Cloudflare.LeakedCredentialCheck, + ); + const all = yield* provider.list(); + + expect(all.length).toBeGreaterThan(0); + expect(all.some((s) => s.zoneId === zoneId)).toBe(true); + + // `stack` is unused here (the singleton always exists on every zone), + // but keep the destroy bookends so the harness state stays clean. + yield* stack.destroy(); + }).pipe(logLevel), ); // Requires a zone with a non-zero custom-detection quota (plan-gated) — the standard diff --git a/packages/alchemy/test/Cloudflare/LoadBalancer/LoadBalancer.test.ts b/packages/alchemy/test/Cloudflare/LoadBalancer/LoadBalancer.test.ts index 0199c9d0c..36160884b 100644 --- a/packages/alchemy/test/Cloudflare/LoadBalancer/LoadBalancer.test.ts +++ b/packages/alchemy/test/Cloudflare/LoadBalancer/LoadBalancer.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -73,7 +76,7 @@ const expectGone = (zoneId: string, loadBalancerId: string) => // Unentitlement probe: pins the typed plan-gate rejection, so it must skip // on entitled accounts — there the create would succeed instead of failing. -test.provider.skipIf(lbEnabled)( +test.provider.skipIf(lbEnabled || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed LoadBalancingNotEnabledForZone error without the LB subscription", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/LoadBalancer/Monitor.test.ts b/packages/alchemy/test/Cloudflare/LoadBalancer/Monitor.test.ts index a91b9b783..5cf75d49d 100644 --- a/packages/alchemy/test/Cloudflare/LoadBalancer/Monitor.test.ts +++ b/packages/alchemy/test/Cloudflare/LoadBalancer/Monitor.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -55,7 +58,7 @@ const expectGone = (accountId: string, monitorId: string) => // Unentitlement probe: pins the typed plan-gate rejection, so it must skip // on entitled accounts — there the create would succeed instead of failing. -test.provider.skipIf(lbEnabled)( +test.provider.skipIf(lbEnabled || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed MonitorIntervalOutOfRange error without the LB subscription", (stack) => Effect.gen(function* () { @@ -156,7 +159,7 @@ test.provider.skipIf(!lbEnabled)( // Ungated: the account-scoped listMonitors enumeration works regardless of // the Load Balancing subscription (it just returns an empty array on an // unentitled account), so this proves the list() op end-to-end live. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list returns an array of monitor attributes", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/LoadBalancer/MonitorGroup.test.ts b/packages/alchemy/test/Cloudflare/LoadBalancer/MonitorGroup.test.ts index 62cc81c64..395c00938 100644 --- a/packages/alchemy/test/Cloudflare/LoadBalancer/MonitorGroup.test.ts +++ b/packages/alchemy/test/Cloudflare/LoadBalancer/MonitorGroup.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -58,7 +61,7 @@ const expectGone = (accountId: string, monitorGroupId: string) => // Unentitlement probe: pins the typed plan-gate rejection, so it must skip // on Enterprise accounts — there the create would succeed instead of failing. -test.provider.skipIf(monitorGroupsEnabled)( +test.provider.skipIf(monitorGroupsEnabled || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed MonitorGroupsNotEnabled error on non-Enterprise accounts", (stack) => Effect.gen(function* () { @@ -98,14 +101,16 @@ test.provider.skipIf(monitorGroupsEnabled)( // Ungated probe: monitor group enumeration is account-scoped and works // regardless of the Enterprise entitlement — a non-entitled account simply // has no groups, so `list()` returns an array (typically empty here). -test.provider("list returns an array of monitor groups", () => - Effect.gen(function* () { - const provider = yield* Provider.findProvider( - Cloudflare.LoadBalancerMonitorGroup, - ); - const all = yield* provider.list(); - expect(Array.isArray(all)).toBe(true); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list returns an array of monitor groups", + () => + Effect.gen(function* () { + const provider = yield* Provider.findProvider( + Cloudflare.LoadBalancerMonitorGroup, + ); + const all = yield* provider.list(); + expect(Array.isArray(all)).toBe(true); + }).pipe(logLevel), ); test.provider.skipIf(!monitorGroupsEnabled)( diff --git a/packages/alchemy/test/Cloudflare/LoadBalancer/Pool.test.ts b/packages/alchemy/test/Cloudflare/LoadBalancer/Pool.test.ts index 873f27177..83927f69b 100644 --- a/packages/alchemy/test/Cloudflare/LoadBalancer/Pool.test.ts +++ b/packages/alchemy/test/Cloudflare/LoadBalancer/Pool.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -56,7 +59,7 @@ const expectGone = (accountId: string, poolId: string) => // Unentitlement probe: pins the typed plan-gate rejection, so it must skip // on entitled accounts — there the create would succeed instead of failing. -test.provider.skipIf(lbEnabled)( +test.provider.skipIf(lbEnabled || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed PoolAccessFailed error without the LB subscription", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/LoadBalancer/_diag.test.ts b/packages/alchemy/test/Cloudflare/LoadBalancer/_diag.test.ts index 71348a419..49d67d6a7 100644 --- a/packages/alchemy/test/Cloudflare/LoadBalancer/_diag.test.ts +++ b/packages/alchemy/test/Cloudflare/LoadBalancer/_diag.test.ts @@ -4,9 +4,12 @@ import * as Test from "@/Test/Vitest"; import { expect } from "@effect/vitest"; import * as Effect from "effect/Effect"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "diag list", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Logpush/Job.test.ts b/packages/alchemy/test/Cloudflare/Logpush/Job.test.ts index 52e12e901..670212732 100644 --- a/packages/alchemy/test/Cloudflare/Logpush/Job.test.ts +++ b/packages/alchemy/test/Cloudflare/Logpush/Job.test.ts @@ -12,6 +12,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import crypto from "node:crypto"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -117,7 +120,7 @@ const waitForDelete = (accountId: string, jobId: number) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create, update, and delete an account-scoped job pushing to R2", (stack) => Effect.gen(function* () { @@ -188,7 +191,7 @@ test.provider( { timeout: 180_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed Logpush job", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/LogsControl/CmbConfig.test.ts b/packages/alchemy/test/Cloudflare/LogsControl/CmbConfig.test.ts index 54cada1e6..a5d88f625 100644 --- a/packages/alchemy/test/Cloudflare/LogsControl/CmbConfig.test.ts +++ b/packages/alchemy/test/Cloudflare/LogsControl/CmbConfig.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -35,7 +38,7 @@ const getCmb = (accountId: string) => }), ); -test.provider.skipIf(entitled)( +test.provider.skipIf(entitled || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed LogsControlNotAuthorized error on unentitled accounts", (stack) => Effect.gen(function* () { @@ -119,7 +122,7 @@ test.provider.skipIf(!entitled)( // account-wide enumeration / `nuke` never blows up on an unentitled account. // The raw-op probe test above already pins the typed tag; here we assert the // nuke-safe empty result. -test.provider.skipIf(entitled)( +test.provider.skipIf(entitled || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list tolerates the typed LogsControlNotAuthorized error on unentitled accounts", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/LogsControl/RetentionFlag.test.ts b/packages/alchemy/test/Cloudflare/LogsControl/RetentionFlag.test.ts index a8d6c732d..5ce0dbb72 100644 --- a/packages/alchemy/test/Cloudflare/LogsControl/RetentionFlag.test.ts +++ b/packages/alchemy/test/Cloudflare/LogsControl/RetentionFlag.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -59,7 +62,7 @@ const setBaseline = (zoneId: string, flag: boolean) => }), ); -test.provider.skipIf(entitled)( +test.provider.skipIf(entitled || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed LogsControlNotAuthorized error on unentitled zones", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/MagicCloudNetworking/CatalogSync.test.ts b/packages/alchemy/test/Cloudflare/MagicCloudNetworking/CatalogSync.test.ts index 91e64811a..6fbab8ba5 100644 --- a/packages/alchemy/test/Cloudflare/MagicCloudNetworking/CatalogSync.test.ts +++ b/packages/alchemy/test/Cloudflare/MagicCloudNetworking/CatalogSync.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -50,7 +53,7 @@ const expectGone = (accountId: string, syncId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "unentitled accounts surface the typed FeatureNotEnabled error", (stack) => Effect.gen(function* () { @@ -147,7 +150,7 @@ test.provider.skipIf(!entitled)( // error and returns `[]`, so the read-only assertion runs everywhere. On an // entitled account we additionally deploy a sync and assert it shows up in // the exhaustively-paginated result. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates account catalog syncs", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/MagicCloudNetworking/CloudIntegration.test.ts b/packages/alchemy/test/Cloudflare/MagicCloudNetworking/CloudIntegration.test.ts index 515e555fc..f6f11ff00 100644 --- a/packages/alchemy/test/Cloudflare/MagicCloudNetworking/CloudIntegration.test.ts +++ b/packages/alchemy/test/Cloudflare/MagicCloudNetworking/CloudIntegration.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -52,7 +55,7 @@ const expectGone = (accountId: string, providerId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "unentitled accounts surface the typed FeatureNotEnabled error", (stack) => Effect.gen(function* () { @@ -89,24 +92,28 @@ test.provider( }).pipe(logLevel), ); -test.provider("list returns a well-typed array of integrations", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list returns a well-typed array of integrations", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); - const provider = yield* Provider.findProvider(Cloudflare.CloudIntegration); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider( + Cloudflare.CloudIntegration, + ); + const all = yield* provider.list(); - // On an unentitled account `list()` catches the typed `FeatureNotEnabled` - // tag and yields `[]`; on an entitled account it enumerates every - // integration. Either way the result is the full Attributes array. - expect(Array.isArray(all)).toBe(true); - for (const item of all) { - expect(typeof item.integrationId).toBe("string"); - expect(typeof item.accountId).toBe("string"); - } + // On an unentitled account `list()` catches the typed `FeatureNotEnabled` + // tag and yields `[]`; on an entitled account it enumerates every + // integration. Either way the result is the full Attributes array. + expect(Array.isArray(all)).toBe(true); + for (const item of all) { + expect(typeof item.integrationId).toBe("string"); + expect(typeof item.accountId).toBe("string"); + } - yield* stack.destroy(); - }).pipe(logLevel), + yield* stack.destroy(); + }).pipe(logLevel), ); test.provider.skipIf(!entitled)( diff --git a/packages/alchemy/test/Cloudflare/MagicCloudNetworking/OnRamp.test.ts b/packages/alchemy/test/Cloudflare/MagicCloudNetworking/OnRamp.test.ts index c0547b185..212b85a88 100644 --- a/packages/alchemy/test/Cloudflare/MagicCloudNetworking/OnRamp.test.ts +++ b/packages/alchemy/test/Cloudflare/MagicCloudNetworking/OnRamp.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -54,7 +57,7 @@ const expectGone = (accountId: string, onrampId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "unentitled accounts surface the typed FeatureNotEnabled error", (stack) => Effect.gen(function* () { @@ -156,29 +159,31 @@ test.provider.skipIf(!entitled || !vpcId || !vpcRegion)( // account `list()` catches the typed `FeatureNotEnabled` and returns a // well-typed `[]`; on an entitled account it returns the account's on-ramps // as the exact `read` Attributes shape (an array, possibly empty). -test.provider("list returns on-ramps or a typed [] when unentitled", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list returns on-ramps or a typed [] when unentitled", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - yield* stack.destroy(); + yield* stack.destroy(); - const canList = yield* mcn.listOnRamps({ accountId }).pipe( - Effect.as(true), - Effect.catchTag("FeatureNotEnabled", () => Effect.succeed(false)), - ); + const canList = yield* mcn.listOnRamps({ accountId }).pipe( + Effect.as(true), + Effect.catchTag("FeatureNotEnabled", () => Effect.succeed(false)), + ); - const provider = yield* Provider.findProvider(Cloudflare.OnRamp); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider(Cloudflare.OnRamp); + const all = yield* provider.list(); - if (!canList) { - // Unentitled — FeatureNotEnabled makes the account non-listable. - expect(all).toEqual([]); - } else { - expect(Array.isArray(all)).toBe(true); - } + if (!canList) { + // Unentitled — FeatureNotEnabled makes the account non-listable. + expect(all).toEqual([]); + } else { + expect(Array.isArray(all)).toBe(true); + } - yield* stack.destroy(); - }).pipe(logLevel), + yield* stack.destroy(); + }).pipe(logLevel), ); test.provider.skipIf(!entitled || !vpcId || !vpcRegion)( diff --git a/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/Config.test.ts b/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/Config.test.ts index e91e150f5..d7917d284 100644 --- a/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/Config.test.ts +++ b/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/Config.test.ts @@ -9,6 +9,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -27,46 +30,48 @@ const forbiddenRetry = { } as const; describe.sequential("MagicNetworkMonitoring.Config list", () => { - test.provider("list enumerates the account MNM config", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the account MNM config", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - yield* stack.destroy(); - // The config is an account singleton with no ownership markers — a - // leftover from an interrupted run would surface as `Unowned` and - // block this stack's create, so normalize to "absent" first. - yield* mnm.deleteConfig({ accountId }).pipe( - Effect.catchTag("MnmConfigNotFound", () => Effect.void), - Effect.retry(forbiddenRetry), - ); + yield* stack.destroy(); + // The config is an account singleton with no ownership markers — a + // leftover from an interrupted run would surface as `Unowned` and + // block this stack's create, so normalize to "absent" first. + yield* mnm.deleteConfig({ accountId }).pipe( + Effect.catchTag("MnmConfigNotFound", () => Effect.void), + Effect.retry(forbiddenRetry), + ); - const deployed = yield* stack.deploy( - Cloudflare.MagicNetworkMonitoringConfig("Config", { - name: "alchemy-mnm-list-test", - defaultSampling: 1, - }), - ); + const deployed = yield* stack.deploy( + Cloudflare.MagicNetworkMonitoringConfig("Config", { + name: "alchemy-mnm-list-test", + defaultSampling: 1, + }), + ); - const provider = yield* Provider.findProvider( - Cloudflare.MagicNetworkMonitoringConfig, - ); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider( + Cloudflare.MagicNetworkMonitoringConfig, + ); + const all = yield* provider.list(); - // Account singleton: when present, exactly one element with the full - // Attributes shape (the same object `read` returns). - expect(all.length).toEqual(1); - const config = all[0]; - expect(config.accountId).toEqual(accountId); - expect(config.name).toEqual(deployed.name); - expect(config.defaultSampling).toEqual(deployed.defaultSampling); - expect(config.routerIps).toEqual([]); - expect(config.warpDevices).toEqual([]); + // Account singleton: when present, exactly one element with the full + // Attributes shape (the same object `read` returns). + expect(all.length).toEqual(1); + const config = all[0]; + expect(config.accountId).toEqual(accountId); + expect(config.name).toEqual(deployed.name); + expect(config.defaultSampling).toEqual(deployed.defaultSampling); + expect(config.routerIps).toEqual([]); + expect(config.warpDevices).toEqual([]); - yield* stack.destroy(); + yield* stack.destroy(); - // With the singleton unset, `list` returns the empty array, not a throw. - const afterDestroy = yield* provider.list(); - expect(afterDestroy).toEqual([]); - }).pipe(logLevel), + // With the singleton unset, `list` returns the empty array, not a throw. + const afterDestroy = yield* provider.list(); + expect(afterDestroy).toEqual([]); + }).pipe(logLevel), ); }); diff --git a/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/MagicNetworkMonitoring.test.ts b/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/MagicNetworkMonitoring.test.ts index ac5126d7f..735948a65 100644 --- a/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/MagicNetworkMonitoring.test.ts +++ b/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/MagicNetworkMonitoring.test.ts @@ -8,6 +8,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -69,7 +72,7 @@ const expectConfigGone = (accountId: string) => ); describe.sequential("MagicNetworkMonitoring", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "creates, updates in place, and deletes the account config", (stack) => Effect.gen(function* () { @@ -137,7 +140,7 @@ describe.sequential("MagicNetworkMonitoring", () => { }).pipe(logLevel), ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "creates a threshold rule, updates it in place, and replaces on type change", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/Rule.test.ts b/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/Rule.test.ts index 9d2dbe540..69c38ed50 100644 --- a/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/Rule.test.ts +++ b/packages/alchemy/test/Cloudflare/MagicNetworkMonitoring/Rule.test.ts @@ -9,6 +9,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -31,56 +34,58 @@ const forbiddenRetry = { // helper and assert the deployed rule appears in the exhaustively- // paginated `list()` result. describe.sequential("MagicNetworkMonitoring.Rule", () => { - test.provider("list enumerates the deployed rule", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed rule", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - yield* stack.destroy(); - // The config is an account singleton with no ownership markers — a - // leftover from an interrupted run would surface as `Unowned` and - // block this stack's create, so normalize to "absent" first. - yield* mnm.deleteConfig({ accountId }).pipe( - Effect.catchTag("MnmConfigNotFound", () => Effect.void), - Effect.retry(forbiddenRetry), - ); + yield* stack.destroy(); + // The config is an account singleton with no ownership markers — a + // leftover from an interrupted run would surface as `Unowned` and + // block this stack's create, so normalize to "absent" first. + yield* mnm.deleteConfig({ accountId }).pipe( + Effect.catchTag("MnmConfigNotFound", () => Effect.void), + Effect.retry(forbiddenRetry), + ); - const { rule } = yield* stack.deploy( - Effect.gen(function* () { - const config = yield* Cloudflare.MagicNetworkMonitoringConfig( - "Config", - { - name: "alchemy-mnm-list-test", - defaultSampling: 1, - }, - ); - // Rules cannot exist without the account config — sequence the - // rule after the config via its accountId output. - const rule = yield* Cloudflare.MagicNetworkMonitoringRule("Rule", { - accountId: config.accountId, - type: "threshold", - prefixes: ["10.0.0.0/24"], - bandwidthThreshold: 1_000_000, - duration: "1m", - }); - return { config, rule }; - }), - ); + const { rule } = yield* stack.deploy( + Effect.gen(function* () { + const config = yield* Cloudflare.MagicNetworkMonitoringConfig( + "Config", + { + name: "alchemy-mnm-list-test", + defaultSampling: 1, + }, + ); + // Rules cannot exist without the account config — sequence the + // rule after the config via its accountId output. + const rule = yield* Cloudflare.MagicNetworkMonitoringRule("Rule", { + accountId: config.accountId, + type: "threshold", + prefixes: ["10.0.0.0/24"], + bandwidthThreshold: 1_000_000, + duration: "1m", + }); + return { config, rule }; + }), + ); - const provider = yield* Provider.findProvider( - Cloudflare.MagicNetworkMonitoringRule, - ); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider( + Cloudflare.MagicNetworkMonitoringRule, + ); + const all = yield* provider.list(); - // The exhaustively-paginated result contains the deployed rule, and - // each element carries the full `read` Attributes shape. - const found = all.find((r) => r.ruleId === rule.ruleId); - expect(found).toBeDefined(); - expect(found?.accountId).toEqual(accountId); - expect(found?.name).toEqual(rule.name); - expect(found?.type).toEqual("threshold"); - expect(found?.prefixes).toEqual(["10.0.0.0/24"]); + // The exhaustively-paginated result contains the deployed rule, and + // each element carries the full `read` Attributes shape. + const found = all.find((r) => r.ruleId === rule.ruleId); + expect(found).toBeDefined(); + expect(found?.accountId).toEqual(accountId); + expect(found?.name).toEqual(rule.name); + expect(found?.type).toEqual("threshold"); + expect(found?.prefixes).toEqual(["10.0.0.0/24"]); - yield* stack.destroy(); - }).pipe(logLevel), + yield* stack.destroy(); + }).pipe(logLevel), ); }); diff --git a/packages/alchemy/test/Cloudflare/MagicTransit/App.test.ts b/packages/alchemy/test/Cloudflare/MagicTransit/App.test.ts index 004fbe128..12f5d12b5 100644 --- a/packages/alchemy/test/Cloudflare/MagicTransit/App.test.ts +++ b/packages/alchemy/test/Cloudflare/MagicTransit/App.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -49,7 +52,7 @@ const expectGone = (accountId: string, appId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "unentitled accounts surface the typed MagicWanUnauthorized error", (stack) => Effect.gen(function* () { @@ -144,7 +147,7 @@ test.provider.skipIf(!entitled)( // Read-only list assertion. Always safe: on an unentitled account the // account-scoped apps list rejects with the typed `MagicWanUnauthorized` / // `Forbidden`, which `list()` maps to a well-typed empty array. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates account apps (well-typed [] when unentitled)", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/MagicTransit/IpsecTunnel.test.ts b/packages/alchemy/test/Cloudflare/MagicTransit/IpsecTunnel.test.ts index 62efa55d0..2e5e65a7e 100644 --- a/packages/alchemy/test/Cloudflare/MagicTransit/IpsecTunnel.test.ts +++ b/packages/alchemy/test/Cloudflare/MagicTransit/IpsecTunnel.test.ts @@ -9,6 +9,9 @@ import * as Redacted from "effect/Redacted"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -44,7 +47,7 @@ const expectGone = (accountId: string, ipsecTunnelId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "unentitled accounts surface the typed MagicTransitNotOnboarded error", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/MagicTransit/Site.test.ts b/packages/alchemy/test/Cloudflare/MagicTransit/Site.test.ts index 746ecd800..a75df848b 100644 --- a/packages/alchemy/test/Cloudflare/MagicTransit/Site.test.ts +++ b/packages/alchemy/test/Cloudflare/MagicTransit/Site.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -40,7 +43,7 @@ const expectGone = (accountId: string, siteId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "unentitled accounts surface the typed MagicWanUnauthorized error", (stack) => Effect.gen(function* () { @@ -85,7 +88,7 @@ test.provider( // `Forbidden` (403) and `list()` returns a well-typed `[]`. On entitled // accounts (CLOUDFLARE_TEST_MAGIC_WAN=1) we deploy a site and assert it // appears in the exhaustively-paginated result. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates account Magic WAN sites", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/MagicTransit/SiteAcl.test.ts b/packages/alchemy/test/Cloudflare/MagicTransit/SiteAcl.test.ts index 9d49a3903..d6a167156 100644 --- a/packages/alchemy/test/Cloudflare/MagicTransit/SiteAcl.test.ts +++ b/packages/alchemy/test/Cloudflare/MagicTransit/SiteAcl.test.ts @@ -7,6 +7,9 @@ import { expect } from "@effect/vitest"; import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -93,7 +96,7 @@ test.provider( { timeout: 180_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "unentitled accounts surface the typed Magic WAN error for ACL listing", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/MagicTransit/SiteLan.test.ts b/packages/alchemy/test/Cloudflare/MagicTransit/SiteLan.test.ts index 4ca617c00..717509349 100644 --- a/packages/alchemy/test/Cloudflare/MagicTransit/SiteLan.test.ts +++ b/packages/alchemy/test/Cloudflare/MagicTransit/SiteLan.test.ts @@ -7,6 +7,9 @@ import { expect } from "@effect/vitest"; import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -23,7 +26,7 @@ const logLevel = Effect.provideService( // entitled accounts. const entitled = !!process.env.CLOUDFLARE_TEST_MAGIC_WAN; -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list returns a well-typed array (empty on unentitled accounts)", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/MagicTransit/StaticRoute.test.ts b/packages/alchemy/test/Cloudflare/MagicTransit/StaticRoute.test.ts index 0fec92710..60d2739ad 100644 --- a/packages/alchemy/test/Cloudflare/MagicTransit/StaticRoute.test.ts +++ b/packages/alchemy/test/Cloudflare/MagicTransit/StaticRoute.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -39,7 +42,7 @@ const expectGone = (accountId: string, routeId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "unentitled accounts surface the typed MagicTransitNotOnboarded error", (stack) => Effect.gen(function* () { @@ -86,20 +89,24 @@ test.provider( // Read-only: list() must resolve via the typed provider and return a // well-typed array even on unentitled accounts (the account-scoped // `MagicTransitNotOnboarded` gate is mapped to `[]`). -test.provider("list returns a well-typed array of routes", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const provider = yield* Provider.findProvider(Cloudflare.MagicStaticRoute); - const all = yield* provider.list(); - expect(Array.isArray(all)).toBe(true); - for (const route of all) { - expect(typeof route.routeId).toBe("string"); - expect(typeof route.accountId).toBe("string"); - } - - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list returns a well-typed array of routes", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const provider = yield* Provider.findProvider( + Cloudflare.MagicStaticRoute, + ); + const all = yield* provider.list(); + expect(Array.isArray(all)).toBe(true); + for (const route of all) { + expect(typeof route.routeId).toBe("string"); + expect(typeof route.accountId).toBe("string"); + } + + yield* stack.destroy(); + }).pipe(logLevel), ); test.provider.skipIf(!entitled)( diff --git a/packages/alchemy/test/Cloudflare/ManagedTransforms/ManagedTransforms.test.ts b/packages/alchemy/test/Cloudflare/ManagedTransforms/ManagedTransforms.test.ts index 268d7583d..6ea47e5fe 100644 --- a/packages/alchemy/test/Cloudflare/ManagedTransforms/ManagedTransforms.test.ts +++ b/packages/alchemy/test/Cloudflare/ManagedTransforms/ManagedTransforms.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); // The account-wide `list` test intermittently fails with a `Forbidden` @@ -94,7 +97,7 @@ const normalizeBaseline = (zoneId: string) => }); describe.sequential("ManagedTransforms", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "manages a named transform, updates in place, and restores it on destroy", (stack) => Effect.gen(function* () { @@ -180,7 +183,7 @@ describe.sequential("ManagedTransforms", () => { { timeout: 240_000 }, ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "destroy restores a transform that was enabled before management", (stack) => Effect.gen(function* () { @@ -237,7 +240,7 @@ describe.sequential("ManagedTransforms", () => { { timeout: 240_000 }, ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "deploy with no transforms named adopts the singleton without writing", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/MtlsCertificate/MtlsCertificate.test.ts b/packages/alchemy/test/Cloudflare/MtlsCertificate/MtlsCertificate.test.ts index e89ed1cf6..6158fe545 100644 --- a/packages/alchemy/test/Cloudflare/MtlsCertificate/MtlsCertificate.test.ts +++ b/packages/alchemy/test/Cloudflare/MtlsCertificate/MtlsCertificate.test.ts @@ -12,6 +12,9 @@ import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; import { CA_CERT_1, CA_CERT_2, LEAF_CERT, LEAF_KEY } from "./fixtures/certs.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -27,39 +30,41 @@ const logLevel = Effect.provideService( // `describe.sequential` runs them one at a time so each create→…→destroy owns // the content exclusively. describe.sequential("MtlsCertificate", () => { - test.provider("create and delete a CA certificate", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const cert = yield* stack.deploy( - Cloudflare.MtlsCertificate("CaCert", { - ca: true, - certificates: CA_CERT_1, - }), - ); - - expect(cert.mtlsCertificateId).toBeDefined(); - expect(cert.ca).toEqual(true); - expect(cert.name).toBeDefined(); - expect(cert.expiresOn).toBeDefined(); - - const actual = yield* mtls.getMtlsCertificate({ - accountId, - mtlsCertificateId: cert.mtlsCertificateId, - }); - expect(actual.id).toEqual(cert.mtlsCertificateId); - expect(actual.ca).toEqual(true); - expect(actual.name).toEqual(cert.name); - - yield* stack.destroy(); - - yield* waitForDelete(accountId, cert.mtlsCertificateId); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create and delete a CA certificate", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const cert = yield* stack.deploy( + Cloudflare.MtlsCertificate("CaCert", { + ca: true, + certificates: CA_CERT_1, + }), + ); + + expect(cert.mtlsCertificateId).toBeDefined(); + expect(cert.ca).toEqual(true); + expect(cert.name).toBeDefined(); + expect(cert.expiresOn).toBeDefined(); + + const actual = yield* mtls.getMtlsCertificate({ + accountId, + mtlsCertificateId: cert.mtlsCertificateId, + }); + expect(actual.id).toEqual(cert.mtlsCertificateId); + expect(actual.ca).toEqual(true); + expect(actual.name).toEqual(cert.name); + + yield* stack.destroy(); + + yield* waitForDelete(accountId, cert.mtlsCertificateId); + }).pipe(logLevel), ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create and delete a leaf certificate with private key", (stack) => Effect.gen(function* () { @@ -91,79 +96,85 @@ describe.sequential("MtlsCertificate", () => { }).pipe(logLevel), ); - test.provider("replaces the certificate when the PEM changes", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const original = yield* stack.deploy( - Cloudflare.MtlsCertificate("ReplaceCert", { - ca: true, - certificates: CA_CERT_1, - }), - ); - - const replaced = yield* stack.deploy( - Cloudflare.MtlsCertificate("ReplaceCert", { - ca: true, - certificates: CA_CERT_2, - }), - ); - - expect(replaced.mtlsCertificateId).toBeDefined(); - expect(replaced.mtlsCertificateId).not.toEqual( - original.mtlsCertificateId, - ); - expect(replaced.serialNumber).not.toEqual(original.serialNumber); - - // The old certificate must be gone after the replacement completes. - yield* waitForDelete(accountId, original.mtlsCertificateId); - - const actual = yield* mtls.getMtlsCertificate({ - accountId, - mtlsCertificateId: replaced.mtlsCertificateId, - }); - expect(actual.id).toEqual(replaced.mtlsCertificateId); - - yield* stack.destroy(); - - yield* waitForDelete(accountId, replaced.mtlsCertificateId); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "replaces the certificate when the PEM changes", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const original = yield* stack.deploy( + Cloudflare.MtlsCertificate("ReplaceCert", { + ca: true, + certificates: CA_CERT_1, + }), + ); + + const replaced = yield* stack.deploy( + Cloudflare.MtlsCertificate("ReplaceCert", { + ca: true, + certificates: CA_CERT_2, + }), + ); + + expect(replaced.mtlsCertificateId).toBeDefined(); + expect(replaced.mtlsCertificateId).not.toEqual( + original.mtlsCertificateId, + ); + expect(replaced.serialNumber).not.toEqual(original.serialNumber); + + // The old certificate must be gone after the replacement completes. + yield* waitForDelete(accountId, original.mtlsCertificateId); + + const actual = yield* mtls.getMtlsCertificate({ + accountId, + mtlsCertificateId: replaced.mtlsCertificateId, + }); + expect(actual.id).toEqual(replaced.mtlsCertificateId); + + yield* stack.destroy(); + + yield* waitForDelete(accountId, replaced.mtlsCertificateId); + }).pipe(logLevel), ); - test.provider("list enumerates the deployed mTLS certificate", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed mTLS certificate", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - yield* stack.destroy(); + yield* stack.destroy(); + + const cert = yield* stack.deploy( + Cloudflare.MtlsCertificate("ListCert", { + ca: true, + certificates: CA_CERT_1, + }), + ); - const cert = yield* stack.deploy( - Cloudflare.MtlsCertificate("ListCert", { - ca: true, - certificates: CA_CERT_1, - }), - ); + const provider = yield* Provider.findProvider( + Cloudflare.MtlsCertificate, + ); - const provider = yield* Provider.findProvider(Cloudflare.MtlsCertificate); + // A freshly-deployed certificate is eventually consistent in the + // account-wide list(); poll until it appears before asserting. + const all = yield* poll({ + description: "list() includes the deployed mTLS certificate", + effect: provider.list(), + predicate: (all) => + all.some((c) => c.mtlsCertificateId === cert.mtlsCertificateId), + }); - // A freshly-deployed certificate is eventually consistent in the - // account-wide list(); poll until it appears before asserting. - const all = yield* poll({ - description: "list() includes the deployed mTLS certificate", - effect: provider.list(), - predicate: (all) => + expect( all.some((c) => c.mtlsCertificateId === cert.mtlsCertificateId), - }); - - expect( - all.some((c) => c.mtlsCertificateId === cert.mtlsCertificateId), - ).toBe(true); + ).toBe(true); - yield* stack.destroy(); + yield* stack.destroy(); - yield* waitForDelete(accountId, cert.mtlsCertificateId); - }).pipe(logLevel), + yield* waitForDelete(accountId, cert.mtlsCertificateId); + }).pipe(logLevel), ); }); diff --git a/packages/alchemy/test/Cloudflare/NetworkInterconnects/Settings.test.ts b/packages/alchemy/test/Cloudflare/NetworkInterconnects/Settings.test.ts index b51f28078..de7adffeb 100644 --- a/packages/alchemy/test/Cloudflare/NetworkInterconnects/Settings.test.ts +++ b/packages/alchemy/test/Cloudflare/NetworkInterconnects/Settings.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -44,7 +47,7 @@ const setBaseline = (accountId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "pins the default ASN, updates in place, and restores the original on destroy", (stack) => Effect.gen(function* () { @@ -115,7 +118,7 @@ test.provider( // rejects with the typed `Forbidden` error, which `list()` maps to `[]` // ("unset"). The read-only assertions below hold in both cases; the // entitled-account content assertion is gated on a non-empty result. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the CNI settings singleton", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Organization/Organization.test.ts b/packages/alchemy/test/Cloudflare/Organization/Organization.test.ts index 1ecbef630..5e93bdf2a 100644 --- a/packages/alchemy/test/Cloudflare/Organization/Organization.test.ts +++ b/packages/alchemy/test/Cloudflare/Organization/Organization.test.ts @@ -7,6 +7,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -41,7 +44,7 @@ const PROFILE = { externalMetadata: "alchemy:test", } as const; -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "unentitled accounts surface the typed Forbidden error", (stack) => Effect.gen(function* () { @@ -79,7 +82,7 @@ test.provider( // account-wide enumeration / `nuke` never blows up on a non-tenant account; // the raw-op probe test above already pins the typed tag. On an entitled // account it returns a well-typed `OrganizationAttributes[]`. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list either enumerates organizations or tolerates the unentitled account", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/OriginCaCertificate/OriginCaCertificate.test.ts b/packages/alchemy/test/Cloudflare/OriginCaCertificate/OriginCaCertificate.test.ts index 11e47a1eb..5b7ed595f 100644 --- a/packages/alchemy/test/Cloudflare/OriginCaCertificate/OriginCaCertificate.test.ts +++ b/packages/alchemy/test/Cloudflare/OriginCaCertificate/OriginCaCertificate.test.ts @@ -10,6 +10,9 @@ import * as Schedule from "effect/Schedule"; import { TEST_CSR } from "./fixtures/csr.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -52,157 +55,165 @@ const expectRevoked = (certificateId: string) => }), ); -test.provider("issue, verify, and revoke a certificate", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const cert = yield* stack.deploy( - Cloudflare.OriginCaCertificate("Cert", { - csr: TEST_CSR, - hostnames: [hostname], - requestType: "origin-rsa", - requestedValidity: 90, - }).pipe(adopt(true)), - ); - - // Issuance is synchronous — the signed PEM comes back on create. - expect(cert.certificateId).toBeTruthy(); - expect(cert.certificate).toContain("-----BEGIN CERTIFICATE-----"); - expect(cert.csr).toContain("-----BEGIN CERTIFICATE REQUEST-----"); - expect(cert.hostnames).toEqual([hostname]); - expect(cert.requestType).toEqual("origin-rsa"); - expect(cert.requestedValidity).toEqual(90); - expect(cert.expiresOn).toBeTruthy(); - - // Out-of-band verification: the certificate is live and not revoked. - const live = yield* getCertificate(cert.certificateId); - expect(live.id).toEqual(cert.certificateId); - expect(live.hostnames).toEqual([hostname]); - expect(live.revokedAt ?? null).toBeNull(); - - // Redeploying identical props is a no-op (same certificate). - const noop = yield* stack.deploy( - Cloudflare.OriginCaCertificate("Cert", { - csr: TEST_CSR, - hostnames: [hostname], - requestType: "origin-rsa", - requestedValidity: 90, - }).pipe(adopt(true)), - ); - expect(noop.certificateId).toEqual(cert.certificateId); - - // Destroy revokes the certificate; a second destroy is idempotent. - yield* stack.destroy(); - yield* expectRevoked(cert.certificateId); - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "issue, verify, and revoke a certificate", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const cert = yield* stack.deploy( + Cloudflare.OriginCaCertificate("Cert", { + csr: TEST_CSR, + hostnames: [hostname], + requestType: "origin-rsa", + requestedValidity: 90, + }).pipe(adopt(true)), + ); + + // Issuance is synchronous — the signed PEM comes back on create. + expect(cert.certificateId).toBeTruthy(); + expect(cert.certificate).toContain("-----BEGIN CERTIFICATE-----"); + expect(cert.csr).toContain("-----BEGIN CERTIFICATE REQUEST-----"); + expect(cert.hostnames).toEqual([hostname]); + expect(cert.requestType).toEqual("origin-rsa"); + expect(cert.requestedValidity).toEqual(90); + expect(cert.expiresOn).toBeTruthy(); + + // Out-of-band verification: the certificate is live and not revoked. + const live = yield* getCertificate(cert.certificateId); + expect(live.id).toEqual(cert.certificateId); + expect(live.hostnames).toEqual([hostname]); + expect(live.revokedAt ?? null).toBeNull(); + + // Redeploying identical props is a no-op (same certificate). + const noop = yield* stack.deploy( + Cloudflare.OriginCaCertificate("Cert", { + csr: TEST_CSR, + hostnames: [hostname], + requestType: "origin-rsa", + requestedValidity: 90, + }).pipe(adopt(true)), + ); + expect(noop.certificateId).toEqual(cert.certificateId); + + // Destroy revokes the certificate; a second destroy is idempotent. + yield* stack.destroy(); + yield* expectRevoked(cert.certificateId); + yield* stack.destroy(); + }).pipe(logLevel), ); -test.provider("list enumerates issued certificates", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const cert = yield* stack.deploy( - Cloudflare.OriginCaCertificate("ListCert", { - csr: TEST_CSR, - hostnames: [hostname], - requestType: "origin-rsa", - requestedValidity: 90, - }).pipe(adopt(true)), - ); - - const provider = yield* Provider.findProvider( - Cloudflare.OriginCaCertificate, - ); - const all = yield* provider.list(); - - // `list()` is account-wide but enumerated per zone with the `zone_id` - // query param; a zone the scoped token can't read for Origin CA rejects - // with the typed `Forbidden` tag, which is swallowed so that zone simply - // contributes []. The standing token can list the test zone, so the - // freshly issued certificate must appear in the exhaustively-paginated - // result in the `read` Attributes shape. - expect(Array.isArray(all)).toBe(true); - const match = all.find((c) => c.certificateId === cert.certificateId); - expect(match).toBeDefined(); - expect(match!.certificateId).toEqual(cert.certificateId); - expect(match!.hostnames).toEqual([hostname]); - expect(match!.requestType).toEqual("origin-rsa"); - - yield* stack.destroy(); - yield* expectRevoked(cert.certificateId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates issued certificates", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const cert = yield* stack.deploy( + Cloudflare.OriginCaCertificate("ListCert", { + csr: TEST_CSR, + hostnames: [hostname], + requestType: "origin-rsa", + requestedValidity: 90, + }).pipe(adopt(true)), + ); + + const provider = yield* Provider.findProvider( + Cloudflare.OriginCaCertificate, + ); + const all = yield* provider.list(); + + // `list()` is account-wide but enumerated per zone with the `zone_id` + // query param; a zone the scoped token can't read for Origin CA rejects + // with the typed `Forbidden` tag, which is swallowed so that zone simply + // contributes []. The standing token can list the test zone, so the + // freshly issued certificate must appear in the exhaustively-paginated + // result in the `read` Attributes shape. + expect(Array.isArray(all)).toBe(true); + const match = all.find((c) => c.certificateId === cert.certificateId); + expect(match).toBeDefined(); + expect(match!.certificateId).toEqual(cert.certificateId); + expect(match!.hostnames).toEqual([hostname]); + expect(match!.requestType).toEqual("origin-rsa"); + + yield* stack.destroy(); + yield* expectRevoked(cert.certificateId); + }).pipe(logLevel), ); -test.provider("replacement on requestedValidity change", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const initial = yield* stack.deploy( - Cloudflare.OriginCaCertificate("ValidityCert", { - csr: TEST_CSR, - hostnames: [hostname], - requestType: "origin-rsa", - requestedValidity: 90, - }).pipe(adopt(true)), - ); - expect(initial.requestedValidity).toEqual(90); - - // There is no update API — changing the validity issues a new - // certificate and revokes the old one. - const replaced = yield* stack.deploy( - Cloudflare.OriginCaCertificate("ValidityCert", { - csr: TEST_CSR, - hostnames: [hostname], - requestType: "origin-rsa", - requestedValidity: 30, - }).pipe(adopt(true)), - ); - - expect(replaced.certificateId).not.toEqual(initial.certificateId); - expect(replaced.requestedValidity).toEqual(30); - yield* expectRevoked(initial.certificateId); - - const live = yield* getCertificate(replaced.certificateId); - expect(live.revokedAt ?? null).toBeNull(); - - yield* stack.destroy(); - yield* expectRevoked(replaced.certificateId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "replacement on requestedValidity change", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const initial = yield* stack.deploy( + Cloudflare.OriginCaCertificate("ValidityCert", { + csr: TEST_CSR, + hostnames: [hostname], + requestType: "origin-rsa", + requestedValidity: 90, + }).pipe(adopt(true)), + ); + expect(initial.requestedValidity).toEqual(90); + + // There is no update API — changing the validity issues a new + // certificate and revokes the old one. + const replaced = yield* stack.deploy( + Cloudflare.OriginCaCertificate("ValidityCert", { + csr: TEST_CSR, + hostnames: [hostname], + requestType: "origin-rsa", + requestedValidity: 30, + }).pipe(adopt(true)), + ); + + expect(replaced.certificateId).not.toEqual(initial.certificateId); + expect(replaced.requestedValidity).toEqual(30); + yield* expectRevoked(initial.certificateId); + + const live = yield* getCertificate(replaced.certificateId); + expect(live.revokedAt ?? null).toBeNull(); + + yield* stack.destroy(); + yield* expectRevoked(replaced.certificateId); + }).pipe(logLevel), ); -test.provider("replacement on hostnames change", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const initial = yield* stack.deploy( - Cloudflare.OriginCaCertificate("HostnamesCert", { - csr: TEST_CSR, - hostnames: [hostname], - requestType: "origin-rsa", - requestedValidity: 90, - }).pipe(adopt(true)), - ); - expect(initial.hostnames).toEqual([hostname]); - - // Hostnames are immutable — changing the set issues a new certificate - // and revokes the old one. - const replaced = yield* stack.deploy( - Cloudflare.OriginCaCertificate("HostnamesCert", { - csr: TEST_CSR, - hostnames: [hostname, altHostname], - requestType: "origin-rsa", - requestedValidity: 90, - }).pipe(adopt(true)), - ); - - expect(replaced.certificateId).not.toEqual(initial.certificateId); - expect([...replaced.hostnames].sort()).toEqual( - [hostname, altHostname].sort(), - ); - yield* expectRevoked(initial.certificateId); - - yield* stack.destroy(); - yield* expectRevoked(replaced.certificateId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "replacement on hostnames change", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const initial = yield* stack.deploy( + Cloudflare.OriginCaCertificate("HostnamesCert", { + csr: TEST_CSR, + hostnames: [hostname], + requestType: "origin-rsa", + requestedValidity: 90, + }).pipe(adopt(true)), + ); + expect(initial.hostnames).toEqual([hostname]); + + // Hostnames are immutable — changing the set issues a new certificate + // and revokes the old one. + const replaced = yield* stack.deploy( + Cloudflare.OriginCaCertificate("HostnamesCert", { + csr: TEST_CSR, + hostnames: [hostname, altHostname], + requestType: "origin-rsa", + requestedValidity: 90, + }).pipe(adopt(true)), + ); + + expect(replaced.certificateId).not.toEqual(initial.certificateId); + expect([...replaced.hostnames].sort()).toEqual( + [hostname, altHostname].sort(), + ); + yield* expectRevoked(initial.certificateId); + + yield* stack.destroy(); + yield* expectRevoked(replaced.certificateId); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/OriginPostQuantumEncryption/OriginPostQuantumEncryption.test.ts b/packages/alchemy/test/Cloudflare/OriginPostQuantumEncryption/OriginPostQuantumEncryption.test.ts index 962fc3e63..543bbc875 100644 --- a/packages/alchemy/test/Cloudflare/OriginPostQuantumEncryption/OriginPostQuantumEncryption.test.ts +++ b/packages/alchemy/test/Cloudflare/OriginPostQuantumEncryption/OriginPostQuantumEncryption.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -63,7 +66,7 @@ const setBaseline = ( ); describe.sequential("OriginPostQuantumEncryption", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "pins the setting and restores the original value on destroy", (stack) => Effect.gen(function* () { @@ -99,7 +102,7 @@ describe.sequential("OriginPostQuantumEncryption", () => { }).pipe(logLevel), ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "updates the value in place and keeps the captured initial value", (stack) => Effect.gen(function* () { @@ -150,7 +153,7 @@ describe.sequential("OriginPostQuantumEncryption", () => { }).pipe(logLevel), ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "no-op redeploy converges without changing the setting", (stack) => Effect.gen(function* () { @@ -192,31 +195,33 @@ describe.sequential("OriginPostQuantumEncryption", () => { // API for this per-zone setting, so `list()` enumerates every zone via // `listAllZones` and reads the singleton in each. Assert the result is // non-empty and contains the standing test zone. - test.provider("list enumerates the setting across all zones", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - const provider = yield* Provider.findProvider( - Cloudflare.OriginPostQuantumEncryption, - ); - // The freshly-minted scoped token propagates eventually-consistently, so - // the account-wide enumeration intermittently 403s (`Forbidden`) or 401s - // (`Unauthorized`). Both are transient here — ride out the blip like - // every other out-of-band call in this suite. - const all = yield* provider.list().pipe( - Effect.retry({ - while: (e) => e._tag === "Forbidden" || e._tag === "Unauthorized", - schedule: forbiddenRetrySchedule, - times: 8, - }), - ); - - expect(all.length).toBeGreaterThan(0); - expect(all.some((s) => s.zoneId === zoneId)).toBe(true); - - // `stack` is unused here (the singleton always exists on every zone), - // but keep the destroy bookend so the harness state stays clean. - yield* stack.destroy(); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the setting across all zones", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; + + const provider = yield* Provider.findProvider( + Cloudflare.OriginPostQuantumEncryption, + ); + // The freshly-minted scoped token propagates eventually-consistently, so + // the account-wide enumeration intermittently 403s (`Forbidden`) or 401s + // (`Unauthorized`). Both are transient here — ride out the blip like + // every other out-of-band call in this suite. + const all = yield* provider.list().pipe( + Effect.retry({ + while: (e) => e._tag === "Forbidden" || e._tag === "Unauthorized", + schedule: forbiddenRetrySchedule, + times: 8, + }), + ); + + expect(all.length).toBeGreaterThan(0); + expect(all.some((s) => s.zoneId === zoneId)).toBe(true); + + // `stack` is unused here (the singleton always exists on every zone), + // but keep the destroy bookend so the harness state stays clean. + yield* stack.destroy(); + }).pipe(logLevel), ); }); diff --git a/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/Certificate.test.ts b/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/Certificate.test.ts index 581174fc4..297820ed8 100644 --- a/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/Certificate.test.ts +++ b/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/Certificate.test.ts @@ -19,6 +19,9 @@ import { KEY_7, } from "./fixtures/certs.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -124,7 +127,7 @@ const purgeCertificates = (zoneId: string) => // `CERT_1` concurrently churn each other (collisions + stale list views). // Run the cases one at a time so each owns its certificate content. describe.sequential("OriginTlsClientAuthCertificate", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "uploads and deletes a zone client certificate", (stack) => Effect.gen(function* () { @@ -159,7 +162,7 @@ describe.sequential("OriginTlsClientAuthCertificate", () => { { timeout: 120_000 }, ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "replaces the certificate when the PEM changes", (stack) => Effect.gen(function* () { @@ -205,7 +208,7 @@ describe.sequential("OriginTlsClientAuthCertificate", () => { // every zone via `listAllZones` and enumerates the per-zone certificate store, // hydrating each into the same `read` Attributes shape. Deploy a certificate // to the standing test zone and assert it appears in the exhaustive result. - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed zone client certificate", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/HostnameAssociation.test.ts b/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/HostnameAssociation.test.ts index ab7d4f68a..ac263db31 100644 --- a/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/HostnameAssociation.test.ts +++ b/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/HostnameAssociation.test.ts @@ -12,6 +12,9 @@ import * as Schedule from "effect/Schedule"; import { CERT_5, CERT_6, KEY_5, KEY_6 } from "./fixtures/certs.ts"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -177,7 +180,7 @@ describe.sequential("HostnameAssociation", () => { }).pipe(Effect.ensuring(stack.destroy().pipe(Effect.ignore)), logLevel), ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "associates a hostname, updates cert and enablement in place, voids on destroy", (stack) => Effect.gen(function* () { @@ -255,7 +258,7 @@ describe.sequential("HostnameAssociation", () => { { timeout: 420_000 }, ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "replaces the association when the hostname changes", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/HostnameCertificate.test.ts b/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/HostnameCertificate.test.ts index c42b53979..ac37a0d33 100644 --- a/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/HostnameCertificate.test.ts +++ b/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/HostnameCertificate.test.ts @@ -18,6 +18,9 @@ import { KEY_8, } from "./fixtures/certs.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -131,7 +134,7 @@ const purgeCertificates = (zoneId: string, pems: string[]) => ); }); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "uploads and deletes a hostname client certificate", (stack) => Effect.gen(function* () { @@ -165,7 +168,7 @@ test.provider( { timeout: 120_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "replaces the hostname certificate when the PEM changes", (stack) => Effect.gen(function* () { @@ -207,7 +210,7 @@ test.provider( { timeout: 120_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed hostname certificate", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/Setting.test.ts b/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/Setting.test.ts index 642beaddf..318572175 100644 --- a/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/Setting.test.ts +++ b/packages/alchemy/test/Cloudflare/OriginTlsClientAuth/Setting.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -61,7 +64,7 @@ const setBaseline = (zoneId: string, enabled: boolean) => // Both cases toggle the same zone-level AOP singleton; run them serially so they don't corrupt each other's captured `initialEnabled` under the global concurrent test config. describe.sequential("Setting", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "enables AOP and restores the original value on destroy", (stack) => Effect.gen(function* () { @@ -94,70 +97,74 @@ describe.sequential("Setting", () => { }).pipe(logLevel), ); - test.provider("updates the enabled flag in place", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - yield* stack.destroy(); - yield* setBaseline(zoneId, false); - - const enabled = yield* stack.deploy( - Cloudflare.OriginTlsClientAuthSetting("AopUpdate", { - zoneId, - enabled: true, - }), - ); - expect(enabled.enabled).toEqual(true); - expect(enabled.initialEnabled).toEqual(false); - - // In-place update — the singleton is never replaced. - const disabled = yield* stack.deploy( - Cloudflare.OriginTlsClientAuthSetting("AopUpdate", { - zoneId, - enabled: false, - }), - ); - expect(disabled.enabled).toEqual(false); - // The original baseline survives the update. - expect(disabled.initialEnabled).toEqual(false); - - const observed = yield* getSetting(zoneId); - expect(observed.enabled).toEqual(false); - - yield* stack.destroy(); - - const restored = yield* getSetting(zoneId); - expect(restored.enabled).toEqual(false); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "updates the enabled flag in place", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; + + yield* stack.destroy(); + yield* setBaseline(zoneId, false); + + const enabled = yield* stack.deploy( + Cloudflare.OriginTlsClientAuthSetting("AopUpdate", { + zoneId, + enabled: true, + }), + ); + expect(enabled.enabled).toEqual(true); + expect(enabled.initialEnabled).toEqual(false); + + // In-place update — the singleton is never replaced. + const disabled = yield* stack.deploy( + Cloudflare.OriginTlsClientAuthSetting("AopUpdate", { + zoneId, + enabled: false, + }), + ); + expect(disabled.enabled).toEqual(false); + // The original baseline survives the update. + expect(disabled.initialEnabled).toEqual(false); + + const observed = yield* getSetting(zoneId); + expect(observed.enabled).toEqual(false); + + yield* stack.destroy(); + + const restored = yield* getSetting(zoneId); + expect(restored.enabled).toEqual(false); + }).pipe(logLevel), ); // Canonical `list()` test (zone-scoped singleton): there is no account-wide // API for this per-zone setting, so `list()` enumerates every zone via // `listAllZones` and reads the singleton in each. Assert the result is // non-empty and contains the standing test zone. - test.provider("list enumerates the setting across all zones", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - const provider = yield* Provider.findProvider( - Cloudflare.OriginTlsClientAuthSetting, - ); - // Ride out fresh-token 403 blips on the account-wide enumeration, like - // every other out-of-band call in this suite. - const all = yield* provider.list().pipe( - Effect.retry({ - while: (e) => e._tag === "Forbidden", - schedule: forbiddenRetrySchedule, - times: 8, - }), - ); - - expect(all.length).toBeGreaterThan(0); - expect(all.some((s) => s.zoneId === zoneId)).toBe(true); - - // `stack` is unused here (the singleton always exists on every zone), - // but keep the destroy bookend so the harness state stays clean. - yield* stack.destroy(); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the setting across all zones", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; + + const provider = yield* Provider.findProvider( + Cloudflare.OriginTlsClientAuthSetting, + ); + // Ride out fresh-token 403 blips on the account-wide enumeration, like + // every other out-of-band call in this suite. + const all = yield* provider.list().pipe( + Effect.retry({ + while: (e) => e._tag === "Forbidden", + schedule: forbiddenRetrySchedule, + times: 8, + }), + ); + + expect(all.length).toBeGreaterThan(0); + expect(all.some((s) => s.zoneId === zoneId)).toBe(true); + + // `stack` is unused here (the singleton always exists on every zone), + // but keep the destroy bookend so the harness state stays clean. + yield* stack.destroy(); + }).pipe(logLevel), ); }); diff --git a/packages/alchemy/test/Cloudflare/PageRule/PageRule.test.ts b/packages/alchemy/test/Cloudflare/PageRule/PageRule.test.ts index 0e9b6ba8f..7f3f49c6e 100644 --- a/packages/alchemy/test/Cloudflare/PageRule/PageRule.test.ts +++ b/packages/alchemy/test/Cloudflare/PageRule/PageRule.test.ts @@ -11,6 +11,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -92,48 +95,50 @@ const purgeRules = (zoneId: string, targets: ReadonlyArray) => ), ); -test.provider("create, verify out-of-band, and destroy a page rule", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, verify out-of-band, and destroy a page rule", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - yield* stack.destroy(); - yield* purgeRules(zoneId, [TARGET_DEFAULT]); + yield* stack.destroy(); + yield* purgeRules(zoneId, [TARGET_DEFAULT]); - const rule = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.PageRule("DefaultRule", { - zoneId, - target: TARGET_DEFAULT, - actions: [ - { id: "cache_level", value: "cache_everything" }, - { id: "edge_cache_ttl", value: 7200 }, - ], - }).pipe(adopt(true)); - }), - ); + const rule = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.PageRule("DefaultRule", { + zoneId, + target: TARGET_DEFAULT, + actions: [ + { id: "cache_level", value: "cache_everything" }, + { id: "edge_cache_ttl", value: 7200 }, + ], + }).pipe(adopt(true)); + }), + ); + + expect(rule.pageRuleId).toBeDefined(); + expect(rule.zoneId).toEqual(zoneId); + expect(rule.target).toEqual(TARGET_DEFAULT); + // Alchemy defaults differ from the raw API: status active, priority 1. + expect(rule.status).toEqual("active"); + expect(rule.priority).toEqual(1); + + const live = yield* getRule(zoneId, rule.pageRuleId); + expect(live.id).toEqual(rule.pageRuleId); + expect(targetOf(live)).toEqual(TARGET_DEFAULT); + expect(live.status).toEqual("active"); + const actionIds = live.actions.map((a) => a.id).sort(); + expect(actionIds).toEqual(["cache_level", "edge_cache_ttl"]); - expect(rule.pageRuleId).toBeDefined(); - expect(rule.zoneId).toEqual(zoneId); - expect(rule.target).toEqual(TARGET_DEFAULT); - // Alchemy defaults differ from the raw API: status active, priority 1. - expect(rule.status).toEqual("active"); - expect(rule.priority).toEqual(1); - - const live = yield* getRule(zoneId, rule.pageRuleId); - expect(live.id).toEqual(rule.pageRuleId); - expect(targetOf(live)).toEqual(TARGET_DEFAULT); - expect(live.status).toEqual("active"); - const actionIds = live.actions.map((a) => a.id).sort(); - expect(actionIds).toEqual(["cache_level", "edge_cache_ttl"]); - - yield* stack.destroy(); - - const gone = yield* findRule(zoneId, TARGET_DEFAULT); - expect(gone).toBeUndefined(); - }).pipe(logLevel), + yield* stack.destroy(); + + const gone = yield* findRule(zoneId, TARGET_DEFAULT); + expect(gone).toBeUndefined(); + }).pipe(logLevel), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "updating actions, status, priority and target syncs in place", (stack) => Effect.gen(function* () { @@ -209,7 +214,7 @@ test.provider( }).pipe(logLevel), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "adoption — existing rule errors without adopt, takes over with adopt(true)", (stack) => Effect.gen(function* () { @@ -284,36 +289,38 @@ test.provider( // every zone via `listAllZones` and lists each zone's Page Rules, hydrating // each into the same `Attributes` shape `read` returns. Deploy a rule with a // deterministic target, then assert it appears in the exhaustive result. -test.provider("list enumerates the deployed page rule", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed page rule", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - yield* stack.destroy(); - yield* purgeRules(zoneId, [TARGET_LIST]); + yield* stack.destroy(); + yield* purgeRules(zoneId, [TARGET_LIST]); - const rule = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.PageRule("ListRule", { - zoneId, - target: TARGET_LIST, - actions: [{ id: "cache_level", value: "cache_everything" }], - }).pipe(adopt(true)); - }), - ); + const rule = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.PageRule("ListRule", { + zoneId, + target: TARGET_LIST, + actions: [{ id: "cache_level", value: "cache_everything" }], + }).pipe(adopt(true)); + }), + ); - const provider = yield* Provider.findProvider(Cloudflare.PageRule); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider(Cloudflare.PageRule); + const all = yield* provider.list(); - expect(all.some((r) => r.pageRuleId === rule.pageRuleId)).toBe(true); - const found = all.find((r) => r.pageRuleId === rule.pageRuleId); - expect(found?.zoneId).toEqual(zoneId); - expect(found?.target).toEqual(TARGET_LIST); + expect(all.some((r) => r.pageRuleId === rule.pageRuleId)).toBe(true); + const found = all.find((r) => r.pageRuleId === rule.pageRuleId); + expect(found?.zoneId).toEqual(zoneId); + expect(found?.target).toEqual(TARGET_LIST); - yield* stack.destroy(); + yield* stack.destroy(); - const gone = yield* findRule(zoneId, TARGET_LIST); - expect(gone).toBeUndefined(); - }).pipe(logLevel), + const gone = yield* findRule(zoneId, TARGET_LIST); + expect(gone).toBeUndefined(); + }).pipe(logLevel), ); /** diff --git a/packages/alchemy/test/Cloudflare/PageShield/Policy.test.ts b/packages/alchemy/test/Cloudflare/PageShield/Policy.test.ts index 6ccda1304..f7e74c80e 100644 --- a/packages/alchemy/test/Cloudflare/PageShield/Policy.test.ts +++ b/packages/alchemy/test/Cloudflare/PageShield/Policy.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -62,7 +65,7 @@ const findPolicyByDescription = (zoneId: string, description: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed PolicyQuotaExceeded error on unentitled zones", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/PageShield/Settings.test.ts b/packages/alchemy/test/Cloudflare/PageShield/Settings.test.ts index c7c14021c..c3cc9e582 100644 --- a/packages/alchemy/test/Cloudflare/PageShield/Settings.test.ts +++ b/packages/alchemy/test/Cloudflare/PageShield/Settings.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -62,7 +65,7 @@ const setBaseline = (zoneId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "enables Page Shield, updates in place, and restores the baseline on destroy", (stack) => Effect.gen(function* () { @@ -137,7 +140,7 @@ test.provider( { timeout: 120_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed NotEntitled error for plan-gated flags", (stack) => Effect.gen(function* () { @@ -173,7 +176,7 @@ test.provider( // API for this per-zone configuration, so `list()` enumerates every zone via // `listAllZones` and reads the singleton in each. Assert the result is // non-empty and contains the standing test zone. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates Page Shield settings across all zones", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Pipelines/LegacyPipeline.test.ts b/packages/alchemy/test/Cloudflare/Pipelines/LegacyPipeline.test.ts index b8a6b91eb..eb4dc47a7 100644 --- a/packages/alchemy/test/Cloudflare/Pipelines/LegacyPipeline.test.ts +++ b/packages/alchemy/test/Cloudflare/Pipelines/LegacyPipeline.test.ts @@ -11,6 +11,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import crypto from "node:crypto"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -113,7 +116,7 @@ const legacy = ( return { bucket, pipeline }; }); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "legacy pipeline: create, in-place update, replace on name change", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Pipelines/Pipeline.test.ts b/packages/alchemy/test/Cloudflare/Pipelines/Pipeline.test.ts index db2591be2..031937eb6 100644 --- a/packages/alchemy/test/Cloudflare/Pipelines/Pipeline.test.ts +++ b/packages/alchemy/test/Cloudflare/Pipelines/Pipeline.test.ts @@ -11,6 +11,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import crypto from "node:crypto"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -73,7 +76,7 @@ const etl = (creds: { return { bucket, stream, sink, pipeline }; }); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed pipeline", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Pipelines/Pipelines.test.ts b/packages/alchemy/test/Cloudflare/Pipelines/Pipelines.test.ts index 637d29dc6..d8ba6b8c3 100644 --- a/packages/alchemy/test/Cloudflare/Pipelines/Pipelines.test.ts +++ b/packages/alchemy/test/Cloudflare/Pipelines/Pipelines.test.ts @@ -11,6 +11,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import crypto from "node:crypto"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -111,7 +114,7 @@ const expectPipelineGone = (accountId: string, pipelineId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "stream: create with defaults, patch http in place, replace on schema change", (stack) => Effect.gen(function* () { @@ -217,7 +220,7 @@ const etl = ( return { bucket, stream, sink, pipeline }; }); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "end-to-end: stream → sql pipeline → r2 sink, replacement on sink change", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Pipelines/Sink.test.ts b/packages/alchemy/test/Cloudflare/Pipelines/Sink.test.ts index 7392ca912..f5e7e1d64 100644 --- a/packages/alchemy/test/Cloudflare/Pipelines/Sink.test.ts +++ b/packages/alchemy/test/Cloudflare/Pipelines/Sink.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import crypto from "node:crypto"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -59,7 +62,7 @@ const r2Credentials = Effect.gen(function* () { }; }); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed sink", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Pipelines/Stream.test.ts b/packages/alchemy/test/Cloudflare/Pipelines/Stream.test.ts index 14c992f6c..653c0fe35 100644 --- a/packages/alchemy/test/Cloudflare/Pipelines/Stream.test.ts +++ b/packages/alchemy/test/Cloudflare/Pipelines/Stream.test.ts @@ -7,6 +7,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -25,7 +28,7 @@ const retryAuthBlip = (eff: Effect.Effect) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed pipeline stream", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Queue/Queue.test.ts b/packages/alchemy/test/Cloudflare/Queue/Queue.test.ts index cbc6a978e..3723838e8 100644 --- a/packages/alchemy/test/Cloudflare/Queue/Queue.test.ts +++ b/packages/alchemy/test/Cloudflare/Queue/Queue.test.ts @@ -11,6 +11,9 @@ import * as Effect from "effect/Effect"; import * as Exit from "effect/Exit"; import { MinimumLogLevel } from "effect/References"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const TEST_STAGE = "test"; @@ -69,81 +72,85 @@ const seedDevQueue = (input: { * observes that the cached id is not a real queue and creates one. The * resulting `queueId` must be a live id and resolvable on Cloudflare. */ -test.provider("promotes a dev queue to a live queue on deploy", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "promotes a dev queue to a live queue on deploy", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - yield* stack.destroy(); + yield* stack.destroy(); - const devQueueId = generateLocalId(); - yield* seedDevQueue({ - stackName: stack.name, - fqn: "Q", - queueId: devQueueId, - queueName: "dev-placeholder-name", - accountId, - }); - - const deployed = yield* stack.deploy( - Effect.gen(function* () { - const queue = yield* Cloudflare.Queue("Q"); - return { queue }; - }), - ); - - // The dev id has been replaced with a real Cloudflare queue id. - expect(isLiveId(deployed.queue.queueId)).toBe(true); - expect(deployed.queue.queueId).not.toEqual(devQueueId); - - // The promoted queue is a real, resolvable Cloudflare resource. - const live = yield* queues.getQueue({ - accountId, - queueId: deployed.queue.queueId, - }); - expect(live.queueId).toEqual(deployed.queue.queueId); - - // And the persisted state now carries the live id, not the dev one. - const persisted = yield* Effect.gen(function* () { - const state = yield* yield* State; - return yield* state.get({ - stack: stack.name, - stage: TEST_STAGE, + const devQueueId = generateLocalId(); + yield* seedDevQueue({ + stackName: stack.name, fqn: "Q", + queueId: devQueueId, + queueName: "dev-placeholder-name", + accountId, }); - }); - expect((persisted as any)?.attr?.queueId).toEqual(deployed.queue.queueId); - yield* stack.destroy(); + const deployed = yield* stack.deploy( + Effect.gen(function* () { + const queue = yield* Cloudflare.Queue("Q"); + return { queue }; + }), + ); + + // The dev id has been replaced with a real Cloudflare queue id. + expect(isLiveId(deployed.queue.queueId)).toBe(true); + expect(deployed.queue.queueId).not.toEqual(devQueueId); + + // The promoted queue is a real, resolvable Cloudflare resource. + const live = yield* queues.getQueue({ + accountId, + queueId: deployed.queue.queueId, + }); + expect(live.queueId).toEqual(deployed.queue.queueId); + + // And the persisted state now carries the live id, not the dev one. + const persisted = yield* Effect.gen(function* () { + const state = yield* yield* State; + return yield* state.get({ + stack: stack.name, + stage: TEST_STAGE, + fqn: "Q", + }); + }); + expect((persisted as any)?.attr?.queueId).toEqual(deployed.queue.queueId); + + yield* stack.destroy(); - // After destroy the promoted (live) queue is gone on Cloudflare. - const exit = yield* Effect.exit( - queues.getQueue({ accountId, queueId: deployed.queue.queueId }), - ); - expect(Exit.isFailure(exit)).toBe(true); - }).pipe(logLevel), + // After destroy the promoted (live) queue is gone on Cloudflare. + const exit = yield* Effect.exit( + queues.getQueue({ accountId, queueId: deployed.queue.queueId }), + ); + expect(Exit.isFailure(exit)).toBe(true); + }).pipe(logLevel), ); // Canonical `list()` test (account-scoped collection): deploy a real // queue, resolve the provider from context via `findProvider`, call // `list()`, and assert the deployed queue appears in the exhaustively- // paginated result. -test.provider("list enumerates the deployed queue", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed queue", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); - const deployed = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.Queue("ListQueue"); - }), - ); + const deployed = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.Queue("ListQueue"); + }), + ); - const provider = yield* Provider.findProvider(Cloudflare.Queue); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider(Cloudflare.Queue); + const all = yield* provider.list(); - expect(all.some((q) => q.queueId === deployed.queueId)).toBe(true); + expect(all.some((q) => q.queueId === deployed.queueId)).toBe(true); - yield* stack.destroy(); - }).pipe(logLevel), + yield* stack.destroy(); + }).pipe(logLevel), ); /** @@ -154,34 +161,36 @@ test.provider("list enumerates the deployed queue", (stack) => * malformed. The live provider's `delete` short-circuits on the non-live * id, so destroy succeeds and the state row is removed cleanly. */ -test.provider("suppresses deletion of a dev-only queue", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const devQueueId = generateLocalId(); - yield* seedDevQueue({ - stackName: stack.name, - fqn: "Q", - queueId: devQueueId, - queueName: "dev-placeholder-name", - accountId, - }); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "suppresses deletion of a dev-only queue", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - // Destroy must not attempt a (malformed) live delete against the dev id. - const exit = yield* Effect.exit(stack.destroy()); - expect(Exit.isSuccess(exit)).toBe(true); + yield* stack.destroy(); - // The dev-only resource is removed from state. - const persisted = yield* Effect.gen(function* () { - const state = yield* yield* State; - return yield* state.get({ - stack: stack.name, - stage: TEST_STAGE, + const devQueueId = generateLocalId(); + yield* seedDevQueue({ + stackName: stack.name, fqn: "Q", + queueId: devQueueId, + queueName: "dev-placeholder-name", + accountId, }); - }); - expect(persisted).toBeUndefined(); - }).pipe(logLevel), + + // Destroy must not attempt a (malformed) live delete against the dev id. + const exit = yield* Effect.exit(stack.destroy()); + expect(Exit.isSuccess(exit)).toBe(true); + + // The dev-only resource is removed from state. + const persisted = yield* Effect.gen(function* () { + const state = yield* yield* State; + return yield* state.get({ + stack: stack.name, + stage: TEST_STAGE, + fqn: "Q", + }); + }); + expect(persisted).toBeUndefined(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Queue/QueueConsumer.test.ts b/packages/alchemy/test/Cloudflare/Queue/QueueConsumer.test.ts index fe9abf3a2..79808d377 100644 --- a/packages/alchemy/test/Cloudflare/Queue/QueueConsumer.test.ts +++ b/packages/alchemy/test/Cloudflare/Queue/QueueConsumer.test.ts @@ -12,6 +12,9 @@ import * as Exit from "effect/Exit"; import { MinimumLogLevel } from "effect/References"; import * as pathe from "pathe"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -29,132 +32,134 @@ const main = pathe.resolve(import.meta.dirname, "consumer-worker.ts"); * issued every reconcile (so settings drift gets corrected even when * `olds.settings` matches `news.settings`). */ -test.provider("create, update settings, replace script, delete", (stack) => - Effect.gen(function* () { - const env = yield* CloudflareEnvironment; - const { accountId } = yield* env; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, update settings, replace script, delete", + (stack) => + Effect.gen(function* () { + const env = yield* CloudflareEnvironment; + const { accountId } = yield* env; - yield* stack.destroy(); + yield* stack.destroy(); - const initial = yield* stack.deploy( - Effect.gen(function* () { - const queue = yield* Cloudflare.Queue("Q"); - const workerA = yield* Cloudflare.Worker("WorkerA", { - main, - compatibility: { date: "2024-01-01" }, - }); - const consumer = yield* Cloudflare.QueueConsumer("Consumer", { - queueId: queue.queueId, - scriptName: workerA.workerName, - settings: { batchSize: 5, maxRetries: 3 }, - }); - return { queue, workerA, consumer }; - }), - ); - - expect(initial.consumer.consumerId).toBeTypeOf("string"); - expect(initial.consumer.scriptName).toEqual(initial.workerA.workerName); - - const live = yield* queues.getConsumer({ - accountId, - queueId: initial.queue.queueId, - consumerId: initial.consumer.consumerId, - }); - expect("scriptName" in live ? live.scriptName : undefined).toEqual( - initial.workerA.workerName, - ); - - // Settings-only change is an update, not a replace — consumerId - // must remain stable. - const updated = yield* stack.deploy( - Effect.gen(function* () { - const queue = yield* Cloudflare.Queue("Q"); - const workerA = yield* Cloudflare.Worker("WorkerA", { - main, - compatibility: { date: "2024-01-01" }, - }); - const consumer = yield* Cloudflare.QueueConsumer("Consumer", { - queueId: queue.queueId, - scriptName: workerA.workerName, - settings: { batchSize: 25, maxRetries: 7 }, - }); - return { queue, workerA, consumer }; - }), - ); - - expect(updated.consumer.consumerId).toEqual(initial.consumer.consumerId); - - const liveUpdated = yield* queues.getConsumer({ - accountId, - queueId: updated.queue.queueId, - consumerId: updated.consumer.consumerId, - }); - expect(liveUpdated.settings?.batchSize).toEqual(25); - expect(liveUpdated.settings?.maxRetries).toEqual(7); - - // Script change is a delete-first replace: Cloudflare's - // updateConsumer silently ignores script_name on an existing - // consumer, and the platform allows only one Worker consumer - // per queue, so the engine must tear the old consumer down - // before creating the new one. WorkerA stays yielded across - // the deploy so it isn't garbage-collected mid-replace and - // race the Worker.delete with Cloudflare's queue↔script sync. - const replaced = yield* stack.deploy( - Effect.gen(function* () { - const queue = yield* Cloudflare.Queue("Q"); - yield* Cloudflare.Worker("WorkerA", { - main, - compatibility: { date: "2024-01-01" }, - }); - const workerB = yield* Cloudflare.Worker("WorkerB", { - main, - compatibility: { date: "2024-01-01" }, - }); - const consumer = yield* Cloudflare.QueueConsumer("Consumer", { - queueId: queue.queueId, - scriptName: workerB.workerName, - settings: { batchSize: 25, maxRetries: 7 }, - }); - return { queue, workerB, consumer }; - }), - ); - - expect(replaced.consumer.consumerId).not.toEqual( - initial.consumer.consumerId, - ); - expect(replaced.consumer.scriptName).toEqual(replaced.workerB.workerName); - - const liveReplaced = yield* queues.getConsumer({ - accountId, - queueId: replaced.queue.queueId, - consumerId: replaced.consumer.consumerId, - }); - expect( - "scriptName" in liveReplaced ? liveReplaced.scriptName : undefined, - ).toEqual(replaced.workerB.workerName); - - // The original consumer must be gone after the replace. - const oldExit = yield* Effect.exit( - queues.getConsumer({ + const initial = yield* stack.deploy( + Effect.gen(function* () { + const queue = yield* Cloudflare.Queue("Q"); + const workerA = yield* Cloudflare.Worker("WorkerA", { + main, + compatibility: { date: "2024-01-01" }, + }); + const consumer = yield* Cloudflare.QueueConsumer("Consumer", { + queueId: queue.queueId, + scriptName: workerA.workerName, + settings: { batchSize: 5, maxRetries: 3 }, + }); + return { queue, workerA, consumer }; + }), + ); + + expect(initial.consumer.consumerId).toBeTypeOf("string"); + expect(initial.consumer.scriptName).toEqual(initial.workerA.workerName); + + const live = yield* queues.getConsumer({ accountId, - queueId: replaced.queue.queueId, + queueId: initial.queue.queueId, consumerId: initial.consumer.consumerId, - }), - ); - expect(Exit.isFailure(oldExit)).toBe(true); + }); + expect("scriptName" in live ? live.scriptName : undefined).toEqual( + initial.workerA.workerName, + ); + + // Settings-only change is an update, not a replace — consumerId + // must remain stable. + const updated = yield* stack.deploy( + Effect.gen(function* () { + const queue = yield* Cloudflare.Queue("Q"); + const workerA = yield* Cloudflare.Worker("WorkerA", { + main, + compatibility: { date: "2024-01-01" }, + }); + const consumer = yield* Cloudflare.QueueConsumer("Consumer", { + queueId: queue.queueId, + scriptName: workerA.workerName, + settings: { batchSize: 25, maxRetries: 7 }, + }); + return { queue, workerA, consumer }; + }), + ); - yield* stack.destroy(); + expect(updated.consumer.consumerId).toEqual(initial.consumer.consumerId); - // Post-destroy: the new consumer must be gone on Cloudflare too. - const exit = yield* Effect.exit( - queues.getConsumer({ + const liveUpdated = yield* queues.getConsumer({ + accountId, + queueId: updated.queue.queueId, + consumerId: updated.consumer.consumerId, + }); + expect(liveUpdated.settings?.batchSize).toEqual(25); + expect(liveUpdated.settings?.maxRetries).toEqual(7); + + // Script change is a delete-first replace: Cloudflare's + // updateConsumer silently ignores script_name on an existing + // consumer, and the platform allows only one Worker consumer + // per queue, so the engine must tear the old consumer down + // before creating the new one. WorkerA stays yielded across + // the deploy so it isn't garbage-collected mid-replace and + // race the Worker.delete with Cloudflare's queue↔script sync. + const replaced = yield* stack.deploy( + Effect.gen(function* () { + const queue = yield* Cloudflare.Queue("Q"); + yield* Cloudflare.Worker("WorkerA", { + main, + compatibility: { date: "2024-01-01" }, + }); + const workerB = yield* Cloudflare.Worker("WorkerB", { + main, + compatibility: { date: "2024-01-01" }, + }); + const consumer = yield* Cloudflare.QueueConsumer("Consumer", { + queueId: queue.queueId, + scriptName: workerB.workerName, + settings: { batchSize: 25, maxRetries: 7 }, + }); + return { queue, workerB, consumer }; + }), + ); + + expect(replaced.consumer.consumerId).not.toEqual( + initial.consumer.consumerId, + ); + expect(replaced.consumer.scriptName).toEqual(replaced.workerB.workerName); + + const liveReplaced = yield* queues.getConsumer({ accountId, queueId: replaced.queue.queueId, consumerId: replaced.consumer.consumerId, - }), - ); - expect(Exit.isFailure(exit)).toBe(true); - }).pipe(logLevel), + }); + expect( + "scriptName" in liveReplaced ? liveReplaced.scriptName : undefined, + ).toEqual(replaced.workerB.workerName); + + // The original consumer must be gone after the replace. + const oldExit = yield* Effect.exit( + queues.getConsumer({ + accountId, + queueId: replaced.queue.queueId, + consumerId: initial.consumer.consumerId, + }), + ); + expect(Exit.isFailure(oldExit)).toBe(true); + + yield* stack.destroy(); + + // Post-destroy: the new consumer must be gone on Cloudflare too. + const exit = yield* Effect.exit( + queues.getConsumer({ + accountId, + queueId: replaced.queue.queueId, + consumerId: replaced.consumer.consumerId, + }), + ); + expect(Exit.isFailure(exit)).toBe(true); + }).pipe(logLevel), ); /** @@ -168,68 +173,70 @@ test.provider("create, update settings, replace script, delete", (stack) => * `noop` and skips drift detection by design — drift correction * only happens when something the user-controlled changes). */ -test.provider("recreates consumer after out-of-band delete", (stack) => - Effect.gen(function* () { - const env = yield* CloudflareEnvironment; - const { accountId } = yield* env; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "recreates consumer after out-of-band delete", + (stack) => + Effect.gen(function* () { + const env = yield* CloudflareEnvironment; + const { accountId } = yield* env; - yield* stack.destroy(); + yield* stack.destroy(); - const initial = yield* stack.deploy( - Effect.gen(function* () { - const queue = yield* Cloudflare.Queue("Q"); - const worker = yield* Cloudflare.Worker("Worker", { - main, - compatibility: { date: "2024-01-01" }, - }); - const consumer = yield* Cloudflare.QueueConsumer("Consumer", { - queueId: queue.queueId, - scriptName: worker.workerName, - settings: { batchSize: 5 }, - }); - return { queue, worker, consumer }; - }), - ); - - // Out-of-band delete via the SDK directly. - yield* queues.deleteConsumer({ - accountId, - queueId: initial.queue.queueId, - consumerId: initial.consumer.consumerId, - }); - - const recovered = yield* stack.deploy( - Effect.gen(function* () { - const queue = yield* Cloudflare.Queue("Q"); - const worker = yield* Cloudflare.Worker("Worker", { - main, - compatibility: { date: "2024-01-01" }, - }); - const consumer = yield* Cloudflare.QueueConsumer("Consumer", { - queueId: queue.queueId, - scriptName: worker.workerName, - settings: { batchSize: 11 }, - }); - return { queue, worker, consumer }; - }), - ); - - expect(recovered.consumer.consumerId).toBeTypeOf("string"); - // The new consumer must be reachable on Cloudflare — the previous - // implementation died with "already exists but could not be found" - // because listConsumers was single-page and ConsumerAlreadyExists - // was caught by a generic `Effect.catch`. - const live = yield* queues.getConsumer({ - accountId, - queueId: recovered.queue.queueId, - consumerId: recovered.consumer.consumerId, - }); - expect("scriptName" in live ? live.scriptName : undefined).toEqual( - recovered.worker.workerName, - ); - - yield* stack.destroy(); - }).pipe(logLevel), + const initial = yield* stack.deploy( + Effect.gen(function* () { + const queue = yield* Cloudflare.Queue("Q"); + const worker = yield* Cloudflare.Worker("Worker", { + main, + compatibility: { date: "2024-01-01" }, + }); + const consumer = yield* Cloudflare.QueueConsumer("Consumer", { + queueId: queue.queueId, + scriptName: worker.workerName, + settings: { batchSize: 5 }, + }); + return { queue, worker, consumer }; + }), + ); + + // Out-of-band delete via the SDK directly. + yield* queues.deleteConsumer({ + accountId, + queueId: initial.queue.queueId, + consumerId: initial.consumer.consumerId, + }); + + const recovered = yield* stack.deploy( + Effect.gen(function* () { + const queue = yield* Cloudflare.Queue("Q"); + const worker = yield* Cloudflare.Worker("Worker", { + main, + compatibility: { date: "2024-01-01" }, + }); + const consumer = yield* Cloudflare.QueueConsumer("Consumer", { + queueId: queue.queueId, + scriptName: worker.workerName, + settings: { batchSize: 11 }, + }); + return { queue, worker, consumer }; + }), + ); + + expect(recovered.consumer.consumerId).toBeTypeOf("string"); + // The new consumer must be reachable on Cloudflare — the previous + // implementation died with "already exists but could not be found" + // because listConsumers was single-page and ConsumerAlreadyExists + // was caught by a generic `Effect.catch`. + const live = yield* queues.getConsumer({ + accountId, + queueId: recovered.queue.queueId, + consumerId: recovered.consumer.consumerId, + }); + expect("scriptName" in live ? live.scriptName : undefined).toEqual( + recovered.worker.workerName, + ); + + yield* stack.destroy(); + }).pipe(logLevel), ); /** @@ -239,68 +246,70 @@ test.provider("recreates consumer after out-of-band delete", (stack) => * `output.consumerId` is missing — so the consumer is adopted instead * of producing a duplicate-create attempt. */ -test.provider("adopts existing consumer after local state loss", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "adopts existing consumer after local state loss", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - yield* stack.destroy(); + yield* stack.destroy(); - const initial = yield* stack.deploy( - Effect.gen(function* () { - const queue = yield* Cloudflare.Queue("Q"); - const worker = yield* Cloudflare.Worker("Worker", { - main, - compatibility: { date: "2024-01-01" }, - }); - const consumer = yield* Cloudflare.QueueConsumer("Consumer", { - queueId: queue.queueId, - scriptName: worker.workerName, + const initial = yield* stack.deploy( + Effect.gen(function* () { + const queue = yield* Cloudflare.Queue("Q"); + const worker = yield* Cloudflare.Worker("Worker", { + main, + compatibility: { date: "2024-01-01" }, + }); + const consumer = yield* Cloudflare.QueueConsumer("Consumer", { + queueId: queue.queueId, + scriptName: worker.workerName, + }); + return { queue, worker, consumer }; + }), + ); + + // Wipe just the QueueConsumer entry — Queue and Worker stay so the + // redeploy reuses the same queueId / scriptName. + yield* Effect.gen(function* () { + const state = yield* yield* State; + yield* state.delete({ + stack: stack.name, + stage: "test", + fqn: "Consumer", }); - return { queue, worker, consumer }; - }), - ); - - // Wipe just the QueueConsumer entry — Queue and Worker stay so the - // redeploy reuses the same queueId / scriptName. - yield* Effect.gen(function* () { - const state = yield* yield* State; - yield* state.delete({ - stack: stack.name, - stage: "test", - fqn: "Consumer", + }).pipe(Effect.provide(stack.state)); + + const adopted = yield* stack.deploy( + Effect.gen(function* () { + const queue = yield* Cloudflare.Queue("Q"); + const worker = yield* Cloudflare.Worker("Worker", { + main, + compatibility: { date: "2024-01-01" }, + }); + const consumer = yield* Cloudflare.QueueConsumer("Consumer", { + queueId: queue.queueId, + scriptName: worker.workerName, + }); + return { queue, worker, consumer }; + }), + ); + + // Adoption: the consumerId from Cloudflare equals the one we + // created originally — we did not duplicate-create. + expect(adopted.consumer.consumerId).toEqual(initial.consumer.consumerId); + + const live = yield* queues.getConsumer({ + accountId, + queueId: adopted.queue.queueId, + consumerId: adopted.consumer.consumerId, }); - }).pipe(Effect.provide(stack.state)); + expect("scriptName" in live ? live.scriptName : undefined).toEqual( + adopted.worker.workerName, + ); - const adopted = yield* stack.deploy( - Effect.gen(function* () { - const queue = yield* Cloudflare.Queue("Q"); - const worker = yield* Cloudflare.Worker("Worker", { - main, - compatibility: { date: "2024-01-01" }, - }); - const consumer = yield* Cloudflare.QueueConsumer("Consumer", { - queueId: queue.queueId, - scriptName: worker.workerName, - }); - return { queue, worker, consumer }; - }), - ); - - // Adoption: the consumerId from Cloudflare equals the one we - // created originally — we did not duplicate-create. - expect(adopted.consumer.consumerId).toEqual(initial.consumer.consumerId); - - const live = yield* queues.getConsumer({ - accountId, - queueId: adopted.queue.queueId, - consumerId: adopted.consumer.consumerId, - }); - expect("scriptName" in live ? live.scriptName : undefined).toEqual( - adopted.worker.workerName, - ); - - yield* stack.destroy(); - }).pipe(logLevel), + yield* stack.destroy(); + }).pipe(logLevel), ); /** @@ -315,7 +324,7 @@ test.provider("adopts existing consumer after local state loss", (stack) => * a clear, actionable error naming both the existing script and the * desired one. */ -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "fails clearly when queue has consumer for different script", (stack) => Effect.gen(function* () { @@ -404,61 +413,63 @@ test.provider( * malformed. The live provider's `delete` short-circuits on the non-live * id, so destroy succeeds and the state row is removed cleanly. */ -test.provider("suppresses deletion of a dev-only consumer", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const devQueueId = generateLocalId(); - const devConsumerId = generateLocalId(); - - yield* Effect.gen(function* () { - const state = yield* yield* State; - yield* state.set({ - stack: stack.name, - stage: "test", - fqn: "Consumer", - value: { - kind: "resource", - status: "created", - resourceType: "Cloudflare.QueueConsumer", - namespace: undefined, +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "suppresses deletion of a dev-only consumer", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const devQueueId = generateLocalId(); + const devConsumerId = generateLocalId(); + + yield* Effect.gen(function* () { + const state = yield* yield* State; + yield* state.set({ + stack: stack.name, + stage: "test", fqn: "Consumer", - logicalId: "Consumer", - instanceId: "00000000000000000000000000000002", - providerVersion: 0, - bindings: [], - downstream: [], - props: { - queueId: devQueueId, - scriptName: "dev-worker", - }, - attr: { - consumerId: devConsumerId, - queueId: devQueueId, - scriptName: "dev-worker", - accountId, - }, - } satisfies CreatedResourceState, + value: { + kind: "resource", + status: "created", + resourceType: "Cloudflare.QueueConsumer", + namespace: undefined, + fqn: "Consumer", + logicalId: "Consumer", + instanceId: "00000000000000000000000000000002", + providerVersion: 0, + bindings: [], + downstream: [], + props: { + queueId: devQueueId, + scriptName: "dev-worker", + }, + attr: { + consumerId: devConsumerId, + queueId: devQueueId, + scriptName: "dev-worker", + accountId, + }, + } satisfies CreatedResourceState, + }); }); - }); - - // Destroy must not attempt a (malformed) live delete against the dev ids. - const exit = yield* Effect.exit(stack.destroy()); - expect(Exit.isSuccess(exit)).toBe(true); - - // The dev-only resource is removed from state. - const persisted = yield* Effect.gen(function* () { - const state = yield* yield* State; - return yield* state.get({ - stack: stack.name, - stage: "test", - fqn: "Consumer", + + // Destroy must not attempt a (malformed) live delete against the dev ids. + const exit = yield* Effect.exit(stack.destroy()); + expect(Exit.isSuccess(exit)).toBe(true); + + // The dev-only resource is removed from state. + const persisted = yield* Effect.gen(function* () { + const state = yield* yield* State; + return yield* state.get({ + stack: stack.name, + stage: "test", + fqn: "Consumer", + }); }); - }); - expect(persisted).toBeUndefined(); - }).pipe(logLevel), + expect(persisted).toBeUndefined(); + }).pipe(logLevel), ); /** @@ -474,75 +485,77 @@ test.provider("suppresses deletion of a dev-only consumer", (stack) => * (not `noop`), so `reconcile` runs, re-observes the live consumer, and * the persisted id is healed back to the real one. */ -test.provider("promotes a dev consumer to a live consumer on deploy", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "promotes a dev consumer to a live consumer on deploy", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - yield* stack.destroy(); + yield* stack.destroy(); - const buildStack = Effect.gen(function* () { - const queue = yield* Cloudflare.Queue("Q"); - const worker = yield* Cloudflare.Worker("Worker", { - main, - compatibility: { date: "2024-01-01" }, - }); - const consumer = yield* Cloudflare.QueueConsumer("Consumer", { - queueId: queue.queueId, - scriptName: worker.workerName, - settings: { batchSize: 5 }, + const buildStack = Effect.gen(function* () { + const queue = yield* Cloudflare.Queue("Q"); + const worker = yield* Cloudflare.Worker("Worker", { + main, + compatibility: { date: "2024-01-01" }, + }); + const consumer = yield* Cloudflare.QueueConsumer("Consumer", { + queueId: queue.queueId, + scriptName: worker.workerName, + settings: { batchSize: 5 }, + }); + return { queue, worker, consumer }; }); - return { queue, worker, consumer }; - }); - - const initial = yield* stack.deploy(buildStack); - expect(isLiveId(initial.consumer.consumerId)).toBe(true); - - // Rewrite the persisted consumerId back to a dev id, simulating a - // consumer that was minted in `alchemy dev`. - const devQueueId = generateLocalId(); - const devConsumerId = generateLocalId(); - yield* Effect.gen(function* () { - const state = yield* yield* State; - const currentConsumer = (yield* state.get({ - stack: stack.name, - stage: "test", - fqn: "Consumer", - })) as CreatedResourceState; - yield* state.set({ - stack: stack.name, - stage: "test", - fqn: "Consumer", - value: { - ...currentConsumer, - attr: { - ...currentConsumer.attr, - queueId: devQueueId, - consumerId: devConsumerId, + + const initial = yield* stack.deploy(buildStack); + expect(isLiveId(initial.consumer.consumerId)).toBe(true); + + // Rewrite the persisted consumerId back to a dev id, simulating a + // consumer that was minted in `alchemy dev`. + const devQueueId = generateLocalId(); + const devConsumerId = generateLocalId(); + yield* Effect.gen(function* () { + const state = yield* yield* State; + const currentConsumer = (yield* state.get({ + stack: stack.name, + stage: "test", + fqn: "Consumer", + })) as CreatedResourceState; + yield* state.set({ + stack: stack.name, + stage: "test", + fqn: "Consumer", + value: { + ...currentConsumer, + attr: { + ...currentConsumer.attr, + queueId: devQueueId, + consumerId: devConsumerId, + }, }, - }, + }); + }); + + const promoted = yield* stack.deploy(buildStack); + + // The dev id was promoted back to the real, live consumer id. + expect(isLiveId(promoted.consumer.consumerId)).toBe(true); + expect(isLiveId(promoted.queue.queueId)).toBe(true); + expect(promoted.consumer.consumerId).not.toEqual(devConsumerId); + expect(promoted.consumer.consumerId).toEqual(initial.consumer.consumerId); + expect(promoted.queue.queueId).toEqual(initial.queue.queueId); + + const live = yield* queues.getConsumer({ + accountId, + queueId: promoted.queue.queueId, + consumerId: promoted.consumer.consumerId, }); - }); - - const promoted = yield* stack.deploy(buildStack); - - // The dev id was promoted back to the real, live consumer id. - expect(isLiveId(promoted.consumer.consumerId)).toBe(true); - expect(isLiveId(promoted.queue.queueId)).toBe(true); - expect(promoted.consumer.consumerId).not.toEqual(devConsumerId); - expect(promoted.consumer.consumerId).toEqual(initial.consumer.consumerId); - expect(promoted.queue.queueId).toEqual(initial.queue.queueId); - - const live = yield* queues.getConsumer({ - accountId, - queueId: promoted.queue.queueId, - consumerId: promoted.consumer.consumerId, - }); - expect("scriptName" in live ? live.scriptName : undefined).toEqual( - promoted.worker.workerName, - ); - - yield* stack.destroy(); - }).pipe(logLevel), + expect("scriptName" in live ? live.scriptName : undefined).toEqual( + promoted.worker.workerName, + ); + + yield* stack.destroy(); + }).pipe(logLevel), ); /** @@ -552,37 +565,39 @@ test.provider("promotes a dev consumer to a live consumer on deploy", (stack) => * consumer, then assert the consumer is present in the exhaustively- * paginated result, hydrated into the same `Attributes` shape `read` returns. */ -test.provider("list enumerates the deployed consumer", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed consumer", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); - const deployed = yield* stack.deploy( - Effect.gen(function* () { - const queue = yield* Cloudflare.Queue("Q"); - const worker = yield* Cloudflare.Worker("Worker", { - main, - compatibility: { date: "2024-01-01" }, - }); - const consumer = yield* Cloudflare.QueueConsumer("Consumer", { - queueId: queue.queueId, - scriptName: worker.workerName, - settings: { batchSize: 7 }, - }); - return { queue, worker, consumer }; - }), - ); - - const provider = yield* Provider.findProvider(Cloudflare.QueueConsumer); - const all = yield* provider.list(); - - const found = all.find( - (c) => c.consumerId === deployed.consumer.consumerId, - ); - expect(found).toBeDefined(); - expect(found?.queueId).toEqual(deployed.queue.queueId); - expect(found?.scriptName).toEqual(deployed.worker.workerName); - expect(found?.accountId).toBeTypeOf("string"); - - yield* stack.destroy(); - }).pipe(logLevel), + const deployed = yield* stack.deploy( + Effect.gen(function* () { + const queue = yield* Cloudflare.Queue("Q"); + const worker = yield* Cloudflare.Worker("Worker", { + main, + compatibility: { date: "2024-01-01" }, + }); + const consumer = yield* Cloudflare.QueueConsumer("Consumer", { + queueId: queue.queueId, + scriptName: worker.workerName, + settings: { batchSize: 7 }, + }); + return { queue, worker, consumer }; + }), + ); + + const provider = yield* Provider.findProvider(Cloudflare.QueueConsumer); + const all = yield* provider.list(); + + const found = all.find( + (c) => c.consumerId === deployed.consumer.consumerId, + ); + expect(found).toBeDefined(); + expect(found?.queueId).toEqual(deployed.queue.queueId); + expect(found?.scriptName).toEqual(deployed.worker.workerName); + expect(found?.accountId).toBeTypeOf("string"); + + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Queue/RoundTrip.test.ts b/packages/alchemy/test/Cloudflare/Queue/RoundTrip.test.ts index 01031bdc5..2033ffff1 100644 --- a/packages/alchemy/test/Cloudflare/Queue/RoundTrip.test.ts +++ b/packages/alchemy/test/Cloudflare/Queue/RoundTrip.test.ts @@ -10,6 +10,9 @@ import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"; import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"; import QueueWorker, { RoundTripQueue } from "./round-trip-worker.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -32,7 +35,7 @@ class CountMismatch extends Data.TaggedError("CountMismatch")<{ * - `RoundTripQueue` (Cloudflare.Queue). * - `QueueRoundTripWorker` — exposes: * - `POST /send?name=K` → enqueues a message via the - * `Cloudflare.QueueBinding` producer. + * `Cloudflare.Queues.WriteQueue` producer. * - subscribe handler → increments the named Counter DO * and stores the body, via * `Cloudflare.messages(RoundTripQueue).subscribe(...)`. @@ -48,7 +51,7 @@ class CountMismatch extends Data.TaggedError("CountMismatch")<{ * RPC stub from inside the queue handler works, and the test * client can read the resulting DO state. */ -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "send → subscribe handler → DO state → polled by test client", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Queue/Subscription.test.ts b/packages/alchemy/test/Cloudflare/Queue/Subscription.test.ts index f203e3218..bf49f57e9 100644 --- a/packages/alchemy/test/Cloudflare/Queue/Subscription.test.ts +++ b/packages/alchemy/test/Cloudflare/Queue/Subscription.test.ts @@ -9,6 +9,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -47,7 +50,7 @@ const expectGone = (accountId: string, subscriptionId: string) => ); describe.sequential("Subscription", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create r2 event subscription into a queue and destroy it", (stack) => Effect.gen(function* () { @@ -105,7 +108,7 @@ describe.sequential("Subscription", () => { }).pipe(logLevel), ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "update mutable props in place (same subscriptionId)", (stack) => Effect.gen(function* () { @@ -192,107 +195,111 @@ describe.sequential("Subscription", () => { }).pipe(logLevel), ); - test.provider("replaces the subscription when the source changes", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const initial = yield* stack.deploy( - Effect.gen(function* () { - const queue = yield* Cloudflare.Queue("SubQueueR", { - name: "alchemy-test-sub-queue-r", - }); - const subscription = yield* Cloudflare.QueueSubscription( - "ReplaceSub", - { - source: { type: "kv" }, - events: ["namespace.created"], - queueId: queue.queueId, - }, - ); - return { queue, subscription }; - }), - ); - - expect(initial.subscription.source).toEqual({ type: "kv" }); - - const replaced = yield* stack.deploy( - Effect.gen(function* () { - const queue = yield* Cloudflare.Queue("SubQueueR", { - name: "alchemy-test-sub-queue-r", - }); - const subscription = yield* Cloudflare.QueueSubscription( - "ReplaceSub", - { - source: { type: "r2" }, - events: ["bucket.created"], - queueId: queue.queueId, - }, - ); - return { queue, subscription }; - }), - ); - - // The source is fixed at creation — changing it produces a new - // subscription identity. - expect(replaced.subscription.subscriptionId).not.toEqual( - initial.subscription.subscriptionId, - ); - expect(replaced.subscription.source).toEqual({ type: "r2" }); - - // The old subscription is gone after the replacement settles. - yield* expectGone(accountId, initial.subscription.subscriptionId); - - const live = yield* getSubscription( - accountId, - replaced.subscription.subscriptionId, - ); - expect(live.events).toEqual(["bucket.created"]); - - yield* stack.destroy(); - - yield* expectGone(accountId, replaced.subscription.subscriptionId); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "replaces the subscription when the source changes", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const initial = yield* stack.deploy( + Effect.gen(function* () { + const queue = yield* Cloudflare.Queue("SubQueueR", { + name: "alchemy-test-sub-queue-r", + }); + const subscription = yield* Cloudflare.QueueSubscription( + "ReplaceSub", + { + source: { type: "kv" }, + events: ["namespace.created"], + queueId: queue.queueId, + }, + ); + return { queue, subscription }; + }), + ); + + expect(initial.subscription.source).toEqual({ type: "kv" }); + + const replaced = yield* stack.deploy( + Effect.gen(function* () { + const queue = yield* Cloudflare.Queue("SubQueueR", { + name: "alchemy-test-sub-queue-r", + }); + const subscription = yield* Cloudflare.QueueSubscription( + "ReplaceSub", + { + source: { type: "r2" }, + events: ["bucket.created"], + queueId: queue.queueId, + }, + ); + return { queue, subscription }; + }), + ); + + // The source is fixed at creation — changing it produces a new + // subscription identity. + expect(replaced.subscription.subscriptionId).not.toEqual( + initial.subscription.subscriptionId, + ); + expect(replaced.subscription.source).toEqual({ type: "r2" }); + + // The old subscription is gone after the replacement settles. + yield* expectGone(accountId, initial.subscription.subscriptionId); + + const live = yield* getSubscription( + accountId, + replaced.subscription.subscriptionId, + ); + expect(live.events).toEqual(["bucket.created"]); + + yield* stack.destroy(); + + yield* expectGone(accountId, replaced.subscription.subscriptionId); + }).pipe(logLevel), ); // Canonical `list()` test (account collection): deploy a subscription, // then enumerate every subscription in the account via the typed provider // and assert the deployed one is present in the exhaustively-paginated // result. - test.provider("list enumerates the deployed subscription", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const deployed = yield* stack.deploy( - Effect.gen(function* () { - const queue = yield* Cloudflare.Queue("ListSubQueue", { - name: "alchemy-test-list-sub-queue", - }); - const subscription = yield* Cloudflare.QueueSubscription( - "ListR2Events", - { - source: { type: "r2" }, - events: ["bucket.created"], - queueId: queue.queueId, - }, - ); - return { queue, subscription }; - }), - ); - - const provider = yield* Provider.findProvider( - Cloudflare.QueueSubscription, - ); - const all = yield* provider.list(); - - expect( - all.some( - (s) => s.subscriptionId === deployed.subscription.subscriptionId, - ), - ).toBe(true); - - yield* stack.destroy(); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed subscription", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const deployed = yield* stack.deploy( + Effect.gen(function* () { + const queue = yield* Cloudflare.Queue("ListSubQueue", { + name: "alchemy-test-list-sub-queue", + }); + const subscription = yield* Cloudflare.QueueSubscription( + "ListR2Events", + { + source: { type: "r2" }, + events: ["bucket.created"], + queueId: queue.queueId, + }, + ); + return { queue, subscription }; + }), + ); + + const provider = yield* Provider.findProvider( + Cloudflare.QueueSubscription, + ); + const all = yield* provider.list(); + + expect( + all.some( + (s) => s.subscriptionId === deployed.subscription.subscriptionId, + ), + ).toBe(true); + + yield* stack.destroy(); + }).pipe(logLevel), ); }); diff --git a/packages/alchemy/test/Cloudflare/Queue/round-trip-worker.ts b/packages/alchemy/test/Cloudflare/Queue/round-trip-worker.ts index 3a56f8223..32bdc51fa 100644 --- a/packages/alchemy/test/Cloudflare/Queue/round-trip-worker.ts +++ b/packages/alchemy/test/Cloudflare/Queue/round-trip-worker.ts @@ -61,7 +61,7 @@ export default class QueueWorker extends Cloudflare.Worker()( Effect.gen(function* () { const counters = yield* Counter; const queueResource = yield* RoundTripQueue; - const queue = yield* Cloudflare.QueueBinding.bind(queueResource); + const queue = yield* Cloudflare.Queues.WriteQueue(queueResource); // Effect-style queue consumer. The handler delegates to the // Counter DO so the test can verify the message landed by @@ -110,7 +110,7 @@ export default class QueueWorker extends Cloudflare.Worker()( }), }; }).pipe( - Effect.provide(Cloudflare.QueueBindingLive), + Effect.provide(Cloudflare.Queues.WriteQueueBinding), Effect.provide(Cloudflare.QueueEventSourceLive), ), ) {} diff --git a/packages/alchemy/test/Cloudflare/R2/Bucket.test.ts b/packages/alchemy/test/Cloudflare/R2/Bucket.test.ts index 47234b259..3ab59be59 100644 --- a/packages/alchemy/test/Cloudflare/R2/Bucket.test.ts +++ b/packages/alchemy/test/Cloudflare/R2/Bucket.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -16,82 +19,86 @@ const logLevel = Effect.provideService( process.env.DEBUG ? "Debug" : "Info", ); -test.provider("create and delete bucket with default props", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create and delete bucket with default props", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - yield* stack.destroy(); + yield* stack.destroy(); - const bucket = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.R2Bucket("DefaultBucket"); - }), - ); + const bucket = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.R2Bucket("DefaultBucket"); + }), + ); - expect(bucket.bucketName).toBeDefined(); - expect(bucket.storageClass).toEqual("Standard"); - expect(bucket.jurisdiction).toEqual("default"); + expect(bucket.bucketName).toBeDefined(); + expect(bucket.storageClass).toEqual("Standard"); + expect(bucket.jurisdiction).toEqual("default"); - const actualBucket = yield* getBucketWhenReady( - bucket.bucketName, - accountId, - ); - expect(actualBucket.name).toEqual(bucket.bucketName); + const actualBucket = yield* getBucketWhenReady( + bucket.bucketName, + accountId, + ); + expect(actualBucket.name).toEqual(bucket.bucketName); - yield* stack.destroy(); + yield* stack.destroy(); - yield* waitForBucketToBeDeleted(bucket.bucketName, accountId); - }).pipe(logLevel), + yield* waitForBucketToBeDeleted(bucket.bucketName, accountId); + }).pipe(logLevel), ); -test.provider("create, update, delete bucket", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, update, delete bucket", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - yield* stack.destroy(); + yield* stack.destroy(); - const bucket = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.R2Bucket("TestBucket", { - name: "test-bucket-initial", - storageClass: "Standard", - }); - }), - ); + const bucket = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.R2Bucket("TestBucket", { + name: "test-bucket-initial", + storageClass: "Standard", + }); + }), + ); - const actualBucket = yield* getBucketWhenReady( - bucket.bucketName, - accountId, - ); - expect(actualBucket.name).toEqual(bucket.bucketName); - expect(actualBucket.storageClass).toEqual("Standard"); + const actualBucket = yield* getBucketWhenReady( + bucket.bucketName, + accountId, + ); + expect(actualBucket.name).toEqual(bucket.bucketName); + expect(actualBucket.storageClass).toEqual("Standard"); - const updatedBucket = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.R2Bucket("TestBucket", { - name: "test-bucket-initial", - storageClass: "InfrequentAccess", - }); - }), - ); + const updatedBucket = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.R2Bucket("TestBucket", { + name: "test-bucket-initial", + storageClass: "InfrequentAccess", + }); + }), + ); - const actualUpdatedBucket = yield* getBucketWhenReady( - updatedBucket.bucketName, - accountId, - ); - expect(actualUpdatedBucket.name).toEqual(updatedBucket.bucketName); - expect(actualUpdatedBucket.storageClass).toEqual("InfrequentAccess"); + const actualUpdatedBucket = yield* getBucketWhenReady( + updatedBucket.bucketName, + accountId, + ); + expect(actualUpdatedBucket.name).toEqual(updatedBucket.bucketName); + expect(actualUpdatedBucket.storageClass).toEqual("InfrequentAccess"); - yield* stack.destroy(); + yield* stack.destroy(); - yield* waitForBucketToBeDeleted(bucket.bucketName, accountId); - }).pipe(logLevel), + yield* waitForBucketToBeDeleted(bucket.bucketName, accountId); + }).pipe(logLevel), ); // Engine-level adoption: R2 buckets have no ownership signal (Cloudflare // doesn't expose tags on R2 buckets), so a name match in `read` is treated // as silent adoption. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "existing bucket (matching name) is silently adopted without --adopt", (stack) => Effect.gen(function* () { @@ -152,146 +159,150 @@ test.provider( }).pipe(logLevel), ); -test.provider("destroying a bucket empties its objects first", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const bucket = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.R2Bucket("BucketWithObjects"); - }), - ); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "destroying a bucket empties its objects first", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - const putObject = (key: string, body: string) => - r2.putObject({ - accountId, - bucketName: bucket.bucketName, - objectName: key, - contentType: "text/plain", - body: new Blob([body], { type: "text/plain" }), - }); - yield* putObject("hello.txt", "hello"); - yield* putObject("nested/world.txt", "world"); + yield* stack.destroy(); - const before = yield* r2 - .listObjects({ - accountId, - bucketName: bucket.bucketName, - perPage: 1000, - }) - .pipe( - Effect.flatMap((page) => { - const keys = (page.result ?? []) - .map((o) => o.key) - .filter((k): k is string => typeof k === "string"); - return keys.length === 2 - ? Effect.succeed(keys) - : Effect.fail(new ListLagError()); - }), - Effect.retry({ - while: (e): e is ListLagError => e instanceof ListLagError, - schedule: Schedule.exponential(200).pipe( - Schedule.both(Schedule.recurs(8)), - ), + const bucket = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.R2Bucket("BucketWithObjects"); }), ); - expect(before.sort()).toEqual(["hello.txt", "nested/world.txt"]); - yield* stack.destroy(); + const putObject = (key: string, body: string) => + r2.putObject({ + accountId, + bucketName: bucket.bucketName, + objectName: key, + contentType: "text/plain", + body: new Blob([body], { type: "text/plain" }), + }); + yield* putObject("hello.txt", "hello"); + yield* putObject("nested/world.txt", "world"); + + const before = yield* r2 + .listObjects({ + accountId, + bucketName: bucket.bucketName, + perPage: 1000, + }) + .pipe( + Effect.flatMap((page) => { + const keys = (page.result ?? []) + .map((o) => o.key) + .filter((k): k is string => typeof k === "string"); + return keys.length === 2 + ? Effect.succeed(keys) + : Effect.fail(new ListLagError()); + }), + Effect.retry({ + while: (e): e is ListLagError => e instanceof ListLagError, + schedule: Schedule.exponential(200).pipe( + Schedule.both(Schedule.recurs(8)), + ), + }), + ); + expect(before.sort()).toEqual(["hello.txt", "nested/world.txt"]); + + yield* stack.destroy(); - yield* waitForBucketToBeDeleted(bucket.bucketName, accountId); - }).pipe(logLevel), + yield* waitForBucketToBeDeleted(bucket.bucketName, accountId); + }).pipe(logLevel), ); -test.provider("lifecycle rules are added, updated, and removed", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - // Create with one rule. - const initial = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.R2Bucket("LifecycleBucket", { - lifecycleRules: [ - { - id: "expire-after-30d", - deleteObjectsTransition: { - condition: { type: "Age", maxAge: 60 * 60 * 24 * 30 }, - }, - }, - ], - }); - }), - ); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "lifecycle rules are added, updated, and removed", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - const initialRules = yield* r2.getBucketLifecycle({ - accountId, - bucketName: initial.bucketName, - }); - expect(initialRules.rules).toHaveLength(1); - expect(initialRules.rules?.[0]?.id).toEqual("expire-after-30d"); - expect(initialRules.rules?.[0]?.enabled).toEqual(true); - expect(initialRules.rules?.[0]?.deleteObjectsTransition?.condition).toEqual( - { type: "Age", maxAge: 60 * 60 * 24 * 30 }, - ); + yield* stack.destroy(); - // Update: change the prefix and add a storage class transition. - yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.R2Bucket("LifecycleBucket", { - lifecycleRules: [ - { - id: "expire-after-30d", - prefix: "logs/", - storageClassTransitions: [ - { - condition: { type: "Age", maxAge: 60 * 60 * 24 * 7 }, - storageClass: "InfrequentAccess", + // Create with one rule. + const initial = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.R2Bucket("LifecycleBucket", { + lifecycleRules: [ + { + id: "expire-after-30d", + deleteObjectsTransition: { + condition: { type: "Age", maxAge: 60 * 60 * 24 * 30 }, }, - ], - deleteObjectsTransition: { - condition: { type: "Age", maxAge: 60 * 60 * 24 * 30 }, }, - }, - ], - }); - }), - ); + ], + }); + }), + ); - const updatedRules = yield* r2.getBucketLifecycle({ - accountId, - bucketName: initial.bucketName, - }); - expect(updatedRules.rules).toHaveLength(1); - expect(updatedRules.rules?.[0]?.conditions.prefix).toEqual("logs/"); - expect(updatedRules.rules?.[0]?.storageClassTransitions).toEqual([ - { - condition: { type: "Age", maxAge: 60 * 60 * 24 * 7 }, - storageClass: "InfrequentAccess", - }, - ]); - - // Clear all rules. - yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.R2Bucket("LifecycleBucket", { - lifecycleRules: [], - }); - }), - ); + const initialRules = yield* r2.getBucketLifecycle({ + accountId, + bucketName: initial.bucketName, + }); + expect(initialRules.rules).toHaveLength(1); + expect(initialRules.rules?.[0]?.id).toEqual("expire-after-30d"); + expect(initialRules.rules?.[0]?.enabled).toEqual(true); + expect( + initialRules.rules?.[0]?.deleteObjectsTransition?.condition, + ).toEqual({ type: "Age", maxAge: 60 * 60 * 24 * 30 }); + + // Update: change the prefix and add a storage class transition. + yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.R2Bucket("LifecycleBucket", { + lifecycleRules: [ + { + id: "expire-after-30d", + prefix: "logs/", + storageClassTransitions: [ + { + condition: { type: "Age", maxAge: 60 * 60 * 24 * 7 }, + storageClass: "InfrequentAccess", + }, + ], + deleteObjectsTransition: { + condition: { type: "Age", maxAge: 60 * 60 * 24 * 30 }, + }, + }, + ], + }); + }), + ); - const clearedRules = yield* r2.getBucketLifecycle({ - accountId, - bucketName: initial.bucketName, - }); - expect(clearedRules.rules ?? []).toEqual([]); + const updatedRules = yield* r2.getBucketLifecycle({ + accountId, + bucketName: initial.bucketName, + }); + expect(updatedRules.rules).toHaveLength(1); + expect(updatedRules.rules?.[0]?.conditions.prefix).toEqual("logs/"); + expect(updatedRules.rules?.[0]?.storageClassTransitions).toEqual([ + { + condition: { type: "Age", maxAge: 60 * 60 * 24 * 7 }, + storageClass: "InfrequentAccess", + }, + ]); - yield* stack.destroy(); - yield* waitForBucketToBeDeleted(initial.bucketName, accountId); - }).pipe(logLevel), + // Clear all rules. + yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.R2Bucket("LifecycleBucket", { + lifecycleRules: [], + }); + }), + ); + + const clearedRules = yield* r2.getBucketLifecycle({ + accountId, + bucketName: initial.bucketName, + }); + expect(clearedRules.rules ?? []).toEqual([]); + + yield* stack.destroy(); + yield* waitForBucketToBeDeleted(initial.bucketName, accountId); + }).pipe(logLevel), ); // R2 bucket creates are eventually consistent — a read immediately after diff --git a/packages/alchemy/test/Cloudflare/R2/BucketEventNotification.test.ts b/packages/alchemy/test/Cloudflare/R2/BucketEventNotification.test.ts index f444ef738..00fb456b3 100644 --- a/packages/alchemy/test/Cloudflare/R2/BucketEventNotification.test.ts +++ b/packages/alchemy/test/Cloudflare/R2/BucketEventNotification.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -98,7 +101,7 @@ const replacementProgram = (opts: { return { bucket, queueA, queueB, notification }; }); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create, update rules in place, and delete", (stack) => Effect.gen(function* () { @@ -228,7 +231,7 @@ test.provider( { timeout: 300_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "changing the queue triggers a replacement", (stack) => Effect.gen(function* () { @@ -281,7 +284,7 @@ test.provider( { timeout: 300_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates deployed bucket event notifications", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/R2/BucketSippy.test.ts b/packages/alchemy/test/Cloudflare/R2/BucketSippy.test.ts index cbb357554..e9768cd14 100644 --- a/packages/alchemy/test/Cloudflare/R2/BucketSippy.test.ts +++ b/packages/alchemy/test/Cloudflare/R2/BucketSippy.test.ts @@ -9,6 +9,9 @@ import * as Redacted from "effect/Redacted"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -75,7 +78,7 @@ const program = (opts: { sippy: boolean }) => // Ungated — exercises everything Sippy exposes without external creds: // the disabled baseline read, idempotent disable, and the typed failure // tags the provider's lifecycle operations rely on. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "reads the disabled baseline and surfaces typed errors without external creds", (stack) => Effect.gen(function* () { @@ -142,7 +145,7 @@ test.provider( // no bucket has Sippy enabled, so the enumeration is a well-typed array that // must NOT contain the freshly-deployed (Sippy-disabled) bucket. This still // exercises the bucket fan-out + per-item typed skip end-to-end. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates buckets that have Sippy enabled", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/R2/CustomDomain.test.ts b/packages/alchemy/test/Cloudflare/R2/CustomDomain.test.ts index 3aa4283c1..31d6a711a 100644 --- a/packages/alchemy/test/Cloudflare/R2/CustomDomain.test.ts +++ b/packages/alchemy/test/Cloudflare/R2/CustomDomain.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers(), state: Cloudflare.state(), @@ -36,63 +39,65 @@ const domain3 = zoneName ? `alchemy-r2-test-multi-b-${suffix}.${zoneName}` : undefined; -test.provider("creates, updates, and deletes a bucket custom domain", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const bucket = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.R2Bucket("DomainBucket", { - domains: [{ name: domain! }], - }); - }), - ); - - expect(bucket.domains).toHaveLength(1); - expect(bucket.domains[0]?.domain).toEqual(domain); - expect(bucket.domains[0]?.enabled).toEqual(true); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "creates, updates, and deletes a bucket custom domain", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - const actual = yield* r2.getBucketDomainCustom({ - accountId, - bucketName: bucket.bucketName, - domain: domain!, - jurisdiction: bucket.jurisdiction, - }); - expect(actual.domain).toEqual(domain); - - const updated = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.R2Bucket("DomainBucket", { - domains: [{ name: domain!, enabled: false }], - }); - }), - ); + yield* stack.destroy(); - expect(updated.domains[0]?.enabled).toEqual(false); + const bucket = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.R2Bucket("DomainBucket", { + domains: [{ name: domain! }], + }); + }), + ); - yield* stack.destroy(); + expect(bucket.domains).toHaveLength(1); + expect(bucket.domains[0]?.domain).toEqual(domain); + expect(bucket.domains[0]?.enabled).toEqual(true); - const deleted = yield* r2 - .getBucketDomainCustom({ + const actual = yield* r2.getBucketDomainCustom({ accountId, bucketName: bucket.bucketName, domain: domain!, jurisdiction: bucket.jurisdiction, - }) - .pipe( - Effect.map(() => false), - Effect.catchTag("DomainNotFound", () => Effect.succeed(true)), - Effect.catchTag("NoSuchBucket", () => Effect.succeed(true)), + }); + expect(actual.domain).toEqual(domain); + + const updated = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.R2Bucket("DomainBucket", { + domains: [{ name: domain!, enabled: false }], + }); + }), ); - expect(deleted).toEqual(true); - yield* waitForBucketToBeDeleted(bucket.bucketName, accountId); - }).pipe(logLevel), + expect(updated.domains[0]?.enabled).toEqual(false); + + yield* stack.destroy(); + + const deleted = yield* r2 + .getBucketDomainCustom({ + accountId, + bucketName: bucket.bucketName, + domain: domain!, + jurisdiction: bucket.jurisdiction, + }) + .pipe( + Effect.map(() => false), + Effect.catchTag("DomainNotFound", () => Effect.succeed(true)), + Effect.catchTag("NoSuchBucket", () => Effect.succeed(true)), + ); + expect(deleted).toEqual(true); + + yield* waitForBucketToBeDeleted(bucket.bucketName, accountId); + }).pipe(logLevel), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "creates, updates, and deletes a bucket with multiple custom domains", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/R2/R2Bucket.test.ts b/packages/alchemy/test/Cloudflare/R2/R2Bucket.test.ts index 28ab3c980..dcb1d320e 100644 --- a/packages/alchemy/test/Cloudflare/R2/R2Bucket.test.ts +++ b/packages/alchemy/test/Cloudflare/R2/R2Bucket.test.ts @@ -5,6 +5,9 @@ import { expect } from "@effect/vitest"; import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -12,7 +15,7 @@ const logLevel = Effect.provideService( process.env.DEBUG ? "Debug" : "Info", ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed R2 bucket", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/R2DataCatalog/R2DataCatalog.test.ts b/packages/alchemy/test/Cloudflare/R2DataCatalog/R2DataCatalog.test.ts index cc6dc879f..6aab13861 100644 --- a/packages/alchemy/test/Cloudflare/R2DataCatalog/R2DataCatalog.test.ts +++ b/packages/alchemy/test/Cloudflare/R2DataCatalog/R2DataCatalog.test.ts @@ -9,6 +9,9 @@ import * as Redacted from "effect/Redacted"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -67,7 +70,7 @@ const expectGone = (accountId: string, bucketName: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "enable, sync maintenance config, register credential, destroy", (stack) => Effect.gen(function* () { @@ -165,7 +168,7 @@ test.provider( { timeout: 240_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed catalog", (stack) => Effect.gen(function* () { @@ -198,7 +201,7 @@ test.provider( { timeout: 240_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "re-enables after out-of-band disable", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/RateLimit/RateLimit.test.ts b/packages/alchemy/test/Cloudflare/RateLimit/RateLimit.test.ts index dc8f0f8db..9925375ce 100644 --- a/packages/alchemy/test/Cloudflare/RateLimit/RateLimit.test.ts +++ b/packages/alchemy/test/Cloudflare/RateLimit/RateLimit.test.ts @@ -7,6 +7,9 @@ import * as Schedule from "effect/Schedule"; import * as HttpClient from "effect/unstable/http/HttpClient"; import Stack from "./fixtures/stack.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test, beforeAll, afterAll, deploy, destroy } = Test.make({ providers: Cloudflare.providers(), }); @@ -45,7 +48,7 @@ const freshKey = () => `fresh-${Math.random().toString(36).slice(2)}`; // suite parameterized by which deployed URL to hit. const behaviorSuite = (label: string, getUrl: () => Effect.Effect) => describe(label, () => { - test( + test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "throttles requests past the configured limit", Effect.gen(function* () { const url = yield* getUrl(); @@ -70,7 +73,7 @@ const behaviorSuite = (label: string, getUrl: () => Effect.Effect) => { timeout: 180_000 }, ); - test( + test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "tracks each key independently", Effect.gen(function* () { const url = yield* getUrl(); diff --git a/packages/alchemy/test/Cloudflare/RegionalHostname/RegionalHostname.test.ts b/packages/alchemy/test/Cloudflare/RegionalHostname/RegionalHostname.test.ts index 9579c2ac7..4fa1f9928 100644 --- a/packages/alchemy/test/Cloudflare/RegionalHostname/RegionalHostname.test.ts +++ b/packages/alchemy/test/Cloudflare/RegionalHostname/RegionalHostname.test.ts @@ -12,6 +12,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -115,7 +118,7 @@ const purgeDnsRecord = (zoneId: string) => // entitlement on the zone. The test probes a raw create once: unentitled // zones assert the typed error tag; entitled zones run the full lifecycle // (create, patch regionKey in place, destroy, verify gone). -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "regional hostname lifecycle (typed error on unentitled zones)", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Registrar/Domain.test.ts b/packages/alchemy/test/Cloudflare/Registrar/Domain.test.ts index 7388eca7e..8a44d4d70 100644 --- a/packages/alchemy/test/Cloudflare/Registrar/Domain.test.ts +++ b/packages/alchemy/test/Cloudflare/Registrar/Domain.test.ts @@ -10,6 +10,9 @@ import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -43,7 +46,7 @@ const findDomain = (accountId: string) => // Both cases mutate the same registered domain's settings; run them serially so they don't corrupt each other's captured `initialSettings` under the global concurrent test config. describe.sequential("Domain", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "adopts a registered domain, no-op syncs, and never releases it on destroy", (stack) => Effect.gen(function* () { @@ -111,7 +114,7 @@ describe.sequential("Domain", () => { { timeout: 120_000 }, ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "updates settings in place and restores the baseline on destroy", (stack) => Effect.gen(function* () { @@ -201,21 +204,25 @@ describe.sequential("Domain", () => { // created via the API, so this is read-only: `list()` enumerates whatever // is already on the account and we assert a well-typed Attributes[] (which // may legitimately be empty if the account has no registered domains). - test.provider("list enumerates registrar domains on the account", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const provider = yield* Provider.findProvider(Cloudflare.RegistrarDomain); - const all = yield* provider.list(); - - expect(Array.isArray(all)).toBe(true); - for (const domain of all) { - expect(typeof domain.domainName).toBe("string"); - expect(typeof domain.accountId).toBe("string"); - expect(domain.initialSettings).toBeDefined(); - } - - yield* stack.destroy(); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates registrar domains on the account", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const provider = yield* Provider.findProvider( + Cloudflare.RegistrarDomain, + ); + const all = yield* provider.list(); + + expect(Array.isArray(all)).toBe(true); + for (const domain of all) { + expect(typeof domain.domainName).toBe("string"); + expect(typeof domain.accountId).toBe("string"); + expect(domain.initialSettings).toBeDefined(); + } + + yield* stack.destroy(); + }).pipe(logLevel), ); }); diff --git a/packages/alchemy/test/Cloudflare/ResourceSharing/Share.test.ts b/packages/alchemy/test/Cloudflare/ResourceSharing/Share.test.ts index 28bbec1f3..6bd799650 100644 --- a/packages/alchemy/test/Cloudflare/ResourceSharing/Share.test.ts +++ b/packages/alchemy/test/Cloudflare/ResourceSharing/Share.test.ts @@ -9,6 +9,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Result from "effect/Result"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -55,7 +58,7 @@ const expectGone = (accountId: string, shareId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "reads succeed and write-blocked accounts surface the typed Forbidden error", (stack) => Effect.gen(function* () { @@ -130,7 +133,7 @@ test.provider( { timeout: 120_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the account's sent shares (typed Attributes), and includes a deployed share when entitled", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/ResourceSharing/ShareResource.test.ts b/packages/alchemy/test/Cloudflare/ResourceSharing/ShareResource.test.ts index 61e9e4ac4..c87eadae7 100644 --- a/packages/alchemy/test/Cloudflare/ResourceSharing/ShareResource.test.ts +++ b/packages/alchemy/test/Cloudflare/ResourceSharing/ShareResource.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -53,7 +56,7 @@ const expectGone = (accountId: string, shareId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates share resources across the account's sent shares (typed Attributes), and includes a deployed entry when entitled", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Rules/List.test.ts b/packages/alchemy/test/Cloudflare/Rules/List.test.ts index 41f36c6e1..66837d767 100644 --- a/packages/alchemy/test/Cloudflare/Rules/List.test.ts +++ b/packages/alchemy/test/Cloudflare/Rules/List.test.ts @@ -9,6 +9,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -54,237 +57,253 @@ const expectGone = (accountId: string, listId: string) => }), ); -test.provider("create and delete an ip list with default name", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const list = yield* stack.deploy( - Cloudflare.RulesList("DefaultList", { - kind: "ip", - description: "alchemy rules list create test", - items: [ - { ip: "203.0.113.7", comment: "scanner" }, - { ip: "198.51.100.0/24" }, - ], - }), - ); - - expect(list.listId).toBeDefined(); - expect(list.accountId).toEqual(accountId); - expect(list.kind).toEqual("ip"); - expect(list.description).toEqual("alchemy rules list create test"); - // List names only allow letters, numbers, and underscores. - expect(list.name).toMatch(/^[a-zA-Z0-9_]+$/); - expect(list.numItems).toEqual(2); - - const live = yield* getList(accountId, list.listId); - expect(live.name).toEqual(list.name); - expect(live.kind).toEqual("ip"); - - const items = yield* getItems(accountId, list.listId); - const ips = items - .map((item) => ("ip" in item ? item.ip : undefined)) - .sort(); - expect(ips).toEqual(["198.51.100.0/24", "203.0.113.7"]); - - yield* stack.destroy(); - - yield* expectGone(accountId, list.listId); - }).pipe(logLevel), -); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create and delete an ip list with default name", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const list = yield* stack.deploy( + Cloudflare.RulesList("DefaultList", { + kind: "ip", + description: "alchemy rules list create test", + items: [ + { ip: "203.0.113.7", comment: "scanner" }, + { ip: "198.51.100.0/24" }, + ], + }), + ); + + expect(list.listId).toBeDefined(); + expect(list.accountId).toEqual(accountId); + expect(list.kind).toEqual("ip"); + expect(list.description).toEqual("alchemy rules list create test"); + // List names only allow letters, numbers, and underscores. + expect(list.name).toMatch(/^[a-zA-Z0-9_]+$/); + expect(list.numItems).toEqual(2); + + const live = yield* getList(accountId, list.listId); + expect(live.name).toEqual(list.name); + expect(live.kind).toEqual("ip"); + + const items = yield* getItems(accountId, list.listId); + const ips = items + .map((item) => ("ip" in item ? item.ip : undefined)) + .sort(); + expect(ips).toEqual(["198.51.100.0/24", "203.0.113.7"]); -test.provider("update description and items in place (same listId)", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const initial = yield* stack.deploy( - Cloudflare.RulesList("UpdateList", { - name: "alchemy_rules_list_update", - kind: "ip", - description: "v1", - items: [{ ip: "203.0.113.1" }, { ip: "203.0.113.2" }], - }), - ); - - expect(initial.name).toEqual("alchemy_rules_list_update"); - expect(initial.description).toEqual("v1"); - expect(initial.numItems).toEqual(2); - - const updated = yield* stack.deploy( - Cloudflare.RulesList("UpdateList", { - name: "alchemy_rules_list_update", - kind: "ip", - description: "v2", - items: [{ ip: "203.0.113.2", comment: "kept" }, { ip: "192.0.2.0/24" }], - }), - ); - - // Same list mutated in place — not a replacement. - expect(updated.listId).toEqual(initial.listId); - expect(updated.description).toEqual("v2"); - expect(updated.numItems).toEqual(2); - - const items = yield* getItems(accountId, updated.listId); - const ips = items - .map((item) => ("ip" in item ? item.ip : undefined)) - .sort(); - expect(ips).toEqual(["192.0.2.0/24", "203.0.113.2"]); - const kept = items.find( - (item) => "ip" in item && item.ip === "203.0.113.2", - ); - expect(kept && "comment" in kept ? kept.comment : undefined).toEqual( - "kept", - ); - - // Redeploying identical props is a no-op (still the same list). - const noop = yield* stack.deploy( - Cloudflare.RulesList("UpdateList", { - name: "alchemy_rules_list_update", - kind: "ip", - description: "v2", - items: [{ ip: "203.0.113.2", comment: "kept" }, { ip: "192.0.2.0/24" }], - }), - ); - expect(noop.listId).toEqual(initial.listId); - - yield* stack.destroy(); - - yield* expectGone(accountId, initial.listId); - }).pipe(logLevel), + yield* stack.destroy(); + + yield* expectGone(accountId, list.listId); + }).pipe(logLevel), ); -test.provider("changing kind replaces the list", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const ipList = yield* stack.deploy( - Cloudflare.RulesList("ReplaceList", { - name: "alchemy_rules_list_replace", - kind: "ip", - items: [{ ip: "203.0.113.9" }], - }), - ); - expect(ipList.kind).toEqual("ip"); - - const hostnameList = yield* stack.deploy( - Cloudflare.RulesList("ReplaceList", { - name: "alchemy_rules_list_replace", - kind: "hostname", - items: [{ hostname: { urlHostname: "example.com" } }], - }), - ); - - // Kind is immutable — the engine must replace the list. - expect(hostnameList.listId).not.toEqual(ipList.listId); - expect(hostnameList.kind).toEqual("hostname"); - - // The old list is gone, the new one is live. - yield* expectGone(accountId, ipList.listId); - const live = yield* getList(accountId, hostnameList.listId); - expect(live.kind).toEqual("hostname"); - - yield* stack.destroy(); - - yield* expectGone(accountId, hostnameList.listId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "update description and items in place (same listId)", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const initial = yield* stack.deploy( + Cloudflare.RulesList("UpdateList", { + name: "alchemy_rules_list_update", + kind: "ip", + description: "v1", + items: [{ ip: "203.0.113.1" }, { ip: "203.0.113.2" }], + }), + ); + + expect(initial.name).toEqual("alchemy_rules_list_update"); + expect(initial.description).toEqual("v1"); + expect(initial.numItems).toEqual(2); + + const updated = yield* stack.deploy( + Cloudflare.RulesList("UpdateList", { + name: "alchemy_rules_list_update", + kind: "ip", + description: "v2", + items: [ + { ip: "203.0.113.2", comment: "kept" }, + { ip: "192.0.2.0/24" }, + ], + }), + ); + + // Same list mutated in place — not a replacement. + expect(updated.listId).toEqual(initial.listId); + expect(updated.description).toEqual("v2"); + expect(updated.numItems).toEqual(2); + + const items = yield* getItems(accountId, updated.listId); + const ips = items + .map((item) => ("ip" in item ? item.ip : undefined)) + .sort(); + expect(ips).toEqual(["192.0.2.0/24", "203.0.113.2"]); + const kept = items.find( + (item) => "ip" in item && item.ip === "203.0.113.2", + ); + expect(kept && "comment" in kept ? kept.comment : undefined).toEqual( + "kept", + ); + + // Redeploying identical props is a no-op (still the same list). + const noop = yield* stack.deploy( + Cloudflare.RulesList("UpdateList", { + name: "alchemy_rules_list_update", + kind: "ip", + description: "v2", + items: [ + { ip: "203.0.113.2", comment: "kept" }, + { ip: "192.0.2.0/24" }, + ], + }), + ); + expect(noop.listId).toEqual(initial.listId); + + yield* stack.destroy(); + + yield* expectGone(accountId, initial.listId); + }).pipe(logLevel), ); -test.provider("recreates after out-of-band delete", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const list = yield* stack.deploy( - Cloudflare.RulesList("HealList", { - name: "alchemy_rules_list_heal", - kind: "ip", - items: [{ ip: "203.0.113.11" }], - }), - ); - - // Delete the list out-of-band. A redeploy with identical props is a - // planner no-op, so change a prop to force reconcile — it must observe - // the list as missing and recreate it instead of failing on a 404. - yield* rules.deleteList({ accountId, listId: list.listId }).pipe( - Effect.retry({ - while: (e) => e._tag === "Forbidden", - schedule: Schedule.exponential("500 millis"), - times: 8, - }), - ); - - const healed = yield* stack.deploy( - Cloudflare.RulesList("HealList", { - name: "alchemy_rules_list_heal", - kind: "ip", - description: "healed", - items: [{ ip: "203.0.113.11" }], - }), - ); - - expect(healed.listId).not.toEqual(list.listId); - expect(healed.description).toEqual("healed"); - const live = yield* getList(accountId, healed.listId); - expect(live.name).toEqual("alchemy_rules_list_heal"); - - yield* stack.destroy(); - - yield* expectGone(accountId, healed.listId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "changing kind replaces the list", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const ipList = yield* stack.deploy( + Cloudflare.RulesList("ReplaceList", { + name: "alchemy_rules_list_replace", + kind: "ip", + items: [{ ip: "203.0.113.9" }], + }), + ); + expect(ipList.kind).toEqual("ip"); + + const hostnameList = yield* stack.deploy( + Cloudflare.RulesList("ReplaceList", { + name: "alchemy_rules_list_replace", + kind: "hostname", + items: [{ hostname: { urlHostname: "example.com" } }], + }), + ); + + // Kind is immutable — the engine must replace the list. + expect(hostnameList.listId).not.toEqual(ipList.listId); + expect(hostnameList.kind).toEqual("hostname"); + + // The old list is gone, the new one is live. + yield* expectGone(accountId, ipList.listId); + const live = yield* getList(accountId, hostnameList.listId); + expect(live.kind).toEqual("hostname"); + + yield* stack.destroy(); + + yield* expectGone(accountId, hostnameList.listId); + }).pipe(logLevel), ); -test.provider("adopts an existing list with the same name", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - // Create a list out-of-band with the exact name the resource will use — - // the create conflict (`ListAlreadyExists`, code 10021) must resolve by - // adopting the existing list. - const preexisting = yield* rules - .createList({ - accountId, - name: "alchemy_rules_list_adopt", - kind: "ip", - }) - .pipe( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "recreates after out-of-band delete", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const list = yield* stack.deploy( + Cloudflare.RulesList("HealList", { + name: "alchemy_rules_list_heal", + kind: "ip", + items: [{ ip: "203.0.113.11" }], + }), + ); + + // Delete the list out-of-band. A redeploy with identical props is a + // planner no-op, so change a prop to force reconcile — it must observe + // the list as missing and recreate it instead of failing on a 404. + yield* rules.deleteList({ accountId, listId: list.listId }).pipe( Effect.retry({ while: (e) => e._tag === "Forbidden", schedule: Schedule.exponential("500 millis"), times: 8, }), - Effect.catchTag("ListAlreadyExists", () => - rules.listLists.items({ accountId }).pipe( - Stream.filter((l) => l.name === "alchemy_rules_list_adopt"), - Stream.runHead, - Effect.map(Option.getOrThrow), - ), - ), ); - const adopted = yield* stack.deploy( - Cloudflare.RulesList("AdoptList", { - name: "alchemy_rules_list_adopt", - kind: "ip", - description: "adopted", - items: [{ ip: "203.0.113.21" }], - }), - ); + const healed = yield* stack.deploy( + Cloudflare.RulesList("HealList", { + name: "alchemy_rules_list_heal", + kind: "ip", + description: "healed", + items: [{ ip: "203.0.113.11" }], + }), + ); + + expect(healed.listId).not.toEqual(list.listId); + expect(healed.description).toEqual("healed"); + const live = yield* getList(accountId, healed.listId); + expect(live.name).toEqual("alchemy_rules_list_heal"); + + yield* stack.destroy(); + + yield* expectGone(accountId, healed.listId); + }).pipe(logLevel), +); + +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "adopts an existing list with the same name", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + // Create a list out-of-band with the exact name the resource will use — + // the create conflict (`ListAlreadyExists`, code 10021) must resolve by + // adopting the existing list. + const preexisting = yield* rules + .createList({ + accountId, + name: "alchemy_rules_list_adopt", + kind: "ip", + }) + .pipe( + Effect.retry({ + while: (e) => e._tag === "Forbidden", + schedule: Schedule.exponential("500 millis"), + times: 8, + }), + Effect.catchTag("ListAlreadyExists", () => + rules.listLists.items({ accountId }).pipe( + Stream.filter((l) => l.name === "alchemy_rules_list_adopt"), + Stream.runHead, + Effect.map(Option.getOrThrow), + ), + ), + ); + + const adopted = yield* stack.deploy( + Cloudflare.RulesList("AdoptList", { + name: "alchemy_rules_list_adopt", + kind: "ip", + description: "adopted", + items: [{ ip: "203.0.113.21" }], + }), + ); - expect(adopted.listId).toEqual(preexisting.id); - expect(adopted.description).toEqual("adopted"); - expect(adopted.numItems).toEqual(1); + expect(adopted.listId).toEqual(preexisting.id); + expect(adopted.description).toEqual("adopted"); + expect(adopted.numItems).toEqual(1); - yield* stack.destroy(); + yield* stack.destroy(); - yield* expectGone(accountId, preexisting.id); - }).pipe(logLevel), + yield* expectGone(accountId, preexisting.id); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Ruleset/AccountEntrypoint.test.ts b/packages/alchemy/test/Cloudflare/Ruleset/AccountEntrypoint.test.ts index db8c978ba..f42669606 100644 --- a/packages/alchemy/test/Cloudflare/Ruleset/AccountEntrypoint.test.ts +++ b/packages/alchemy/test/Cloudflare/Ruleset/AccountEntrypoint.test.ts @@ -9,6 +9,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Result from "effect/Result"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -42,7 +45,7 @@ const getEntrypointRules = (accountId: string) => // standard testing account every PUT fails with the typed `PhaseNotEntitled` // error (code 50002). The test probes once: unentitled accounts assert the // typed tag; entitled accounts run the full lifecycle. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "account entrypoint lifecycle (typed PhaseNotEntitled on unentitled accounts)", (stack) => Effect.gen(function* () { @@ -154,7 +157,7 @@ test.provider( { timeout: 120_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the account phase entrypoints", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Ruleset/CustomRuleset.test.ts b/packages/alchemy/test/Cloudflare/Ruleset/CustomRuleset.test.ts index c98bb69d7..cbea38c00 100644 --- a/packages/alchemy/test/Cloudflare/Ruleset/CustomRuleset.test.ts +++ b/packages/alchemy/test/Cloudflare/Ruleset/CustomRuleset.test.ts @@ -9,6 +9,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Result from "effect/Result"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -42,7 +45,7 @@ const getRuleset = (accountId: string, rulesetId: string) => // (code 50002: "not entitled to use the phase http_request_firewall_custom"). // The test probes once: unentitled accounts assert the typed tag; entitled // accounts run the full lifecycle. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "custom ruleset lifecycle (typed PhaseNotEntitled on unentitled accounts)", (stack) => Effect.gen(function* () { @@ -146,7 +149,7 @@ test.provider( // create with the typed `PhaseNotEntitled` error, so we still verify `list()` // returns an array without throwing; entitled accounts additionally deploy a // ruleset and assert it appears in the exhaustively-paginated result. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed custom ruleset", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Ruleset/Ruleset.test.ts b/packages/alchemy/test/Cloudflare/Ruleset/Ruleset.test.ts index 3a3818169..147a6cfc1 100644 --- a/packages/alchemy/test/Cloudflare/Ruleset/Ruleset.test.ts +++ b/packages/alchemy/test/Cloudflare/Ruleset/Ruleset.test.ts @@ -11,6 +11,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -32,7 +35,7 @@ type TestRulesetPhase = typeof phase; // on `alchemy-test-2.us`; run them serially so they can't clobber each other // under the global concurrent test config. describe.sequential("Ruleset", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "creates, updates, and deletes a zone phase entrypoint ruleset", (stack) => Effect.gen(function* () { @@ -113,7 +116,7 @@ describe.sequential("Ruleset", () => { }).pipe(logLevel), ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "creates and tears down a ruleset whose zone is provisioned in the same deploy", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Rum/Rule.test.ts b/packages/alchemy/test/Cloudflare/Rum/Rule.test.ts index ce7cf9f88..fb458db25 100644 --- a/packages/alchemy/test/Cloudflare/Rum/Rule.test.ts +++ b/packages/alchemy/test/Cloudflare/Rum/Rule.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -148,87 +151,89 @@ const program = ( // These cases all manage the single account-unique zone-based site + // ruleset on `alchemy-test-3.us`, so they must not run concurrently. describe.sequential("RumRule", () => { - test.provider("create, update in place, and delete a rule", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - const zone = yield* findZoneByName({ accountId, name: zoneName }); - if (!zone) { - return yield* Effect.die( - new Error(`zone "${zoneName}" not found in account`), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, update in place, and delete a rule", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + const zone = yield* findZoneByName({ accountId, name: zoneName }); + if (!zone) { + return yield* Effect.die( + new Error(`zone "${zoneName}" not found in account`), + ); + } + + yield* stack.destroy(); + yield* cleanupLeftoverSites(accountId, zone.id); + + const initial = yield* retryingQuota( + stack.deploy( + program(zone.id, { + host: zoneName, + paths: ["/blog/*"], + inclusive: false, + }), + ), ); - } + if (initial === undefined) return yield* quotaExhausted(stack); + + expect(initial.site.rulesetId).toBeTruthy(); + expect(initial.rule.id).toBeTruthy(); + expect(initial.rule.rulesetId).toEqual(initial.site.rulesetId); + expect(initial.rule.accountId).toEqual(accountId); + expect(initial.rule.host).toEqual(zoneName); + expect(initial.rule.paths).toEqual(["/blog/*"]); + expect(initial.rule.inclusive).toEqual(false); + expect(initial.rule.isPaused).toEqual(false); + + // Verify out-of-band against the live API. + const live = yield* findRule( + accountId, + initial.rule.rulesetId, + initial.rule.id, + ); + expect(live).toBeDefined(); + expect(live?.host).toEqual(zoneName); + expect(live?.paths).toEqual(["/blog/*"]); + expect(live?.inclusive).toEqual(false); - yield* stack.destroy(); - yield* cleanupLeftoverSites(accountId, zone.id); + // Update paths and pause the rule in place — same rule id. + const updated = yield* stack.deploy( + program(zone.id, { + host: zoneName, + paths: ["/blog/*", "/admin/*"], + inclusive: false, + isPaused: true, + }), + ); + expect(updated.rule.id).toEqual(initial.rule.id); + expect(updated.rule.paths).toEqual(["/blog/*", "/admin/*"]); + expect(updated.rule.isPaused).toEqual(true); - const initial = yield* retryingQuota( - stack.deploy( + const liveUpdated = yield* findRule( + accountId, + updated.rule.rulesetId, + updated.rule.id, + ); + expect(liveUpdated?.paths).toEqual(["/blog/*", "/admin/*"]); + expect(liveUpdated?.isPaused).toEqual(true); + + // Redeploying identical props is a no-op (still the same rule). + const noop = yield* stack.deploy( program(zone.id, { host: zoneName, - paths: ["/blog/*"], + paths: ["/blog/*", "/admin/*"], inclusive: false, + isPaused: true, }), - ), - ); - if (initial === undefined) return yield* quotaExhausted(stack); - - expect(initial.site.rulesetId).toBeTruthy(); - expect(initial.rule.id).toBeTruthy(); - expect(initial.rule.rulesetId).toEqual(initial.site.rulesetId); - expect(initial.rule.accountId).toEqual(accountId); - expect(initial.rule.host).toEqual(zoneName); - expect(initial.rule.paths).toEqual(["/blog/*"]); - expect(initial.rule.inclusive).toEqual(false); - expect(initial.rule.isPaused).toEqual(false); - - // Verify out-of-band against the live API. - const live = yield* findRule( - accountId, - initial.rule.rulesetId, - initial.rule.id, - ); - expect(live).toBeDefined(); - expect(live?.host).toEqual(zoneName); - expect(live?.paths).toEqual(["/blog/*"]); - expect(live?.inclusive).toEqual(false); - - // Update paths and pause the rule in place — same rule id. - const updated = yield* stack.deploy( - program(zone.id, { - host: zoneName, - paths: ["/blog/*", "/admin/*"], - inclusive: false, - isPaused: true, - }), - ); - expect(updated.rule.id).toEqual(initial.rule.id); - expect(updated.rule.paths).toEqual(["/blog/*", "/admin/*"]); - expect(updated.rule.isPaused).toEqual(true); - - const liveUpdated = yield* findRule( - accountId, - updated.rule.rulesetId, - updated.rule.id, - ); - expect(liveUpdated?.paths).toEqual(["/blog/*", "/admin/*"]); - expect(liveUpdated?.isPaused).toEqual(true); - - // Redeploying identical props is a no-op (still the same rule). - const noop = yield* stack.deploy( - program(zone.id, { - host: zoneName, - paths: ["/blog/*", "/admin/*"], - inclusive: false, - isPaused: true, - }), - ); - expect(noop.rule.id).toEqual(initial.rule.id); - - const rulesetId = initial.rule.rulesetId; - yield* stack.destroy(); - - yield* expectGone(accountId, rulesetId, initial.rule.id); - }).pipe(logLevel), + ); + expect(noop.rule.id).toEqual(initial.rule.id); + + const rulesetId = initial.rule.rulesetId; + yield* stack.destroy(); + + yield* expectGone(accountId, rulesetId, initial.rule.id); + }).pipe(logLevel), ); // Canonical `list()` test: rules live under a RumSite's implicit ruleset and @@ -236,111 +241,115 @@ describe.sequential("RumRule", () => { // (paginated) and fans out the per-ruleset rule list. Deploy a real rule, // then assert it appears in the exhaustively-enumerated result. Gated behind // the same account-wide `MaxRulesExceeded` quota the other cases handle. - test.provider("list enumerates rules across all rulesets", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - const zone = yield* findZoneByName({ accountId, name: zoneName }); - if (!zone) { - return yield* Effect.die( - new Error(`zone "${zoneName}" not found in account`), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates rules across all rulesets", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + const zone = yield* findZoneByName({ accountId, name: zoneName }); + if (!zone) { + return yield* Effect.die( + new Error(`zone "${zoneName}" not found in account`), + ); + } + + yield* stack.destroy(); + yield* cleanupLeftoverSites(accountId, zone.id); + + const deployed = yield* retryingQuota( + stack.deploy( + program(zone.id, { + host: zoneName, + paths: ["/list-test/*"], + inclusive: false, + }), + ), ); - } + if (deployed === undefined) return yield* quotaExhausted(stack); - yield* stack.destroy(); - yield* cleanupLeftoverSites(accountId, zone.id); + const provider = yield* Provider.findProvider(Cloudflare.RumRule); + const all = yield* provider.list(); - const deployed = yield* retryingQuota( - stack.deploy( - program(zone.id, { - host: zoneName, - paths: ["/list-test/*"], - inclusive: false, - }), - ), - ); - if (deployed === undefined) return yield* quotaExhausted(stack); - - const provider = yield* Provider.findProvider(Cloudflare.RumRule); - const all = yield* provider.list(); - - expect( - all.some( - (r) => - r.id === deployed.rule.id && - r.rulesetId === deployed.rule.rulesetId, - ), - ).toBe(true); - - yield* stack.destroy(); - }).pipe(logLevel), + expect( + all.some( + (r) => + r.id === deployed.rule.id && + r.rulesetId === deployed.rule.rulesetId, + ), + ).toBe(true); + + yield* stack.destroy(); + }).pipe(logLevel), ); - test.provider("recreates after out-of-band delete", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - const zone = yield* findZoneByName({ accountId, name: zoneName }); - if (!zone) { - return yield* Effect.die( - new Error(`zone "${zoneName}" not found in account`), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "recreates after out-of-band delete", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + const zone = yield* findZoneByName({ accountId, name: zoneName }); + if (!zone) { + return yield* Effect.die( + new Error(`zone "${zoneName}" not found in account`), + ); + } + + yield* stack.destroy(); + yield* cleanupLeftoverSites(accountId, zone.id); + + const initial = yield* retryingQuota( + stack.deploy( + program(zone.id, { + host: zoneName, + paths: ["/heal/*"], + inclusive: false, + }), + ), ); - } + if (initial === undefined) return yield* quotaExhausted(stack); - yield* stack.destroy(); - yield* cleanupLeftoverSites(accountId, zone.id); - - const initial = yield* retryingQuota( - stack.deploy( - program(zone.id, { - host: zoneName, - paths: ["/heal/*"], - inclusive: false, - }), - ), - ); - if (initial === undefined) return yield* quotaExhausted(stack); - - // Delete the rule out-of-band. A redeploy with identical props is a - // planner no-op, so change a prop to force reconcile — it must observe - // the rule as missing and recreate it instead of failing. - yield* rum - .deleteRule({ - accountId, - rulesetId: initial.rule.rulesetId, - ruleId: initial.rule.id, - }) - .pipe( - Effect.retry({ - while: (e) => e._tag === "Forbidden", - schedule: Schedule.exponential("500 millis"), - times: 8, - }), + // Delete the rule out-of-band. A redeploy with identical props is a + // planner no-op, so change a prop to force reconcile — it must observe + // the rule as missing and recreate it instead of failing. + yield* rum + .deleteRule({ + accountId, + rulesetId: initial.rule.rulesetId, + ruleId: initial.rule.id, + }) + .pipe( + Effect.retry({ + while: (e) => e._tag === "Forbidden", + schedule: Schedule.exponential("500 millis"), + times: 8, + }), + ); + + const healed = yield* retryingQuota( + stack.deploy( + program(zone.id, { + host: zoneName, + paths: ["/heal-v2/*"], + inclusive: false, + }), + ), ); + if (healed === undefined) return yield* quotaExhausted(stack); - const healed = yield* retryingQuota( - stack.deploy( - program(zone.id, { - host: zoneName, - paths: ["/heal-v2/*"], - inclusive: false, - }), - ), - ); - if (healed === undefined) return yield* quotaExhausted(stack); - - expect(healed.rule.id).not.toEqual(initial.rule.id); - expect(healed.rule.paths).toEqual(["/heal-v2/*"]); + expect(healed.rule.id).not.toEqual(initial.rule.id); + expect(healed.rule.paths).toEqual(["/heal-v2/*"]); - const live = yield* findRule( - accountId, - healed.rule.rulesetId, - healed.rule.id, - ); - expect(live?.paths).toEqual(["/heal-v2/*"]); + const live = yield* findRule( + accountId, + healed.rule.rulesetId, + healed.rule.id, + ); + expect(live?.paths).toEqual(["/heal-v2/*"]); - const rulesetId = healed.rule.rulesetId; - yield* stack.destroy(); + const rulesetId = healed.rule.rulesetId; + yield* stack.destroy(); - yield* expectGone(accountId, rulesetId, healed.rule.id); - }).pipe(logLevel), + yield* expectGone(accountId, rulesetId, healed.rule.id); + }).pipe(logLevel), ); }); diff --git a/packages/alchemy/test/Cloudflare/Rum/Site.test.ts b/packages/alchemy/test/Cloudflare/Rum/Site.test.ts index 766575f70..14aaf7972 100644 --- a/packages/alchemy/test/Cloudflare/Rum/Site.test.ts +++ b/packages/alchemy/test/Cloudflare/Rum/Site.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -45,80 +48,84 @@ const expectGone = (accountId: string, siteTag: string) => }), ); -test.provider("create and delete a host (gray-clouded) site", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create and delete a host (gray-clouded) site", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - yield* stack.destroy(); + yield* stack.destroy(); - const site = yield* stack.deploy( - Cloudflare.RumSite("HostSite", { - host: `create.${zoneName}`, - }), - ); + const site = yield* stack.deploy( + Cloudflare.RumSite("HostSite", { + host: `create.${zoneName}`, + }), + ); - expect(site.siteTag).toBeTruthy(); - expect(site.siteToken).toBeTruthy(); - expect(site.snippet).toContain(site.siteToken); - expect(site.accountId).toEqual(accountId); - expect(site.host).toEqual(`create.${zoneName}`); - expect(site.autoInstall).toEqual(false); + expect(site.siteTag).toBeTruthy(); + expect(site.siteToken).toBeTruthy(); + expect(site.snippet).toContain(site.siteToken); + expect(site.accountId).toEqual(accountId); + expect(site.host).toEqual(`create.${zoneName}`); + expect(site.autoInstall).toEqual(false); - // Verify out-of-band against the live API. - const live = yield* getSite(accountId, site.siteTag); - expect(live.siteTag).toEqual(site.siteTag); - expect(live.host).toEqual(`create.${zoneName}`); + // Verify out-of-band against the live API. + const live = yield* getSite(accountId, site.siteTag); + expect(live.siteTag).toEqual(site.siteTag); + expect(live.host).toEqual(`create.${zoneName}`); - yield* stack.destroy(); + yield* stack.destroy(); - yield* expectGone(accountId, site.siteTag); - }).pipe(logLevel), + yield* expectGone(accountId, site.siteTag); + }).pipe(logLevel), ); -test.provider("update mutable props in place (same siteTag)", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "update mutable props in place (same siteTag)", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - yield* stack.destroy(); + yield* stack.destroy(); - const initial = yield* stack.deploy( - Cloudflare.RumSite("UpdateSite", { - host: `update.${zoneName}`, - }), - ); + const initial = yield* stack.deploy( + Cloudflare.RumSite("UpdateSite", { + host: `update.${zoneName}`, + }), + ); - expect(initial.host).toEqual(`update.${zoneName}`); - expect(initial.autoInstall).toEqual(false); + expect(initial.host).toEqual(`update.${zoneName}`); + expect(initial.autoInstall).toEqual(false); - // Changing the hostname is an in-place update of the same site. - const updated = yield* stack.deploy( - Cloudflare.RumSite("UpdateSite", { - host: `update-v2.${zoneName}`, - }), - ); + // Changing the hostname is an in-place update of the same site. + const updated = yield* stack.deploy( + Cloudflare.RumSite("UpdateSite", { + host: `update-v2.${zoneName}`, + }), + ); - expect(updated.siteTag).toEqual(initial.siteTag); - expect(updated.siteToken).toEqual(initial.siteToken); - expect(updated.host).toEqual(`update-v2.${zoneName}`); + expect(updated.siteTag).toEqual(initial.siteTag); + expect(updated.siteToken).toEqual(initial.siteToken); + expect(updated.host).toEqual(`update-v2.${zoneName}`); - const live = yield* getSite(accountId, updated.siteTag); - expect(live.host).toEqual(`update-v2.${zoneName}`); + const live = yield* getSite(accountId, updated.siteTag); + expect(live.host).toEqual(`update-v2.${zoneName}`); - // Redeploying identical props is a no-op (still the same site). - const noop = yield* stack.deploy( - Cloudflare.RumSite("UpdateSite", { - host: `update-v2.${zoneName}`, - }), - ); - expect(noop.siteTag).toEqual(initial.siteTag); + // Redeploying identical props is a no-op (still the same site). + const noop = yield* stack.deploy( + Cloudflare.RumSite("UpdateSite", { + host: `update-v2.${zoneName}`, + }), + ); + expect(noop.siteTag).toEqual(initial.siteTag); - yield* stack.destroy(); + yield* stack.destroy(); - yield* expectGone(accountId, initial.siteTag); - }).pipe(logLevel), + yield* expectGone(accountId, initial.siteTag); + }).pipe(logLevel), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "zone (orange-clouded) site with autoInstall, replaced on identity flip", (stack) => Effect.gen(function* () { @@ -173,61 +180,65 @@ test.provider( }).pipe(logLevel), ); -test.provider("list enumerates the deployed RUM site", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed RUM site", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); - const deployed = yield* stack.deploy( - Cloudflare.RumSite("ListSite", { - host: `list.${zoneName}`, - }), - ); + const deployed = yield* stack.deploy( + Cloudflare.RumSite("ListSite", { + host: `list.${zoneName}`, + }), + ); - const provider = yield* Provider.findProvider(Cloudflare.RumSite); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider(Cloudflare.RumSite); + const all = yield* provider.list(); - expect(all.some((s) => s.siteTag === deployed.siteTag)).toBe(true); + expect(all.some((s) => s.siteTag === deployed.siteTag)).toBe(true); - yield* stack.destroy(); - }).pipe(logLevel), + yield* stack.destroy(); + }).pipe(logLevel), ); -test.provider("recreates after out-of-band delete", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const site = yield* stack.deploy( - Cloudflare.RumSite("HealSite", { - host: `heal.${zoneName}`, - }), - ); - - // Delete the site out-of-band. A redeploy with identical props is a - // planner no-op, so change a prop to force reconcile — it must observe - // the site as missing and recreate it instead of failing on a 404. - yield* rum.deleteSiteInfo({ accountId, siteId: site.siteTag }).pipe( - Effect.retry({ - while: (e) => e._tag === "Forbidden", - schedule: Schedule.exponential("500 millis"), - times: 8, - }), - ); - - const healed = yield* stack.deploy( - Cloudflare.RumSite("HealSite", { - host: `heal-v2.${zoneName}`, - }), - ); - - expect(healed.siteTag).not.toEqual(site.siteTag); - expect(healed.host).toEqual(`heal-v2.${zoneName}`); - const live = yield* getSite(accountId, healed.siteTag); - expect(live.host).toEqual(`heal-v2.${zoneName}`); - - yield* stack.destroy(); - - yield* expectGone(accountId, healed.siteTag); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "recreates after out-of-band delete", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const site = yield* stack.deploy( + Cloudflare.RumSite("HealSite", { + host: `heal.${zoneName}`, + }), + ); + + // Delete the site out-of-band. A redeploy with identical props is a + // planner no-op, so change a prop to force reconcile — it must observe + // the site as missing and recreate it instead of failing on a 404. + yield* rum.deleteSiteInfo({ accountId, siteId: site.siteTag }).pipe( + Effect.retry({ + while: (e) => e._tag === "Forbidden", + schedule: Schedule.exponential("500 millis"), + times: 8, + }), + ); + + const healed = yield* stack.deploy( + Cloudflare.RumSite("HealSite", { + host: `heal-v2.${zoneName}`, + }), + ); + + expect(healed.siteTag).not.toEqual(site.siteTag); + expect(healed.host).toEqual(`heal-v2.${zoneName}`); + const live = yield* getSite(accountId, healed.siteTag); + expect(live.host).toEqual(`heal-v2.${zoneName}`); + + yield* stack.destroy(); + + yield* expectGone(accountId, healed.siteTag); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/SchemaValidation/OperationSetting.test.ts b/packages/alchemy/test/Cloudflare/SchemaValidation/OperationSetting.test.ts index 09a0c87f8..df205c155 100644 --- a/packages/alchemy/test/Cloudflare/SchemaValidation/OperationSetting.test.ts +++ b/packages/alchemy/test/Cloudflare/SchemaValidation/OperationSetting.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -40,7 +43,7 @@ const getOverrideOob = (zoneId: string, operationId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "sets a per-operation override, updates it in place, and clears it on destroy", (stack) => Effect.gen(function* () { @@ -105,7 +108,7 @@ test.provider( // `listAllZones` and exhaustively paginates each zone's operation settings. // Deploy one override and assert it appears in the result, keyed by its // (zoneId, operationId) identity. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed per-operation override", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/SchemaValidation/Schema.test.ts b/packages/alchemy/test/Cloudflare/SchemaValidation/Schema.test.ts index 409861bd4..79e33e9f6 100644 --- a/packages/alchemy/test/Cloudflare/SchemaValidation/Schema.test.ts +++ b/packages/alchemy/test/Cloudflare/SchemaValidation/Schema.test.ts @@ -11,6 +11,9 @@ import * as Path from "effect/Path"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -53,7 +56,7 @@ const getSchemaOob = (zoneId: string, schemaId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "uploads a schema, enables in place, replaces on disable and source change, destroys", (stack) => Effect.gen(function* () { @@ -150,7 +153,7 @@ test.provider( // Canonical `list()` test (zone-scoped collection): `list()` enumerates every // zone in the account via `listAllZones` and exhaustively paginates each // zone's schemas. Deploy a real schema and assert it appears in the result. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed schema across all zones", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/SchemaValidation/Settings.test.ts b/packages/alchemy/test/Cloudflare/SchemaValidation/Settings.test.ts index d34aa0f96..8c1263aee 100644 --- a/packages/alchemy/test/Cloudflare/SchemaValidation/Settings.test.ts +++ b/packages/alchemy/test/Cloudflare/SchemaValidation/Settings.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -59,7 +62,7 @@ const setBaseline = (zoneId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "pins zone settings, updates in place, and restores the baseline on destroy", (stack) => Effect.gen(function* () { @@ -121,7 +124,7 @@ test.provider( // API for this per-zone setting, so `list()` enumerates every zone via // `listAllZones` and reads the singleton in each. Assert the result is // non-empty and contains the standing test zone. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the schema validation settings across all zones", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Secret/Secret.test.ts b/packages/alchemy/test/Cloudflare/Secret/Secret.test.ts index 1a1e01756..d4cf06b26 100644 --- a/packages/alchemy/test/Cloudflare/Secret/Secret.test.ts +++ b/packages/alchemy/test/Cloudflare/Secret/Secret.test.ts @@ -14,6 +14,9 @@ import { OBJECT_VAR_VALUE, STRING_VAR_VALUE, } from "./fixtures/worker.ts"; + +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; /** * `Config.redacted("CONFIG_SECRET")` resolves against the active * `ConfigProvider` at deploy time. The default provider reads from @@ -65,7 +68,7 @@ const fetchWhenReady = (url: string) => ); }); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "Config.redacted with literal default round-trips to runtime as Redacted", Effect.gen(function* () { const { url } = yield* stack; @@ -83,7 +86,7 @@ test( { timeout: 180_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "Config.redacted resolved from env deploys as a secret_text and round-trips", Effect.gen(function* () { const { url } = yield* stack; @@ -100,7 +103,7 @@ test( { timeout: 180_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "Config.string round-trips to runtime as a string", Effect.gen(function* () { const { url } = yield* stack; @@ -114,7 +117,7 @@ test( { timeout: 180_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "Config.number round-trips to runtime preserving the number type", Effect.gen(function* () { const { url } = yield* stack; @@ -128,7 +131,7 @@ test( { timeout: 180_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "Config.string with object default round-trips to runtime preserving nested shape", Effect.gen(function* () { const { url } = yield* stack; diff --git a/packages/alchemy/test/Cloudflare/SecretsStore/Secret.test.ts b/packages/alchemy/test/Cloudflare/SecretsStore/Secret.test.ts index 6d099a535..40c4ef1e8 100644 --- a/packages/alchemy/test/Cloudflare/SecretsStore/Secret.test.ts +++ b/packages/alchemy/test/Cloudflare/SecretsStore/Secret.test.ts @@ -6,6 +6,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Redacted from "effect/Redacted"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -17,28 +20,30 @@ const logLevel = Effect.provideService( // Secrets Store and there is no account-wide secret enumeration API, so // `list()` enumerates every store and lists the secrets inside each. Deploy a // store + secret, then assert the deployed secret appears in the result. -test.provider("list enumerates the deployed secret across stores", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const deployed = yield* stack.deploy( - Effect.gen(function* () { - const store = yield* Cloudflare.SecretsStore("ListSecretStore"); - return yield* Cloudflare.Secret("ListSecret", { - store, - value: Redacted.make("sk-list-test-value"), - }); - }), - ); - - const provider = yield* Provider.findProvider(Cloudflare.Secret); - const all = yield* provider.list(); - - expect(all.some((s) => s.secretId === deployed.secretId)).toBe(true); - const found = all.find((s) => s.secretId === deployed.secretId)!; - expect(found.secretName).toEqual(deployed.secretName); - expect(found.storeId).toEqual(deployed.storeId); - - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed secret across stores", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const deployed = yield* stack.deploy( + Effect.gen(function* () { + const store = yield* Cloudflare.SecretsStore("ListSecretStore"); + return yield* Cloudflare.Secret("ListSecret", { + store, + value: Redacted.make("sk-list-test-value"), + }); + }), + ); + + const provider = yield* Provider.findProvider(Cloudflare.Secret); + const all = yield* provider.list(); + + expect(all.some((s) => s.secretId === deployed.secretId)).toBe(true); + const found = all.find((s) => s.secretId === deployed.secretId)!; + expect(found.secretName).toEqual(deployed.secretName); + expect(found.storeId).toEqual(deployed.storeId); + + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/SecretsStore/SecretsStore.test.ts b/packages/alchemy/test/Cloudflare/SecretsStore/SecretsStore.test.ts index 32edd4c26..0b525c152 100644 --- a/packages/alchemy/test/Cloudflare/SecretsStore/SecretsStore.test.ts +++ b/packages/alchemy/test/Cloudflare/SecretsStore/SecretsStore.test.ts @@ -13,6 +13,9 @@ import * as HttpBody from "effect/unstable/http/HttpBody"; import * as HttpClient from "effect/unstable/http/HttpClient"; import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); /** @@ -153,22 +156,24 @@ it.live( // result. Bracketed with `stack.destroy()` at start and end; note the // SecretsStore provider's `delete` is an intentional no-op (account-level // infra), so destroy never tears down the shared store. -test.provider("list enumerates the deployed secrets store", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed secrets store", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); - const deployed = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.SecretsStore("ListStore"); - }), - ); + const deployed = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.SecretsStore("ListStore"); + }), + ); - const provider = yield* Provider.findProvider(Cloudflare.SecretsStore); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider(Cloudflare.SecretsStore); + const all = yield* provider.list(); - expect(all.length).toBeGreaterThan(0); - expect(all.some((s) => s.storeId === deployed.storeId)).toBe(true); + expect(all.length).toBeGreaterThan(0); + expect(all.some((s) => s.storeId === deployed.storeId)).toBe(true); - yield* stack.destroy(); - }), + yield* stack.destroy(); + }), ); diff --git a/packages/alchemy/test/Cloudflare/SecurityTxt/SecurityTxt.test.ts b/packages/alchemy/test/Cloudflare/SecurityTxt/SecurityTxt.test.ts index 1e19a1a88..cb16d07a6 100644 --- a/packages/alchemy/test/Cloudflare/SecurityTxt/SecurityTxt.test.ts +++ b/packages/alchemy/test/Cloudflare/SecurityTxt/SecurityTxt.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -64,7 +67,7 @@ const contact = ["mailto:security@alchemy.run"]; const expires = "2030-01-01T00:00:00Z"; describe.sequential("SecurityTxt", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "creates a security.txt, verifies out-of-band, and deletes on destroy", (stack) => Effect.gen(function* () { @@ -111,69 +114,71 @@ describe.sequential("SecurityTxt", () => { }).pipe(logLevel), ); - test.provider("updates mutable fields in place (full-replace PUT)", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - yield* stack.destroy(); - yield* clearBaseline(zoneId); - - const created = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.SecurityTxt("SecurityTxt", { - zoneId, - contact, - expires, - }); - }), - ); - expect(created.policy).toBeUndefined(); - - const updated = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.SecurityTxt("SecurityTxt", { - zoneId, - contact, - expires, - policy: ["https://alchemy.run/security-policy"], - preferredLanguages: "en, es", - }); - }), - ); - - // Same singleton replaced in place — zone identity is unchanged. - expect(updated.zoneId).toEqual(zoneId); - expect(updated.policy).toEqual(["https://alchemy.run/security-policy"]); - expect(updated.preferredLanguages).toEqual("en, es"); - - const live = yield* getSecurityTxt(zoneId); - expect(typeof live).not.toEqual("string"); - if (typeof live !== "string") { - expect(live.policy).toEqual(["https://alchemy.run/security-policy"]); - expect(live.preferredLanguages).toEqual("en, es"); - } - - // Dropping the optional fields converges back to the minimal file. - const reverted = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.SecurityTxt("SecurityTxt", { - zoneId, - contact, - expires, - }); - }), - ); - expect(reverted.policy).toBeUndefined(); - expect(reverted.preferredLanguages).toBeUndefined(); - - yield* stack.destroy(); - - const gone = yield* getSecurityTxt(zoneId); - expect(gone).toEqual(""); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "updates mutable fields in place (full-replace PUT)", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; + + yield* stack.destroy(); + yield* clearBaseline(zoneId); + + const created = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.SecurityTxt("SecurityTxt", { + zoneId, + contact, + expires, + }); + }), + ); + expect(created.policy).toBeUndefined(); + + const updated = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.SecurityTxt("SecurityTxt", { + zoneId, + contact, + expires, + policy: ["https://alchemy.run/security-policy"], + preferredLanguages: "en, es", + }); + }), + ); + + // Same singleton replaced in place — zone identity is unchanged. + expect(updated.zoneId).toEqual(zoneId); + expect(updated.policy).toEqual(["https://alchemy.run/security-policy"]); + expect(updated.preferredLanguages).toEqual("en, es"); + + const live = yield* getSecurityTxt(zoneId); + expect(typeof live).not.toEqual("string"); + if (typeof live !== "string") { + expect(live.policy).toEqual(["https://alchemy.run/security-policy"]); + expect(live.preferredLanguages).toEqual("en, es"); + } + + // Dropping the optional fields converges back to the minimal file. + const reverted = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.SecurityTxt("SecurityTxt", { + zoneId, + contact, + expires, + }); + }), + ); + expect(reverted.policy).toBeUndefined(); + expect(reverted.preferredLanguages).toBeUndefined(); + + yield* stack.destroy(); + + const gone = yield* getSecurityTxt(zoneId); + expect(gone).toEqual(""); + }).pipe(logLevel), ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "disables the file without deleting it, then destroy removes it", (stack) => Effect.gen(function* () { @@ -224,43 +229,45 @@ describe.sequential("SecurityTxt", () => { // API for this per-zone file, so `list()` enumerates every zone via // `listAllZones` and reads each. Only configured zones are emitted, so deploy // a security.txt on the standing test zone and assert it appears. - test.provider("list enumerates configured security.txt files", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - yield* stack.destroy(); - yield* clearBaseline(zoneId); - - const deployed = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.SecurityTxt("SecurityTxt", { - zoneId, - contact, - expires, - }); - }), - ); - - const provider = yield* Provider.findProvider(Cloudflare.SecurityTxt); - // Ride out token eventual-consistency 403s on the per-zone reads. - const all = yield* provider.list().pipe( - Effect.retry({ - while: (e) => e._tag === "Forbidden", - schedule: forbiddenRetrySchedule, - times: 8, - }), - ); - - expect(all.length).toBeGreaterThan(0); - const entry = all.find((s) => s.zoneId === deployed.zoneId); - expect(entry).toBeDefined(); - expect(entry?.contact).toEqual(contact); - expect(entry?.expires).toEqual(expires); - - yield* stack.destroy(); - - const gone = yield* getSecurityTxt(zoneId); - expect(gone).toEqual(""); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates configured security.txt files", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; + + yield* stack.destroy(); + yield* clearBaseline(zoneId); + + const deployed = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.SecurityTxt("SecurityTxt", { + zoneId, + contact, + expires, + }); + }), + ); + + const provider = yield* Provider.findProvider(Cloudflare.SecurityTxt); + // Ride out token eventual-consistency 403s on the per-zone reads. + const all = yield* provider.list().pipe( + Effect.retry({ + while: (e) => e._tag === "Forbidden", + schedule: forbiddenRetrySchedule, + times: 8, + }), + ); + + expect(all.length).toBeGreaterThan(0); + const entry = all.find((s) => s.zoneId === deployed.zoneId); + expect(entry).toBeDefined(); + expect(entry?.contact).toEqual(contact); + expect(entry?.expires).toEqual(expires); + + yield* stack.destroy(); + + const gone = yield* getSecurityTxt(zoneId); + expect(gone).toEqual(""); + }).pipe(logLevel), ); }); diff --git a/packages/alchemy/test/Cloudflare/Snippets/Snippet.test.ts b/packages/alchemy/test/Cloudflare/Snippets/Snippet.test.ts index bd014d4f7..b75752a39 100644 --- a/packages/alchemy/test/Cloudflare/Snippets/Snippet.test.ts +++ b/packages/alchemy/test/Cloudflare/Snippets/Snippet.test.ts @@ -10,6 +10,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -65,7 +68,7 @@ const findSnippet = (zoneId: string, name: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create with generated name, update code in place, destroy", (stack) => Effect.gen(function* () { @@ -110,50 +113,52 @@ test.provider( }).pipe(logLevel), ); -test.provider("renaming an explicit snippet triggers replacement", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "renaming an explicit snippet triggers replacement", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - yield* stack.destroy(); + yield* stack.destroy(); - const initial = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.Snippet("RenamedSnippet", { - zoneId, - name: NAME_EXPLICIT, - code: codeV1, - }).pipe(adopt(true)); - }), - ); - expect(initial.name).toEqual(NAME_EXPLICIT); - - const live = yield* findSnippet(zoneId, NAME_EXPLICIT); - expect(live?.snippetName).toEqual(NAME_EXPLICIT); - - // The name is the snippet's identity — a rename is a replacement. - const replaced = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.Snippet("RenamedSnippet", { - zoneId, - name: NAME_REPLACED, - code: codeV1, - }).pipe(adopt(true)); - }), - ); - expect(replaced.name).toEqual(NAME_REPLACED); + const initial = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.Snippet("RenamedSnippet", { + zoneId, + name: NAME_EXPLICIT, + code: codeV1, + }).pipe(adopt(true)); + }), + ); + expect(initial.name).toEqual(NAME_EXPLICIT); + + const live = yield* findSnippet(zoneId, NAME_EXPLICIT); + expect(live?.snippetName).toEqual(NAME_EXPLICIT); + + // The name is the snippet's identity — a rename is a replacement. + const replaced = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.Snippet("RenamedSnippet", { + zoneId, + name: NAME_REPLACED, + code: codeV1, + }).pipe(adopt(true)); + }), + ); + expect(replaced.name).toEqual(NAME_REPLACED); - const newLive = yield* findSnippet(zoneId, NAME_REPLACED); - expect(newLive?.snippetName).toEqual(NAME_REPLACED); + const newLive = yield* findSnippet(zoneId, NAME_REPLACED); + expect(newLive?.snippetName).toEqual(NAME_REPLACED); - // The old snippet was deleted as part of the replacement. - const oldGone = yield* findSnippet(zoneId, NAME_EXPLICIT); - expect(oldGone).toBeUndefined(); + // The old snippet was deleted as part of the replacement. + const oldGone = yield* findSnippet(zoneId, NAME_EXPLICIT); + expect(oldGone).toBeUndefined(); - yield* stack.destroy(); + yield* stack.destroy(); - const gone = yield* findSnippet(zoneId, NAME_REPLACED); - expect(gone).toBeUndefined(); - }).pipe(logLevel), + const gone = yield* findSnippet(zoneId, NAME_REPLACED); + expect(gone).toBeUndefined(); + }).pipe(logLevel), ); const NAME_LIST = "alchemy_snippet_list_test"; diff --git a/packages/alchemy/test/Cloudflare/Snippets/SnippetRules.test.ts b/packages/alchemy/test/Cloudflare/Snippets/SnippetRules.test.ts index c0f6516d3..7486399bc 100644 --- a/packages/alchemy/test/Cloudflare/Snippets/SnippetRules.test.ts +++ b/packages/alchemy/test/Cloudflare/Snippets/SnippetRules.test.ts @@ -11,6 +11,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -119,7 +122,7 @@ const purgeRules = (zoneId: string) => // case's snippet delete races the other's rule that still references it // (`snippet is still used`). They are therefore folded into ONE sequential // case: create/update/destroy lifecycle PLUS the `list()` enumeration. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "snippet rules — create, update, list, and destroy in dependency order", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Spectrum/Application.test.ts b/packages/alchemy/test/Cloudflare/Spectrum/Application.test.ts index 547c7630e..c7b7b4098 100644 --- a/packages/alchemy/test/Cloudflare/Spectrum/Application.test.ts +++ b/packages/alchemy/test/Cloudflare/Spectrum/Application.test.ts @@ -13,6 +13,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -93,7 +96,7 @@ const purgeApps = (zoneId: string, dnsName: string, protocol: string) => const resolveEntitledZoneName = (zoneId: string) => zones.getZone({ zoneId }).pipe(Effect.map((z) => z.name)); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed SpectrumProtocolNotAvailable error on unentitled zones", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Speed/TestSchedule.test.ts b/packages/alchemy/test/Cloudflare/Speed/TestSchedule.test.ts index b86f62cce..d24c680f6 100644 --- a/packages/alchemy/test/Cloudflare/Speed/TestSchedule.test.ts +++ b/packages/alchemy/test/Cloudflare/Speed/TestSchedule.test.ts @@ -12,6 +12,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); // Cloudflare counts schedule *creations* against a per-day quota and does NOT @@ -296,7 +299,7 @@ test.provider.skipIf(!runSpeedScheduleTests)( { timeout: 120_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "adoption — existing schedule errors without adopt, takes over with adopt(true)", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Ssl/CertificatePack.test.ts b/packages/alchemy/test/Cloudflare/Ssl/CertificatePack.test.ts index 1998c29f5..7f0792330 100644 --- a/packages/alchemy/test/Cloudflare/Ssl/CertificatePack.test.ts +++ b/packages/alchemy/test/Cloudflare/Ssl/CertificatePack.test.ts @@ -10,6 +10,9 @@ import * as Result from "effect/Result"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -52,7 +55,7 @@ const getPack = (zoneId: string, certificatePackId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed AdvancedCertificateManagerRequired error on unentitled zones", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Ssl/UniversalSsl.test.ts b/packages/alchemy/test/Cloudflare/Ssl/UniversalSsl.test.ts index 76183d7dc..acfef8b55 100644 --- a/packages/alchemy/test/Cloudflare/Ssl/UniversalSsl.test.ts +++ b/packages/alchemy/test/Cloudflare/Ssl/UniversalSsl.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -66,7 +69,7 @@ const setBaseline = (zoneId: string, enabled: boolean) => ); describe.sequential("UniversalSsl", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "disables Universal SSL and restores the original value on destroy", (stack) => Effect.gen(function* () { @@ -102,7 +105,7 @@ describe.sequential("UniversalSsl", () => { { timeout: 180_000 }, ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "updates enabled in place and keeps the captured initial value", (stack) => Effect.gen(function* () { @@ -148,7 +151,7 @@ describe.sequential("UniversalSsl", () => { { timeout: 180_000 }, ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "destroy restores a disabled baseline when managing from a disabled zone", (stack) => Effect.gen(function* () { @@ -189,19 +192,21 @@ describe.sequential("UniversalSsl", () => { // API for this per-zone setting, so `list()` enumerates every zone via // `listAllZones` and reads the singleton in each. Assert the result is // non-empty and contains the standing test zone. - test.provider("list enumerates the setting across all zones", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the setting across all zones", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - const provider = yield* Provider.findProvider(Cloudflare.UniversalSsl); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider(Cloudflare.UniversalSsl); + const all = yield* provider.list(); - expect(all.length).toBeGreaterThan(0); - expect(all.some((s) => s.zoneId === zoneId)).toBe(true); + expect(all.length).toBeGreaterThan(0); + expect(all.some((s) => s.zoneId === zoneId)).toBe(true); - // `stack` is unused here (the singleton always exists on every zone), - // but keep the destroy bookend so the harness state stays clean. - yield* stack.destroy(); - }).pipe(logLevel), + // `stack` is unused here (the singleton always exists on every zone), + // but keep the destroy bookend so the harness state stays clean. + yield* stack.destroy(); + }).pipe(logLevel), ); }); diff --git a/packages/alchemy/test/Cloudflare/StateStore/State.test.ts b/packages/alchemy/test/Cloudflare/StateStore/State.test.ts index 4044da0aa..58f422347 100644 --- a/packages/alchemy/test/Cloudflare/StateStore/State.test.ts +++ b/packages/alchemy/test/Cloudflare/StateStore/State.test.ts @@ -6,6 +6,9 @@ import { expect } from "@effect/vitest"; import * as Effect from "effect/Effect"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + /** * Live-API tests for the deployed Cloudflare State Store at * `alchemy-state-store`. The State service is wired up via @@ -75,7 +78,7 @@ const replacedState = (fqn: string) => ({ // read as one sequence (first case wipes it, the rest build it up and tear it // down). They must run serially under the global concurrent test config. describe.sequential("State", () => { - test( + test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "getVersion returns the current STATE_STORE_VERSION", Effect.gen(function* () { const store = yield* yield* State; @@ -85,7 +88,7 @@ describe.sequential("State", () => { { timeout: 60_000 }, ); - test( + test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "DELETE /state/stacks/:stack wipes the test namespace (cleanup)", Effect.gen(function* () { const store = yield* yield* State; @@ -96,7 +99,7 @@ describe.sequential("State", () => { { timeout: 60_000 }, ); - test( + test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "PUT /resources/:fqn (setState) persists a resource", Effect.gen(function* () { const store = yield* yield* State; @@ -114,7 +117,7 @@ describe.sequential("State", () => { { timeout: 60_000 }, ); - test( + test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "GET /resources/:fqn (getState) reads back the persisted value", Effect.gen(function* () { const store = yield* yield* State; @@ -127,7 +130,7 @@ describe.sequential("State", () => { { timeout: 60_000 }, ); - test( + test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "GET /resources/:fqn returns undefined for a missing fqn", Effect.gen(function* () { const store = yield* yield* State; @@ -141,7 +144,7 @@ describe.sequential("State", () => { { timeout: 60_000 }, ); - test( + test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "GET /resources (listResources) returns the FQNs in the stage", Effect.gen(function* () { const store = yield* yield* State; @@ -161,7 +164,7 @@ describe.sequential("State", () => { { timeout: 60_000 }, ); - test( + test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "GET /stacks (listStacks) includes the registered test stack", Effect.gen(function* () { const store = yield* yield* State; @@ -171,7 +174,7 @@ describe.sequential("State", () => { { timeout: 60_000 }, ); - test( + test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "GET /stacks/:stack/stages (listStages) includes the test stage", Effect.gen(function* () { const store = yield* yield* State; @@ -181,7 +184,7 @@ describe.sequential("State", () => { { timeout: 60_000 }, ); - test( + test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "PUT /output (setStackOutput) persists a stack output", Effect.gen(function* () { const store = yield* yield* State; @@ -195,7 +198,7 @@ describe.sequential("State", () => { { timeout: 60_000 }, ); - test( + test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "GET /output (getStackOutput) reads back the persisted output", Effect.gen(function* () { const store = yield* yield* State; @@ -205,7 +208,7 @@ describe.sequential("State", () => { { timeout: 60_000 }, ); - test( + test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "GET /output returns undefined for an un-deployed stage", Effect.gen(function* () { const store = yield* yield* State; @@ -218,7 +221,7 @@ describe.sequential("State", () => { { timeout: 60_000 }, ); - test( + test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "GET /replaced-resources (getReplacedResources) returns status===replaced rows", Effect.gen(function* () { const store = yield* yield* State; @@ -239,7 +242,7 @@ describe.sequential("State", () => { { timeout: 60_000 }, ); - test( + test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "DELETE /resources/:fqn (deleteState) removes a single resource", Effect.gen(function* () { const store = yield* yield* State; @@ -253,7 +256,7 @@ describe.sequential("State", () => { { timeout: 60_000 }, ); - test( + test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "DELETE /stacks/:stack?stage=... clears one stage but leaves the stack registered", Effect.gen(function* () { const store = yield* yield* State; @@ -269,7 +272,7 @@ describe.sequential("State", () => { { timeout: 60_000 }, ); - test( + test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "DELETE /stacks/:stack (no stage) removes the stack from listStacks", Effect.gen(function* () { const store = yield* yield* State; @@ -287,7 +290,7 @@ describe.sequential("State", () => { * memoization, edge propagation, intermediary content-type munging) * one of these calls should surface it. */ - test( + test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "setState 100x sequential — surfaces transient failures", Effect.gen(function* () { const store = yield* yield* State; @@ -326,7 +329,7 @@ describe.sequential("State", () => { * (`Store.getByName(stack)`) and the HttpApiBuilder layer * initialization that wouldn't show up serially. */ - test( + test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "setState 100x concurrent — surfaces racy transient failures", Effect.gen(function* () { const store = yield* yield* State; @@ -371,7 +374,7 @@ describe.sequential("State", () => { * pattern. The test fails on the first non-2xx so any regression of * the underlying issue is caught immediately. */ - test( + test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "GET+PUT interleaved 30x5 — engine traffic pattern", Effect.gen(function* () { const store = yield* yield* State; diff --git a/packages/alchemy/test/Cloudflare/Stream/LiveInput.test.ts b/packages/alchemy/test/Cloudflare/Stream/LiveInput.test.ts index 54f6094b9..47b9d5e67 100644 --- a/packages/alchemy/test/Cloudflare/Stream/LiveInput.test.ts +++ b/packages/alchemy/test/Cloudflare/Stream/LiveInput.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -42,7 +45,7 @@ const expectGone = (accountId: string, liveInputId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create, update in place, and delete a live input", (stack) => Effect.gen(function* () { @@ -101,7 +104,7 @@ test.provider( { timeout: 120_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "recreates after out-of-band delete", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Stream/LiveInputOutput.test.ts b/packages/alchemy/test/Cloudflare/Stream/LiveInputOutput.test.ts index 830ded44e..90608458b 100644 --- a/packages/alchemy/test/Cloudflare/Stream/LiveInputOutput.test.ts +++ b/packages/alchemy/test/Cloudflare/Stream/LiveInputOutput.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -52,7 +55,7 @@ const expectGone = (accountId: string, liveInputId: string, outputId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create, toggle enabled in place, replace destination, and delete", (stack) => Effect.gen(function* () { @@ -164,7 +167,7 @@ test.provider( { timeout: 120_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "recreates after out-of-band delete", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Stream/SigningKey.test.ts b/packages/alchemy/test/Cloudflare/Stream/SigningKey.test.ts index 8c76a90d0..d587edea1 100644 --- a/packages/alchemy/test/Cloudflare/Stream/SigningKey.test.ts +++ b/packages/alchemy/test/Cloudflare/Stream/SigningKey.test.ts @@ -9,6 +9,9 @@ import * as Redacted from "effect/Redacted"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -46,7 +49,7 @@ const expectGone = (accountId: string, keyId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create and delete a signing key", (stack) => Effect.gen(function* () { @@ -81,7 +84,7 @@ test.provider( { timeout: 120_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed signing key", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Stream/Watermark.test.ts b/packages/alchemy/test/Cloudflare/Stream/Watermark.test.ts index 44acf9a5f..8c93b3704 100644 --- a/packages/alchemy/test/Cloudflare/Stream/Watermark.test.ts +++ b/packages/alchemy/test/Cloudflare/Stream/Watermark.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -47,7 +50,7 @@ const expectGone = (accountId: string, watermarkId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create and delete a watermark with default name", (stack) => Effect.gen(function* () { @@ -79,7 +82,7 @@ test.provider( { timeout: 120_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "prop change replaces the watermark (create-only resource)", (stack) => Effect.gen(function* () { @@ -139,7 +142,7 @@ test.provider( // enumerate every watermark profile in the account via the provider's // `list()` and assert the deployed uid is present in the exhaustively // paginated result. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed watermark", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Stream/Webhook.test.ts b/packages/alchemy/test/Cloudflare/Stream/Webhook.test.ts index 086575a01..858073359 100644 --- a/packages/alchemy/test/Cloudflare/Stream/Webhook.test.ts +++ b/packages/alchemy/test/Cloudflare/Stream/Webhook.test.ts @@ -9,6 +9,9 @@ import * as Redacted from "effect/Redacted"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -44,7 +47,7 @@ const expectGone = (accountId: string) => // The Stream webhook is an account-level singleton, so the whole // lifecycle lives in one sequential test to avoid the tests fighting // over the single slot. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "configure, update, and delete the account webhook", (stack) => Effect.gen(function* () { @@ -107,7 +110,7 @@ test.provider( // entitlement (mutating the webhook is covered by the gated lifecycle test // above, which fails with the typed Cloudflare `StreamSubscriptionRequired` // error code 10010 on un-entitled accounts). -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the account Stream webhook", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Tags/ZoneResourceTags.test.ts b/packages/alchemy/test/Cloudflare/Tags/ZoneResourceTags.test.ts index a83a92b0f..b68ca459c 100644 --- a/packages/alchemy/test/Cloudflare/Tags/ZoneResourceTags.test.ts +++ b/packages/alchemy/test/Cloudflare/Tags/ZoneResourceTags.test.ts @@ -10,6 +10,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -72,149 +75,157 @@ const expectTagsCleared = ( Effect.map((tags) => expect(tags).toEqual({})), ); -test.provider("create, update, and clear tags on a DNS record", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - yield* stack.destroy(); - - const v1 = yield* stack.deploy( - Effect.gen(function* () { - const record = yield* Cloudflare.DnsRecord("CrudTaggedRecord", { - zoneId, - name: CRUD_RECORD_NAME, - type: "A", - content: "203.0.113.50", - }).pipe(adopt(true)); - const tags = yield* Cloudflare.ZoneResourceTags("CrudRecordTags", { - zoneId, - resourceType: "dns_record", - resourceId: record.recordId, - tags: { env: "test", team: "alchemy" }, - }).pipe(adopt(true)); - return { record, tags }; - }), - ); - - expect(v1.tags.zoneId).toEqual(zoneId); - expect(v1.tags.resourceType).toEqual("dns_record"); - expect(v1.tags.resourceId).toEqual(v1.record.recordId); - expect(v1.tags.tags).toEqual({ env: "test", team: "alchemy" }); - expect(v1.tags.etag).toBeTruthy(); - - const live = yield* getTags(zoneId, v1.record.recordId, "dns_record"); - expect(live).toEqual({ env: "test", team: "alchemy" }); - - // In-place update — PUT replaces the full set: change `env`, drop - // `team`, add `owner`. - const v2 = yield* stack.deploy( - Effect.gen(function* () { - const record = yield* Cloudflare.DnsRecord("CrudTaggedRecord", { - zoneId, - name: CRUD_RECORD_NAME, - type: "A", - content: "203.0.113.50", - }).pipe(adopt(true)); - const tags = yield* Cloudflare.ZoneResourceTags("CrudRecordTags", { - zoneId, - resourceType: "dns_record", - resourceId: record.recordId, - tags: { env: "prod", owner: "qa" }, - }).pipe(adopt(true)); - return { record, tags }; - }), - ); - - // Same target record — the tag set updated in place. - expect(v2.record.recordId).toEqual(v1.record.recordId); - expect(v2.tags.tags).toEqual({ env: "prod", owner: "qa" }); - - const updated = yield* getTags(zoneId, v2.record.recordId, "dns_record"); - expect(updated).toEqual({ env: "prod", owner: "qa" }); - - yield* stack.destroy(); - - yield* expectTagsCleared(zoneId, v1.record.recordId, "dns_record"); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, update, and clear tags on a DNS record", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; + + yield* stack.destroy(); + + const v1 = yield* stack.deploy( + Effect.gen(function* () { + const record = yield* Cloudflare.DnsRecord("CrudTaggedRecord", { + zoneId, + name: CRUD_RECORD_NAME, + type: "A", + content: "203.0.113.50", + }).pipe(adopt(true)); + const tags = yield* Cloudflare.ZoneResourceTags("CrudRecordTags", { + zoneId, + resourceType: "dns_record", + resourceId: record.recordId, + tags: { env: "test", team: "alchemy" }, + }).pipe(adopt(true)); + return { record, tags }; + }), + ); + + expect(v1.tags.zoneId).toEqual(zoneId); + expect(v1.tags.resourceType).toEqual("dns_record"); + expect(v1.tags.resourceId).toEqual(v1.record.recordId); + expect(v1.tags.tags).toEqual({ env: "test", team: "alchemy" }); + expect(v1.tags.etag).toBeTruthy(); + + const live = yield* getTags(zoneId, v1.record.recordId, "dns_record"); + expect(live).toEqual({ env: "test", team: "alchemy" }); + + // In-place update — PUT replaces the full set: change `env`, drop + // `team`, add `owner`. + const v2 = yield* stack.deploy( + Effect.gen(function* () { + const record = yield* Cloudflare.DnsRecord("CrudTaggedRecord", { + zoneId, + name: CRUD_RECORD_NAME, + type: "A", + content: "203.0.113.50", + }).pipe(adopt(true)); + const tags = yield* Cloudflare.ZoneResourceTags("CrudRecordTags", { + zoneId, + resourceType: "dns_record", + resourceId: record.recordId, + tags: { env: "prod", owner: "qa" }, + }).pipe(adopt(true)); + return { record, tags }; + }), + ); + + // Same target record — the tag set updated in place. + expect(v2.record.recordId).toEqual(v1.record.recordId); + expect(v2.tags.tags).toEqual({ env: "prod", owner: "qa" }); + + const updated = yield* getTags(zoneId, v2.record.recordId, "dns_record"); + expect(updated).toEqual({ env: "prod", owner: "qa" }); + + yield* stack.destroy(); + + yield* expectTagsCleared(zoneId, v1.record.recordId, "dns_record"); + }).pipe(logLevel), ); -test.provider("tag the zone itself and clear on destroy", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - yield* stack.destroy(); - // Normalize the baseline — clear any leftover zone tags from - // interrupted runs (only this suite tags the shared test zone). - yield* resourceTagging - .deleteZoneTag({ zoneId, resourceId: zoneId, resourceType: "zone" }) - .pipe(Effect.retry(forbiddenRetry)); - - const deployed = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.ZoneResourceTags("ZoneTags", { - zoneId, - resourceType: "zone", - resourceId: zoneId, - tags: { "alchemy-zone-probe": "v1" }, - }).pipe(adopt(true)); - }), - ); - - expect(deployed.tags).toEqual({ "alchemy-zone-probe": "v1" }); - - const live = yield* getTags(zoneId, zoneId, "zone"); - expect(live).toEqual({ "alchemy-zone-probe": "v1" }); - - yield* stack.destroy(); - - yield* expectTagsCleared(zoneId, zoneId, "zone"); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "tag the zone itself and clear on destroy", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; + + yield* stack.destroy(); + // Normalize the baseline — clear any leftover zone tags from + // interrupted runs (only this suite tags the shared test zone). + yield* resourceTagging + .deleteZoneTag({ zoneId, resourceId: zoneId, resourceType: "zone" }) + .pipe(Effect.retry(forbiddenRetry)); + + const deployed = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.ZoneResourceTags("ZoneTags", { + zoneId, + resourceType: "zone", + resourceId: zoneId, + tags: { "alchemy-zone-probe": "v1" }, + }).pipe(adopt(true)); + }), + ); + + expect(deployed.tags).toEqual({ "alchemy-zone-probe": "v1" }); + + const live = yield* getTags(zoneId, zoneId, "zone"); + expect(live).toEqual({ "alchemy-zone-probe": "v1" }); + + yield* stack.destroy(); + + yield* expectTagsCleared(zoneId, zoneId, "zone"); + }).pipe(logLevel), ); -test.provider("list enumerates tagged zone-scoped resources", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - yield* stack.destroy(); - - const deployed = yield* stack.deploy( - Effect.gen(function* () { - const record = yield* Cloudflare.DnsRecord("ListTaggedRecord", { - zoneId, - name: LIST_RECORD_NAME, - type: "A", - content: "203.0.113.50", - }).pipe(adopt(true)); - const tags = yield* Cloudflare.ZoneResourceTags("ListRecordTags", { - zoneId, - resourceType: "dns_record", - resourceId: record.recordId, - tags: { env: "test", team: "alchemy" }, - }).pipe(adopt(true)); - return { record, tags }; - }), - ); - - const provider = yield* Provider.findProvider(Cloudflare.ZoneResourceTags); - - // The account-wide tag index is eventually consistent — poll until the - // freshly-tagged record shows up (bounded so it fails fast). - const all = yield* provider.list().pipe( - Effect.repeat({ - schedule: Schedule.exponential("1 second"), - until: (rows) => - rows.some((r) => r.resourceId === deployed.record.recordId), - times: 8, - }), - ); - - const found = all.find((r) => r.resourceId === deployed.record.recordId); - expect(found).toBeDefined(); - expect(found?.zoneId).toEqual(zoneId); - expect(found?.resourceType).toEqual("dns_record"); - expect(found?.tags).toEqual({ env: "test", team: "alchemy" }); - expect(found?.etag).toBeTruthy(); - - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates tagged zone-scoped resources", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; + + yield* stack.destroy(); + + const deployed = yield* stack.deploy( + Effect.gen(function* () { + const record = yield* Cloudflare.DnsRecord("ListTaggedRecord", { + zoneId, + name: LIST_RECORD_NAME, + type: "A", + content: "203.0.113.50", + }).pipe(adopt(true)); + const tags = yield* Cloudflare.ZoneResourceTags("ListRecordTags", { + zoneId, + resourceType: "dns_record", + resourceId: record.recordId, + tags: { env: "test", team: "alchemy" }, + }).pipe(adopt(true)); + return { record, tags }; + }), + ); + + const provider = yield* Provider.findProvider( + Cloudflare.ZoneResourceTags, + ); + + // The account-wide tag index is eventually consistent — poll until the + // freshly-tagged record shows up (bounded so it fails fast). + const all = yield* provider.list().pipe( + Effect.repeat({ + schedule: Schedule.exponential("1 second"), + until: (rows) => + rows.some((r) => r.resourceId === deployed.record.recordId), + times: 8, + }), + ); + + const found = all.find((r) => r.resourceId === deployed.record.recordId); + expect(found).toBeDefined(); + expect(found?.zoneId).toEqual(zoneId); + expect(found?.resourceType).toEqual("dns_record"); + expect(found?.tags).toEqual({ env: "test", team: "alchemy" }); + expect(found?.etag).toBeTruthy(); + + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/TokenValidation/TokenValidation.test.ts b/packages/alchemy/test/Cloudflare/TokenValidation/TokenValidation.test.ts index 9cdc2336d..79bf0fee7 100644 --- a/packages/alchemy/test/Cloudflare/TokenValidation/TokenValidation.test.ts +++ b/packages/alchemy/test/Cloudflare/TokenValidation/TokenValidation.test.ts @@ -11,6 +11,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { JWKS_KEY_1, JWKS_KEY_2 } from "./fixtures/jwks.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -70,7 +73,7 @@ const getRule = (zoneId: string, ruleId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed TokenValidationNotEntitled error on unentitled accounts", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Tunnel/Configuration.test.ts b/packages/alchemy/test/Cloudflare/Tunnel/Configuration.test.ts index 8c4650139..c370ac41b 100644 --- a/packages/alchemy/test/Cloudflare/Tunnel/Configuration.test.ts +++ b/packages/alchemy/test/Cloudflare/Tunnel/Configuration.test.ts @@ -6,6 +6,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -19,7 +22,7 @@ describe("Tunnel Configuration", () => { // enumerates every cfd_tunnel in the account and reads each tunnel's config. // Deploy a tunnel + configuration, then assert the deployed tunnel appears in // the listed result. - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates configurations across all tunnels", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Tunnel/HostnameRoute.test.ts b/packages/alchemy/test/Cloudflare/Tunnel/HostnameRoute.test.ts index e17484165..8231cdccd 100644 --- a/packages/alchemy/test/Cloudflare/Tunnel/HostnameRoute.test.ts +++ b/packages/alchemy/test/Cloudflare/Tunnel/HostnameRoute.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -54,7 +57,7 @@ const cleanupRoutesByHostname = (accountId: string, hostname: string) => Effect.catchTag("Forbidden", () => Effect.void), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create, update comment in place, and destroy a hostname route", (stack) => Effect.gen(function* () { @@ -127,7 +130,7 @@ test.provider( { timeout: 90_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed hostname route", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Tunnel/Route.test.ts b/packages/alchemy/test/Cloudflare/Tunnel/Route.test.ts index e057b6f79..066e9aac7 100644 --- a/packages/alchemy/test/Cloudflare/Tunnel/Route.test.ts +++ b/packages/alchemy/test/Cloudflare/Tunnel/Route.test.ts @@ -9,6 +9,9 @@ import * as Option from "effect/Option"; import { MinimumLogLevel } from "effect/References"; import * as Stream from "effect/Stream"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -24,190 +27,198 @@ const NETWORK_UPDATE = "10.99.2.0/24"; const NETWORK_ADOPT = "10.99.3.0/24"; const NETWORK_LIST = "10.99.4.0/24"; -test.provider("create and delete route with default props", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create and delete route with default props", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const { tunnel, route } = yield* stack.deploy( + Effect.gen(function* () { + const tunnel = yield* Cloudflare.Tunnel("RouteHostTunnel", { + adopt: true, + }); + const route = yield* Cloudflare.TunnelRoute("DefaultRoute", { + tunnelId: tunnel.tunnelId, + network: NETWORK_DEFAULT, + adopt: true, + }); + return { tunnel, route }; + }), + ); - yield* stack.destroy(); + expect(route.routeId).toBeDefined(); + expect(route.network).toEqual(NETWORK_DEFAULT); + expect(route.tunnelId).toEqual(tunnel.tunnelId); + expect(route.accountId).toEqual(accountId); - const { tunnel, route } = yield* stack.deploy( - Effect.gen(function* () { - const tunnel = yield* Cloudflare.Tunnel("RouteHostTunnel", { - adopt: true, - }); - const route = yield* Cloudflare.TunnelRoute("DefaultRoute", { - tunnelId: tunnel.tunnelId, - network: NETWORK_DEFAULT, - adopt: true, - }); - return { tunnel, route }; - }), - ); - - expect(route.routeId).toBeDefined(); - expect(route.network).toEqual(NETWORK_DEFAULT); - expect(route.tunnelId).toEqual(tunnel.tunnelId); - expect(route.accountId).toEqual(accountId); - - const actual = yield* zeroTrust.getNetworkRoute({ - accountId, - routeId: route.routeId, - }); - expect(actual.id).toEqual(route.routeId); - expect(actual.network).toEqual(NETWORK_DEFAULT); - expect(actual.tunnelId).toEqual(tunnel.tunnelId); - - yield* stack.destroy(); - }).pipe(logLevel), + const actual = yield* zeroTrust.getNetworkRoute({ + accountId, + routeId: route.routeId, + }); + expect(actual.id).toEqual(route.routeId); + expect(actual.network).toEqual(NETWORK_DEFAULT); + expect(actual.tunnelId).toEqual(tunnel.tunnelId); + + yield* stack.destroy(); + }).pipe(logLevel), ); -test.provider("updating the comment patches in place", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "updating the comment patches in place", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const initial = yield* stack.deploy( + Effect.gen(function* () { + const tunnel = yield* Cloudflare.Tunnel("RouteUpdateTunnel", { + adopt: true, + }); + const route = yield* Cloudflare.TunnelRoute("UpdateRoute", { + tunnelId: tunnel.tunnelId, + network: NETWORK_UPDATE, + comment: "v1", + adopt: true, + }); + return { tunnel, route }; + }), + ); - yield* stack.destroy(); + expect(initial.route.comment).toEqual("v1"); + + const updated = yield* stack.deploy( + Effect.gen(function* () { + const tunnel = yield* Cloudflare.Tunnel("RouteUpdateTunnel", { + adopt: true, + }); + const route = yield* Cloudflare.TunnelRoute("UpdateRoute", { + tunnelId: tunnel.tunnelId, + network: NETWORK_UPDATE, + comment: "v2", + adopt: true, + }); + return { tunnel, route }; + }), + ); - const initial = yield* stack.deploy( - Effect.gen(function* () { - const tunnel = yield* Cloudflare.Tunnel("RouteUpdateTunnel", { - adopt: true, - }); - const route = yield* Cloudflare.TunnelRoute("UpdateRoute", { - tunnelId: tunnel.tunnelId, - network: NETWORK_UPDATE, - comment: "v1", - adopt: true, - }); - return { tunnel, route }; - }), - ); - - expect(initial.route.comment).toEqual("v1"); - - const updated = yield* stack.deploy( - Effect.gen(function* () { - const tunnel = yield* Cloudflare.Tunnel("RouteUpdateTunnel", { - adopt: true, - }); - const route = yield* Cloudflare.TunnelRoute("UpdateRoute", { - tunnelId: tunnel.tunnelId, - network: NETWORK_UPDATE, - comment: "v2", - adopt: true, - }); - return { tunnel, route }; - }), - ); - - expect(updated.route.routeId).toEqual(initial.route.routeId); - expect(updated.route.comment).toEqual("v2"); - - const actual = yield* zeroTrust.getNetworkRoute({ - accountId, - routeId: updated.route.routeId, - }); - expect(actual.comment).toEqual("v2"); - - yield* stack.destroy(); - }).pipe(logLevel), -); + expect(updated.route.routeId).toEqual(initial.route.routeId); + expect(updated.route.comment).toEqual("v2"); -test.provider("adopt: takes over a pre-existing route", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - // Stand up just the tunnel via the stack so we have a stable tunnelId to - // attach a manually-created route to. - const { tunnel } = yield* stack.deploy( - Effect.gen(function* () { - const tunnel = yield* Cloudflare.Tunnel("RouteAdoptTunnel", { - adopt: true, - }); - return { tunnel }; - }), - ); - - // Manually create the route outside of alchemy's state. The reconcile - // path with `adopt: true` should find this and take ownership rather - // than failing or creating a duplicate. Route networks are unique - // account-wide, so on Conflict (a prior failed run leaked the route) - // reuse the existing one — that's an equally valid pre-existing route. - const pre = yield* zeroTrust - .createNetworkRoute({ + const actual = yield* zeroTrust.getNetworkRoute({ accountId, - tunnelId: tunnel.tunnelId, - network: NETWORK_ADOPT, - comment: "pre-existing", - }) - .pipe( - Effect.catch(() => - zeroTrust.listNetworkRoutes - .items({ - accountId, - isDeleted: false, - networkSubset: NETWORK_ADOPT, - networkSuperset: NETWORK_ADOPT, - }) - .pipe( - Stream.filter((r) => r.network === NETWORK_ADOPT), - Stream.runHead, - Effect.map(Option.getOrUndefined), - ), - ), + routeId: updated.route.routeId, + }); + expect(actual.comment).toEqual("v2"); + + yield* stack.destroy(); + }).pipe(logLevel), +); + +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "adopt: takes over a pre-existing route", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + // Stand up just the tunnel via the stack so we have a stable tunnelId to + // attach a manually-created route to. + const { tunnel } = yield* stack.deploy( + Effect.gen(function* () { + const tunnel = yield* Cloudflare.Tunnel("RouteAdoptTunnel", { + adopt: true, + }); + return { tunnel }; + }), ); - expect(pre?.id).toBeDefined(); - - const adopted = yield* stack.deploy( - Effect.gen(function* () { - const tunnel = yield* Cloudflare.Tunnel("RouteAdoptTunnel", { - adopt: true, - }); - const route = yield* Cloudflare.TunnelRoute("AdoptRoute", { + + // Manually create the route outside of alchemy's state. The reconcile + // path with `adopt: true` should find this and take ownership rather + // than failing or creating a duplicate. Route networks are unique + // account-wide, so on Conflict (a prior failed run leaked the route) + // reuse the existing one — that's an equally valid pre-existing route. + const pre = yield* zeroTrust + .createNetworkRoute({ + accountId, tunnelId: tunnel.tunnelId, network: NETWORK_ADOPT, - adopt: true, - }); - return { route }; - }), - ); + comment: "pre-existing", + }) + .pipe( + Effect.catch(() => + zeroTrust.listNetworkRoutes + .items({ + accountId, + isDeleted: false, + networkSubset: NETWORK_ADOPT, + networkSuperset: NETWORK_ADOPT, + }) + .pipe( + Stream.filter((r) => r.network === NETWORK_ADOPT), + Stream.runHead, + Effect.map(Option.getOrUndefined), + ), + ), + ); + expect(pre?.id).toBeDefined(); + + const adopted = yield* stack.deploy( + Effect.gen(function* () { + const tunnel = yield* Cloudflare.Tunnel("RouteAdoptTunnel", { + adopt: true, + }); + const route = yield* Cloudflare.TunnelRoute("AdoptRoute", { + tunnelId: tunnel.tunnelId, + network: NETWORK_ADOPT, + adopt: true, + }); + return { route }; + }), + ); - expect(adopted.route.routeId).toEqual(pre?.id); - expect(adopted.route.network).toEqual(NETWORK_ADOPT); + expect(adopted.route.routeId).toEqual(pre?.id); + expect(adopted.route.network).toEqual(NETWORK_ADOPT); - yield* stack.destroy(); - }).pipe(logLevel), + yield* stack.destroy(); + }).pipe(logLevel), ); -test.provider("list enumerates the deployed route", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed route", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const { route } = yield* stack.deploy( + Effect.gen(function* () { + const tunnel = yield* Cloudflare.Tunnel("RouteListTunnel", { + adopt: true, + }); + const route = yield* Cloudflare.TunnelRoute("ListRoute", { + tunnelId: tunnel.tunnelId, + network: NETWORK_LIST, + adopt: true, + }); + return { route }; + }), + ); + + const provider = yield* Provider.findProvider(Cloudflare.TunnelRoute); + const all = yield* provider.list(); - yield* stack.destroy(); + const found = all.find((r) => r.routeId === route.routeId); + expect(found).toBeDefined(); + expect(found?.network).toEqual(NETWORK_LIST); + expect(found?.accountId).toEqual(accountId); - const { route } = yield* stack.deploy( - Effect.gen(function* () { - const tunnel = yield* Cloudflare.Tunnel("RouteListTunnel", { - adopt: true, - }); - const route = yield* Cloudflare.TunnelRoute("ListRoute", { - tunnelId: tunnel.tunnelId, - network: NETWORK_LIST, - adopt: true, - }); - return { route }; - }), - ); - - const provider = yield* Provider.findProvider(Cloudflare.TunnelRoute); - const all = yield* provider.list(); - - const found = all.find((r) => r.routeId === route.routeId); - expect(found).toBeDefined(); - expect(found?.network).toEqual(NETWORK_LIST); - expect(found?.accountId).toEqual(accountId); - - yield* stack.destroy(); - }).pipe(logLevel), + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Tunnel/VirtualNetwork.test.ts b/packages/alchemy/test/Cloudflare/Tunnel/VirtualNetwork.test.ts index 08dbd792b..07bc5aa97 100644 --- a/packages/alchemy/test/Cloudflare/Tunnel/VirtualNetwork.test.ts +++ b/packages/alchemy/test/Cloudflare/Tunnel/VirtualNetwork.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -46,147 +49,155 @@ const expectGone = (accountId: string, virtualNetworkId: string) => }), ); -test.provider("create, verify, and destroy a virtual network", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, verify, and destroy a virtual network", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - yield* stack.destroy(); + yield* stack.destroy(); - const vnet = yield* stack.deploy( - Cloudflare.TunnelVirtualNetwork("BasicVnet", { - name: "alchemy-zt-vnet-basic", - comment: "alchemy test vnet", - }).pipe(adopt(true)), - ); + const vnet = yield* stack.deploy( + Cloudflare.TunnelVirtualNetwork("BasicVnet", { + name: "alchemy-zt-vnet-basic", + comment: "alchemy test vnet", + }).pipe(adopt(true)), + ); - expect(vnet.virtualNetworkId).toBeTruthy(); - expect(vnet.accountId).toEqual(accountId); - expect(vnet.name).toEqual("alchemy-zt-vnet-basic"); - expect(vnet.comment).toEqual("alchemy test vnet"); - expect(vnet.isDefaultNetwork).toBe(false); + expect(vnet.virtualNetworkId).toBeTruthy(); + expect(vnet.accountId).toEqual(accountId); + expect(vnet.name).toEqual("alchemy-zt-vnet-basic"); + expect(vnet.comment).toEqual("alchemy test vnet"); + expect(vnet.isDefaultNetwork).toBe(false); - const live = yield* getVnet(accountId, vnet.virtualNetworkId); - expect(live.name).toEqual("alchemy-zt-vnet-basic"); - expect(live.comment).toEqual("alchemy test vnet"); + const live = yield* getVnet(accountId, vnet.virtualNetworkId); + expect(live.name).toEqual("alchemy-zt-vnet-basic"); + expect(live.comment).toEqual("alchemy test vnet"); - yield* stack.destroy(); - yield* expectGone(accountId, vnet.virtualNetworkId); - }).pipe(logLevel), + yield* stack.destroy(); + yield* expectGone(accountId, vnet.virtualNetworkId); + }).pipe(logLevel), ); -test.provider("update name and comment in place (same id)", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const initial = yield* stack.deploy( - Cloudflare.TunnelVirtualNetwork("UpdateVnet", { - name: "alchemy-zt-vnet-update", - comment: "v1", - }).pipe(adopt(true)), - ); - - const updated = yield* stack.deploy( - Cloudflare.TunnelVirtualNetwork("UpdateVnet", { - name: "alchemy-zt-vnet-update-v2", - comment: "v2", - }).pipe(adopt(true)), - ); - - // Same vnet mutated in place — not a replacement. - expect(updated.virtualNetworkId).toEqual(initial.virtualNetworkId); - expect(updated.name).toEqual("alchemy-zt-vnet-update-v2"); - expect(updated.comment).toEqual("v2"); - - const live = yield* getVnet(accountId, updated.virtualNetworkId); - expect(live.name).toEqual("alchemy-zt-vnet-update-v2"); - expect(live.comment).toEqual("v2"); - - // Redeploying identical props is a no-op. - const noop = yield* stack.deploy( - Cloudflare.TunnelVirtualNetwork("UpdateVnet", { - name: "alchemy-zt-vnet-update-v2", - comment: "v2", - }).pipe(adopt(true)), - ); - expect(noop.virtualNetworkId).toEqual(initial.virtualNetworkId); - - yield* stack.destroy(); - yield* expectGone(accountId, initial.virtualNetworkId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "update name and comment in place (same id)", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const initial = yield* stack.deploy( + Cloudflare.TunnelVirtualNetwork("UpdateVnet", { + name: "alchemy-zt-vnet-update", + comment: "v1", + }).pipe(adopt(true)), + ); + + const updated = yield* stack.deploy( + Cloudflare.TunnelVirtualNetwork("UpdateVnet", { + name: "alchemy-zt-vnet-update-v2", + comment: "v2", + }).pipe(adopt(true)), + ); + + // Same vnet mutated in place — not a replacement. + expect(updated.virtualNetworkId).toEqual(initial.virtualNetworkId); + expect(updated.name).toEqual("alchemy-zt-vnet-update-v2"); + expect(updated.comment).toEqual("v2"); + + const live = yield* getVnet(accountId, updated.virtualNetworkId); + expect(live.name).toEqual("alchemy-zt-vnet-update-v2"); + expect(live.comment).toEqual("v2"); + + // Redeploying identical props is a no-op. + const noop = yield* stack.deploy( + Cloudflare.TunnelVirtualNetwork("UpdateVnet", { + name: "alchemy-zt-vnet-update-v2", + comment: "v2", + }).pipe(adopt(true)), + ); + expect(noop.virtualNetworkId).toEqual(initial.virtualNetworkId); + + yield* stack.destroy(); + yield* expectGone(accountId, initial.virtualNetworkId); + }).pipe(logLevel), ); -test.provider("recreates after out-of-band delete", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const vnet = yield* stack.deploy( - Cloudflare.TunnelVirtualNetwork("HealVnet", { - name: "alchemy-zt-vnet-heal", - comment: "v1", - }).pipe(adopt(true)), - ); - - yield* zeroTrust - .deleteNetworkVirtualNetwork({ - accountId, - virtualNetworkId: vnet.virtualNetworkId, - }) - .pipe( - Effect.retry({ - while: (e) => e._tag === "Forbidden", - schedule: Schedule.exponential("500 millis"), - times: 8, - }), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "recreates after out-of-band delete", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const vnet = yield* stack.deploy( + Cloudflare.TunnelVirtualNetwork("HealVnet", { + name: "alchemy-zt-vnet-heal", + comment: "v1", + }).pipe(adopt(true)), + ); + + yield* zeroTrust + .deleteNetworkVirtualNetwork({ + accountId, + virtualNetworkId: vnet.virtualNetworkId, + }) + .pipe( + Effect.retry({ + while: (e) => e._tag === "Forbidden", + schedule: Schedule.exponential("500 millis"), + times: 8, + }), + ); + + // Change a prop to force reconcile — it must observe the vnet as + // missing and recreate it instead of failing on a 404. + const healed = yield* stack.deploy( + Cloudflare.TunnelVirtualNetwork("HealVnet", { + name: "alchemy-zt-vnet-heal", + comment: "v2", + }).pipe(adopt(true)), ); - // Change a prop to force reconcile — it must observe the vnet as - // missing and recreate it instead of failing on a 404. - const healed = yield* stack.deploy( - Cloudflare.TunnelVirtualNetwork("HealVnet", { - name: "alchemy-zt-vnet-heal", - comment: "v2", - }).pipe(adopt(true)), - ); - - expect(healed.virtualNetworkId).not.toEqual(vnet.virtualNetworkId); - expect(healed.comment).toEqual("v2"); - const live = yield* getVnet(accountId, healed.virtualNetworkId); - expect(live.comment).toEqual("v2"); - - yield* stack.destroy(); - yield* expectGone(accountId, healed.virtualNetworkId); - }).pipe(logLevel), + expect(healed.virtualNetworkId).not.toEqual(vnet.virtualNetworkId); + expect(healed.comment).toEqual("v2"); + const live = yield* getVnet(accountId, healed.virtualNetworkId); + expect(live.comment).toEqual("v2"); + + yield* stack.destroy(); + yield* expectGone(accountId, healed.virtualNetworkId); + }).pipe(logLevel), ); -test.provider("list enumerates the deployed virtual network", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const deployed = yield* stack.deploy( - Cloudflare.TunnelVirtualNetwork("ListVnet", { - name: "alchemy-zt-vnet-list", - comment: "alchemy list test vnet", - }).pipe(adopt(true)), - ); - - const provider = yield* Provider.findProvider( - Cloudflare.TunnelVirtualNetwork, - ); - const all = yield* provider.list(); - - expect( - all.some((v) => v.virtualNetworkId === deployed.virtualNetworkId), - ).toBe(true); - const found = all.find( - (v) => v.virtualNetworkId === deployed.virtualNetworkId, - ); - expect(found?.name).toEqual("alchemy-zt-vnet-list"); - - yield* stack.destroy(); - yield* expectGone(deployed.accountId, deployed.virtualNetworkId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed virtual network", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const deployed = yield* stack.deploy( + Cloudflare.TunnelVirtualNetwork("ListVnet", { + name: "alchemy-zt-vnet-list", + comment: "alchemy list test vnet", + }).pipe(adopt(true)), + ); + + const provider = yield* Provider.findProvider( + Cloudflare.TunnelVirtualNetwork, + ); + const all = yield* provider.list(); + + expect( + all.some((v) => v.virtualNetworkId === deployed.virtualNetworkId), + ).toBe(true); + const found = all.find( + (v) => v.virtualNetworkId === deployed.virtualNetworkId, + ); + expect(found?.name).toEqual("alchemy-zt-vnet-list"); + + yield* stack.destroy(); + yield* expectGone(deployed.accountId, deployed.virtualNetworkId); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Tunnel/WarpConnector.test.ts b/packages/alchemy/test/Cloudflare/Tunnel/WarpConnector.test.ts index aaf54e90b..41264223a 100644 --- a/packages/alchemy/test/Cloudflare/Tunnel/WarpConnector.test.ts +++ b/packages/alchemy/test/Cloudflare/Tunnel/WarpConnector.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import * as Redacted from "effect/Redacted"; import { MinimumLogLevel } from "effect/References"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers(), state: Cloudflare.state(), @@ -27,7 +30,7 @@ const getLiveConnector = (accountId: string, tunnelId: string) => Effect.catchTag("TunnelNotFound", () => Effect.succeed(undefined)), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create, rename in place, and destroy a WARP Connector tunnel", (stack) => Effect.gen(function* () { @@ -86,7 +89,7 @@ test.provider( // Canonical `list()` test (account collection): deploy a WARP Connector // tunnel, resolve the provider via the typed `Provider.findProvider`, and // assert the deployed tunnel appears in the exhaustively-paginated result. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed WARP Connector tunnel", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Turnstile/Widget.test.ts b/packages/alchemy/test/Cloudflare/Turnstile/Widget.test.ts index 391a63d9a..b2971ffec 100644 --- a/packages/alchemy/test/Cloudflare/Turnstile/Widget.test.ts +++ b/packages/alchemy/test/Cloudflare/Turnstile/Widget.test.ts @@ -9,6 +9,9 @@ import * as Redacted from "effect/Redacted"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -45,161 +48,171 @@ const expectGone = (accountId: string, sitekey: string) => }), ); -test.provider("create and delete a widget with default name", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const widget = yield* stack.deploy( - Cloudflare.TurnstileWidget("DefaultWidget", { - domains: [zoneName], - mode: "managed", - }), - ); - - expect(widget.sitekey).toBeDefined(); - expect(Redacted.value(widget.secret)).toBeTruthy(); - expect(widget.accountId).toEqual(accountId); - expect(widget.domains).toEqual([zoneName]); - expect(widget.mode).toEqual("managed"); - expect(widget.region).toEqual("world"); - - const live = yield* getWidget(accountId, widget.sitekey); - expect(live.sitekey).toEqual(widget.sitekey); - expect(live.domains).toEqual([zoneName]); - expect(live.mode).toEqual("managed"); - - yield* stack.destroy(); - - yield* expectGone(accountId, widget.sitekey); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create and delete a widget with default name", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const widget = yield* stack.deploy( + Cloudflare.TurnstileWidget("DefaultWidget", { + domains: [zoneName], + mode: "managed", + }), + ); + + expect(widget.sitekey).toBeDefined(); + expect(Redacted.value(widget.secret)).toBeTruthy(); + expect(widget.accountId).toEqual(accountId); + expect(widget.domains).toEqual([zoneName]); + expect(widget.mode).toEqual("managed"); + expect(widget.region).toEqual("world"); + + const live = yield* getWidget(accountId, widget.sitekey); + expect(live.sitekey).toEqual(widget.sitekey); + expect(live.domains).toEqual([zoneName]); + expect(live.mode).toEqual("managed"); + + yield* stack.destroy(); + + yield* expectGone(accountId, widget.sitekey); + }).pipe(logLevel), ); -test.provider("update mutable props in place (same sitekey)", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const initial = yield* stack.deploy( - Cloudflare.TurnstileWidget("UpdateWidget", { - name: "alchemy-turnstile-update", - domains: [zoneName], - mode: "managed", - }), - ); - - expect(initial.name).toEqual("alchemy-turnstile-update"); - expect(initial.mode).toEqual("managed"); - - const updated = yield* stack.deploy( - Cloudflare.TurnstileWidget("UpdateWidget", { - name: "alchemy-turnstile-update-v2", - domains: [zoneName, `www.${zoneName}`], - mode: "invisible", - }), - ); - - // Same widget mutated in place — not a replacement. - expect(updated.sitekey).toEqual(initial.sitekey); - expect(updated.name).toEqual("alchemy-turnstile-update-v2"); - expect(updated.mode).toEqual("invisible"); - expect([...updated.domains].sort()).toEqual( - [zoneName, `www.${zoneName}`].sort(), - ); - - const live = yield* getWidget(accountId, updated.sitekey); - expect(live.name).toEqual("alchemy-turnstile-update-v2"); - expect(live.mode).toEqual("invisible"); - expect([...live.domains].sort()).toEqual( - [zoneName, `www.${zoneName}`].sort(), - ); - - // Redeploying identical props is a no-op (still the same widget). - const noop = yield* stack.deploy( - Cloudflare.TurnstileWidget("UpdateWidget", { - name: "alchemy-turnstile-update-v2", - domains: [zoneName, `www.${zoneName}`], - mode: "invisible", - }), - ); - expect(noop.sitekey).toEqual(initial.sitekey); - - yield* stack.destroy(); - - yield* expectGone(accountId, initial.sitekey); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "update mutable props in place (same sitekey)", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const initial = yield* stack.deploy( + Cloudflare.TurnstileWidget("UpdateWidget", { + name: "alchemy-turnstile-update", + domains: [zoneName], + mode: "managed", + }), + ); + + expect(initial.name).toEqual("alchemy-turnstile-update"); + expect(initial.mode).toEqual("managed"); + + const updated = yield* stack.deploy( + Cloudflare.TurnstileWidget("UpdateWidget", { + name: "alchemy-turnstile-update-v2", + domains: [zoneName, `www.${zoneName}`], + mode: "invisible", + }), + ); + + // Same widget mutated in place — not a replacement. + expect(updated.sitekey).toEqual(initial.sitekey); + expect(updated.name).toEqual("alchemy-turnstile-update-v2"); + expect(updated.mode).toEqual("invisible"); + expect([...updated.domains].sort()).toEqual( + [zoneName, `www.${zoneName}`].sort(), + ); + + const live = yield* getWidget(accountId, updated.sitekey); + expect(live.name).toEqual("alchemy-turnstile-update-v2"); + expect(live.mode).toEqual("invisible"); + expect([...live.domains].sort()).toEqual( + [zoneName, `www.${zoneName}`].sort(), + ); + + // Redeploying identical props is a no-op (still the same widget). + const noop = yield* stack.deploy( + Cloudflare.TurnstileWidget("UpdateWidget", { + name: "alchemy-turnstile-update-v2", + domains: [zoneName, `www.${zoneName}`], + mode: "invisible", + }), + ); + expect(noop.sitekey).toEqual(initial.sitekey); + + yield* stack.destroy(); + + yield* expectGone(accountId, initial.sitekey); + }).pipe(logLevel), ); -test.provider("recreates after out-of-band delete", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const widget = yield* stack.deploy( - Cloudflare.TurnstileWidget("HealWidget", { - name: "alchemy-turnstile-heal", - domains: [zoneName], - mode: "non-interactive", - }), - ); - - // Delete the widget out-of-band. A redeploy with identical props is a - // planner no-op, so change a prop to force reconcile — it must observe - // the widget as missing and recreate it instead of failing on a 404. - yield* turnstile.deleteWidget({ accountId, sitekey: widget.sitekey }).pipe( - Effect.retry({ - while: (e) => e._tag === "Forbidden", - schedule: Schedule.exponential("500 millis"), - times: 8, - }), - ); - - const healed = yield* stack.deploy( - Cloudflare.TurnstileWidget("HealWidget", { - name: "alchemy-turnstile-heal", - domains: [zoneName], - mode: "managed", - }), - ); - - expect(healed.sitekey).not.toEqual(widget.sitekey); - expect(healed.mode).toEqual("managed"); - const live = yield* getWidget(accountId, healed.sitekey); - expect(live.name).toEqual("alchemy-turnstile-heal"); - expect(live.mode).toEqual("managed"); - - yield* stack.destroy(); - - yield* expectGone(accountId, healed.sitekey); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "recreates after out-of-band delete", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const widget = yield* stack.deploy( + Cloudflare.TurnstileWidget("HealWidget", { + name: "alchemy-turnstile-heal", + domains: [zoneName], + mode: "non-interactive", + }), + ); + + // Delete the widget out-of-band. A redeploy with identical props is a + // planner no-op, so change a prop to force reconcile — it must observe + // the widget as missing and recreate it instead of failing on a 404. + yield* turnstile + .deleteWidget({ accountId, sitekey: widget.sitekey }) + .pipe( + Effect.retry({ + while: (e) => e._tag === "Forbidden", + schedule: Schedule.exponential("500 millis"), + times: 8, + }), + ); + + const healed = yield* stack.deploy( + Cloudflare.TurnstileWidget("HealWidget", { + name: "alchemy-turnstile-heal", + domains: [zoneName], + mode: "managed", + }), + ); + + expect(healed.sitekey).not.toEqual(widget.sitekey); + expect(healed.mode).toEqual("managed"); + const live = yield* getWidget(accountId, healed.sitekey); + expect(live.name).toEqual("alchemy-turnstile-heal"); + expect(live.mode).toEqual("managed"); + + yield* stack.destroy(); + + yield* expectGone(accountId, healed.sitekey); + }).pipe(logLevel), ); -test.provider("list enumerates the deployed widget", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const widget = yield* stack.deploy( - Cloudflare.TurnstileWidget("ListWidget", { - name: "alchemy-turnstile-list", - domains: [zoneName], - mode: "managed", - }), - ); - - const provider = yield* Provider.findProvider(Cloudflare.TurnstileWidget); - const all = yield* provider.list(); - - const found = all.find((w) => w.sitekey === widget.sitekey); - expect(found).toBeDefined(); - expect(found?.name).toEqual("alchemy-turnstile-list"); - expect(found?.domains).toEqual([zoneName]); - expect(found?.mode).toEqual("managed"); - // list() hydrates the write-only secret to match the read shape. - expect(Redacted.value(found!.secret)).toBeTruthy(); - - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed widget", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const widget = yield* stack.deploy( + Cloudflare.TurnstileWidget("ListWidget", { + name: "alchemy-turnstile-list", + domains: [zoneName], + mode: "managed", + }), + ); + + const provider = yield* Provider.findProvider(Cloudflare.TurnstileWidget); + const all = yield* provider.list(); + + const found = all.find((w) => w.sitekey === widget.sitekey); + expect(found).toBeDefined(); + expect(found?.name).toEqual("alchemy-turnstile-list"); + expect(found?.domains).toEqual([zoneName]); + expect(found?.mode).toEqual("managed"); + // list() hydrates the write-only secret to match the read shape. + expect(Redacted.value(found!.secret)).toBeTruthy(); + + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/UrlNormalization/UrlNormalization.test.ts b/packages/alchemy/test/Cloudflare/UrlNormalization/UrlNormalization.test.ts index 7e02d5797..040cca956 100644 --- a/packages/alchemy/test/Cloudflare/UrlNormalization/UrlNormalization.test.ts +++ b/packages/alchemy/test/Cloudflare/UrlNormalization/UrlNormalization.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -60,7 +63,7 @@ const resetToDefaults = (zoneId: string) => ); describe.sequential("UrlNormalization", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "configures URL normalization and resets to defaults on destroy", (stack) => Effect.gen(function* () { @@ -98,53 +101,55 @@ describe.sequential("UrlNormalization", () => { }).pipe(logLevel), ); - test.provider("updates scope and type in place", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - yield* stack.destroy(); - yield* resetToDefaults(zoneId); - - const initial = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.UrlNormalization("UrlNormalization", { - zoneId, - scope: "both", - type: "rfc3986", - }); - }), - ); - - expect(initial.scope).toEqual("both"); - expect(initial.type).toEqual("rfc3986"); - - // Same singleton updated in place via a full-replace PUT. - const updated = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.UrlNormalization("UrlNormalization", { - zoneId, - scope: "incoming", - type: "cloudflare", - }); - }), - ); - - expect(updated.scope).toEqual("incoming"); - expect(updated.type).toEqual("cloudflare"); - - const live = yield* getUrlNormalization(zoneId); - expect(live.scope).toEqual("incoming"); - expect(live.type).toEqual("cloudflare"); - - yield* stack.destroy(); - - const reset = yield* getUrlNormalization(zoneId); - expect(reset.scope).toEqual("incoming"); - expect(reset.type).toEqual("cloudflare"); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "updates scope and type in place", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; + + yield* stack.destroy(); + yield* resetToDefaults(zoneId); + + const initial = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.UrlNormalization("UrlNormalization", { + zoneId, + scope: "both", + type: "rfc3986", + }); + }), + ); + + expect(initial.scope).toEqual("both"); + expect(initial.type).toEqual("rfc3986"); + + // Same singleton updated in place via a full-replace PUT. + const updated = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.UrlNormalization("UrlNormalization", { + zoneId, + scope: "incoming", + type: "cloudflare", + }); + }), + ); + + expect(updated.scope).toEqual("incoming"); + expect(updated.type).toEqual("cloudflare"); + + const live = yield* getUrlNormalization(zoneId); + expect(live.scope).toEqual("incoming"); + expect(live.type).toEqual("cloudflare"); + + yield* stack.destroy(); + + const reset = yield* getUrlNormalization(zoneId); + expect(reset.scope).toEqual("incoming"); + expect(reset.type).toEqual("cloudflare"); + }).pipe(logLevel), ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "applies Cloudflare defaults when scope and type are omitted", (stack) => Effect.gen(function* () { @@ -190,21 +195,23 @@ describe.sequential("UrlNormalization", () => { // API for this per-zone setting, so `list()` enumerates every zone via // `listAllZones` and reads the singleton in each. Assert the result is // non-empty and contains the standing test zone. - test.provider("list enumerates URL normalization across all zones", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - const provider = yield* Provider.findProvider( - Cloudflare.UrlNormalization, - ); - const all = yield* provider.list(); - - expect(all.length).toBeGreaterThan(0); - expect(all.some((s) => s.zoneId === zoneId)).toBe(true); - - // `stack` is unused here (the singleton always exists on every zone), - // but keep the destroy bookend so the harness state stays clean. - yield* stack.destroy(); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates URL normalization across all zones", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; + + const provider = yield* Provider.findProvider( + Cloudflare.UrlNormalization, + ); + const all = yield* provider.list(); + + expect(all.length).toBeGreaterThan(0); + expect(all.some((s) => s.zoneId === zoneId)).toBe(true); + + // `stack` is unused here (the singleton always exists on every zone), + // but keep the destroy bookend so the harness state stays clean. + yield* stack.destroy(); + }).pipe(logLevel), ); }); diff --git a/packages/alchemy/test/Cloudflare/Vectorize/VectorizeIndex.test.ts b/packages/alchemy/test/Cloudflare/Vectorize/VectorizeIndex.test.ts index 3caa7dc36..8b863951d 100644 --- a/packages/alchemy/test/Cloudflare/Vectorize/VectorizeIndex.test.ts +++ b/packages/alchemy/test/Cloudflare/Vectorize/VectorizeIndex.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -15,120 +18,128 @@ const logLevel = Effect.provideService( process.env.DEBUG ? "Debug" : "Info", ); -test.provider("create and delete index with explicit dimensions", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create and delete index with explicit dimensions", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - yield* stack.destroy(); + yield* stack.destroy(); - const index = yield* stack.deploy( - Cloudflare.VectorizeIndex("DefaultIndex", { - dimensions: 768, - metric: "cosine", - }), - ); + const index = yield* stack.deploy( + Cloudflare.VectorizeIndex("DefaultIndex", { + dimensions: 768, + metric: "cosine", + }), + ); - expect(index.indexName).toBeDefined(); - expect(index.dimensions).toEqual(768); - expect(index.metric).toEqual("cosine"); + expect(index.indexName).toBeDefined(); + expect(index.dimensions).toEqual(768); + expect(index.metric).toEqual("cosine"); - const actual = yield* vectorize.getIndex({ - accountId, - indexName: index.indexName, - }); - expect(actual.name).toEqual(index.indexName); - expect(actual.config?.dimensions).toEqual(768); + const actual = yield* vectorize.getIndex({ + accountId, + indexName: index.indexName, + }); + expect(actual.name).toEqual(index.indexName); + expect(actual.config?.dimensions).toEqual(768); - yield* stack.destroy(); + yield* stack.destroy(); - yield* waitForDelete(accountId, index.indexName); - }).pipe(logLevel), + yield* waitForDelete(accountId, index.indexName); + }).pipe(logLevel), ); -test.provider("create index from a preset", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const index = yield* stack.deploy( - Cloudflare.VectorizeIndex("PresetIndex", { - preset: "@cf/baai/bge-base-en-v1.5", - description: "preset index", - }), - ); - - const actual = yield* vectorize.getIndex({ - accountId, - indexName: index.indexName, - }); - // bge-base resolves to 768 dimensions. - expect(actual.config?.dimensions).toEqual(768); - expect(index.description).toEqual("preset index"); - - yield* stack.destroy(); - - yield* waitForDelete(accountId, index.indexName); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create index from a preset", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const index = yield* stack.deploy( + Cloudflare.VectorizeIndex("PresetIndex", { + preset: "@cf/baai/bge-base-en-v1.5", + description: "preset index", + }), + ); + + const actual = yield* vectorize.getIndex({ + accountId, + indexName: index.indexName, + }); + // bge-base resolves to 768 dimensions. + expect(actual.config?.dimensions).toEqual(768); + expect(index.description).toEqual("preset index"); + + yield* stack.destroy(); + + yield* waitForDelete(accountId, index.indexName); + }).pipe(logLevel), ); -test.provider("replaces index when dimensions change", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const index = yield* stack.deploy( - Cloudflare.VectorizeIndex("ReplaceIndex", { - dimensions: 32, - metric: "cosine", - }), - ); - expect(index.dimensions).toEqual(32); - expect(index.metric).toEqual("cosine"); - - const replaced = yield* stack.deploy( - Cloudflare.VectorizeIndex("ReplaceIndex", { - dimensions: 64, - metric: "euclidean", - }), - ); - - const actual = yield* vectorize.getIndex({ - accountId, - indexName: replaced.indexName, - }); - expect(actual.config?.dimensions).toEqual(64); - expect(actual.config?.metric).toEqual("euclidean"); - - yield* stack.destroy(); - - yield* waitForDelete(accountId, replaced.indexName); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "replaces index when dimensions change", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const index = yield* stack.deploy( + Cloudflare.VectorizeIndex("ReplaceIndex", { + dimensions: 32, + metric: "cosine", + }), + ); + expect(index.dimensions).toEqual(32); + expect(index.metric).toEqual("cosine"); + + const replaced = yield* stack.deploy( + Cloudflare.VectorizeIndex("ReplaceIndex", { + dimensions: 64, + metric: "euclidean", + }), + ); + + const actual = yield* vectorize.getIndex({ + accountId, + indexName: replaced.indexName, + }); + expect(actual.config?.dimensions).toEqual(64); + expect(actual.config?.metric).toEqual("euclidean"); + + yield* stack.destroy(); + + yield* waitForDelete(accountId, replaced.indexName); + }).pipe(logLevel), ); -test.provider("list enumerates the deployed index", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed index", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - yield* stack.destroy(); + yield* stack.destroy(); - const index = yield* stack.deploy( - Cloudflare.VectorizeIndex("ListIndex", { - dimensions: 768, - metric: "cosine", - }), - ); + const index = yield* stack.deploy( + Cloudflare.VectorizeIndex("ListIndex", { + dimensions: 768, + metric: "cosine", + }), + ); - const provider = yield* Provider.findProvider(Cloudflare.VectorizeIndex); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider(Cloudflare.VectorizeIndex); + const all = yield* provider.list(); - expect(all.some((x) => x.indexName === index.indexName)).toBe(true); + expect(all.some((x) => x.indexName === index.indexName)).toBe(true); - yield* stack.destroy(); + yield* stack.destroy(); - yield* waitForDelete(accountId, index.indexName); - }).pipe(logLevel), + yield* waitForDelete(accountId, index.indexName); + }).pipe(logLevel), ); const waitForDelete = (accountId: string, indexName: string) => diff --git a/packages/alchemy/test/Cloudflare/Vectorize/VectorizeIndexBinding.test.ts b/packages/alchemy/test/Cloudflare/Vectorize/VectorizeIndexBinding.test.ts index 4a9a45ca1..32db3f800 100644 --- a/packages/alchemy/test/Cloudflare/Vectorize/VectorizeIndexBinding.test.ts +++ b/packages/alchemy/test/Cloudflare/Vectorize/VectorizeIndexBinding.test.ts @@ -9,6 +9,9 @@ import { HttpClientResponse } from "effect/unstable/http"; import * as HttpClient from "effect/unstable/http/HttpClient"; import VectorizeWorker from "./fixtures/vectorize-worker.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -24,7 +27,7 @@ const logLevel = Effect.provideService( * `getByIds`). Vectorize mutations are eventually consistent, so after * upserting we retry the query/get routes until the vectors are visible. */ -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "VectorizeIndex.bind exercises the client surface", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Vectorize/VectorizeMetadataIndex.test.ts b/packages/alchemy/test/Cloudflare/Vectorize/VectorizeMetadataIndex.test.ts index a05147d96..840a76496 100644 --- a/packages/alchemy/test/Cloudflare/Vectorize/VectorizeMetadataIndex.test.ts +++ b/packages/alchemy/test/Cloudflare/Vectorize/VectorizeMetadataIndex.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -55,310 +58,301 @@ const waitForIndexGone = (accountId: string, indexName: string) => schedule: Schedule.both(Schedule.spaced("2 seconds"), Schedule.recurs(15)), }); -describe.skipIf(!!process.env.NO_SLOW_TESTS)( - "Cloudflare.VectorizeMetadataIndex", - () => { - test.provider( - "create and delete a metadata index", - (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const { index, meta } = yield* stack.deploy( - Effect.gen(function* () { - const index = yield* Cloudflare.VectorizeIndex("ParentIdx", { - dimensions: 32, - metric: "cosine", - }); - const meta = yield* Cloudflare.VectorizeMetadataIndex("MetaIdx", { - indexName: index.indexName, - propertyName: "category", - indexType: "string", - }); - return { index, meta }; - }), - ); - - expect(meta.propertyName).toBe("category"); - expect(meta.indexType).toBe("string"); - expect(meta.indexName).toBe(index.indexName); - - // The metadata index appears in the parent's list once Cloudflare - // processes the async mutation — slow and variable under full - // concurrent load, so use the wider capped poll. - const entries = yield* poll({ - description: "metadata index exists with propertyName=category", - effect: listMetadataIndexes(accountId, index.indexName), - predicate: (entries) => - entries.some((e) => e.propertyName === "category"), - schedule: metaIndexPoll, - }); - expect( - entries.find((e) => e.propertyName === "category")?.indexType, - ).toBe("String"); - - yield* stack.destroy(); - - // Both parent and metadata index are gone. - const after = yield* listMetadataIndexes(accountId, index.indexName); - expect(after.length).toBe(0); - }).pipe(Effect.ensuring(stack.destroy().pipe(Effect.ignore)), logLevel), - // The single metadata-index materialization can take up to the ~240s - // poll ceiling under concurrent load — give headroom above the cap so a - // healthy-but-slow run never races the vitest timeout. - { timeout: 300_000 }, - ); - - test.provider( - "multiple metadata indexes on the same parent coexist", - (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const { index } = yield* stack.deploy( - Effect.gen(function* () { - const index = yield* Cloudflare.VectorizeIndex("MultiParent", { - dimensions: 32, - metric: "cosine", - }); - yield* Cloudflare.VectorizeMetadataIndex("CategoryMeta", { - indexName: index.indexName, - propertyName: "category", - indexType: "string", - }); - yield* Cloudflare.VectorizeMetadataIndex("PriceMeta", { - indexName: index.indexName, - propertyName: "price", - indexType: "number", - }); - return { index }; - }), - ); - const entries = yield* poll({ - description: "metadata index includes category and price", - effect: listMetadataIndexes(accountId, index.indexName), - predicate: (entries) => - entries.some((e) => e.propertyName === "category") && - entries.some((e) => e.propertyName === "price"), - schedule: multiMetaIndexPoll, - }); - expect( - entries.find((e) => e.propertyName === "category")?.indexType, - ).toBe("String"); - expect( - entries.find((e) => e.propertyName === "price")?.indexType, - ).toBe("Number"); - - yield* stack.destroy(); - }).pipe( - // Guarantee teardown even if a poll/assertion fails or the test is - // interrupted by a timeout — the scratch stack's state is in-memory - // only, so a body that throws before the trailing `destroy()` would - // otherwise leak the parent + metadata indexes with no next-run - // cleanup. - Effect.ensuring(stack.destroy().pipe(Effect.ignore)), - logLevel, - ), - // TWO sequential metadata-index materializations on one parent, capped at - // the wider ~330s `multiMetaIndexPoll` ceiling under full concurrent load - // — give the test headroom above that cap (plus create/destroy overhead) - // so a healthy-but-slow run never races the vitest timeout. The poll cap - // still makes a real regression fail fast. - { timeout: 420_000 }, - ); - - test.provider( - "replacing the parent index also replaces the metadata index", - (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - // Initial deploy with dimensions=32. - const { index: oldIndex } = yield* stack.deploy( - Effect.gen(function* () { - const index = yield* Cloudflare.VectorizeIndex("ReplaceParent", { - dimensions: 32, - metric: "cosine", - }); - yield* Cloudflare.VectorizeMetadataIndex("ReplaceMeta", { +describe.skipIf(!!process.env.FAST)("Cloudflare.VectorizeMetadataIndex", () => { + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create and delete a metadata index", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const { index, meta } = yield* stack.deploy( + Effect.gen(function* () { + const index = yield* Cloudflare.VectorizeIndex("ParentIdx", { + dimensions: 32, + metric: "cosine", + }); + const meta = yield* Cloudflare.VectorizeMetadataIndex("MetaIdx", { + indexName: index.indexName, + propertyName: "category", + indexType: "string", + }); + return { index, meta }; + }), + ); + + expect(meta.propertyName).toBe("category"); + expect(meta.indexType).toBe("string"); + expect(meta.indexName).toBe(index.indexName); + + // The metadata index appears in the parent's list once Cloudflare + // processes the async mutation — slow and variable under full + // concurrent load, so use the wider capped poll. + const entries = yield* poll({ + description: "metadata index exists with propertyName=category", + effect: listMetadataIndexes(accountId, index.indexName), + predicate: (entries) => + entries.some((e) => e.propertyName === "category"), + schedule: metaIndexPoll, + }); + expect( + entries.find((e) => e.propertyName === "category")?.indexType, + ).toBe("String"); + + yield* stack.destroy(); + + // Both parent and metadata index are gone. + const after = yield* listMetadataIndexes(accountId, index.indexName); + expect(after.length).toBe(0); + }).pipe(Effect.ensuring(stack.destroy().pipe(Effect.ignore)), logLevel), + // The single metadata-index materialization can take up to the ~240s + // poll ceiling under concurrent load — give headroom above the cap so a + // healthy-but-slow run never races the vitest timeout. + { timeout: 300_000 }, + ); + + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "multiple metadata indexes on the same parent coexist", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const { index } = yield* stack.deploy( + Effect.gen(function* () { + const index = yield* Cloudflare.VectorizeIndex("MultiParent", { + dimensions: 32, + metric: "cosine", + }); + yield* Cloudflare.VectorizeMetadataIndex("CategoryMeta", { + indexName: index.indexName, + propertyName: "category", + indexType: "string", + }); + yield* Cloudflare.VectorizeMetadataIndex("PriceMeta", { + indexName: index.indexName, + propertyName: "price", + indexType: "number", + }); + return { index }; + }), + ); + const entries = yield* poll({ + description: "metadata index includes category and price", + effect: listMetadataIndexes(accountId, index.indexName), + predicate: (entries) => + entries.some((e) => e.propertyName === "category") && + entries.some((e) => e.propertyName === "price"), + schedule: multiMetaIndexPoll, + }); + expect( + entries.find((e) => e.propertyName === "category")?.indexType, + ).toBe("String"); + expect(entries.find((e) => e.propertyName === "price")?.indexType).toBe( + "Number", + ); + + yield* stack.destroy(); + }).pipe( + // Guarantee teardown even if a poll/assertion fails or the test is + // interrupted by a timeout — the scratch stack's state is in-memory + // only, so a body that throws before the trailing `destroy()` would + // otherwise leak the parent + metadata indexes with no next-run + // cleanup. + Effect.ensuring(stack.destroy().pipe(Effect.ignore)), + logLevel, + ), + // TWO sequential metadata-index materializations on one parent, capped at + // the wider ~330s `multiMetaIndexPoll` ceiling under full concurrent load + // — give the test headroom above that cap (plus create/destroy overhead) + // so a healthy-but-slow run never races the vitest timeout. The poll cap + // still makes a real regression fail fast. + { timeout: 420_000 }, + ); + + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "replacing the parent index also replaces the metadata index", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + // Initial deploy with dimensions=32. + const { index: oldIndex } = yield* stack.deploy( + Effect.gen(function* () { + const index = yield* Cloudflare.VectorizeIndex("ReplaceParent", { + dimensions: 32, + metric: "cosine", + }); + yield* Cloudflare.VectorizeMetadataIndex("ReplaceMeta", { + indexName: index.indexName, + propertyName: "tag", + indexType: "string", + }); + return { index }; + }), + ); + yield* poll({ + description: "metadata index exists with propertyName=tag", + effect: listMetadataIndexes(accountId, oldIndex.indexName), + predicate: (entries) => entries.some((e) => e.propertyName === "tag"), + schedule: metaIndexPoll, + }); + + // Re-deploy with different dimensions — the parent replaces, which + // also replaces the metadata index on the new parent. + const { index: newIndex, meta: newMeta } = yield* stack.deploy( + Effect.gen(function* () { + const index = yield* Cloudflare.VectorizeIndex("ReplaceParent", { + dimensions: 64, + metric: "cosine", + }); + const meta = yield* Cloudflare.VectorizeMetadataIndex( + "ReplaceMeta", + { indexName: index.indexName, propertyName: "tag", indexType: "string", - }); - return { index }; - }), - ); - yield* poll({ - description: "metadata index exists with propertyName=tag", - effect: listMetadataIndexes(accountId, oldIndex.indexName), - predicate: (entries) => - entries.some((e) => e.propertyName === "tag"), - schedule: metaIndexPoll, - }); - - // Re-deploy with different dimensions — the parent replaces, which - // also replaces the metadata index on the new parent. - const { index: newIndex, meta: newMeta } = yield* stack.deploy( - Effect.gen(function* () { - const index = yield* Cloudflare.VectorizeIndex("ReplaceParent", { - dimensions: 64, - metric: "cosine", - }); - const meta = yield* Cloudflare.VectorizeMetadataIndex( - "ReplaceMeta", - { - indexName: index.indexName, - propertyName: "tag", - indexType: "string", - }, - ); - return { index, meta }; - }), - ); - - expect(newIndex.indexName).not.toBe(oldIndex.indexName); - expect(newMeta.indexName).toBe(newIndex.indexName); - - // Old parent is gone — bounded typed wait for the replacement's - // delete of the old index to settle. - const oldGone = yield* waitForIndexGone( - accountId, - oldIndex.indexName, - ); - expect(oldGone).toBe(true); - - // The new parent has the metadata index. - yield* poll({ - description: "metadata index exists with propertyName=tag", - effect: listMetadataIndexes(accountId, newIndex.indexName), - predicate: (entries) => - entries.some((e) => e.propertyName === "tag"), - schedule: metaIndexPoll, - }); - - yield* stack.destroy(); - }).pipe(Effect.ensuring(stack.destroy().pipe(Effect.ignore)), logLevel), - // Two sequential metadata-index materializations plus a parent - // replacement (delete old + create new) and a bounded gone-wait — give - // this genuinely slow create+replace+poll lifecycle real headroom above - // the two poll caps (2 x ~240s) + the ~30s gone-wait so a healthy run - // never races the timeout. Still bounded so a real regression fails fast. - { timeout: 600_000 }, - ); - - test.provider( - "list enumerates the deployed metadata index", - (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const { index } = yield* stack.deploy( - Effect.gen(function* () { - const index = yield* Cloudflare.VectorizeIndex("ListParent", { - dimensions: 32, - metric: "cosine", - }); - yield* Cloudflare.VectorizeMetadataIndex("ListMeta", { - indexName: index.indexName, - propertyName: "category", - indexType: "string", - }); - return { index }; - }), - ); - - const provider = yield* Provider.findProvider( - Cloudflare.VectorizeMetadataIndex, - ); - - // Cloudflare processes the create as an async mutation, so the entry - // appears in list() once the mutation is applied — slow and variable - // under full concurrent load, so use the wider capped poll. - const all = yield* poll({ - description: "list() includes the deployed metadata index", - effect: provider.list(), - predicate: (all) => - all.some( - (x) => - x.indexName === index.indexName && - x.propertyName === "category", - ), - schedule: metaIndexPoll, - }); - - const entry = all.find( - (x) => - x.indexName === index.indexName && x.propertyName === "category", - ); - expect(entry?.indexType).toBe("string"); - expect(entry?.accountId).toBeDefined(); - - yield* stack.destroy(); - }).pipe(Effect.ensuring(stack.destroy().pipe(Effect.ignore)), logLevel), - // The single metadata-index materialization can take up to the ~240s - // poll ceiling under concurrent load — give headroom above the cap. - { timeout: 300_000 }, - ); - - test.provider( - "destroy is idempotent when the parent index was deleted out-of-band", - (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const { index } = yield* stack.deploy( - Effect.gen(function* () { - const index = yield* Cloudflare.VectorizeIndex("OobParent", { - dimensions: 32, - metric: "cosine", - }); - yield* Cloudflare.VectorizeMetadataIndex("OobMeta", { - indexName: index.indexName, - propertyName: "ns", - indexType: "string", - }); - return { index }; - }), - ); - yield* poll({ - description: "metadata index exists with propertyName=ns", - effect: listMetadataIndexes(accountId, index.indexName), - predicate: (entries) => - entries.some((e) => e.propertyName === "ns"), - schedule: metaIndexPoll, - }); - - // Simulate Cloudflare's cascading delete: drop the parent directly. - // On Cloudflare's side this also removes the metadata index. - yield* vectorize.deleteIndex({ - accountId, - indexName: index.indexName, - }); - - // Bounded typed wait for the out-of-band delete to actually settle - // before exercising the idempotent `destroy` path. - const gone = yield* waitForIndexGone(accountId, index.indexName); - expect(gone).toBe(true); - - // The metadata index provider's delete tolerates 404/410 from the - // missing parent, so `destroy` succeeds without erroring. - yield* stack.destroy(); - }).pipe(Effect.ensuring(stack.destroy().pipe(Effect.ignore)), logLevel), - // One metadata-index materialization (capped at ~240s under concurrent - // load) plus an out-of-band delete + gone-wait — keep headroom above the - // poll ceiling so a healthy-but-slow run never races the vitest timeout. - { timeout: 300_000 }, - ); - }, -); + }, + ); + return { index, meta }; + }), + ); + + expect(newIndex.indexName).not.toBe(oldIndex.indexName); + expect(newMeta.indexName).toBe(newIndex.indexName); + + // Old parent is gone — bounded typed wait for the replacement's + // delete of the old index to settle. + const oldGone = yield* waitForIndexGone(accountId, oldIndex.indexName); + expect(oldGone).toBe(true); + + // The new parent has the metadata index. + yield* poll({ + description: "metadata index exists with propertyName=tag", + effect: listMetadataIndexes(accountId, newIndex.indexName), + predicate: (entries) => entries.some((e) => e.propertyName === "tag"), + schedule: metaIndexPoll, + }); + + yield* stack.destroy(); + }).pipe(Effect.ensuring(stack.destroy().pipe(Effect.ignore)), logLevel), + // Two sequential metadata-index materializations plus a parent + // replacement (delete old + create new) and a bounded gone-wait — give + // this genuinely slow create+replace+poll lifecycle real headroom above + // the two poll caps (2 x ~240s) + the ~30s gone-wait so a healthy run + // never races the timeout. Still bounded so a real regression fails fast. + { timeout: 600_000 }, + ); + + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed metadata index", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const { index } = yield* stack.deploy( + Effect.gen(function* () { + const index = yield* Cloudflare.VectorizeIndex("ListParent", { + dimensions: 32, + metric: "cosine", + }); + yield* Cloudflare.VectorizeMetadataIndex("ListMeta", { + indexName: index.indexName, + propertyName: "category", + indexType: "string", + }); + return { index }; + }), + ); + + const provider = yield* Provider.findProvider( + Cloudflare.VectorizeMetadataIndex, + ); + + // Cloudflare processes the create as an async mutation, so the entry + // appears in list() once the mutation is applied — slow and variable + // under full concurrent load, so use the wider capped poll. + const all = yield* poll({ + description: "list() includes the deployed metadata index", + effect: provider.list(), + predicate: (all) => + all.some( + (x) => + x.indexName === index.indexName && + x.propertyName === "category", + ), + schedule: metaIndexPoll, + }); + + const entry = all.find( + (x) => + x.indexName === index.indexName && x.propertyName === "category", + ); + expect(entry?.indexType).toBe("string"); + expect(entry?.accountId).toBeDefined(); + + yield* stack.destroy(); + }).pipe(Effect.ensuring(stack.destroy().pipe(Effect.ignore)), logLevel), + // The single metadata-index materialization can take up to the ~240s + // poll ceiling under concurrent load — give headroom above the cap. + { timeout: 300_000 }, + ); + + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "destroy is idempotent when the parent index was deleted out-of-band", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const { index } = yield* stack.deploy( + Effect.gen(function* () { + const index = yield* Cloudflare.VectorizeIndex("OobParent", { + dimensions: 32, + metric: "cosine", + }); + yield* Cloudflare.VectorizeMetadataIndex("OobMeta", { + indexName: index.indexName, + propertyName: "ns", + indexType: "string", + }); + return { index }; + }), + ); + yield* poll({ + description: "metadata index exists with propertyName=ns", + effect: listMetadataIndexes(accountId, index.indexName), + predicate: (entries) => entries.some((e) => e.propertyName === "ns"), + schedule: metaIndexPoll, + }); + + // Simulate Cloudflare's cascading delete: drop the parent directly. + // On Cloudflare's side this also removes the metadata index. + yield* vectorize.deleteIndex({ + accountId, + indexName: index.indexName, + }); + + // Bounded typed wait for the out-of-band delete to actually settle + // before exercising the idempotent `destroy` path. + const gone = yield* waitForIndexGone(accountId, index.indexName); + expect(gone).toBe(true); + + // The metadata index provider's delete tolerates 404/410 from the + // missing parent, so `destroy` succeeds without erroring. + yield* stack.destroy(); + }).pipe(Effect.ensuring(stack.destroy().pipe(Effect.ignore)), logLevel), + // One metadata-index materialization (capped at ~240s under concurrent + // load) plus an out-of-band delete + gone-wait — keep headroom above the + // poll ceiling so a healthy-but-slow run never races the vitest timeout. + { timeout: 300_000 }, + ); +}); const listMetadataIndexes = Effect.fn(function* ( accountId: string, diff --git a/packages/alchemy/test/Cloudflare/VpcService/VpcService.test.ts b/packages/alchemy/test/Cloudflare/VpcService/VpcService.test.ts index 7ca512f4e..8b110625f 100644 --- a/packages/alchemy/test/Cloudflare/VpcService/VpcService.test.ts +++ b/packages/alchemy/test/Cloudflare/VpcService/VpcService.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -16,117 +19,121 @@ const logLevel = Effect.provideService( process.env.DEBUG ? "Debug" : "Info", ); -test.provider("create, update, delete vpc service", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const { tunnel, service } = yield* stack.deploy( - Effect.gen(function* () { - const tunnel = yield* Cloudflare.Tunnel("VpcTunnel", { - ingress: [{ service: "http://localhost:8080" }], - adopt: true, - }); - const service = yield* Cloudflare.VpcService("VpcSvc", { - httpPort: 8080, - host: { - hostname: "localhost", - resolverNetwork: { tunnelId: tunnel.tunnelId }, - }, - adopt: true, - }); - return { tunnel, service }; - }), - ); - - expect(service.serviceId).toBeDefined(); - expect(service.serviceType).toEqual("http"); - expect(service.httpPort).toEqual(8080); - expect(service.host).toMatchObject({ - hostname: "localhost", - resolverNetwork: { tunnelId: tunnel.tunnelId }, - }); - - const fetched = yield* connectivity.getDirectoryService({ - accountId, - serviceId: service.serviceId, - }); - expect(fetched.serviceId).toEqual(service.serviceId); - expect(fetched.httpPort).toEqual(8080); - - const updated = yield* stack.deploy( - Effect.gen(function* () { - const tunnel = yield* Cloudflare.Tunnel("VpcTunnel", { - ingress: [{ service: "http://localhost:8080" }], - adopt: true, - }); - return yield* Cloudflare.VpcService("VpcSvc", { - httpPort: 3000, - httpsPort: 3001, - host: { - hostname: "localhost", - resolverNetwork: { tunnelId: tunnel.tunnelId }, - }, - adopt: true, - }); - }), - ); - - expect(updated.serviceId).toEqual(service.serviceId); - expect(updated.httpPort).toEqual(3000); - expect(updated.httpsPort).toEqual(3001); - - const fetchedUpdated = yield* connectivity.getDirectoryService({ - accountId, - serviceId: service.serviceId, - }); - expect(fetchedUpdated.httpPort).toEqual(3000); - expect(fetchedUpdated.httpsPort).toEqual(3001); - - yield* stack.destroy(); - - yield* waitForServiceToBeDeleted(service.serviceId, accountId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, update, delete vpc service", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const { tunnel, service } = yield* stack.deploy( + Effect.gen(function* () { + const tunnel = yield* Cloudflare.Tunnel("VpcTunnel", { + ingress: [{ service: "http://localhost:8080" }], + adopt: true, + }); + const service = yield* Cloudflare.VpcService("VpcSvc", { + httpPort: 8080, + host: { + hostname: "localhost", + resolverNetwork: { tunnelId: tunnel.tunnelId }, + }, + adopt: true, + }); + return { tunnel, service }; + }), + ); + + expect(service.serviceId).toBeDefined(); + expect(service.serviceType).toEqual("http"); + expect(service.httpPort).toEqual(8080); + expect(service.host).toMatchObject({ + hostname: "localhost", + resolverNetwork: { tunnelId: tunnel.tunnelId }, + }); + + const fetched = yield* connectivity.getDirectoryService({ + accountId, + serviceId: service.serviceId, + }); + expect(fetched.serviceId).toEqual(service.serviceId); + expect(fetched.httpPort).toEqual(8080); + + const updated = yield* stack.deploy( + Effect.gen(function* () { + const tunnel = yield* Cloudflare.Tunnel("VpcTunnel", { + ingress: [{ service: "http://localhost:8080" }], + adopt: true, + }); + return yield* Cloudflare.VpcService("VpcSvc", { + httpPort: 3000, + httpsPort: 3001, + host: { + hostname: "localhost", + resolverNetwork: { tunnelId: tunnel.tunnelId }, + }, + adopt: true, + }); + }), + ); + + expect(updated.serviceId).toEqual(service.serviceId); + expect(updated.httpPort).toEqual(3000); + expect(updated.httpsPort).toEqual(3001); + + const fetchedUpdated = yield* connectivity.getDirectoryService({ + accountId, + serviceId: service.serviceId, + }); + expect(fetchedUpdated.httpPort).toEqual(3000); + expect(fetchedUpdated.httpsPort).toEqual(3001); + + yield* stack.destroy(); + + yield* waitForServiceToBeDeleted(service.serviceId, accountId); + }).pipe(logLevel), ); -test.provider("create vpc service with ipv4 host", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const service = yield* stack.deploy( - Effect.gen(function* () { - const tunnel = yield* Cloudflare.Tunnel("Ipv4Tunnel", { - ingress: [{ service: "http://localhost:8080" }], - adopt: true, - }); - return yield* Cloudflare.VpcService("Ipv4Svc", { - httpPort: 8080, - host: { - ipv4: "192.168.1.100", - network: { tunnelId: tunnel.tunnelId }, - }, - adopt: true, - }); - }), - ); - - expect(service.host).toMatchObject({ - ipv4: "192.168.1.100", - }); - expect("ipv6" in service.host).toBe(false); - - const fetched = yield* connectivity.getDirectoryService({ - accountId, - serviceId: service.serviceId, - }); - expect((fetched.host as { ipv4?: string }).ipv4).toEqual("192.168.1.100"); - - yield* stack.destroy(); - yield* waitForServiceToBeDeleted(service.serviceId, accountId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create vpc service with ipv4 host", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const service = yield* stack.deploy( + Effect.gen(function* () { + const tunnel = yield* Cloudflare.Tunnel("Ipv4Tunnel", { + ingress: [{ service: "http://localhost:8080" }], + adopt: true, + }); + return yield* Cloudflare.VpcService("Ipv4Svc", { + httpPort: 8080, + host: { + ipv4: "192.168.1.100", + network: { tunnelId: tunnel.tunnelId }, + }, + adopt: true, + }); + }), + ); + + expect(service.host).toMatchObject({ + ipv4: "192.168.1.100", + }); + expect("ipv6" in service.host).toBe(false); + + const fetched = yield* connectivity.getDirectoryService({ + accountId, + serviceId: service.serviceId, + }); + expect((fetched.host as { ipv4?: string }).ipv4).toEqual("192.168.1.100"); + + yield* stack.destroy(); + yield* waitForServiceToBeDeleted(service.serviceId, accountId); + }).pipe(logLevel), ); // TODO: re-enable once distilled ships the union-ordering fix @@ -167,40 +174,42 @@ test.provider.skip("create vpc service with dual-stack host", (stack) => }).pipe(logLevel), ); -test.provider("list enumerates the deployed vpc service", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - const service = yield* stack.deploy( - Effect.gen(function* () { - const tunnel = yield* Cloudflare.Tunnel("ListTunnel", { - ingress: [{ service: "http://localhost:8080" }], - adopt: true, - }); - return yield* Cloudflare.VpcService("ListSvc", { - httpPort: 8080, - host: { - hostname: "localhost", - resolverNetwork: { tunnelId: tunnel.tunnelId }, - }, - adopt: true, - }); - }), - ); - - const provider = yield* Provider.findProvider(Cloudflare.VpcService); - const all = yield* provider.list(); - - const found = all.find((s) => s.serviceId === service.serviceId); - expect(found).toBeDefined(); - expect(found?.serviceName).toEqual(service.serviceName); - expect(found?.accountId).toEqual(accountId); - - yield* stack.destroy(); - yield* waitForServiceToBeDeleted(service.serviceId, accountId); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed vpc service", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + const service = yield* stack.deploy( + Effect.gen(function* () { + const tunnel = yield* Cloudflare.Tunnel("ListTunnel", { + ingress: [{ service: "http://localhost:8080" }], + adopt: true, + }); + return yield* Cloudflare.VpcService("ListSvc", { + httpPort: 8080, + host: { + hostname: "localhost", + resolverNetwork: { tunnelId: tunnel.tunnelId }, + }, + adopt: true, + }); + }), + ); + + const provider = yield* Provider.findProvider(Cloudflare.VpcService); + const all = yield* provider.list(); + + const found = all.find((s) => s.serviceId === service.serviceId); + expect(found).toBeDefined(); + expect(found?.serviceName).toEqual(service.serviceName); + expect(found?.accountId).toEqual(accountId); + + yield* stack.destroy(); + yield* waitForServiceToBeDeleted(service.serviceId, accountId); + }).pipe(logLevel), ); const waitForServiceToBeDeleted = Effect.fn(function* ( diff --git a/packages/alchemy/test/Cloudflare/VpcService/VpcServiceRef.test.ts b/packages/alchemy/test/Cloudflare/VpcService/VpcServiceRef.test.ts index 097d313ed..870a78156 100644 --- a/packages/alchemy/test/Cloudflare/VpcService/VpcServiceRef.test.ts +++ b/packages/alchemy/test/Cloudflare/VpcService/VpcServiceRef.test.ts @@ -4,6 +4,9 @@ import { expect } from "@effect/vitest"; import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -11,40 +14,42 @@ const logLevel = Effect.provideService( process.env.DEBUG ? "Debug" : "Info", ); -test.provider("reference vpc service by name and by id", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const service = yield* stack.deploy( - Effect.gen(function* () { - const tunnel = yield* Cloudflare.Tunnel("RefTunnel", { - ingress: [{ service: "http://localhost:8080" }], - adopt: true, - }); - return yield* Cloudflare.VpcService("RefSvc", { - httpPort: 8080, - host: { - hostname: "localhost", - resolverNetwork: { tunnelId: tunnel.tunnelId }, - }, - adopt: true, - }); - }), - ); - - const refByName = yield* Cloudflare.VpcServiceRef({ - name: service.serviceName, - }); - expect(refByName.serviceId).toEqual(service.serviceId); - expect(refByName.serviceName).toEqual(service.serviceName); - expect(refByName.httpPort).toEqual(service.httpPort); - - const refById = yield* Cloudflare.VpcServiceRef({ - serviceId: service.serviceId, - }); - expect(refById.serviceId).toEqual(service.serviceId); - expect(refById.serviceName).toEqual(service.serviceName); - - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "reference vpc service by name and by id", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const service = yield* stack.deploy( + Effect.gen(function* () { + const tunnel = yield* Cloudflare.Tunnel("RefTunnel", { + ingress: [{ service: "http://localhost:8080" }], + adopt: true, + }); + return yield* Cloudflare.VpcService("RefSvc", { + httpPort: 8080, + host: { + hostname: "localhost", + resolverNetwork: { tunnelId: tunnel.tunnelId }, + }, + adopt: true, + }); + }), + ); + + const refByName = yield* Cloudflare.VpcServiceRef({ + name: service.serviceName, + }); + expect(refByName.serviceId).toEqual(service.serviceId); + expect(refByName.serviceName).toEqual(service.serviceName); + expect(refByName.httpPort).toEqual(service.httpPort); + + const refById = yield* Cloudflare.VpcServiceRef({ + serviceId: service.serviceId, + }); + expect(refById.serviceId).toEqual(service.serviceId); + expect(refById.serviceName).toEqual(service.serviceName); + + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/VulnerabilityScanner/Credential.test.ts b/packages/alchemy/test/Cloudflare/VulnerabilityScanner/Credential.test.ts index cf2fc90a1..8a3426029 100644 --- a/packages/alchemy/test/Cloudflare/VulnerabilityScanner/Credential.test.ts +++ b/packages/alchemy/test/Cloudflare/VulnerabilityScanner/Credential.test.ts @@ -7,6 +7,9 @@ import * as Effect from "effect/Effect"; import * as Redacted from "effect/Redacted"; import { MinimumLogLevel } from "effect/References"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -18,46 +21,51 @@ const logLevel = Effect.provideService( const SET_LIST = "alchemy-vulnscanner-cred-list-set"; const CRED_LIST = "alchemy-vulnscanner-cred-list"; -test.provider("list enumerates the deployed credential", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const deployed = yield* stack.deploy( - Effect.gen(function* () { - const set = yield* Cloudflare.VulnScannerCredentialSet("ListSet", { - name: SET_LIST, - }).pipe(adopt(true)); - const credential = yield* Cloudflare.VulnScannerCredential("ListCred", { - credentialSetId: set.credentialSetId, - name: CRED_LIST, - location: "header", - locationName: "authorization", - value: Redacted.make("Bearer alchemy-list-test"), - }).pipe(adopt(true)); - return { set, credential }; - }), - ); - - const provider = yield* Provider.findProvider( - Cloudflare.VulnScannerCredential, - ); - const all = yield* provider.list(); - - // Parent fan-out over credential sets contains the deployed credential, - // each element shaped exactly like `read`'s Attributes (value is - // write-only and absent from both). - expect( - all.some((x) => x.credentialId === deployed.credential.credentialId), - ).toBe(true); - const found = all.find( - (x) => x.credentialId === deployed.credential.credentialId, - ); - expect(found?.credentialSetId).toEqual(deployed.set.credentialSetId); - expect(found?.name).toEqual(CRED_LIST); - expect(found?.location).toEqual("header"); - expect(found?.locationName).toEqual("authorization"); - expect(found?.accountId).toEqual(deployed.credential.accountId); - - yield* stack.destroy(); - }).pipe(logLevel), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed credential", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const deployed = yield* stack.deploy( + Effect.gen(function* () { + const set = yield* Cloudflare.VulnScannerCredentialSet("ListSet", { + name: SET_LIST, + }).pipe(adopt(true)); + const credential = yield* Cloudflare.VulnScannerCredential( + "ListCred", + { + credentialSetId: set.credentialSetId, + name: CRED_LIST, + location: "header", + locationName: "authorization", + value: Redacted.make("Bearer alchemy-list-test"), + }, + ).pipe(adopt(true)); + return { set, credential }; + }), + ); + + const provider = yield* Provider.findProvider( + Cloudflare.VulnScannerCredential, + ); + const all = yield* provider.list(); + + // Parent fan-out over credential sets contains the deployed credential, + // each element shaped exactly like `read`'s Attributes (value is + // write-only and absent from both). + expect( + all.some((x) => x.credentialId === deployed.credential.credentialId), + ).toBe(true); + const found = all.find( + (x) => x.credentialId === deployed.credential.credentialId, + ); + expect(found?.credentialSetId).toEqual(deployed.set.credentialSetId); + expect(found?.name).toEqual(CRED_LIST); + expect(found?.location).toEqual("header"); + expect(found?.locationName).toEqual("authorization"); + expect(found?.accountId).toEqual(deployed.credential.accountId); + + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/VulnerabilityScanner/CredentialSet.test.ts b/packages/alchemy/test/Cloudflare/VulnerabilityScanner/CredentialSet.test.ts index de98c8865..2c688ae42 100644 --- a/packages/alchemy/test/Cloudflare/VulnerabilityScanner/CredentialSet.test.ts +++ b/packages/alchemy/test/Cloudflare/VulnerabilityScanner/CredentialSet.test.ts @@ -10,6 +10,9 @@ import * as Redacted from "effect/Redacted"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -76,86 +79,90 @@ const findCredential = ( ), ); -test.provider("create, rename, and destroy a credential set", (stack) => - Effect.gen(function* () { - const accountId = yield* accountIdOf; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, rename, and destroy a credential set", + (stack) => + Effect.gen(function* () { + const accountId = yield* accountIdOf; - yield* stack.destroy(); + yield* stack.destroy(); - const initial = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.VulnScannerCredentialSet("DefaultSet", { - name: SET_DEFAULT, - }).pipe(adopt(true)); - }), - ); + const initial = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.VulnScannerCredentialSet("DefaultSet", { + name: SET_DEFAULT, + }).pipe(adopt(true)); + }), + ); - expect(initial.credentialSetId).toBeDefined(); - expect(initial.accountId).toEqual(accountId); - expect(initial.name).toEqual(SET_DEFAULT); - - // Out-of-band verification against the live API. - const live = yield* getCredentialSet(accountId, initial.credentialSetId); - expect(live.id).toEqual(initial.credentialSetId); - expect(live.name).toEqual(SET_DEFAULT); - - // Rename in place — same UUID. - const renamed = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.VulnScannerCredentialSet("DefaultSet", { - name: `${SET_DEFAULT}-renamed`, - }).pipe(adopt(true)); - }), - ); - expect(renamed.credentialSetId).toEqual(initial.credentialSetId); - expect(renamed.name).toEqual(`${SET_DEFAULT}-renamed`); + expect(initial.credentialSetId).toBeDefined(); + expect(initial.accountId).toEqual(accountId); + expect(initial.name).toEqual(SET_DEFAULT); - const liveRenamed = yield* getCredentialSet( - accountId, - renamed.credentialSetId, - ); - expect(liveRenamed.name).toEqual(`${SET_DEFAULT}-renamed`); + // Out-of-band verification against the live API. + const live = yield* getCredentialSet(accountId, initial.credentialSetId); + expect(live.id).toEqual(initial.credentialSetId); + expect(live.name).toEqual(SET_DEFAULT); + + // Rename in place — same UUID. + const renamed = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.VulnScannerCredentialSet("DefaultSet", { + name: `${SET_DEFAULT}-renamed`, + }).pipe(adopt(true)); + }), + ); + expect(renamed.credentialSetId).toEqual(initial.credentialSetId); + expect(renamed.name).toEqual(`${SET_DEFAULT}-renamed`); - yield* stack.destroy(); + const liveRenamed = yield* getCredentialSet( + accountId, + renamed.credentialSetId, + ); + expect(liveRenamed.name).toEqual(`${SET_DEFAULT}-renamed`); - const gone = yield* findCredentialSet(accountId, initial.credentialSetId); - expect(gone).toBeUndefined(); - }).pipe(logLevel), + yield* stack.destroy(); + + const gone = yield* findCredentialSet(accountId, initial.credentialSetId); + expect(gone).toBeUndefined(); + }).pipe(logLevel), ); -test.provider("list enumerates the deployed credential set", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed credential set", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); - const deployed = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.VulnScannerCredentialSet("ListSet", { - name: SET_LIST, - }).pipe(adopt(true)); - }), - ); + const deployed = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.VulnScannerCredentialSet("ListSet", { + name: SET_LIST, + }).pipe(adopt(true)); + }), + ); - const provider = yield* Provider.findProvider( - Cloudflare.VulnScannerCredentialSet, - ); - const all = yield* provider.list(); - - // Exhaustively-paginated account collection contains the deployed set, - // each element shaped exactly like `read`'s Attributes. - expect( - all.some((x) => x.credentialSetId === deployed.credentialSetId), - ).toBe(true); - const found = all.find( - (x) => x.credentialSetId === deployed.credentialSetId, - ); - expect(found?.name).toEqual(SET_LIST); - expect(found?.accountId).toEqual(deployed.accountId); + const provider = yield* Provider.findProvider( + Cloudflare.VulnScannerCredentialSet, + ); + const all = yield* provider.list(); + + // Exhaustively-paginated account collection contains the deployed set, + // each element shaped exactly like `read`'s Attributes. + expect( + all.some((x) => x.credentialSetId === deployed.credentialSetId), + ).toBe(true); + const found = all.find( + (x) => x.credentialSetId === deployed.credentialSetId, + ); + expect(found?.name).toEqual(SET_LIST); + expect(found?.accountId).toEqual(deployed.accountId); - yield* stack.destroy(); - }).pipe(logLevel), + yield* stack.destroy(); + }).pipe(logLevel), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "credential lifecycle — create, mutate in place, rotate value, destroy", (stack) => Effect.gen(function* () { @@ -244,7 +251,7 @@ test.provider( }).pipe(logLevel), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "moving a credential to a different set triggers replacement", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/VulnerabilityScanner/TargetEnvironment.test.ts b/packages/alchemy/test/Cloudflare/VulnerabilityScanner/TargetEnvironment.test.ts index 8119e1567..460b278d6 100644 --- a/packages/alchemy/test/Cloudflare/VulnerabilityScanner/TargetEnvironment.test.ts +++ b/packages/alchemy/test/Cloudflare/VulnerabilityScanner/TargetEnvironment.test.ts @@ -10,6 +10,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -66,50 +69,52 @@ const findTargetEnvironment = ( ), ); -test.provider("create, verify, and destroy a target environment", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - const accountId = yield* accountIdOf; - - yield* stack.destroy(); - - const env = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.VulnScannerTargetEnvironment("DefaultEnv", { - name: NAME_DEFAULT, - zoneId, - description: "alchemy test target environment", - }).pipe(adopt(true)); - }), - ); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, verify, and destroy a target environment", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; + const accountId = yield* accountIdOf; - expect(env.targetEnvironmentId).toBeDefined(); - expect(env.accountId).toEqual(accountId); - expect(env.name).toEqual(NAME_DEFAULT); - expect(env.zoneId).toEqual(zoneId); - expect(env.description).toEqual("alchemy test target environment"); + yield* stack.destroy(); - // Out-of-band verification against the live API. - const live = yield* getTargetEnvironment( - accountId, - env.targetEnvironmentId, - ); - expect(live.id).toEqual(env.targetEnvironmentId); - expect(live.name).toEqual(NAME_DEFAULT); - expect(live.target.zoneTag).toEqual(zoneId); - expect(live.description).toEqual("alchemy test target environment"); + const env = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.VulnScannerTargetEnvironment("DefaultEnv", { + name: NAME_DEFAULT, + zoneId, + description: "alchemy test target environment", + }).pipe(adopt(true)); + }), + ); - yield* stack.destroy(); + expect(env.targetEnvironmentId).toBeDefined(); + expect(env.accountId).toEqual(accountId); + expect(env.name).toEqual(NAME_DEFAULT); + expect(env.zoneId).toEqual(zoneId); + expect(env.description).toEqual("alchemy test target environment"); - const gone = yield* findTargetEnvironment( - accountId, - env.targetEnvironmentId, - ); - expect(gone).toBeUndefined(); - }).pipe(logLevel), + // Out-of-band verification against the live API. + const live = yield* getTargetEnvironment( + accountId, + env.targetEnvironmentId, + ); + expect(live.id).toEqual(env.targetEnvironmentId); + expect(live.name).toEqual(NAME_DEFAULT); + expect(live.target.zoneTag).toEqual(zoneId); + expect(live.description).toEqual("alchemy test target environment"); + + yield* stack.destroy(); + + const gone = yield* findTargetEnvironment( + accountId, + env.targetEnvironmentId, + ); + expect(gone).toBeUndefined(); + }).pipe(logLevel), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "updating name and description patches in place; clearing description", (stack) => Effect.gen(function* () { @@ -184,35 +189,37 @@ test.provider( // Canonical `list()` test (account collection): deploy a target environment, // then enumerate the account-scoped collection and assert the deployed item // appears in the exhaustively-paginated result. -test.provider("list enumerates the deployed target environment", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - yield* stack.destroy(); - - const env = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.VulnScannerTargetEnvironment("ListEnv", { - name: NAME_LIST, - zoneId, - }).pipe(adopt(true)); - }), - ); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed target environment", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - const provider = yield* Provider.findProvider( - Cloudflare.VulnScannerTargetEnvironment, - ); - const all = yield* provider.list(); + yield* stack.destroy(); - expect( - all.some((e) => e.targetEnvironmentId === env.targetEnvironmentId), - ).toBe(true); - const found = all.find( - (e) => e.targetEnvironmentId === env.targetEnvironmentId, - ); - expect(found?.name).toEqual(NAME_LIST); - expect(found?.zoneId).toEqual(zoneId); + const env = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.VulnScannerTargetEnvironment("ListEnv", { + name: NAME_LIST, + zoneId, + }).pipe(adopt(true)); + }), + ); + + const provider = yield* Provider.findProvider( + Cloudflare.VulnScannerTargetEnvironment, + ); + const all = yield* provider.list(); + + expect( + all.some((e) => e.targetEnvironmentId === env.targetEnvironmentId), + ).toBe(true); + const found = all.find( + (e) => e.targetEnvironmentId === env.targetEnvironmentId, + ); + expect(found?.name).toEqual(NAME_LIST); + expect(found?.zoneId).toEqual(zoneId); - yield* stack.destroy(); - }).pipe(logLevel), + yield* stack.destroy(); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/WaitingRoom/Settings.test.ts b/packages/alchemy/test/Cloudflare/WaitingRoom/Settings.test.ts index 4abe09b1d..b64547331 100644 --- a/packages/alchemy/test/Cloudflare/WaitingRoom/Settings.test.ts +++ b/packages/alchemy/test/Cloudflare/WaitingRoom/Settings.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -51,7 +54,7 @@ const getSetting = (zoneId: string) => // Both cases mutate the same zone-level Waiting Room settings singleton; run them serially so they don't corrupt each other's captured baseline under the global concurrent test config. describe.sequential("Settings", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "pins the settings to the default baseline without touching the API", (stack) => Effect.gen(function* () { @@ -88,7 +91,7 @@ describe.sequential("Settings", () => { }).pipe(logLevel), ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed ZoneNotEntitled error when enabling on unentitled zones", (stack) => Effect.gen(function* () { @@ -154,21 +157,23 @@ describe.sequential("Settings", () => { // API for these per-zone settings, so `list()` enumerates every zone via // `listAllZones` and reads the singleton in each. Assert the result is // non-empty and contains the standing test zone. - test.provider("list enumerates the settings across all zones", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - const provider = yield* Provider.findProvider( - Cloudflare.WaitingRoomSettings, - ); - const all = yield* provider.list(); - - expect(all.length).toBeGreaterThan(0); - expect(all.some((s) => s.zoneId === zoneId)).toBe(true); - - // `stack` is unused here (the singleton always exists on every zone), - // but keep the destroy bookend so the harness state stays clean. - yield* stack.destroy(); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the settings across all zones", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; + + const provider = yield* Provider.findProvider( + Cloudflare.WaitingRoomSettings, + ); + const all = yield* provider.list(); + + expect(all.length).toBeGreaterThan(0); + expect(all.some((s) => s.zoneId === zoneId)).toBe(true); + + // `stack` is unused here (the singleton always exists on every zone), + // but keep the destroy bookend so the harness state stays clean. + yield* stack.destroy(); + }).pipe(logLevel), ); }); diff --git a/packages/alchemy/test/Cloudflare/WaitingRoom/WaitingRoom.test.ts b/packages/alchemy/test/Cloudflare/WaitingRoom/WaitingRoom.test.ts index ff0a10d5c..326576266 100644 --- a/packages/alchemy/test/Cloudflare/WaitingRoom/WaitingRoom.test.ts +++ b/packages/alchemy/test/Cloudflare/WaitingRoom/WaitingRoom.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -68,7 +71,7 @@ const findRoomByName = (zoneId: string, name: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed ZoneNotEntitled error on unentitled zones", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Web3/Web3Hostname.test.ts b/packages/alchemy/test/Cloudflare/Web3/Web3Hostname.test.ts index b13556c15..bc864720f 100644 --- a/packages/alchemy/test/Cloudflare/Web3/Web3Hostname.test.ts +++ b/packages/alchemy/test/Cloudflare/Web3/Web3Hostname.test.ts @@ -10,6 +10,9 @@ import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -94,7 +97,7 @@ const waitUntilGone = (zoneId: string, name: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed Web3HostnameNotEntitled error on unentitled accounts", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Website/StaticSite.test.ts b/packages/alchemy/test/Cloudflare/Website/StaticSite.test.ts index 568614784..d93eac268 100644 --- a/packages/alchemy/test/Cloudflare/Website/StaticSite.test.ts +++ b/packages/alchemy/test/Cloudflare/Website/StaticSite.test.ts @@ -15,6 +15,9 @@ import { waitForWorkerToBeDeleted, } from "../Utils/Worker.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -25,7 +28,7 @@ const logLevel = Effect.provideService( const fixtureDir = pathe.resolve(import.meta.dirname, "staticsite-fixture"); const workerEntry = pathe.resolve(import.meta.dirname, "fixtures/worker.ts"); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "StaticSite: editing a source file republishes the assets in a single deploy", (stack) => Effect.gen(function* () { @@ -96,7 +99,7 @@ test.provider( { timeout: 360_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "StaticSite: class form deploys and serves the built assets", (stack) => Effect.gen(function* () { @@ -155,7 +158,7 @@ test.provider( // hash and ships new bytes — repeated here for symmetry. // ───────────────────────────────────────────────────────────────────── -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "StaticSite: relocating the project (and deleting the old one) preserves hash.assets", (stack) => Effect.gen(function* () { @@ -238,7 +241,7 @@ test.provider( { timeout: 360_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "StaticSite: a bundle-only change keeps the asset manifest (hash.assets stable)", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Website/Vite.test.ts b/packages/alchemy/test/Cloudflare/Website/Vite.test.ts index 429328181..3b9a5c6bd 100644 --- a/packages/alchemy/test/Cloudflare/Website/Vite.test.ts +++ b/packages/alchemy/test/Cloudflare/Website/Vite.test.ts @@ -16,6 +16,9 @@ import { waitForWorkerToBeDeleted, } from "../Utils/Worker.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -33,7 +36,7 @@ const fixtureDir = pathe.resolve(import.meta.dirname, "vite-fixture"); // root as `cwd`. const tempRoot = pathe.resolve(import.meta.dirname, "../../../.tmp"); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "Vite: editing a source file republishes the assets in a single deploy", (stack) => Effect.gen(function* () { @@ -100,7 +103,7 @@ test.provider( { timeout: 360_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "Vite: class form deploys and serves the built assets", (stack) => Effect.gen(function* () { @@ -157,7 +160,7 @@ test.provider( // test fail loudly if anything still depends on the recorded path. // ───────────────────────────────────────────────────────────────────── -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "Vite: relocating rootDir (and deleting the old one) is a no-op when sources are identical", (stack) => Effect.gen(function* () { @@ -230,7 +233,7 @@ test.provider( { timeout: 360_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "Vite: `env` props are inlined as `import.meta.env.*` into the bundle", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Workers/CronEventSource.test.ts b/packages/alchemy/test/Cloudflare/Workers/CronEventSource.test.ts index f213f3176..fdb977028 100644 --- a/packages/alchemy/test/Cloudflare/Workers/CronEventSource.test.ts +++ b/packages/alchemy/test/Cloudflare/Workers/CronEventSource.test.ts @@ -20,7 +20,7 @@ const logLevel = Effect.provideService( const stack = beforeAll(deploy(Stack)); afterAll.skipIf(!!process.env.NO_DESTROY)(destroy(Stack)); -test.skipIf(!!process.env.NO_SLOW_TESTS)( +test.skipIf(!!process.env.FAST)( "deployed worker fires the scheduled handler on its cron trigger", Effect.gen(function* () { const { url, crons } = yield* stack; @@ -65,5 +65,5 @@ test.skipIf(!!process.env.NO_SLOW_TESTS)( expect(t).toBeGreaterThanOrEqual(resetAt); } }).pipe(logLevel), - { timeout: 300_000 }, + { timeout: 120_000 }, ); diff --git a/packages/alchemy/test/Cloudflare/Workers/DurableObjectNamespace.test.ts b/packages/alchemy/test/Cloudflare/Workers/DurableObjectNamespace.test.ts index 7b62a44e8..b365840fa 100644 --- a/packages/alchemy/test/Cloudflare/Workers/DurableObjectNamespace.test.ts +++ b/packages/alchemy/test/Cloudflare/Workers/DurableObjectNamespace.test.ts @@ -9,6 +9,9 @@ import * as HttpClient from "effect/unstable/http/HttpClient"; import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"; import Stack from "./fixtures/do-rpc/stack.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test, beforeAll, afterAll, deploy, destroy } = Test.make({ providers: Cloudflare.providers(), }); @@ -43,7 +46,7 @@ const freshConn = HttpClient.mapRequest( HttpClientRequest.setHeader("connection", "close"), ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "durable object methods can use binding clients", Effect.gen(function* () { const { url } = yield* stack; @@ -71,7 +74,7 @@ test( // The DO exposes `tick(n): Stream` and the Worker forwards it to the // HTTP response with `HttpServerResponse.stream`. The client reads the body as // newline-delimited integers. With `/tick/5` we expect ["0","1","2","3","4"]. -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "tick streams sequential values from a durable object (tutorial repro)", Effect.gen(function* () { const { url } = yield* stack; @@ -201,7 +204,7 @@ const consumerWorkerScript = `export default { }; `; -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "async worker durable object binding accepts scriptName", (scratch) => Effect.gen(function* () { @@ -266,7 +269,7 @@ test.provider( // v2 — rename `DO_A` → `DO_A_v2` (className change, same binding id) // v3 — add a brand-new DO class `DO_B` alongside `DO_A_v2` // v4 — delete `DO_A`, keep only `DO_B` -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "durable object class migrations across redeploys", (scratch) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Workers/ObservabilityDestination.test.ts b/packages/alchemy/test/Cloudflare/Workers/ObservabilityDestination.test.ts index 29eafaac6..16d1fd250 100644 --- a/packages/alchemy/test/Cloudflare/Workers/ObservabilityDestination.test.ts +++ b/packages/alchemy/test/Cloudflare/Workers/ObservabilityDestination.test.ts @@ -12,6 +12,9 @@ import * as Stream from "effect/Stream"; import * as HttpClient from "effect/unstable/http/HttpClient"; import * as pathe from "pathe"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -87,7 +90,7 @@ const expectGone = (accountId: string, name: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create and delete a destination with default name", (stack) => Effect.gen(function* () { @@ -126,7 +129,7 @@ test.provider( { timeout: 300_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "update url, headers, and enabled in place (same slug)", (stack) => Effect.gen(function* () { @@ -232,7 +235,7 @@ test.provider( { timeout: 300_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "replacement on name change (new slug, old destination removed)", (stack) => Effect.gen(function* () { @@ -282,7 +285,7 @@ test.provider( { timeout: 300_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed destination", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Workers/PrebuiltWorker.test.ts b/packages/alchemy/test/Cloudflare/Workers/PrebuiltWorker.test.ts index 7bb953434..b3720e292 100644 --- a/packages/alchemy/test/Cloudflare/Workers/PrebuiltWorker.test.ts +++ b/packages/alchemy/test/Cloudflare/Workers/PrebuiltWorker.test.ts @@ -9,6 +9,9 @@ import * as pathe from "pathe"; import { expectUrlContains } from "../Utils/Http.ts"; import { waitForWorkerToBeDeleted } from "../Utils/Worker.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -19,7 +22,7 @@ const logLevel = Effect.provideService( const main = pathe.resolve(import.meta.dirname, "fixtures/prebuilt/worker.mjs"); describe.concurrent("Cloudflare.Worker with bundle: false", () => { - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "uploads a prebuilt module graph byte-for-byte", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Workers/Route.test.ts b/packages/alchemy/test/Cloudflare/Workers/Route.test.ts index 5f92cb23e..a594a0aa4 100644 --- a/packages/alchemy/test/Cloudflare/Workers/Route.test.ts +++ b/packages/alchemy/test/Cloudflare/Workers/Route.test.ts @@ -13,6 +13,9 @@ import * as Schedule from "effect/Schedule"; import * as Stream from "effect/Stream"; import * as pathe from "pathe"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -91,7 +94,7 @@ const purgeRoutes = (zoneId: string, pattern: string) => ), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create and delete an opt-out route (no script)", (stack) => Effect.gen(function* () { @@ -127,7 +130,7 @@ test.provider( { timeout: 300_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "route to a Worker, then update pattern and script in place", (stack) => Effect.gen(function* () { @@ -191,7 +194,7 @@ test.provider( { timeout: 300_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "adoption — existing route errors without adopt, takes over with adopt(true)", (stack) => Effect.gen(function* () { @@ -259,7 +262,7 @@ const PATTERN_LIST = `alchemy-route-list.${zoneName}/*`; // `listAllZones`, paginates `listRoutes` per zone, and hydrates each into the // `read` Attributes shape. Deploy a real route and assert it appears in the // exhaustively-enumerated result. -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed route across all zones", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Workers/Worker.test.ts b/packages/alchemy/test/Cloudflare/Workers/Worker.test.ts index 1dcb18057..a0ed226a9 100644 --- a/packages/alchemy/test/Cloudflare/Workers/Worker.test.ts +++ b/packages/alchemy/test/Cloudflare/Workers/Worker.test.ts @@ -27,6 +27,9 @@ import { import type { Counter, Meter } from "./fixtures/do-counter-worker.ts"; import InternalWorker from "./fixtures/internal-worker.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -41,175 +44,183 @@ const doMain = pathe.resolve( ); describe.concurrent("Cloudflare.Worker", () => { - test.provider("create, update, delete worker", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - const s = yield* Stack; + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, update, delete worker", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + const s = yield* Stack; - yield* stack.destroy(); + yield* stack.destroy(); - const worker = yield* stack.deploy( - Effect.gen(function* () { - yield* R2.R2Bucket("Bucket", { - storageClass: "Standard", - }); + const worker = yield* stack.deploy( + Effect.gen(function* () { + yield* R2.R2Bucket("Bucket", { + storageClass: "Standard", + }); - const worker = yield* Cloudflare.Worker("TestWorker", { - main, - subdomain: { enabled: true, previewsEnabled: true }, - compatibility: { - date: "2024-01-01", - }, - }); + const worker = yield* Cloudflare.Worker("TestWorker", { + main, + subdomain: { enabled: true, previewsEnabled: true }, + compatibility: { + date: "2024-01-01", + }, + }); - return worker; - }), - ); - - const actualWorker = yield* findWorker(worker.workerName, accountId); - expect(actualWorker?.scriptName).toEqual(worker.workerName); - expect(yield* getWorkerTags(worker.workerName, accountId)).toContain( - `alchemy:stack:${s.name}`, - ); - expect(yield* getWorkerTags(worker.workerName, accountId)).toContain( - `alchemy:stage:${s.stage}`, - ); - expect(yield* getWorkerTags(worker.workerName, accountId)).toContain( - "alchemy:id:TestWorker", - ); - - // Verify the workers.dev subdomain is enabled on Cloudflare - // (rather than just trusting the resource's output attributes). - expect(worker.url).toBeDefined(); - const initialSubdomain = yield* workers.getScriptSubdomain({ - accountId, - scriptName: worker.workerName, - }); - expect(initialSubdomain).toEqual({ - enabled: true, - previewsEnabled: true, - }); - - // Update the worker - const updatedWorker = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.Worker("TestWorker", { - main, - subdomain: { enabled: true, previewsEnabled: true }, - compatibility: { - date: "2024-01-01", - }, - }); - }), - ); - - const actualUpdatedWorker = yield* findWorker( - updatedWorker.workerName, - accountId, - ); - expect(actualUpdatedWorker?.scriptName).toEqual(updatedWorker.workerName); - const actualUpdatedSubdomain = yield* workers.getScriptSubdomain({ - accountId, - scriptName: updatedWorker.workerName, - }); - expect(actualUpdatedSubdomain).toEqual({ - enabled: true, - previewsEnabled: true, - }); - - yield* stack.destroy(); - - yield* waitForWorkerToBeDeleted(worker.workerName, accountId); - }).pipe(logLevel), + return worker; + }), + ); + + const actualWorker = yield* findWorker(worker.workerName, accountId); + expect(actualWorker?.scriptName).toEqual(worker.workerName); + expect(yield* getWorkerTags(worker.workerName, accountId)).toContain( + `alchemy:stack:${s.name}`, + ); + expect(yield* getWorkerTags(worker.workerName, accountId)).toContain( + `alchemy:stage:${s.stage}`, + ); + expect(yield* getWorkerTags(worker.workerName, accountId)).toContain( + "alchemy:id:TestWorker", + ); + + // Verify the workers.dev subdomain is enabled on Cloudflare + // (rather than just trusting the resource's output attributes). + expect(worker.url).toBeDefined(); + const initialSubdomain = yield* workers.getScriptSubdomain({ + accountId, + scriptName: worker.workerName, + }); + expect(initialSubdomain).toEqual({ + enabled: true, + previewsEnabled: true, + }); + + // Update the worker + const updatedWorker = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.Worker("TestWorker", { + main, + subdomain: { enabled: true, previewsEnabled: true }, + compatibility: { + date: "2024-01-01", + }, + }); + }), + ); + + const actualUpdatedWorker = yield* findWorker( + updatedWorker.workerName, + accountId, + ); + expect(actualUpdatedWorker?.scriptName).toEqual( + updatedWorker.workerName, + ); + const actualUpdatedSubdomain = yield* workers.getScriptSubdomain({ + accountId, + scriptName: updatedWorker.workerName, + }); + expect(actualUpdatedSubdomain).toEqual({ + enabled: true, + previewsEnabled: true, + }); + + yield* stack.destroy(); + + yield* waitForWorkerToBeDeleted(worker.workerName, accountId); + }).pipe(logLevel), ); - test.provider("create, update, delete worker with assets", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - const s = yield* Stack; - - yield* stack.destroy(); - - const worker = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.Worker("TestWorkerWithAssets", { - main, - assets: pathe.resolve(import.meta.dirname, "assets"), - subdomain: { enabled: true, previewsEnabled: true }, - compatibility: { - date: "2024-01-01", - }, - }); - }), - ); - - const actualWorker = yield* findWorker(worker.workerName, accountId); - expect(actualWorker?.scriptName).toEqual(worker.workerName); - expect(yield* getWorkerTags(worker.workerName, accountId)).toContain( - `alchemy:stack:${s.name}`, - ); - expect(yield* getWorkerTags(worker.workerName, accountId)).toContain( - `alchemy:stage:${s.stage}`, - ); - expect(yield* getWorkerTags(worker.workerName, accountId)).toContain( - "alchemy:id:TestWorkerWithAssets", - ); - - // Verify the worker has assets - expect(worker.hash?.assets).toBeDefined(); - - // Verify the workers.dev subdomain is enabled on Cloudflare - // (rather than just trusting the resource's output attributes). - expect(worker.url).toBeDefined(); - const assetsWorkerSubdomain = yield* workers.getScriptSubdomain({ - accountId, - scriptName: worker.workerName, - }); - expect(assetsWorkerSubdomain).toEqual({ - enabled: true, - previewsEnabled: true, - }); - - // Update the worker - const updatedWorker = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.Worker("TestWorkerWithAssets", { - main, - assets: pathe.resolve(import.meta.dirname, "assets"), - subdomain: { enabled: true, previewsEnabled: true }, - compatibility: { - date: "2024-01-01", - }, - }); - }), - ); - - const actualUpdatedWorker = yield* findWorker( - updatedWorker.workerName, - accountId, - ); - expect(actualUpdatedWorker?.scriptName).toEqual(updatedWorker.workerName); - expect(updatedWorker.hash?.assets).toBeDefined(); - - // Final update - const finalWorker = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.Worker("TestWorkerWithAssets", { - main, - url: true, - assets: pathe.resolve(import.meta.dirname, "assets"), - subdomain: { enabled: true, previewsEnabled: true }, - compatibility: { - date: "2024-01-01", - }, - }); - }), - ); + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, update, delete worker with assets", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + const s = yield* Stack; + + yield* stack.destroy(); - yield* stack.destroy(); + const worker = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.Worker("TestWorkerWithAssets", { + main, + assets: pathe.resolve(import.meta.dirname, "assets"), + subdomain: { enabled: true, previewsEnabled: true }, + compatibility: { + date: "2024-01-01", + }, + }); + }), + ); - yield* waitForWorkerToBeDeleted(finalWorker.workerName, accountId); - }).pipe(logLevel), + const actualWorker = yield* findWorker(worker.workerName, accountId); + expect(actualWorker?.scriptName).toEqual(worker.workerName); + expect(yield* getWorkerTags(worker.workerName, accountId)).toContain( + `alchemy:stack:${s.name}`, + ); + expect(yield* getWorkerTags(worker.workerName, accountId)).toContain( + `alchemy:stage:${s.stage}`, + ); + expect(yield* getWorkerTags(worker.workerName, accountId)).toContain( + "alchemy:id:TestWorkerWithAssets", + ); + + // Verify the worker has assets + expect(worker.hash?.assets).toBeDefined(); + + // Verify the workers.dev subdomain is enabled on Cloudflare + // (rather than just trusting the resource's output attributes). + expect(worker.url).toBeDefined(); + const assetsWorkerSubdomain = yield* workers.getScriptSubdomain({ + accountId, + scriptName: worker.workerName, + }); + expect(assetsWorkerSubdomain).toEqual({ + enabled: true, + previewsEnabled: true, + }); + + // Update the worker + const updatedWorker = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.Worker("TestWorkerWithAssets", { + main, + assets: pathe.resolve(import.meta.dirname, "assets"), + subdomain: { enabled: true, previewsEnabled: true }, + compatibility: { + date: "2024-01-01", + }, + }); + }), + ); + + const actualUpdatedWorker = yield* findWorker( + updatedWorker.workerName, + accountId, + ); + expect(actualUpdatedWorker?.scriptName).toEqual( + updatedWorker.workerName, + ); + expect(updatedWorker.hash?.assets).toBeDefined(); + + // Final update + const finalWorker = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.Worker("TestWorkerWithAssets", { + main, + url: true, + assets: pathe.resolve(import.meta.dirname, "assets"), + subdomain: { enabled: true, previewsEnabled: true }, + compatibility: { + date: "2024-01-01", + }, + }); + }), + ); + + yield* stack.destroy(); + + yield* waitForWorkerToBeDeleted(finalWorker.workerName, accountId); + }).pipe(logLevel), ); // ───────────────────────────────────────────────────────────────────── @@ -234,7 +245,7 @@ describe.concurrent("Cloudflare.Worker", () => { const assetsFixtureDir = pathe.resolve(import.meta.dirname, "assets"); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "Worker assets: relocating to a fresh path with identical bytes preserves hash and keeps assets", (stack) => Effect.gen(function* () { @@ -292,7 +303,7 @@ describe.concurrent("Cloudflare.Worker", () => { { timeout: 360_000 }, ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "Worker assets: editing a file changes the hash and republishes the manifest", (stack) => Effect.gen(function* () { @@ -353,7 +364,7 @@ describe.concurrent("Cloudflare.Worker", () => { { timeout: 360_000 }, ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "Worker assets: a bundle-only change keeps the asset manifest (hash.assets stable)", (stack) => Effect.gen(function* () { @@ -418,43 +429,45 @@ describe.concurrent("Cloudflare.Worker", () => { { timeout: 360_000 }, ); - test.provider("create, update, delete internal worker", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - const s = yield* Stack; - - yield* stack.destroy(); - - const worker = yield* stack.deploy( - Effect.gen(function* () { - return yield* InternalWorker; - }), - ); - - const actualWorker = yield* findWorker(worker.workerName, accountId); - expect(actualWorker?.scriptName).toEqual(worker.workerName); - expect(yield* getWorkerTags(worker.workerName, accountId)).toContain( - `alchemy:stack:${s.name}`, - ); - expect(yield* getWorkerTags(worker.workerName, accountId)).toContain( - `alchemy:stage:${s.stage}`, - ); - expect(yield* getWorkerTags(worker.workerName, accountId)).toContain( - "alchemy:id:InternalWorker", - ); - - const updatedWorker = yield* stack.deploy( - Effect.gen(function* () { - return yield* InternalWorker; - }), - ); - - expect(updatedWorker.workerName).toEqual(worker.workerName); - - yield* stack.destroy(); - - yield* waitForWorkerToBeDeleted(worker.workerName, accountId); - }).pipe(logLevel), + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "create, update, delete internal worker", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + const s = yield* Stack; + + yield* stack.destroy(); + + const worker = yield* stack.deploy( + Effect.gen(function* () { + return yield* InternalWorker; + }), + ); + + const actualWorker = yield* findWorker(worker.workerName, accountId); + expect(actualWorker?.scriptName).toEqual(worker.workerName); + expect(yield* getWorkerTags(worker.workerName, accountId)).toContain( + `alchemy:stack:${s.name}`, + ); + expect(yield* getWorkerTags(worker.workerName, accountId)).toContain( + `alchemy:stage:${s.stage}`, + ); + expect(yield* getWorkerTags(worker.workerName, accountId)).toContain( + "alchemy:id:InternalWorker", + ); + + const updatedWorker = yield* stack.deploy( + Effect.gen(function* () { + return yield* InternalWorker; + }), + ); + + expect(updatedWorker.workerName).toEqual(worker.workerName); + + yield* stack.destroy(); + + yield* waitForWorkerToBeDeleted(worker.workerName, accountId); + }).pipe(logLevel), ); // ── Engine-level adoption ───────────────────────────────────────────────── @@ -477,7 +490,7 @@ describe.concurrent("Cloudflare.Worker", () => { // never seen it" (e.g. CLI-driven first deploy on a fresh machine, or a // state-store reset). - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "owned worker (matching alchemy tags) is silently adopted without --adopt", (stack) => Effect.gen(function* () { @@ -556,79 +569,81 @@ describe.concurrent("Cloudflare.Worker", () => { }).pipe(logLevel), ); - test.provider("adopt(true) takes over a foreign-tagged worker", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; - - yield* stack.destroy(); - - // Phase 1: deploy under logical id "Original" with an explicit - // physical name. The Cloudflare Worker is now tagged - // `alchemy:id:Original` — i.e. owned by *that* logical resource. - const physicalName = `alchemy-test-adopt-takeover-${Math.random() - .toString(36) - .slice(2, 8)}`; - const original = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.Worker("Original", { - main, - name: physicalName, - subdomain: { enabled: true, previewsEnabled: true }, - compatibility: { date: "2024-01-01" }, - }); - }), - ); - expect(yield* findWorker(original.workerName, accountId)).toBeDefined(); - expect(yield* getWorkerTags(physicalName, accountId)).toContain( - "alchemy:id:Original", - ); - - // Wipe state for the "Original" entry; the worker stays on Cloudflare. - yield* Effect.gen(function* () { - const state = yield* yield* State; - yield* state.delete({ - stack: stack.name, - stage: "test", - fqn: "Original", - }); - }).pipe(Effect.provide(stack.state)); - - // Phase 2: redeploy under a *different* logical id with the same - // physical name and `adopt(true)`. `Worker.read` returns - // `Unowned(attrs)` because the existing tags identify a different - // logical id; with adopt enabled the engine takes over and the - // follow-up create/update rewrites the tags. (The rejection path - // — same scenario without `adopt(true)` — is covered by the unit - // tests in `plan.test.ts`.) - const takenOver = yield* stack - .deploy( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "adopt(true) takes over a foreign-tagged worker", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; + + yield* stack.destroy(); + + // Phase 1: deploy under logical id "Original" with an explicit + // physical name. The Cloudflare Worker is now tagged + // `alchemy:id:Original` — i.e. owned by *that* logical resource. + const physicalName = `alchemy-test-adopt-takeover-${Math.random() + .toString(36) + .slice(2, 8)}`; + const original = yield* stack.deploy( Effect.gen(function* () { - return yield* Cloudflare.Worker("Different", { + return yield* Cloudflare.Worker("Original", { main, name: physicalName, subdomain: { enabled: true, previewsEnabled: true }, compatibility: { date: "2024-01-01" }, }); }), - ) - .pipe(adopt(true)); + ); + expect(yield* findWorker(original.workerName, accountId)).toBeDefined(); + expect(yield* getWorkerTags(physicalName, accountId)).toContain( + "alchemy:id:Original", + ); - expect(takenOver.workerName).toEqual(physicalName); + // Wipe state for the "Original" entry; the worker stays on Cloudflare. + yield* Effect.gen(function* () { + const state = yield* yield* State; + yield* state.delete({ + stack: stack.name, + stage: "test", + fqn: "Original", + }); + }).pipe(Effect.provide(stack.state)); - const newTags = yield* getWorkerTags(physicalName, accountId); - expect(newTags).toContain("alchemy:id:Different"); - expect(newTags).not.toContain("alchemy:id:Original"); + // Phase 2: redeploy under a *different* logical id with the same + // physical name and `adopt(true)`. `Worker.read` returns + // `Unowned(attrs)` because the existing tags identify a different + // logical id; with adopt enabled the engine takes over and the + // follow-up create/update rewrites the tags. (The rejection path + // — same scenario without `adopt(true)` — is covered by the unit + // tests in `plan.test.ts`.) + const takenOver = yield* stack + .deploy( + Effect.gen(function* () { + return yield* Cloudflare.Worker("Different", { + main, + name: physicalName, + subdomain: { enabled: true, previewsEnabled: true }, + compatibility: { date: "2024-01-01" }, + }); + }), + ) + .pipe(adopt(true)); - yield* stack.destroy(); - yield* waitForWorkerToBeDeleted(physicalName, accountId); - }).pipe(logLevel), + expect(takenOver.workerName).toEqual(physicalName); + + const newTags = yield* getWorkerTags(physicalName, accountId); + expect(newTags).toContain("alchemy:id:Different"); + expect(newTags).not.toContain("alchemy:id:Original"); + + yield* stack.destroy(); + yield* waitForWorkerToBeDeleted(physicalName, accountId); + }).pipe(logLevel), ); // First-deploy behaviour: the default (omitting `url`) must enable // the workers.dev subdomain, and `url: false` must disable it. Both // are asserted against live Cloudflare state via `getScriptSubdomain`, // not just the resource's output attributes. - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "url defaults to enabling the workers.dev subdomain on first deploy", (stack) => Effect.gen(function* () { @@ -653,7 +668,7 @@ describe.concurrent("Cloudflare.Worker", () => { }).pipe(logLevel), ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "url: false disables the workers.dev subdomain on first deploy", (stack) => Effect.gen(function* () { @@ -685,7 +700,7 @@ describe.concurrent("Cloudflare.Worker", () => { // drove the API call symmetrically — but the new observed-vs- // desired check inside reconcile must still flip the toggle when // props really do change. - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "toggling url between deploys flips the workers.dev subdomain", (stack) => Effect.gen(function* () { @@ -729,7 +744,7 @@ describe.concurrent("Cloudflare.Worker", () => { // a redeploy must observe `previewsEnabled` and flip it back on. The // pre-fix reconciler diffed only `enabled` against desired, so it // skipped the API call and let the drift persist. - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "redeploy re-enables previewsEnabled when externally disabled", (stack) => Effect.gen(function* () { @@ -781,7 +796,7 @@ describe.concurrent("Cloudflare.Worker", () => { // `domains` should reflect the workers.dev URL when the subdomain is // enabled and be empty when it isn't. `worker.url` is just `domains[0]`, // so the two must stay in lockstep across deploys. - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "domains reflects the workers.dev subdomain and tracks url", (stack) => Effect.gen(function* () { @@ -819,7 +834,7 @@ describe.concurrent("Cloudflare.Worker", () => { // when the subdomain is enabled. `worker.url` is `domains[0]`, so the // custom domain wins. const customDomainZone = process.env.CLOUDFLARE_TEST_WORKER_DOMAIN_ZONE_NAME; - test.provider.skipIf(!customDomainZone)( + test.provider.skipIf(!customDomainZone || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "domains puts custom domains before workers.dev and url is the first", (stack) => Effect.gen(function* () { @@ -870,35 +885,37 @@ describe.concurrent("Cloudflare.Worker", () => { // Canonical `list()` test (account collection): deploy a real Worker and // assert it shows up in the exhaustively-paginated account-wide listing. - test.provider("list enumerates the deployed worker", (stack) => - Effect.gen(function* () { - const { accountId } = yield* yield* CloudflareEnvironment; + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed worker", + (stack) => + Effect.gen(function* () { + const { accountId } = yield* yield* CloudflareEnvironment; - yield* stack.destroy(); + yield* stack.destroy(); - const worker = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.Worker("ListWorker", { - main, - compatibility: { date: "2024-01-01" }, - }); - }), - ); + const worker = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.Worker("ListWorker", { + main, + compatibility: { date: "2024-01-01" }, + }); + }), + ); - const provider = yield* Provider.findProvider(Cloudflare.Worker); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider(Cloudflare.Worker); + const all = yield* provider.list(); - expect(all.some((w) => w.workerName === worker.workerName)).toBe(true); - const found = all.find((w) => w.workerName === worker.workerName); - expect(found?.workerId).toEqual(worker.workerId); - expect(found?.accountId).toEqual(accountId); + expect(all.some((w) => w.workerName === worker.workerName)).toBe(true); + const found = all.find((w) => w.workerName === worker.workerName); + expect(found?.workerId).toEqual(worker.workerId); + expect(found?.accountId).toEqual(accountId); - yield* stack.destroy(); - yield* waitForWorkerToBeDeleted(worker.workerName, accountId); - }).pipe(logLevel), + yield* stack.destroy(); + yield* waitForWorkerToBeDeleted(worker.workerName, accountId); + }).pipe(logLevel), ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "downstream referencing worker.url is not re-updated when the worker changes", (stack) => // Regression: a downstream resource that references `worker.url` as a @@ -949,7 +966,7 @@ describe.concurrent("Cloudflare.Worker", () => { { timeout: 180_000 }, ); - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "worker.durableObjectNamespaces stability across DO and worker changes", (stack) => // Exercises plan actions for a downstream resource whose props reference diff --git a/packages/alchemy/test/Cloudflare/Workers/WorkerBinding.test.ts b/packages/alchemy/test/Cloudflare/Workers/WorkerBinding.test.ts index 82c554624..a72c49b3a 100644 --- a/packages/alchemy/test/Cloudflare/Workers/WorkerBinding.test.ts +++ b/packages/alchemy/test/Cloudflare/Workers/WorkerBinding.test.ts @@ -7,6 +7,9 @@ import * as Schedule from "effect/Schedule"; import * as HttpClient from "effect/unstable/http/HttpClient"; import Stack from "./fixtures/worker-worker-binding/stack.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test, beforeAll, afterAll, deploy, destroy } = Test.make({ providers: Cloudflare.providers(), }); @@ -26,7 +29,7 @@ const coldStartRetry = Effect.retry({ times: 10, }); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "target worker's own fetch handler responds", Effect.gen(function* () { const { targetUrl } = yield* stack; @@ -39,7 +42,7 @@ test( { timeout: 60_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "async caller can call target's RPC method via service binding", Effect.gen(function* () { const { asyncCallerUrl } = yield* stack; @@ -54,7 +57,7 @@ test( { timeout: 180_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "effect caller can call target's RPC method via bindWorker", Effect.gen(function* () { const { effectCallerUrl } = yield* stack; diff --git a/packages/alchemy/test/Cloudflare/Workers/WorkerEnv.test.ts b/packages/alchemy/test/Cloudflare/Workers/WorkerEnv.test.ts index cda290629..28dabd359 100644 --- a/packages/alchemy/test/Cloudflare/Workers/WorkerEnv.test.ts +++ b/packages/alchemy/test/Cloudflare/Workers/WorkerEnv.test.ts @@ -6,6 +6,9 @@ import { MinimumLogLevel } from "effect/References"; import { expectUrlContains } from "../Utils/Http.ts"; import Stack from "./fixtures/env/stack.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + // Populate process.env before deploy so the worker fixture's // `Config.xxx(...)` reads resolve at deploy time (default provider). const CONFIG_STR_VALUE = (process.env.CONFIG_STR = "config-string-value"); @@ -28,7 +31,7 @@ const stack = beforeAll(deploy(Stack)); afterAll.skipIf(!!process.env.NO_DESTROY)(destroy(Stack)); describe.concurrent("Cloudflare.Worker env bindings", () => { - test( + test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "async worker round-trips every supported binding shape", Effect.gen(function* () { const { asyncUrl } = yield* stack; @@ -61,7 +64,7 @@ describe.concurrent("Cloudflare.Worker env bindings", () => { }).pipe(logLevel), ); - test( + test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "effect worker round-trips env: literals and Redacted via WorkerEnvironment", Effect.gen(function* () { const { effectUrl } = yield* stack; @@ -85,7 +88,7 @@ describe.concurrent("Cloudflare.Worker env bindings", () => { }).pipe(logLevel), ); - test( + test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "effect worker resolves the yielded VersionMetadata binding", Effect.gen(function* () { const { effectUrl } = yield* stack; @@ -102,7 +105,7 @@ describe.concurrent("Cloudflare.Worker env bindings", () => { }).pipe(logLevel), ); - test( + test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "effect worker round-trips Config.xxx bindings captured in Init", Effect.gen(function* () { const { effectUrl } = yield* stack; diff --git a/packages/alchemy/test/Cloudflare/Workers/WorkerLoader.test.ts b/packages/alchemy/test/Cloudflare/Workers/WorkerLoader.test.ts index fa1b4deaf..520a4f7ae 100644 --- a/packages/alchemy/test/Cloudflare/Workers/WorkerLoader.test.ts +++ b/packages/alchemy/test/Cloudflare/Workers/WorkerLoader.test.ts @@ -7,6 +7,9 @@ import * as Schedule from "effect/Schedule"; import * as HttpClient from "effect/unstable/http/HttpClient"; import Stack from "./fixtures/dynamic-worker-loader/stack.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test, beforeAll, afterAll, deploy, destroy } = Test.make({ providers: Cloudflare.providers(), }); @@ -42,7 +45,7 @@ const readJson = (url: string) => const stack = beforeAll(deploy(Stack)); afterAll.skipIf(!!process.env.NO_DESTROY)(destroy(Stack)); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "async worker loads and proxies to a dynamic worker via env binding", Effect.gen(function* () { const { asyncWorkerUrl } = yield* stack; @@ -52,7 +55,7 @@ test( { timeout: 180_000 }, ); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "effect worker loads and proxies to a dynamic worker via yield* loader", Effect.gen(function* () { const { effectWorkerUrl } = yield* stack; diff --git a/packages/alchemy/test/Cloudflare/Workers/Workflow.test.ts b/packages/alchemy/test/Cloudflare/Workers/Workflow.test.ts index 356c97ef6..d685695c7 100644 --- a/packages/alchemy/test/Cloudflare/Workers/Workflow.test.ts +++ b/packages/alchemy/test/Cloudflare/Workers/Workflow.test.ts @@ -9,6 +9,9 @@ import * as HttpClient from "effect/unstable/http/HttpClient"; import Stack from "./fixtures/workflow/stack.ts"; import WorkflowTestWorker from "./fixtures/workflow/workflow-worker.ts"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test, beforeAll, afterAll, deploy, destroy } = Test.make({ providers: Cloudflare.providers(), }); @@ -83,7 +86,7 @@ const runWorkflowToCompletion = (url: string) => return lastStatus; }); -test( +test.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "deployed worker can run a workflow to completion", Effect.gen(function* () { const out = yield* stack; @@ -124,7 +127,9 @@ test( // in the workflows generator/spec by the service owner, then regenerated. Once // applied, drop the skipIf gate (`CLOUDFLARE_TEST_WORKFLOW_LIST`) and this test // passes unchanged. -test.provider.skipIf(!process.env.CLOUDFLARE_TEST_WORKFLOW_LIST)( +test.provider.skipIf( + !process.env.CLOUDFLARE_TEST_WORKFLOW_LIST || SKIP_NON_EPHEMRAL_ACCOUNT_TESTS, +)( "list enumerates the deployed workflow", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Workers/fixtures/cron/cron-worker.ts b/packages/alchemy/test/Cloudflare/Workers/fixtures/cron/cron-worker.ts index 39320cb67..4263ea832 100644 --- a/packages/alchemy/test/Cloudflare/Workers/fixtures/cron/cron-worker.ts +++ b/packages/alchemy/test/Cloudflare/Workers/fixtures/cron/cron-worker.ts @@ -11,8 +11,8 @@ import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse"; export class CronCounter extends Cloudflare.DurableObjectNamespace()( "CronCounter", Effect.gen(function* () { + const state = yield* Cloudflare.DurableObjectState; return Effect.gen(function* () { - const state = yield* Cloudflare.DurableObjectState; let times = (yield* state.storage.get("times")) ?? []; return { record: Effect.fn(function* (time: number) { diff --git a/packages/alchemy/test/Cloudflare/Workers/fixtures/do-rpc/object.ts b/packages/alchemy/test/Cloudflare/Workers/fixtures/do-rpc/object.ts index ba99ceee7..c1c020760 100644 --- a/packages/alchemy/test/Cloudflare/Workers/fixtures/do-rpc/object.ts +++ b/packages/alchemy/test/Cloudflare/Workers/fixtures/do-rpc/object.ts @@ -10,7 +10,7 @@ const KV = Cloudflare.KVNamespace("DurableObjectWorkerEnvironmentKV", { export class WorkerEnvironmentKVObject extends Cloudflare.DurableObjectNamespace()( "WorkerEnvironmentKVObject", Effect.gen(function* () { - const kv = yield* Cloudflare.KVNamespace.bind(KV); + const kv = yield* Cloudflare.KV.ReadWriteNamespace(KV); return Effect.gen(function* () { return { @@ -26,5 +26,5 @@ export class WorkerEnvironmentKVObject extends Cloudflare.DurableObjectNamespace ), }; }); - }).pipe(Effect.provide(Cloudflare.KVNamespaceBindingLive)), + }).pipe(Effect.provide(Cloudflare.KV.ReadWriteNamespaceBinding)), ) {} diff --git a/packages/alchemy/test/Cloudflare/Workers/fixtures/http-api/object.ts b/packages/alchemy/test/Cloudflare/Workers/fixtures/http-api/object.ts index aa56539aa..81cc91943 100644 --- a/packages/alchemy/test/Cloudflare/Workers/fixtures/http-api/object.ts +++ b/packages/alchemy/test/Cloudflare/Workers/fixtures/http-api/object.ts @@ -1,13 +1,22 @@ import * as Cloudflare from "@/Cloudflare"; import { Layer } from "effect"; import * as Effect from "effect/Effect"; +import * as Path from "effect/Path"; import { HttpRouter } from "effect/unstable/http"; +import * as Etag from "effect/unstable/http/Etag"; +import * as HttpPlatform from "effect/unstable/http/HttpPlatform"; import * as HttpApi from "effect/unstable/httpapi/HttpApi"; import * as HttpApiBuilder from "effect/unstable/httpapi/HttpApiBuilder"; import * as HttpApiGroup from "effect/unstable/httpapi/HttpApiGroup"; import { createTask, decodeTask, encodeTask, getTask, Task } from "./api.ts"; +const HttpPlatformStub = Layer.succeed(HttpPlatform.HttpPlatform, { + fileResponse: () => Effect.die("HttpPlatform.fileResponse not supported"), + fileWebResponse: () => + Effect.die("HttpPlatform.fileWebResponse not supported"), +}); + export class TasksDOGroup extends HttpApiGroup.make("TasksDO") .add(getTask) .add(createTask) {} @@ -22,9 +31,9 @@ export class TaskDOApi extends HttpApi.make("TaskDOApi").add(TasksDOGroup) {} export default class TasksObject extends Cloudflare.DurableObjectNamespace()( "TasksObject", Effect.gen(function* () { - return Effect.gen(function* () { - const state = yield* Cloudflare.DurableObjectState; + const state = yield* Cloudflare.DurableObjectState; + return Effect.gen(function* () { const tasksGroup = HttpApiBuilder.group( TaskDOApi, "TasksDO", @@ -51,6 +60,7 @@ export default class TasksObject extends Cloudflare.DurableObjectNamespace @@ -94,5 +94,5 @@ export default class HttpApiTestWorker extends Cloudflare.Worker("count")) ?? 0; const handlers = CounterRpcs.toLayer({ diff --git a/packages/alchemy/test/Cloudflare/Workers/fixtures/rpc-http/object.ts b/packages/alchemy/test/Cloudflare/Workers/fixtures/rpc-http/object.ts index 4d0527286..35dca544a 100644 --- a/packages/alchemy/test/Cloudflare/Workers/fixtures/rpc-http/object.ts +++ b/packages/alchemy/test/Cloudflare/Workers/fixtures/rpc-http/object.ts @@ -14,8 +14,8 @@ import { DoRpcs } from "./group.ts"; */ export default class RpcHttpTestObject extends Cloudflare.DurableObjectNamespace()( "RpcHttpTestObject", - Effect.gen(function* () { - return Effect.gen(function* () { + Effect.succeed( + Effect.gen(function* () { let counter = 0; const handlersLayer = DoRpcs.toLayer({ @@ -58,6 +58,6 @@ export default class RpcHttpTestObject extends Cloudflare.DurableObjectNamespace ), ), }; - }); - }), + }), + ), ) {} diff --git a/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-do/object.ts b/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-do/object.ts index 40fbbd82b..ba2c298d2 100644 --- a/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-do/object.ts +++ b/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-do/object.ts @@ -24,10 +24,9 @@ export class Counter extends Cloudflare.DurableObjectNamespace< export const CounterLive = Counter.make( Effect.gen(function* () { const db = yield* Cloudflare.D1Connection.bind(MyDB); + const state = yield* Cloudflare.DurableObjectState; return Effect.gen(function* () { - const state = yield* Cloudflare.DurableObjectState; - // D1's `exec()` splits on newlines and rejects multi-line statements // ("incomplete input: SQLITE_ERROR"). Keep the DDL on a single line. yield* db.exec( diff --git a/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-do/stack.ts b/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-do/stack.ts index 6e0d7cdf1..1de4cfb74 100644 --- a/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-do/stack.ts +++ b/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-do/stack.ts @@ -1,9 +1,10 @@ import * as Cloudflare from "@/Cloudflare"; import * as Alchemy from "@/index"; import * as Effect from "effect/Effect"; -import WorkerALayer, { WorkerA } from "./workerA.ts"; +import * as Layer from "effect/Layer"; +import WorkerALive, { WorkerA } from "./workerA.ts"; import WorkerB from "./workerB.ts"; -import WorkerCLayer, { WorkerC } from "./workerC.ts"; +import WorkerCLive, { WorkerC } from "./workerC.ts"; export default Alchemy.Stack( "TaggedDOExample", @@ -21,5 +22,5 @@ export default Alchemy.Stack( urlB: workerB.url.as(), urlC: workerC.url.as(), }; - }).pipe(Effect.provide(WorkerALayer), Effect.provide(WorkerCLayer)), + }).pipe(Effect.provide(Layer.provideMerge(WorkerALive, WorkerCLive))), ); diff --git a/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-do/workerA.ts b/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-do/workerA.ts index 56f9af4ea..763fa9ce8 100644 --- a/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-do/workerA.ts +++ b/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-do/workerA.ts @@ -8,13 +8,13 @@ import { Counter, CounterLive } from "./object.ts"; // Tag export class WorkerA extends Cloudflare.Worker()( "WorkerA", - { - main: import.meta.filename, - }, ) {} // Layer export default WorkerA.make( + { + main: import.meta.filename, + }, Effect.gen(function* () { const counter = yield* Counter; diff --git a/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-do/workerC.ts b/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-do/workerC.ts index 3ca5fb4cb..3bf889869 100644 --- a/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-do/workerC.ts +++ b/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-do/workerC.ts @@ -10,9 +10,6 @@ import { Counter, CounterLive } from "./object.ts"; // WorkerC are isolated from the instances under WorkerA/B. export class WorkerC extends Cloudflare.Worker()( "WorkerC", - { - main: import.meta.filename, - }, ) {} // Layer — uses `Counter.from(WorkerC)` (self-reference) instead of @@ -20,6 +17,9 @@ export class WorkerC extends Cloudflare.Worker()( // `.from(Self)` form is the recommended style for code that may be // extracted into a reusable Layer. export default WorkerC.make( + { + main: import.meta.filename, + }, Effect.gen(function* () { const counter = yield* Counter.from(WorkerC); diff --git a/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-rpc-do/object.ts b/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-rpc-do/object.ts index f8ab9a3d3..0ee1f19b4 100644 --- a/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-rpc-do/object.ts +++ b/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-rpc-do/object.ts @@ -23,10 +23,9 @@ export class Counter extends Cloudflare.RpcDurableObjectNamespace()( export const CounterLive = Counter.make( Effect.gen(function* () { const db = yield* Cloudflare.D1Connection.bind(MyDB); + const state = yield* Cloudflare.DurableObjectState; return Effect.gen(function* () { - const state = yield* Cloudflare.DurableObjectState; - // D1's `exec()` splits on newlines and rejects multi-line statements; // keep the DDL on a single line. yield* db.exec( diff --git a/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-rpc-do/workerA.ts b/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-rpc-do/workerA.ts index 0d1826360..df9112722 100644 --- a/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-rpc-do/workerA.ts +++ b/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-rpc-do/workerA.ts @@ -17,7 +17,6 @@ import { Counter, CounterLive } from "./object.ts"; export class WorkerA extends Cloudflare.RpcWorker()( "WorkerA", { - main: import.meta.filename, schema: CounterRpcs, }, ) {} @@ -25,6 +24,9 @@ export class WorkerA extends Cloudflare.RpcWorker()( // Layer — yielding `Counter` resolves to WorkerA's local hosted // namespace (the `CounterLive` Layer below populates the tag). export default WorkerA.make( + { + main: import.meta.filename, + }, Effect.gen(function* () { const counter = yield* Counter; diff --git a/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-rpc-do/workerC.ts b/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-rpc-do/workerC.ts index 4c853d047..378fbbfaf 100644 --- a/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-rpc-do/workerC.ts +++ b/packages/alchemy/test/Cloudflare/Workers/fixtures/tagged-rpc-do/workerC.ts @@ -11,9 +11,6 @@ import { Counter, CounterLive } from "./object.ts"; // instances of the same `Counter` class. export class WorkerC extends Cloudflare.Worker()( "WorkerC", - { - main: import.meta.filename, - }, ) {} // Layer — uses `Counter.from(WorkerC)` (self-reference) instead of @@ -21,6 +18,9 @@ export class WorkerC extends Cloudflare.Worker()( // `.from(Self)` form is the recommended style for code that may be // extracted into a reusable Layer. export default WorkerC.make( + { + main: import.meta.filename, + }, Effect.gen(function* () { const counter = yield* Counter.from(WorkerC); diff --git a/packages/alchemy/test/Cloudflare/Workers/fixtures/workflow/test-workflow.ts b/packages/alchemy/test/Cloudflare/Workers/fixtures/workflow/test-workflow.ts index 4f0e281c6..06f5c3382 100644 --- a/packages/alchemy/test/Cloudflare/Workers/fixtures/workflow/test-workflow.ts +++ b/packages/alchemy/test/Cloudflare/Workers/fixtures/workflow/test-workflow.ts @@ -14,7 +14,6 @@ export default class TestWorkflow extends Cloudflare.Workflow()( "TestWorkflow", Effect.gen(function* () { return Effect.fn(function* (input: { value: string }) { - console.log("greeted"); const env = yield* Cloudflare.WorkerEnvironment; const greeted = yield* Cloudflare.task( diff --git a/packages/alchemy/test/Cloudflare/WorkersForPlatforms/DispatchNamespace.test.ts b/packages/alchemy/test/Cloudflare/WorkersForPlatforms/DispatchNamespace.test.ts index 45d9ac097..61d532e5c 100644 --- a/packages/alchemy/test/Cloudflare/WorkersForPlatforms/DispatchNamespace.test.ts +++ b/packages/alchemy/test/Cloudflare/WorkersForPlatforms/DispatchNamespace.test.ts @@ -8,6 +8,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -83,7 +86,7 @@ const expectScriptGone = ( }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create, replace, and destroy a dispatch namespace", (stack) => Effect.gen(function* () { @@ -138,7 +141,7 @@ const program = () => return { namespace }; }); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "upload, update in place, and destroy a namespace script", (stack) => Effect.gen(function* () { @@ -164,7 +167,7 @@ test.provider( { timeout: 120_000 }, ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates the deployed dispatch namespace", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Zaraz/ZarazConfig.test.ts b/packages/alchemy/test/Cloudflare/Zaraz/ZarazConfig.test.ts index 553434f2c..e9fc6b58b 100644 --- a/packages/alchemy/test/Cloudflare/Zaraz/ZarazConfig.test.ts +++ b/packages/alchemy/test/Cloudflare/Zaraz/ZarazConfig.test.ts @@ -10,6 +10,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import { describe } from "vitest"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -173,7 +176,7 @@ describe.sequential("ZarazConfig", () => { // API for the per-zone Zaraz config, so `list()` enumerates every zone via // `listAllZones` and reads the singleton in each. Read-only — asserts the // result is well-typed, non-empty, and contains the standing test zone. - test.provider( + test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "list enumerates Zaraz config across all zones", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Zone/CustomNameservers.test.ts b/packages/alchemy/test/Cloudflare/Zone/CustomNameservers.test.ts index 1af571992..f022daae9 100644 --- a/packages/alchemy/test/Cloudflare/Zone/CustomNameservers.test.ts +++ b/packages/alchemy/test/Cloudflare/Zone/CustomNameservers.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -51,7 +54,7 @@ const getCustomNs = (zoneId: string) => }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "pins the custom nameserver toggle and leaves the zone at its baseline on destroy", (stack) => Effect.gen(function* () { @@ -104,7 +107,7 @@ test.provider( }).pipe(logLevel), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed CustomNameserverSetNotFound error when enabling without an account nameserver set", (stack) => Effect.gen(function* () { diff --git a/packages/alchemy/test/Cloudflare/Zone/Hold.test.ts b/packages/alchemy/test/Cloudflare/Zone/Hold.test.ts index 52b6d9421..57a54fed0 100644 --- a/packages/alchemy/test/Cloudflare/Zone/Hold.test.ts +++ b/packages/alchemy/test/Cloudflare/Zone/Hold.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -68,7 +71,7 @@ const isHeld = (hold: zones.GetHoldResponse): boolean => hold.hold === true; const includesSubdomains = (hold: zones.GetHoldResponse): boolean => hold.includeSubdomains === true || hold.includeSubdomains === "true"; -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "surfaces the typed ZoneHoldsRequireEnterprise error on non-Enterprise zones", (stack) => Effect.gen(function* () { @@ -105,20 +108,22 @@ test.provider( // `listAllZones` and reads the hold state in each. Assert the result is // non-empty and contains the standing test zone. This works on any plan // (reading the hold state needs no Enterprise entitlement). -test.provider("list enumerates the hold state across all zones", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the hold state across all zones", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - yield* stack.destroy(); + yield* stack.destroy(); - const provider = yield* Provider.findProvider(Cloudflare.ZoneHold); - const all = yield* provider.list(); + const provider = yield* Provider.findProvider(Cloudflare.ZoneHold); + const all = yield* provider.list(); - expect(all.length).toBeGreaterThan(0); - expect(all.some((h) => h.zoneId === zoneId)).toBe(true); + expect(all.length).toBeGreaterThan(0); + expect(all.some((h) => h.zoneId === zoneId)).toBe(true); - yield* stack.destroy(); - }).pipe(logLevel), + yield* stack.destroy(); + }).pipe(logLevel), ); test.provider.skipIf(!enterpriseZoneId)( diff --git a/packages/alchemy/test/Cloudflare/Zone/Setting.test.ts b/packages/alchemy/test/Cloudflare/Zone/Setting.test.ts index c042a40f4..ff7caa505 100644 --- a/packages/alchemy/test/Cloudflare/Zone/Setting.test.ts +++ b/packages/alchemy/test/Cloudflare/Zone/Setting.test.ts @@ -9,6 +9,9 @@ import * as Effect from "effect/Effect"; import { MinimumLogLevel } from "effect/References"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); const logLevel = Effect.provideService( @@ -61,7 +64,7 @@ const setBaseline = (zoneId: string, settingId: string, value: unknown) => const valueOf = (setting: zones.GetSettingResponse): unknown => (setting as { value?: unknown }).value; -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "pins a toggle setting and restores the original value on destroy", (stack) => Effect.gen(function* () { @@ -98,7 +101,7 @@ test.provider( }).pipe(logLevel), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "updates a numeric setting in place and keeps the captured initial value", (stack) => Effect.gen(function* () { @@ -146,7 +149,7 @@ test.provider( }).pipe(logLevel), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "changing settingId replaces — old setting restored, new setting pinned", (stack) => Effect.gen(function* () { @@ -205,42 +208,44 @@ test.provider( // setting, emitting one Attributes per (zone, setting) — the same shape // `read` produces. Deploy a deterministic setting on the standing test zone // and assert that exact (zone, setting) entry shows up in the listing. -test.provider("list enumerates the deployed (zone, setting) pair", (stack) => - Effect.gen(function* () { - const zoneId = yield* resolveZoneId; - - yield* stack.destroy(); - // Known baseline so the value we assert against is deterministic. - yield* setBaseline(zoneId, "always_use_https", "off"); - - const deployed = yield* stack.deploy( - Effect.gen(function* () { - return yield* Cloudflare.ZoneSetting("AlwaysUseHttps", { - zoneId, - settingId: "always_use_https", - value: "on", - }); - }), - ); - expect(deployed.settingId).toEqual("always_use_https"); - expect(deployed.value).toEqual("on"); +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates the deployed (zone, setting) pair", + (stack) => + Effect.gen(function* () { + const zoneId = yield* resolveZoneId; - const provider = yield* Provider.findProvider(Cloudflare.ZoneSetting); - const all = yield* provider.list(); + yield* stack.destroy(); + // Known baseline so the value we assert against is deterministic. + yield* setBaseline(zoneId, "always_use_https", "off"); - // The deployed (zone, setting) pair is present, hydrated into the exact - // `read`/`Attributes` shape (one row per (zoneId, settingId)). - expect(all.length).toBeGreaterThan(0); - const entry = all.find( - (s) => s.zoneId === zoneId && s.settingId === "always_use_https", - ); - expect(entry).toBeDefined(); - expect(entry?.value).toEqual("on"); + const deployed = yield* stack.deploy( + Effect.gen(function* () { + return yield* Cloudflare.ZoneSetting("AlwaysUseHttps", { + zoneId, + settingId: "always_use_https", + value: "on", + }); + }), + ); + expect(deployed.settingId).toEqual("always_use_https"); + expect(deployed.value).toEqual("on"); + + const provider = yield* Provider.findProvider(Cloudflare.ZoneSetting); + const all = yield* provider.list(); - yield* stack.destroy(); + // The deployed (zone, setting) pair is present, hydrated into the exact + // `read`/`Attributes` shape (one row per (zoneId, settingId)). + expect(all.length).toBeGreaterThan(0); + const entry = all.find( + (s) => s.zoneId === zoneId && s.settingId === "always_use_https", + ); + expect(entry).toBeDefined(); + expect(entry?.value).toEqual("on"); + + yield* stack.destroy(); - // Capture-and-restore: destroy put the setting back to its baseline. - const restored = yield* getSetting(zoneId, "always_use_https"); - expect(valueOf(restored)).toEqual("off"); - }).pipe(logLevel), + // Capture-and-restore: destroy put the setting back to its baseline. + const restored = yield* getSetting(zoneId, "always_use_https"); + expect(valueOf(restored)).toEqual("off"); + }).pipe(logLevel), ); diff --git a/packages/alchemy/test/Cloudflare/Zone/Zone.test.ts b/packages/alchemy/test/Cloudflare/Zone/Zone.test.ts index d02ec5d58..a14642496 100644 --- a/packages/alchemy/test/Cloudflare/Zone/Zone.test.ts +++ b/packages/alchemy/test/Cloudflare/Zone/Zone.test.ts @@ -12,6 +12,9 @@ import * as Data from "effect/Data"; import * as Effect from "effect/Effect"; import * as Schedule from "effect/Schedule"; +const SKIP_NON_EPHEMRAL_ACCOUNT_TESTS = + process.env.SKIP_NON_EPHEMRAL_ACCOUNT_TESTS === "1"; + const { test } = Test.make({ providers: Cloudflare.providers() }); // Cloudflare's POST /zones rejects reserved pseudo-TLDs (`.test`, `.local`, @@ -23,7 +26,7 @@ const { test } = Test.make({ providers: Cloudflare.providers() }); const zoneNameFor = (accountId: string, label: string) => process.env.TEST_ZONE_NAME ?? `alchemy-${label}-${accountId}.com`; -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create zone retains by default — destroy() opts in to deletion", (stack) => Effect.gen(function* () { @@ -76,7 +79,7 @@ test.provider( }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "create zone retains by default — survives stack.destroy()", (stack) => Effect.gen(function* () { @@ -105,7 +108,7 @@ test.provider( }), ); -test.provider( +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( "adoption — existing zone errors without adopt, takes over with adopt(true)", (stack) => Effect.gen(function* () { @@ -172,31 +175,33 @@ test.provider( // Standing test zone — always present in the testing account. const TEST_ZONE_NAME = "alchemy-test-2.us"; -test.provider("list enumerates every zone in the account", (stack) => - Effect.gen(function* () { - yield* stack.destroy(); - - const { accountId } = yield* yield* CloudflareEnvironment; - const testZone = yield* findZoneByName({ - accountId, - name: TEST_ZONE_NAME, - }); - expect(testZone).toBeDefined(); - - const provider = yield* Provider.findProvider(Cloudflare.Zone); - const all = yield* provider.list(); - - // Exhaustive enumeration must include the standing test zone, returned in - // the full `read` Attributes shape. - const found = all.find((z) => z.zoneId === testZone!.id); - expect(found).toBeDefined(); - expect(found!.name).toBe(TEST_ZONE_NAME); - expect(found!.accountId).toBe(accountId); - expect(typeof found!.createdOn).toBe("string"); - expect(Array.isArray(found!.nameServers)).toBe(true); - - yield* stack.destroy(); - }), +test.provider.skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)( + "list enumerates every zone in the account", + (stack) => + Effect.gen(function* () { + yield* stack.destroy(); + + const { accountId } = yield* yield* CloudflareEnvironment; + const testZone = yield* findZoneByName({ + accountId, + name: TEST_ZONE_NAME, + }); + expect(testZone).toBeDefined(); + + const provider = yield* Provider.findProvider(Cloudflare.Zone); + const all = yield* provider.list(); + + // Exhaustive enumeration must include the standing test zone, returned in + // the full `read` Attributes shape. + const found = all.find((z) => z.zoneId === testZone!.id); + expect(found).toBeDefined(); + expect(found!.name).toBe(TEST_ZONE_NAME); + expect(found!.accountId).toBe(accountId); + expect(typeof found!.createdOn).toBe("string"); + expect(Array.isArray(found!.nameServers)).toBe(true); + + yield* stack.destroy(); + }), ); const waitForZoneToBeDeleted = Effect.fn(function* (zoneId: string) { diff --git a/packages/alchemy/test/Local/fixtures/rpc-spawner-devserver-parent.ts b/packages/alchemy/test/Local/fixtures/rpc-spawner-devserver-parent.ts index 7f40bd5ba..822f7888f 100644 --- a/packages/alchemy/test/Local/fixtures/rpc-spawner-devserver-parent.ts +++ b/packages/alchemy/test/Local/fixtures/rpc-spawner-devserver-parent.ts @@ -3,6 +3,10 @@ // then idles until the test harness kills this parent process. // Relative imports (not `@/` alias) so this file runs under both Bun and Node // without a paths-aware loader. +import { unwrapRpcHandlers } from "@/Local/RpcSerialization.ts"; +import type { RpcProxyApi } from "@/Local/RpcServer.ts"; +import { layerServer, RpcSpawner } from "@/Local/RpcSpawner.ts"; +import { PlatformServices } from "@/Util/PlatformServices.ts"; import { newWebSocketRpcSession } from "capnweb"; import * as Deferred from "effect/Deferred"; import * as Effect from "effect/Effect"; @@ -12,10 +16,6 @@ import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient"; import * as HttpBody from "effect/unstable/http/HttpBody"; import * as HttpClient from "effect/unstable/http/HttpClient"; import fs from "node:fs"; -import { layerServer, RpcSpawner } from "../../../src/Local/RpcSpawner.ts"; -import { unwrapRpcHandlers } from "../../../src/Local/RpcSerialization.ts"; -import type { RpcProxyApi } from "../../../src/Local/RpcServer.ts"; -import { PlatformServices } from "../../../src/Util/PlatformServices.ts"; const sidecarEntry = process.argv[2]; const command = process.argv[3]; @@ -47,8 +47,11 @@ const program = Effect.gen(function* () { .pipe(Effect.flatMap((res) => res.text)); const session = newWebSocketRpcSession(wsUrl); - const wrapped = yield* Effect.promise(() => - session.getProvider("Build.DevServer"), + const wrapped = yield* Effect.promise( + () => + session.getProvider("Build.DevServer") as ReturnType< + RpcProxyApi["getProvider"] + >, ); const provider = unwrapRpcHandlers(wrapped, []); @@ -62,7 +65,7 @@ const program = Effect.gen(function* () { olds: undefined, output: undefined, session: {} as never, - bindings: {}, + bindings: [], }); const pid = yield* Effect.sync(() => { diff --git a/packages/alchemy/test/apply.test.ts b/packages/alchemy/test/apply.test.ts index 529b18099..5405bbd79 100644 --- a/packages/alchemy/test/apply.test.ts +++ b/packages/alchemy/test/apply.test.ts @@ -1,5 +1,5 @@ import { Cli } from "@/Cli/Cli"; -import * as Construct from "@/Construct"; +import * as Namespace from "@/Namespace.ts"; import * as Output from "@/Output"; import * as RemovalPolicy from "@/RemovalPolicy.ts"; import { Stack } from "@/Stack"; @@ -263,26 +263,27 @@ describe("basic operations", () => { "should resolve bindings inside constructs using namespaced resources", (stack) => Effect.gen(function* () { - const Site = Construct.fn(function* (_id: string, _props: {}) { - const bucket = yield* BindingTarget("Bucket", { - string: "bucket-value", - }); - const distribution = yield* BindingTarget("Distribution", { - string: "distribution-value", - }); + const Site = (id: string, _props: {}) => + Effect.gen(function* () { + const bucket = yield* BindingTarget("Bucket", { + string: "bucket-value", + }); + const distribution = yield* BindingTarget("Distribution", { + string: "distribution-value", + }); - yield* bucket.bind("Policy", { - env: { - BUCKET: bucket.string, - DISTRIBUTION: distribution.string, - }, - }); + yield* bucket.bind("Policy", { + env: { + BUCKET: bucket.string, + DISTRIBUTION: distribution.string, + }, + }); - return { - bucket, - distribution, - }; - }); + return { + bucket, + distribution, + }; + }).pipe(Namespace.push(id)); const output = yield* Site("MarketingSite", {}).pipe(stack.deploy); @@ -454,13 +455,13 @@ describe("FQN separator in logical ID", () => { // child resource's logical ID is "owner/repo". const fqn = "ReleaseService/alchemy-run/alchemy-effect"; - const Host = Construct.fn(function* (_id: string, _props: {}) { - return yield* TestResource("alchemy-run/alchemy-effect", { - string: "v1", - }); - }); - - yield* stack.deploy(Host("ReleaseService", {})); + yield* stack.deploy( + Effect.gen(function* () { + return yield* TestResource("alchemy-run/alchemy-effect", { + string: "v1", + }); + }).pipe(Namespace.push("ReleaseService")), + ); expect((yield* getState(fqn))?.status).toEqual("created"); @@ -4174,12 +4175,10 @@ describe("artifacts", () => { "isolates artifact bags by FQN for namespaced resources with the same leaf logical ID", (stack) => Effect.gen(function* () { - const Site = Construct.fn(function* ( - _id: string, - props: { value: string }, - ) { - return yield* ArtifactProbe("Shared", { value: props.value }); - }); + const Site = (id: string, props: { value: string }) => + Effect.gen(function* () { + return yield* ArtifactProbe("Shared", { value: props.value }); + }).pipe(Namespace.push(id)); yield* Effect.gen(function* () { yield* Site("Left", { value: "left-v1" }); diff --git a/packages/alchemy/test/plan.test.ts b/packages/alchemy/test/plan.test.ts index fcaf70b6c..2562f2941 100644 --- a/packages/alchemy/test/plan.test.ts +++ b/packages/alchemy/test/plan.test.ts @@ -1,7 +1,7 @@ import { adopt, AdoptPolicy, Unowned } from "@/AdoptPolicy"; -import * as Construct from "@/Construct"; import { dedupeBindings } from "@/Diff"; import type { Input, InputProps } from "@/Input"; +import * as Namespace from "@/Namespace.ts"; import * as Output from "@/Output"; import * as Plan from "@/Plan"; import { UnsatisfiedResourceCycle } from "@/Plan"; @@ -160,12 +160,10 @@ test( downstream: [], }, }); - const Site = Construct.fn(function* ( - _id: string, - props: { value: string }, - ) { - return yield* ArtifactProbe("Shared", { value: props.value }); - }); + const Site = (id: string, props: { value: string }) => + Effect.gen(function* () { + return yield* ArtifactProbe("Shared", { value: props.value }); + }).pipe(Namespace.push(id)); const plan = yield* Effect.gen(function* () { const left = yield* Site("Left", { value: "left-v2" }); @@ -1164,21 +1162,22 @@ describe("construct namespaces", () => { test( "namespaced construct bindings resolve into the plan graph", Effect.gen(function* () { - const Site = Construct.fn(function* (_id: string, _props: {}) { - const bucket = yield* BindingTarget("Bucket", { - name: "bucket", - }); - const distribution = yield* BindingTarget("Distribution", { - name: "distribution", - }); - yield* bucket.bind("Policy", { - env: { - BUCKET: bucket.string, - DISTRIBUTION: distribution.string, - }, - }); - return { bucket, distribution }; - }); + const Site = (id: string, _props: {}) => + Effect.gen(function* () { + const bucket = yield* BindingTarget("Bucket", { + name: "bucket", + }); + const distribution = yield* BindingTarget("Distribution", { + name: "distribution", + }); + yield* bucket.bind("Policy", { + env: { + BUCKET: bucket.string, + DISTRIBUTION: distribution.string, + }, + }); + return { bucket, distribution }; + }).pipe(Namespace.push(id)); const plan = yield* Effect.gen(function* () { yield* Site("MarketingSite", {}); @@ -1232,14 +1231,12 @@ describe("construct namespaces", () => { test( "same child logical ids in different constructs do not collide", Effect.gen(function* () { - const Site = Construct.fn(function* ( - _id: string, - props: { name: string }, - ) { - return yield* Bucket("Bucket", { - name: props.name, - }); - }); + const Site = (id: string, props: { name: string }) => + Effect.gen(function* () { + return yield* Bucket("Bucket", { + name: props.name, + }); + }).pipe(Namespace.push(id)); const plan = yield* Effect.gen(function* () { yield* Site("MarketingSite", { @@ -1276,27 +1273,28 @@ describe("construct namespaces", () => { test( "binding-only cycles inside a construct do not become downstream edges", Effect.gen(function* () { - const Site = Construct.fn(function* (_id: string, _props: {}) { - const A = yield* BindingTarget("A", { - string: "a-value", - }); - const B = yield* BindingTarget("B", { - string: "b-value", - }); + const Site = (id: string, _props: {}) => + Effect.gen(function* () { + const A = yield* BindingTarget("A", { + string: "a-value", + }); + const B = yield* BindingTarget("B", { + string: "b-value", + }); - yield* A.bind("FromB", { - env: { - PEER: B.string, - }, - }); - yield* B.bind("FromA", { - env: { - PEER: A.string, - }, - }); + yield* A.bind("FromB", { + env: { + PEER: B.string, + }, + }); + yield* B.bind("FromA", { + env: { + PEER: A.string, + }, + }); - return { A, B }; - }); + return { A, B }; + }).pipe(Namespace.push(id)); const plan = yield* Effect.gen(function* () { yield* Site("MarketingSite", {}); diff --git a/packages/alchemy/test/types/Agent.ts b/packages/alchemy/test/types/Agent.ts index 7fb48a580..9a796329f 100644 --- a/packages/alchemy/test/types/Agent.ts +++ b/packages/alchemy/test/types/Agent.ts @@ -17,88 +17,85 @@ const _agentEff = Effect.gen(function* () { const _gen = Effect.gen(function* () { // bind the Sandbox Container to the Agent DO - const sandbox = yield* Cloudflare.bindContainer(Sandbox); + const sandbox = yield* Sandbox; return Effect.gen(function* () { const state = yield* Cloudflare.DurableObjectState; // get the container instance - const container = yield* Cloudflare.start(sandbox, { - enableInternet: true, - }); - - container.getTcpPort(1080); - container.getUser(); + sandbox.getTcpPort(1080); + sandbox.getUser(); return { getProfile: () => state.storage.get("Profile"), }; - }); + }).pipe( + Effect.provide( + Cloudflare.layerContainer(Sandbox, { + enableInternet: true, + }), + ), + ); }); export const Agent2 = Cloudflare.DurableObjectNamespace( "Agents", Effect.gen(function* () { // bind the Sandbox Container to the Agent DO - const sandbox = yield* Cloudflare.bindContainer(Sandbox); + const sandbox = yield* Sandbox; + const state = yield* Cloudflare.DurableObjectState; return Effect.gen(function* () { - const state = yield* Cloudflare.DurableObjectState; - - // get the container instance - const container = yield* Cloudflare.start(sandbox, { - enableInternet: true, - }); - - container.getTcpPort(1080); - container.getUser(); + sandbox.getTcpPort(1080); + sandbox.getUser(); return { getProfile: () => state.storage.get("Profile"), }; }); - }), + }).pipe( + Effect.provide( + Cloudflare.layerContainer(Sandbox, { + enableInternet: true, + }), + ), + ), ); export class Agent3 extends Cloudflare.DurableObjectNamespace()( "Agents", Effect.gen(function* () { // bind the Sandbox Container to the Agent DO - const sandbox = yield* Cloudflare.bindContainer(Sandbox); + const sandbox = yield* Sandbox; + const state = yield* Cloudflare.DurableObjectState; return Effect.gen(function* () { - const state = yield* Cloudflare.DurableObjectState; - // get the container instance - const container = yield* Cloudflare.start(sandbox, { - enableInternet: true, - }); - - container.getTcpPort(1080); - container.getUser(); + sandbox.getTcpPort(1080); + sandbox.getUser(); return { getProfile: () => state.storage.get("Profile"), }; }); - }), + }).pipe( + Effect.provide( + Cloudflare.layerContainer(Sandbox, { + enableInternet: true, + }), + ), + ), ) {} export default class Agent extends Cloudflare.DurableObjectNamespace()( "Agents", Effect.gen(function* () { // bind the Sandbox Container to the Agent DO - const sandbox = yield* Cloudflare.bindContainer(Sandbox); + const sandbox = yield* Sandbox; + const state = yield* Cloudflare.DurableObjectState; return Effect.gen(function* () { - const state = yield* Cloudflare.DurableObjectState; - - // get the container instance - const container = yield* Cloudflare.start(sandbox, { - enableInternet: true, - }); - - const connection = yield* container.getTcpPort(1080); + const connection = yield* sandbox.getTcpPort(1080); const sessions = new Map(); @@ -160,5 +157,11 @@ export default class Agent extends Cloudflare.DurableObjectNamespace()( }), }; }); - }), + }).pipe( + Effect.provide( + Cloudflare.layerContainer(Sandbox, { + enableInternet: true, + }), + ), + ), ) {} diff --git a/packages/alchemy/test/types/Api.ts b/packages/alchemy/test/types/Api.ts index b59ba2331..c3fcd615a 100644 --- a/packages/alchemy/test/types/Api.ts +++ b/packages/alchemy/test/types/Api.ts @@ -1,4 +1,5 @@ import * as Cloudflare from "@/Cloudflare"; +import type { RuntimeContext } from "@/RuntimeContext.ts"; import * as Effect from "effect/Effect"; import { HttpServerRequest } from "effect/unstable/http/HttpServerRequest"; import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse"; @@ -111,16 +112,21 @@ export default class Api extends Cloudflare.Worker()( export class Api3 extends Cloudflare.Worker< Api3, { - getUser: () => Effect.Effect<{ id: string; name: string }>; + getUser: () => Effect.Effect< + { id: string; name: string }, + never, + RuntimeContext + >; } ->()("Api3", { - main: import.meta.filename, - observability: { - enabled: true, - }, -}) {} +>()("Api3") {} export const Api3Live = Api3.make( + { + main: import.meta.filename, + observability: { + enabled: true, + }, + }, Effect.gen(function* () { const agent = yield* Agent; return { diff --git a/packages/alchemy/test/types/Sandbox.ts b/packages/alchemy/test/types/Sandbox.ts index 8aac1bba6..0a608850e 100644 --- a/packages/alchemy/test/types/Sandbox.ts +++ b/packages/alchemy/test/types/Sandbox.ts @@ -5,26 +5,26 @@ import * as Stream from "effect/Stream"; import { HttpServerRequest } from "effect/unstable/http/HttpServerRequest"; import * as HttpServerResponse from "effect/unstable/http/HttpServerResponse"; import * as ChildProcess from "effect/unstable/process/ChildProcess"; +import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner"; export class Sandbox extends Cloudflare.Container< Sandbox, { getUser: () => Effect.Effect<{ id: string; name: string }>; } ->()( - "Sandbox", +>()("Sandbox") {} + +export const SandboxLive = Sandbox.make( Stack.useSync((stack) => ({ main: import.meta.filename, // handler: "SandboxLive", instanceType: stack.stage === "prod" ? "standard-1" : "dev", dockerfile: `FROM alpine:latest`, })), -) {} - -export const SandboxLive = Sandbox.make( Effect.gen(function* () { // bind dependencies // yield* Cloudflare.Queue() + const cp = yield* ChildProcessSpawner; // return http effect return { @@ -34,7 +34,7 @@ export const SandboxLive = Sandbox.make( // upgrade to web socket const socket = yield* request.upgrade; const writeMessage = yield* socket.writer; - const cmd = yield* ChildProcess.make("ffmpeg", ["-version"]); + const cmd = yield* cp.spawn(ChildProcess.make("ffmpeg", ["-version"])); const [exitCode] = yield* Effect.all( [ cmd.exitCode, diff --git a/packages/pr-package/src/Worker.ts b/packages/pr-package/src/Worker.ts index d36756e42..0bcfaba32 100644 --- a/packages/pr-package/src/Worker.ts +++ b/packages/pr-package/src/Worker.ts @@ -25,8 +25,8 @@ export interface HandlerOptions extends AliasParserOptions { } const bindings = Layer.mergeAll( - Cloudflare.R2BucketBindingLive, - Cloudflare.KVNamespaceBindingLive, + Cloudflare.R2.ReadWriteBucketBinding, + Cloudflare.KV.ReadWriteNamespaceBinding, Cloudflare.SecretBindingLive, ); @@ -52,8 +52,8 @@ const bindings = Layer.mergeAll( */ export const handler = (options: HandlerOptions = {}) => Effect.gen(function* () { - const r2 = yield* Cloudflare.R2Bucket.bind(yield* Bucket); - const kv = yield* Cloudflare.KVNamespace.bind(yield* TagIndex); + const r2 = yield* Cloudflare.R2.ReadWriteBucket(yield* Bucket); + const kv = yield* Cloudflare.KV.ReadWriteNamespace(yield* TagIndex); const authToken = yield* Cloudflare.Secret.bind(yield* AuthToken); const packages = yield* PackageStore; diff --git a/scripts/codemod-binding-policy-providers.ts b/scripts/codemod-binding-policy-providers.ts new file mode 100644 index 000000000..30c1640de --- /dev/null +++ b/scripts/codemod-binding-policy-providers.ts @@ -0,0 +1,79 @@ +import * as path from "node:path"; + +import { Node, Project, SyntaxKind } from "ts-morph"; + +// Provider directory (relative to packages/alchemy/src) to migrate, e.g. "AWS" +// or "Cloudflare". Defaults to "AWS". +const provider = process.argv[2] ?? "AWS"; + +const srcRoot = path.join(import.meta.dir, "../packages/alchemy/src", provider); +const providersPath = path.join(srcRoot, "Providers.ts"); +const tsConfig = path.join(import.meta.dir, "../packages/alchemy/tsconfig.json"); + +const project = new Project({ + tsConfigFilePath: tsConfig, + skipAddingFilesFromTsConfig: true, +}); + +project.addSourceFilesAtPaths(`${srcRoot}/**/*.ts`); + +let filesChanged = 0; +const changed: string[] = []; + +for (const sourceFile of project.getSourceFiles()) { + // Never touch Providers.ts itself. + if (path.resolve(sourceFile.getFilePath()) === path.resolve(providersPath)) { + continue; + } + + let mutated = false; + + for (const call of sourceFile.getDescendantsOfKind( + SyntaxKind.CallExpression, + )) { + const callee = call.getExpression(); + if (!Node.isPropertyAccessExpression(callee)) continue; + if (callee.getName() !== "Policy") continue; + if (callee.getExpression().getText() !== "Binding") continue; + + const typeArgs = call.getTypeArguments(); + // Only migrate the two-type-argument form; three already has Providers. + if (typeArgs.length !== 2) continue; + + call.addTypeArgument("Providers"); + mutated = true; + } + + if (!mutated) continue; + + // Add `import type { Providers } from "/Providers.ts";` if absent. + const alreadyImported = sourceFile + .getImportDeclarations() + .some((decl) => + decl + .getNamedImports() + .some((named) => named.getName() === "Providers"), + ); + + if (!alreadyImported) { + const fromDir = path.dirname(sourceFile.getFilePath()); + let rel = path.relative(fromDir, providersPath).split(path.sep).join("/"); + if (!rel.startsWith(".")) rel = `./${rel}`; + + sourceFile.addImportDeclaration({ + isTypeOnly: true, + namedImports: ["Providers"], + moduleSpecifier: rel, + }); + } + + filesChanged += 1; + changed.push(path.relative(process.cwd(), sourceFile.getFilePath())); +} + +await project.save(); + +console.log(`Modified ${filesChanged} file(s):`); +for (const file of changed.sort()) { + console.log(` ${file}`); +} diff --git a/scripts/parse-cf-failures.ts b/scripts/parse-cf-failures.ts new file mode 100644 index 000000000..6920e337f --- /dev/null +++ b/scripts/parse-cf-failures.ts @@ -0,0 +1,107 @@ +#!/usr/bin/env bun +/** + * Parse a vitest JSON report (+ console log fallback) into a flat list of + * failed tests: file, test name, error. Throwaway helper for + * test-with-temporary-account.ts runs. + * + * bun scripts/parse-cf-failures.ts + */ +import { readFileSync, writeFileSync } from "node:fs"; + +const [, , jsonPath, logPath, outPath] = process.argv; +if (!jsonPath || !outPath) { + console.error( + "usage: bun scripts/parse-cf-failures.ts ", + ); + process.exit(1); +} + +const stripAnsi = (s: string): string => s.replace(/\x1b\[[0-9;]*m/g, ""); + +const relFile = (name: string): string => { + const norm = name.replaceAll("\\", "/"); + const i = norm.indexOf("packages/alchemy/"); + return i >= 0 ? norm.slice(i + "packages/alchemy/".length) : norm; +}; + +// First meaningful line of a failure message (the bit before the stack trace). +const firstErrorLine = (msg: string): string => { + for (const raw of msg.split("\n")) { + const line = raw.trimEnd(); + if (!line.trim()) continue; + if (/^\s*(❯|at )/.test(line)) break; + return line.trim(); + } + return ""; +}; + +// Build a name -> error map from the console log, for STACK_TRACE_ERROR cases +// where the JSON message is just a useless placeholder. +const consoleErrors = new Map(); +if (logPath) { + const lines = stripAnsi(readFileSync(logPath, "utf8")).split("\n"); + for (let i = 0; i < lines.length; i++) { + const m = lines[i].match(/ FAIL\s+\|\w+\|\s+\S+ > (.+)$/); + if (!m) continue; + const name = m[1].trim(); + // Look ahead for the first error-ish line before the stack / next FAIL. + for (let j = i + 1; j < Math.min(i + 6, lines.length); j++) { + const l = lines[j].trimEnd(); + if (!l.trim() || / FAIL /.test(l)) continue; + if (/^\s*❯/.test(l) || /^⎯+/.test(l)) break; + if (!consoleErrors.has(name)) consoleErrors.set(name, l.trim()); + break; + } + } +} + +const report = JSON.parse(readFileSync(jsonPath, "utf8")); + +interface Failure { + file: string; + name: string; + error: string; +} +const failures: Failure[] = []; + +for (const tr of report.testResults ?? []) { + const file = relFile(tr.name); + for (const a of tr.assertionResults ?? []) { + if (a.status !== "failed") continue; + const name = [...(a.ancestorTitles ?? []), a.title].join(" > "); + const msg0 = a.failureMessages?.[0] ?? ""; + let error = firstErrorLine(msg0); + if (!error || error.includes("STACK_TRACE_ERROR")) { + error = consoleErrors.get(name) ?? error ?? "(no error message captured)"; + } + failures.push({ file, name, error }); + } +} + +failures.sort( + (a, b) => a.file.localeCompare(b.file) || a.name.localeCompare(b.name), +); + +// Group by file for readability. +const byFile = new Map(); +for (const f of failures) { + const arr = byFile.get(f.file) ?? []; + arr.push(f); + byFile.set(f.file, arr); +} + +const out: string[] = []; +out.push(`# Failed Cloudflare tests (temporary account run)`); +out.push(`# total failed: ${failures.length} across ${byFile.size} files`); +out.push(""); +for (const [file, fs] of [...byFile.entries()].sort()) { + out.push(file); + for (const f of fs) { + out.push(` - ${f.name}`); + out.push(` ${f.error}`); + } + out.push(""); +} + +writeFileSync(outPath, out.join("\n")); +console.log(`Wrote ${failures.length} failures to ${outPath}`); diff --git a/scripts/skip-non-ephemeral.ts b/scripts/skip-non-ephemeral.ts new file mode 100644 index 000000000..3159a0ab4 --- /dev/null +++ b/scripts/skip-non-ephemeral.ts @@ -0,0 +1,139 @@ +#!/usr/bin/env bun +/** + * Codemod: gate tests behind `skipIf(SKIP_NON_EPHEMRAL_ACCOUNT_TESTS)` so the + * temporary-account run can skip tests that can't pass on a fresh, unentitled + * account. + * + * # gate the specific tests marked with `!!!` in a failures report + * bun scripts/skip-non-ephemeral.ts cf-temp-failures.txt + * + * # gate EVERY test in each file listed (one path per line) — e.g. all + * # Worker/Queue suites that blow the account's resource caps + * bun scripts/skip-non-ephemeral.ts --files worker-queue-files.txt + */ +import { readFileSync } from "node:fs"; +import * as nodePath from "node:path"; +import { Node, Project } from "ts-morph"; + +const SKIP_CONST = "SKIP_NON_EPHEMRAL_ACCOUNT_TESTS"; +const SKIP_DECL = `const ${SKIP_CONST} = process.env.${SKIP_CONST} === "1";`; + +const args = process.argv.slice(2); +const wholeFileMode = args[0] === "--files"; +const inputPath = (wholeFileMode ? args[1] : args[0]) ?? "cf-temp-failures.txt"; +const pkgDir = nodePath.resolve(import.meta.dir, "../packages/alchemy"); + +// ---- 1. Build file -> (Set | "*"). "*" means gate every test. ---- +const targets = new Map<string, Set<string> | "*">(); +if (wholeFileMode) { + for (const raw of readFileSync(inputPath, "utf8").split("\n")) { + const f = raw.trim(); + if (f) targets.set(f, "*"); + } +} else { + let currentFile: string | undefined; + for (const raw of readFileSync(inputPath, "utf8").split("\n")) { + const header = raw.match(/^(test\/.*\.test\.ts)\s*$/); + if (header) { + currentFile = header[1]; + continue; + } + const marked = raw.match(/^\s*-\s*(?:!!!\s*)+(.+?)\s*$/); + if (marked && currentFile) { + // Display name is `ancestor > ... > title`; the test() literal is the + // last segment (describe titles are separate calls). + const title = marked[1].split(" > ").pop()!.trim(); + const entry = targets.get(currentFile); + if (entry === "*") continue; + if (!entry) targets.set(currentFile, new Set([title])); + else entry.add(title); + } + } +} + +// ---- 2. Apply the codemod per file ---- +const project = new Project({ + tsConfigFilePath: nodePath.join(pkgDir, "tsconfig.json"), + skipAddingFilesFromTsConfig: true, +}); + +const literalValue = (node: Node): string | undefined => { + if ( + Node.isStringLiteral(node) || + Node.isNoSubstitutionTemplateLiteral(node) + ) { + return node.getLiteralValue(); + } + return undefined; +}; + +let filesModified = 0; +let callsGated = 0; +const unmatched: string[] = []; + +for (const [relFile, titles] of targets) { + const abs = nodePath.join(pkgDir, relFile); + const sf = project.addSourceFileAtPath(abs); + + // Collect target call expressions first, then mutate. + const hits: { node: import("ts-morph").CallExpression; title: string }[] = []; + const seen = new Set<string>(); + sf.forEachDescendant((node) => { + if (!Node.isCallExpression(node)) return; + const callee = node.getExpression(); + if (!/^(test|it)\b/.test(callee.getText())) return; + const args = node.getArguments(); + if (args.length === 0) return; + const name = literalValue(args[0]); + if (name === undefined) return; + if (titles !== "*" && !titles.has(name)) return; + hits.push({ node, title: name }); + seen.add(name); + }); + + if (titles !== "*") { + for (const title of titles) { + if (!seen.has(title)) unmatched.push(`${relFile} :: ${title}`); + } + } + if (hits.length === 0) continue; + + const gatedBefore = callsGated; + for (const { node } of hits) { + const callee = node.getExpression(); + // Idempotent: already gated on a previous run. + if (callee.getText().includes(SKIP_CONST)) continue; + // Merge into an existing `.skipIf(cond)` rather than double-wrapping. + if ( + Node.isCallExpression(callee) && + callee.getExpression().getText().endsWith(".skipIf") + ) { + const cond = callee.getArguments()[0]; + if (cond) { + cond.replaceWithText(`(${cond.getText()}) || ${SKIP_CONST}`); + callsGated++; + continue; + } + } + callee.replaceWithText(`${callee.getText()}.skipIf(${SKIP_CONST})`); + callsGated++; + } + + if (callsGated === gatedBefore) continue; // nothing new to gate + + // Declare the constant once, after the last import. + if (!sf.getVariableDeclaration(SKIP_CONST)) { + const imports = sf.getImportDeclarations(); + const insertIdx = + imports.length > 0 ? imports[imports.length - 1].getChildIndex() + 1 : 0; + sf.insertStatements(insertIdx, `\n${SKIP_DECL}`); + } + + sf.saveSync(); + filesModified++; +} + +console.log(`Files modified: ${filesModified}`); +console.log(`Tests gated: ${callsGated}`); +console.log(`Unmatched: ${unmatched.length}`); +for (const u of unmatched) console.log(` ! ${u}`); diff --git a/scripts/test-with-temporary-account.ts b/scripts/test-with-temporary-account.ts new file mode 100644 index 000000000..c45bf1445 --- /dev/null +++ b/scripts/test-with-temporary-account.ts @@ -0,0 +1,193 @@ +#!/usr/bin/env bun +/** + * Provision a throwaway Cloudflare *temporary preview account* and run the + * Alchemy test suite against it — no real Cloudflare login or long-lived API + * token required. + * + * It uses the public, proof-of-work-gated provisioning API (the same flow as + * `wrangler deploy --temporary`, see https://blog.cloudflare.com/temporary-accounts/) + * exposed by `@distilled.cloud/cloudflare/provisioning`, then runs vitest with + * the temporary account's credentials injected. + * + * The temporary account self-expires (~60 min), so there is nothing to clean + * up afterwards. + * + * Usage: + * # run the Cloudflare suite against a fresh temporary account (default) + * bun scripts/test-with-temporary-account.ts + * + * # target a narrower path + * bun scripts/test-with-temporary-account.ts test/Cloudflare/KV/Namespace.test.ts + * + * # write a vitest JSON report + * bun scripts/test-with-temporary-account.ts --json cf-temp-results.json + * + * Note: a temporary account is brand new and unentitled — it has no zones, no + * plan features, and tight resource caps (e.g. 5 Workers). Tests that can't pass + * on a temp account are gated behind `SKIP_NON_EPHEMRAL_ACCOUNT_TESTS` (set + * automatically here) — see scripts/skip-non-ephemeral.ts. + */ +import { fromApiToken } from "@distilled.cloud/cloudflare/Credentials"; +import * as Provisioning from "@distilled.cloud/cloudflare/provisioning"; +import { Effect } from "effect"; +import * as Redacted from "effect/Redacted"; +import * as FetchHttpClient from "effect/unstable/http/FetchHttpClient"; +import { createHash } from "node:crypto"; +import * as nodePath from "node:path"; + +const BOLD = "\x1b[1m"; +const GREEN = "\x1b[32m"; +const RED = "\x1b[31m"; +const DIM = "\x1b[2m"; +const RESET = "\x1b[0m"; + +// Cloudflare's terms accepted by provisioning a temporary account. +const TERMS = { + termsOfService: "https://www.cloudflare.com/terms/", + privacyPolicy: "https://www.cloudflare.com/privacypolicy/", +} as const; + +// Upper bound on total proof-of-work (k*g hashes), mirroring wrangler's cap so +// a malformed/hostile challenge can't make us spin forever. +const POW_MAX_ITERATIONS = 64_000_000; + +// Sensitive fields decode to `Redacted<string>` at runtime even though the +// static type says `string` — reveal so the token is actually usable. +const reveal = (value: string): string => + Redacted.isRedacted(value) ? Redacted.value(value) : value; + +/** + * Provision a temporary preview account: mint a proof-of-work challenge, solve + * it locally (sequential SHA-256 chain), and redeem it for account-scoped + * credentials. Requires a `Credentials` + `HttpClient` layer; the endpoints are + * public so the credentials are only a placeholder. + */ +const provisionTemporaryAccount = Effect.gen(function* () { + const challenge = yield* Provisioning.createTemporaryAccountChallenge({}); + + const seedBytes = Buffer.from(challenge.seed, "base64url").length; + if ( + !Number.isInteger(challenge.k) || + !Number.isInteger(challenge.g) || + challenge.k <= 0 || + challenge.g <= 0 || + challenge.k * challenge.g > POW_MAX_ITERATIONS || + seedBytes !== 32 + ) { + return yield* Effect.die( + new Error( + `Unsupported proof-of-work challenge (k=${challenge.k}, g=${challenge.g}, seed=${seedBytes}B)`, + ), + ); + } + + // Solve the PoW: chain of k segments of g SHA-256 hashes, checkpoint each + // boundary, then standard-base64 the concatenated checkpoints. + const checkpoints = yield* Effect.sync(() => { + const seed = Buffer.from(challenge.seed, "base64url"); + const acc: Buffer[] = Array.from({ length: challenge.k + 1 }); + let h = createHash("sha256").update(seed).digest(); + acc[0] = h; + for (let j = 0; j < challenge.k; j++) { + for (let i = 0; i < challenge.g; i++) { + h = createHash("sha256").update(h).digest(); + } + acc[j + 1] = h; + } + return Buffer.concat(acc).toString("base64"); + }); + + const result = yield* Provisioning.createTemporaryAccount({ + termsOfService: TERMS.termsOfService, + privacyPolicy: TERMS.privacyPolicy, + acceptTermsOfService: "yes", + challengeToken: challenge.challengeToken, + solution: { checkpoints }, + }); + + return { + accountId: result.account.id, + accountName: result.account.name, + apiToken: reveal(result.account.apiToken), + expiresAt: result.account.expiresAt, + claimUrl: result.claim.url, + }; +}); + +const main = async () => { + console.log( + `${DIM}Provisioning a temporary Cloudflare account (solving proof-of-work)…${RESET}`, + ); + + const creds = await Effect.runPromise( + provisionTemporaryAccount.pipe( + // Public endpoints ignore auth, but the SDK client always attaches a + // Bearer token — provide a placeholder. + Effect.provide( + fromApiToken({ + apiToken: process.env.CLOUDFLARE_API_TOKEN ?? "unauthenticated", + apiBaseUrl: process.env.CLOUDFLARE_API_BASE_URL, + }), + ), + Effect.provide(FetchHttpClient.layer), + ), + ); + + console.log( + `${GREEN}✓${RESET} account ${BOLD}${creds.accountId}${RESET} (${creds.accountName}) ${DIM}expires ${creds.expiresAt}${RESET}`, + ); + console.log(`${DIM} claim: ${creds.claimUrl}${RESET}`); + console.log( + `${DIM}Running Alchemy test suite against the temporary account…${RESET}\n`, + ); + + const pkgDir = nodePath.resolve(import.meta.dir, "../packages/alchemy"); + + // Parse args: `--json <path>` writes a vitest JSON report; everything else is + // a vitest path filter (default: the whole Cloudflare suite). + const rawArgs = process.argv.slice(2); + let jsonOut: string | undefined; + const pathArgs: string[] = []; + for (let i = 0; i < rawArgs.length; i++) { + if (rawArgs[i] === "--json") jsonOut = rawArgs[++i]; + else pathArgs.push(rawArgs[i]); + } + if (pathArgs.length === 0) pathArgs.push("test/Cloudflare"); + // Absolute so the report lands here (vitest's cwd is packages/alchemy). + if (jsonOut) jsonOut = nodePath.resolve(jsonOut); + + const env = { + ...process.env, + // Force Alchemy's Cloudflare auth to read credentials from the environment + // (CI path) under a throwaway profile so it never picks up a stored profile + // pointing at a real account. + CI: "true", + ALCHEMY_PROFILE: "cf-temporary-account", + CLOUDFLARE_ACCOUNT_ID: creds.accountId, + CLOUDFLARE_API_TOKEN: creds.apiToken, + // Skip tests that can't pass on a fresh, unentitled temporary account + // (gated via skipIf — see scripts/skip-non-ephemeral.ts). + SKIP_NON_EPHEMRAL_ACCOUNT_TESTS: "1", + }; + + const reporters = jsonOut + ? ["--reporter=default", "--reporter=json", `--outputFile=${jsonOut}`] + : ["--reporter=default"]; + const proc = Bun.spawn(["bunx", "vitest", "run", ...pathArgs, ...reporters], { + cwd: pkgDir, + env, + stdio: ["inherit", "inherit", "inherit"], + }); + + const exitCode = await proc.exited; + if (exitCode === 0) { + console.log(`\n${GREEN}${BOLD}✓ Test suite passed${RESET}`); + } else { + console.log( + `\n${RED}${BOLD}✗ Test suite failed (exit ${exitCode})${RESET}`, + ); + } + process.exitCode = exitCode; +}; + +void main(); diff --git a/tsconfig.json b/tsconfig.json index 3120b2c07..d06748048 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,9 @@ { "files": [], - "exclude": ["node_modules", "dist"], + "exclude": [ + "node_modules", + "dist" + ], "references": [ // distilled (dependencies first so `tsgo -b` emits their `lib/` before the // packages that consume them — otherwise a clean build races the not-yet-emitted @@ -75,6 +78,9 @@ { "path": "./examples/aws-vite/tsconfig.json" }, + { + "path": "./examples/cloudflare-agent/tsconfig.json" + }, { "path": "./examples/cloudflare-static-site/tsconfig.json" }, @@ -120,4 +126,4 @@ } ] } -} +} \ No newline at end of file diff --git a/website/plugins/twoslash-diff-prefix.mjs b/website/plugins/twoslash-diff-prefix.mjs index 8d6e1bb35..b52c1ddbd 100644 --- a/website/plugins/twoslash-diff-prefix.mjs +++ b/website/plugins/twoslash-diff-prefix.mjs @@ -7,7 +7,7 @@ * ```typescript twoslash * // @errors: 2345 * import { Bucket } from "./bucket.ts"; - * + const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); + * + const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); * ``` * * How it works: diff --git a/website/src/components/marketing-islands/ServiceLayerSwap.tsx b/website/src/components/marketing-islands/ServiceLayerSwap.tsx index 2622d1743..644858309 100644 --- a/website/src/components/marketing-islands/ServiceLayerSwap.tsx +++ b/website/src/components/marketing-islands/ServiceLayerSwap.tsx @@ -35,7 +35,7 @@ const IMPLS: Impl[] = [ layer: "SessionsKV", resourceLabel: "Sessions", resourceSub: "Cloudflare.KVNamespace", - bindCall: "KVNamespace.bind", + bindCall: "KV.ReadWriteNamespace", color: CF_COLOR, kind: "kv", }, diff --git a/website/src/content/docs/blog/2026-05-08-beta-35.md b/website/src/content/docs/blog/2026-05-08-beta-35.md index 0f18b4ee4..9ba5a6c4b 100644 --- a/website/src/content/docs/blog/2026-05-08-beta-35.md +++ b/website/src/content/docs/blog/2026-05-08-beta-35.md @@ -47,7 +47,7 @@ export default class Api extends Cloudflare.Worker<Api>()( "Api", { main: import.meta.filename }, Effect.gen(function* () { - const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); + const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); const queueResource = yield* Queue; const queue = yield* Cloudflare.QueueBinding.bind(queueResource); @@ -87,7 +87,7 @@ export default class Api extends Cloudflare.Worker<Api>()( }).pipe( Effect.provide( Layer.mergeAll( - Cloudflare.R2BucketBindingLive, + Cloudflare.R2.ReadWriteBucketBinding, Cloudflare.QueueBindingLive, Cloudflare.QueueEventSourceLive, // required for `messages(...)` to dispatch ), diff --git a/website/src/content/docs/blog/2026-05-12-beta-37.md b/website/src/content/docs/blog/2026-05-12-beta-37.md index 863ae515a..631990318 100644 --- a/website/src/content/docs/blog/2026-05-12-beta-37.md +++ b/website/src/content/docs/blog/2026-05-12-beta-37.md @@ -237,7 +237,7 @@ export default class Backend extends Cloudflare.Worker<Backend>()( "Backend", { main: import.meta.filename }, Effect.gen(function* () { - const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); + const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); return { // RPC method — callable via `backend.hello(key)` on the other side. @@ -261,7 +261,7 @@ export default class Backend extends Cloudflare.Worker<Backend>()( return HttpServerResponse.text("method not allowed", { status: 405 }); }), }; - }).pipe(Effect.provide(Cloudflare.R2BucketBindingLive)), + }).pipe(Effect.provide(Cloudflare.R2.ReadWriteBucketBinding)), ) {} ``` diff --git a/website/src/content/docs/blog/2026-05-20-beta-41.md b/website/src/content/docs/blog/2026-05-20-beta-41.md index d4b27661f..62b5b3aeb 100644 --- a/website/src/content/docs/blog/2026-05-20-beta-41.md +++ b/website/src/content/docs/blog/2026-05-20-beta-41.md @@ -105,7 +105,7 @@ export default Cloudflare.Worker( "Worker", { main: import.meta.filename }, Effect.gen(function* () { - const tasks = yield* Cloudflare.R2Bucket.bind(Bucket); + const tasks = yield* Cloudflare.R2.ReadWrite(Bucket); const tasksGroup = HttpApiBuilder.group(TaskApi, "Tasks", (h) => h.handle("getTask", ({ params }) => @@ -123,7 +123,7 @@ export default Cloudflare.Worker( HttpRouter.toHttpEffect, ), }; - }).pipe(Effect.provide(Cloudflare.R2BucketBindingLive)), + }).pipe(Effect.provide(Cloudflare.R2.ReadWriteBucketBinding)), ); ``` @@ -152,7 +152,7 @@ export default Cloudflare.Worker( "Worker", { main: import.meta.filename }, Effect.gen(function* () { - const tasks = yield* Cloudflare.R2Bucket.bind(Bucket); + const tasks = yield* Cloudflare.R2.ReadWrite(Bucket); const handlersLayer = TaskRpcs.toLayer({ getTask: ({ id }) => /* ... */, @@ -165,7 +165,7 @@ export default Cloudflare.Worker( Effect.provide(RpcSerialization.layerJson), ), }; - }).pipe(Effect.provide(Cloudflare.R2BucketBindingLive)), + }).pipe(Effect.provide(Cloudflare.R2.ReadWriteBucketBinding)), ); ``` diff --git a/website/src/content/docs/concepts/binding.mdx b/website/src/content/docs/concepts/binding.mdx index 2421ba8a3..746335c71 100644 --- a/website/src/content/docs/concepts/binding.mdx +++ b/website/src/content/docs/concepts/binding.mdx @@ -18,7 +18,7 @@ bindings fit into a Platform's runtime, see ## A binding in one line ```typescript -const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); +const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); // later, in fetch: yield* bucket.put("hello.txt", "world"); @@ -41,7 +41,7 @@ export default Cloudflare.Worker( "Worker", { main: import.meta.filename }, Effect.gen(function* () { - const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); + const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); return { fetch: Effect.gen(function* () { const obj = yield* bucket.get("key"); @@ -118,8 +118,8 @@ On Cloudflare, the same call attaches a native Worker binding (R2, KV, D1, Durable Object…) instead of an IAM policy: ```typescript -const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); -const kv = yield* Cloudflare.KVNamespace.bind(Sessions); +const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); +const kv = yield* Cloudflare.KV.ReadWriteNamespace(Sessions); ``` The runtime API is identical to the AWS counterpart — code that diff --git a/website/src/content/docs/concepts/layers.mdx b/website/src/content/docs/concepts/layers.mdx index bae25d2d1..d44efbef1 100644 --- a/website/src/content/docs/concepts/layers.mdx +++ b/website/src/content/docs/concepts/layers.mdx @@ -32,7 +32,7 @@ export default Cloudflare.Worker( "Api", { main: import.meta.filename }, Effect.gen(function* () { - const kv = yield* Cloudflare.KVNamespaceBinding.bind(MyKV); + const kv = yield* Cloudflare.KV.ReadWriteNamespace(MyKV); return { fetch: Effect.gen(function* () { @@ -46,7 +46,7 @@ export default Cloudflare.Worker( This works, but the handler is welded to KV. The `fetch` body mentions `kv.get`, knows the value shape comes back as `"json"`, -and propagates `KVNamespaceError`. Moving the data to DynamoDB or +and propagates `NamespaceError`. Moving the data to DynamoDB or swapping in an in-memory fake for tests means rewriting `fetch` — not just the storage wiring. @@ -89,7 +89,7 @@ export const JobServiceKV = Layer.effect( JobService, Effect.gen(function* () { const MyKV = yield* Cloudflare.KVNamespace("MyKV"); - const kv = yield* Cloudflare.KVNamespaceBinding.bind(MyKV); + const kv = yield* Cloudflare.KV.ReadWriteNamespace(MyKV); return { getJob: Effect.fn(function* (id: string) { @@ -105,7 +105,7 @@ Three things happen in one expression: 1. **The KV namespace is a real resource.** It joins the [Stack](/concepts/stack), goes through plan/create/update like anything else. -2. **The binding is wired** — `KVNamespaceBinding.bind(MyKV)` +2. **The binding is wired** — `KV.ReadWriteNamespace(MyKV)` attaches the KV namespace to whichever Worker eventually consumes this Layer. 3. **A typed `JobService` is returned.** Callers see only `getJob`. @@ -148,7 +148,7 @@ The handler that called `kv.get` directly had a `fetch` body typed roughly: ```typescript -Effect.Effect<Response, KVNamespaceError, Alchemy.RuntimeContext> +Effect.Effect<Response, NamespaceError, Alchemy.RuntimeContext> ``` The KV-specific error and the implicit dependence on a Cloudflare @@ -162,8 +162,8 @@ Effect.Effect<Response, JobError, Alchemy.RuntimeContext> The Cloudflare-specific surface is gone — absorbed by the Layer: ```typescript -export const KVNamespaceBindingLive = Layer.effect( - KVNamespaceBinding, +export const ReadWriteNamespaceBinding = Layer.effect( + NamespaceReadWrite, Effect.gen(function* () { const env = yield* WorkerEnvironment; // ← required here, once // ...returns a client that closes over env @@ -171,7 +171,7 @@ export const KVNamespaceBindingLive = Layer.effect( ); ``` -The Worker that consumes `KVNamespaceBindingLive` satisfies +The Worker that consumes `ReadWriteNamespaceBinding` satisfies `WorkerEnvironment` in one place; downstream callers see only `RuntimeContext`. Try the swap on a Cloudflare Worker: @@ -197,7 +197,7 @@ Cloudflare.Worker( { main: import.meta.filename }, Effect.gen(function* () { // ─── Init: declare dependencies ─── - const kv = yield* Cloudflare.KVNamespaceBinding.bind(MyKV); + const kv = yield* Cloudflare.KV.ReadWriteNamespace(MyKV); return { // ─── Runtime: use them ─── @@ -214,7 +214,7 @@ inside the runtime closure. It is not provided at plantime, cold start init, or anywhere else. So an Effect like: ```typescript -kv.get(...): Effect.Effect<Job | null, KVNamespaceError, Alchemy.RuntimeContext> +kv.get(...): Effect.Effect<Job | null, NamespaceError, Alchemy.RuntimeContext> ``` is one the type system *guarantees* can only run in the runtime diff --git a/website/src/content/docs/concepts/phases.mdx b/website/src/content/docs/concepts/phases.mdx index eed735d74..fee008a9a 100644 --- a/website/src/content/docs/concepts/phases.mdx +++ b/website/src/content/docs/concepts/phases.mdx @@ -30,7 +30,7 @@ Cloudflare.Worker( { main: import.meta.filename }, Effect.gen(function* () { // ─── Init phase ─── - const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); + const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); return { // ─── Runtime phase ─── @@ -139,8 +139,8 @@ The init/runtime split lets you write code that: ```typescript Effect.gen(function* () { // Init: runs once per cold start - const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); - const kv = yield* Cloudflare.KVNamespace.bind(KV); + const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); + const kv = yield* Cloudflare.KV.ReadWriteNamespace(KV); return { // Runtime: runs per request diff --git a/website/src/content/docs/concepts/platform.mdx b/website/src/content/docs/concepts/platform.mdx index efec06614..1a60aaa6d 100644 --- a/website/src/content/docs/concepts/platform.mdx +++ b/website/src/content/docs/concepts/platform.mdx @@ -30,7 +30,7 @@ export default Cloudflare.Worker( { main: import.meta.filename }, Effect.gen(function* () { // Init: bind a resource. The binding is the typed SDK. - const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); + const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); return { // Runtime: per-request handler. @@ -60,8 +60,8 @@ syntax `yield* SomeResource.bind(target)` does three things at once: 3. Returns a typed handle you call at runtime ```typescript -const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); -const kv = yield* Cloudflare.KVNamespace.bind(Sessions); +const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); +const kv = yield* Cloudflare.KV.ReadWriteNamespace(Sessions); // Inside fetch: yield* bucket.put("key", "value"); @@ -142,7 +142,7 @@ export default Cloudflare.Worker( "Worker", { main: import.meta.filename }, Effect.gen(function* () { - const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); + const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); return { fetch: Effect.gen(function* () { /* ... */ diff --git a/website/src/content/docs/guides/effect-http-api.mdx b/website/src/content/docs/guides/effect-http-api.mdx index ee44ab445..cc1d6fa47 100644 --- a/website/src/content/docs/guides/effect-http-api.mdx +++ b/website/src/content/docs/guides/effect-http-api.mdx @@ -142,7 +142,7 @@ export default Cloudflare.Worker( "Worker", { main: import.meta.filename }, Effect.gen(function* () { -+ const tasks = yield* Cloudflare.R2Bucket.bind(Tasks); ++ const tasks = yield* Cloudflare.R2.ReadWrite(Tasks); return {}; }), @@ -150,7 +150,7 @@ export default Cloudflare.Worker( ``` We'll provide the runtime side of this binding -(`Cloudflare.R2BucketBindingLive`) in step 3c when we wire up the +(`Cloudflare.R2.ReadWriteBucketBinding`) in step 3c when we wire up the `fetch` handler. ### 3b. Construct the handler group inside Init @@ -167,7 +167,7 @@ safe to call inside Init. ```typescript twoslash // @noErrors Effect.gen(function* () { - const tasks = yield* Cloudflare.R2Bucket.bind(Tasks); + const tasks = yield* Cloudflare.R2.ReadWrite(Tasks); + const tasksGroup = HttpApiBuilder.group(TaskApi, "Tasks", (handlers) => + handlers @@ -283,7 +283,7 @@ export default Cloudflare.Worker( "Worker", { main: import.meta.filename }, Effect.gen(function* () { - const tasks = yield* Cloudflare.R2Bucket.bind(Tasks); + const tasks = yield* Cloudflare.R2.ReadWrite(Tasks); const tasksGroup = HttpApiBuilder.group(TaskApi, "Tasks", (handlers) => handlers @@ -325,7 +325,7 @@ export default Cloudflare.Worker( HttpRouter.toHttpEffect, ), }; - }).pipe(Effect.provide(Cloudflare.R2BucketBindingLive)), + }).pipe(Effect.provide(Cloudflare.R2.ReadWriteBucketBinding)), ); ``` @@ -514,7 +514,7 @@ under the hood: +import TasksObject, { TaskDOApi } from "./object.ts"; Effect.gen(function* () { - const tasks = yield* Cloudflare.R2Bucket.bind(Tasks); + const tasks = yield* Cloudflare.R2.ReadWrite(Tasks); + const tasksDO = yield* TasksObject; + + const getTaskDOClient = (id: string = "default") => diff --git a/website/src/content/docs/guides/effect-rpc.mdx b/website/src/content/docs/guides/effect-rpc.mdx index cc8b434b6..be8c72893 100644 --- a/website/src/content/docs/guides/effect-rpc.mdx +++ b/website/src/content/docs/guides/effect-rpc.mdx @@ -128,7 +128,7 @@ export default Cloudflare.Worker( "Worker", { main: import.meta.filename }, Effect.gen(function* () { -+ const tasks = yield* Cloudflare.R2Bucket.bind(Tasks); ++ const tasks = yield* Cloudflare.R2.ReadWrite(Tasks); return {}; }), @@ -136,7 +136,7 @@ export default Cloudflare.Worker( ``` We'll provide the runtime side of this binding -(`Cloudflare.R2BucketBindingLive`) in step 3c when we wire up the +(`Cloudflare.R2.ReadWriteBucketBinding`) in step 3c when we wire up the `fetch` handler. ### 3b. Construct the handlers inside Init @@ -153,7 +153,7 @@ is pure construction — it builds a value, it doesn't run the server. ```typescript twoslash // @noErrors Effect.gen(function* () { - const tasks = yield* Cloudflare.R2Bucket.bind(Tasks); + const tasks = yield* Cloudflare.R2.ReadWrite(Tasks); + const handlersLayer = TaskRpcs.toLayer({ + getTask: ({ id }) => @@ -231,7 +231,7 @@ export default Cloudflare.Worker( "Worker", { main: import.meta.filename }, Effect.gen(function* () { - const tasks = yield* Cloudflare.R2Bucket.bind(Tasks); + const tasks = yield* Cloudflare.R2.ReadWrite(Tasks); const handlersLayer = TaskRpcs.toLayer({ getTask: ({ id }) => @@ -266,7 +266,7 @@ export default Cloudflare.Worker( Effect.provide(RpcSerialization.layerJson), ), }; - }).pipe(Effect.provide(Cloudflare.R2BucketBindingLive)), + }).pipe(Effect.provide(Cloudflare.R2.ReadWriteBucketBinding)), ); ``` @@ -480,7 +480,7 @@ for `RpcClient.layerProtocolHttp` and you get a typed +import { DoRpcs } from "./rpcs.ts"; Effect.gen(function* () { - const tasks = yield* Cloudflare.R2Bucket.bind(Tasks); + const tasks = yield* Cloudflare.R2.ReadWrite(Tasks); + const tasksDO = yield* TasksObject; + + const makeDOClient = (id: string = "default") => diff --git a/website/src/content/docs/guides/infrastructure-layers.mdx b/website/src/content/docs/guides/infrastructure-layers.mdx index 5a5aa1ec5..2d34f7cc5 100644 --- a/website/src/content/docs/guides/infrastructure-layers.mdx +++ b/website/src/content/docs/guides/infrastructure-layers.mdx @@ -93,7 +93,7 @@ Layer. ## Bind the namespace -`KVNamespaceBinding.bind(Namespace)` wires the namespace into the +`KV.ReadWriteNamespace(Namespace)` wires the namespace into the consuming Worker — binding name, IAM, env injection — and returns a typed client. @@ -102,7 +102,7 @@ a typed client. JobService, Effect.gen(function* () { const Namespace = yield* Cloudflare.KVNamespace("Jobs"); -+ const kv = yield* Cloudflare.KVNamespaceBinding.bind(Namespace); ++ const kv = yield* Cloudflare.KV.ReadWriteNamespace(Namespace); return { // ... @@ -211,24 +211,24 @@ KV namespace resource into the Stack: ``` The Worker still doesn't type-check — `JobServiceKV` itself depends -on `KVNamespaceBinding`, which is satisfied by -`KVNamespaceBindingLive`. +on `NamespaceReadWrite`, which is satisfied by +`ReadWriteNamespaceBinding`. ## Provide the runtime binding `Layer.provide` satisfies `JobServiceKV`'s dependency on -`KVNamespaceBinding` privately, so the consumer only sees `JobService`: +`NamespaceReadWrite` privately, so the consumer only sees `JobService`: ```diff lang="typescript" }).pipe( Effect.provide( - JobServiceKV, -+ JobServiceKV.pipe(Layer.provide(Cloudflare.KVNamespaceBindingLive)), ++ JobServiceKV.pipe(Layer.provide(Cloudflare.KV.ReadWriteNamespaceBinding)), ), ), ``` -`KVNamespaceBindingLive` requires `WorkerEnvironment`, which the +`ReadWriteNamespaceBinding` requires `WorkerEnvironment`, which the Cloudflare Worker runtime satisfies automatically at cold start. ## Reuse a Layer across Workers @@ -247,7 +247,7 @@ has a stable logical id: // ... }).pipe( Effect.provide( -+ JobServiceKV.pipe(Layer.provide(Cloudflare.KVNamespaceBindingLive)), ++ JobServiceKV.pipe(Layer.provide(Cloudflare.KV.ReadWriteNamespaceBinding)), ), ), ); @@ -302,8 +302,8 @@ runtime binding provided to that Layer — swap over: ```diff lang="typescript" }).pipe( Effect.provide( -- JobServiceKV.pipe(Layer.provide(Cloudflare.KVNamespaceBindingLive)), -+ JobServiceR2.pipe(Layer.provide(Cloudflare.R2BucketBindingLive)), +- JobServiceKV.pipe(Layer.provide(Cloudflare.KV.ReadWriteNamespaceBinding)), ++ JobServiceR2.pipe(Layer.provide(Cloudflare.R2.ReadWriteBucketBinding)), ), ), ``` diff --git a/website/src/content/docs/guides/migrating-from-v1.mdx b/website/src/content/docs/guides/migrating-from-v1.mdx index d4764240c..29812a568 100644 --- a/website/src/content/docs/guides/migrating-from-v1.mdx +++ b/website/src/content/docs/guides/migrating-from-v1.mdx @@ -155,7 +155,7 @@ import { Bucket } from "./bucket.ts"; +export default Cloudflare.Worker("Worker", + { main: import.meta.filename }, + Effect.gen(function* () { -+ const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); ++ const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); + + return { + fetch: Effect.gen(function* () { diff --git a/website/src/content/docs/tutorial/cloudflare/containers.mdx b/website/src/content/docs/tutorial/cloudflare/containers.mdx index 509910e7c..be41cad6c 100644 --- a/website/src/content/docs/tutorial/cloudflare/containers.mdx +++ b/website/src/content/docs/tutorial/cloudflare/containers.mdx @@ -254,7 +254,7 @@ that lives in `Sandbox.runtime.ts` and Rolldown tree-shakes it out. ## Bind the container in the outer phase `Cloudflare.Container.bind(Sandbox)` registers the binding the -same way `R2Bucket.bind` or `KVNamespace.bind` do. It belongs in +same way `R2.ReadWriteBucket` or `KV.ReadWriteNamespace` do. It belongs in the **outer** init phase — that runs once when the DO class is wired to its Worker. If you put it in the inner phase, every new DO instance would re-bind, which is wasteful and wrong. diff --git a/website/src/content/docs/tutorial/cloudflare/queue-consumer.mdx b/website/src/content/docs/tutorial/cloudflare/queue-consumer.mdx index f319c7b03..8be94ea8a 100644 --- a/website/src/content/docs/tutorial/cloudflare/queue-consumer.mdx +++ b/website/src/content/docs/tutorial/cloudflare/queue-consumer.mdx @@ -7,7 +7,7 @@ sidebar: A Cloudflare Queue gives you reliable, at-least-once delivery between Workers. The Worker that *sends* messages uses -`Cloudflare.QueueBinding.bind(...)` from the [Queue producer +`Cloudflare.Queues.WriteQueue(...)` from the [Queue producer binding](/providers/cloudflare/queue) — covered in the resource docs. This tutorial focuses on the *consumer* side: receiving batches with the Effect-style @@ -47,7 +47,7 @@ test can read it back. ## Bind the Queue producer In the Worker init phase, yield the queue resource and ask -`QueueBinding` for a typed sender. The sender exposes `send` / +`Cloudflare.Queues.WriteQueue` for a typed sender. The sender exposes `send` / `sendBatch` that round-trip through Cloudflare's runtime. ```typescript @@ -61,14 +61,14 @@ export default Cloudflare.Worker( "Api", { main: import.meta.filename }, Effect.gen(function* () { - const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); + const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); const queueResource = yield* Queue; - const queue = yield* Cloudflare.QueueBinding.bind(queueResource); + const queue = yield* Cloudflare.Queues.WriteQueue(queueResource); return { fetch: Effect.gen(function* () { return new Response("ok"); }) }; }).pipe( - Effect.provide(Cloudflare.QueueBindingLive), - Effect.provide(Cloudflare.R2BucketBindingLive), + Effect.provide(Cloudflare.Queues.WriteQueueBinding), + Effect.provide(Cloudflare.R2.ReadWriteBucketBinding), ), ); ``` @@ -93,9 +93,9 @@ expected to return `Effect.Effect<void>`. +} Effect.gen(function* () { - const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); + const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); const queueResource = yield* Queue; - const queue = yield* Cloudflare.QueueBinding.bind(queueResource); + const queue = yield* Cloudflare.Queues.WriteQueue(queueResource); + yield* Cloudflare.messages<QueueMessageBody>(queueResource).subscribe( + (stream) => @@ -152,8 +152,8 @@ alongside the other binding lives. ```diff lang="typescript" }).pipe( + Effect.provide(Cloudflare.QueueEventSourceLive), - Effect.provide(Cloudflare.QueueBindingLive), - Effect.provide(Cloudflare.R2BucketBindingLive), + Effect.provide(Cloudflare.Queues.WriteQueueBinding), + Effect.provide(Cloudflare.R2.ReadWriteBucketBinding), ), ``` diff --git a/website/src/content/docs/tutorial/cloudflare/vite-spa.mdx b/website/src/content/docs/tutorial/cloudflare/vite-spa.mdx index b7f88f15f..eaef8082e 100644 --- a/website/src/content/docs/tutorial/cloudflare/vite-spa.mdx +++ b/website/src/content/docs/tutorial/cloudflare/vite-spa.mdx @@ -492,7 +492,7 @@ concurrency), factor it into its own Worker: + "Backend", + { main: import.meta.filename }, + Effect.gen(function* () { -+ const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); ++ const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); + + return { + hello: Effect.fn("Backend.hello")(function* (key: string) { @@ -513,7 +513,7 @@ concurrency), factor it into its own Worker: + return HttpServerResponse.raw(object.body); + }), + }; -+ }).pipe(Effect.provide(Cloudflare.R2BucketBindingLive)), ++ }).pipe(Effect.provide(Cloudflare.R2.ReadWriteBucketBinding)), +) {} ``` diff --git a/website/src/content/docs/tutorial/cloudflare/workflows.mdx b/website/src/content/docs/tutorial/cloudflare/workflows.mdx index 356f38395..600470445 100644 --- a/website/src/content/docs/tutorial/cloudflare/workflows.mdx +++ b/website/src/content/docs/tutorial/cloudflare/workflows.mdx @@ -88,7 +88,7 @@ something binds to it. ## Bind KV to the workflow -`Cloudflare.KVNamespace.bind(KV)` belongs in the workflow's outer +`Cloudflare.KV.ReadWriteNamespace(KV)` belongs in the workflow's outer init phase. It registers the binding on the workflow's worker and returns a typed Effect-native client whose methods (`get`, `put`, `list`, `delete`) are Effects you can `yield*` directly: @@ -104,7 +104,7 @@ export default class NotifyWorkflow extends Cloudflare.Workflow<NotifyWorkflow>( "Notifier", Effect.gen(function* () { const rooms = yield* Room; -+ const kv = yield* Cloudflare.KVNamespace.bind(KV); ++ const kv = yield* Cloudflare.KV.ReadWriteNamespace(KV); return Effect.fn(function* (input: { roomId: string; message: string }) { const { roomId, message } = input; @@ -136,7 +136,7 @@ export default class NotifyWorkflow extends Cloudflare.Workflow<NotifyWorkflow>( "Notifier", Effect.gen(function* () { const rooms = yield* Room; - const kv = yield* Cloudflare.KVNamespace.bind(KV); + const kv = yield* Cloudflare.KV.ReadWriteNamespace(KV); return Effect.fn(function* (input: { roomId: string; message: string }) { const { roomId, message } = input; @@ -184,7 +184,7 @@ export default class NotifyWorkflow extends Cloudflare.Workflow<NotifyWorkflow>( "Notifier", Effect.gen(function* () { const rooms = yield* Room; - const kv = yield* Cloudflare.KVNamespace.bind(KV); + const kv = yield* Cloudflare.KV.ReadWriteNamespace(KV); return Effect.fn(function* (input: { roomId: string; message: string }) { const { roomId, message } = input; @@ -230,7 +230,7 @@ export default class NotifyWorkflow extends Cloudflare.Workflow<NotifyWorkflow>( "Notifier", Effect.gen(function* () { const rooms = yield* Room; - const kv = yield* Cloudflare.KVNamespace.bind(KV); + const kv = yield* Cloudflare.KV.ReadWriteNamespace(KV); return Effect.fn(function* (input: { roomId: string; message: string }) { const { roomId, message } = input; diff --git a/website/src/content/docs/tutorial/part-2.mdx b/website/src/content/docs/tutorial/part-2.mdx index b2a4639ab..8da1d6173 100644 --- a/website/src/content/docs/tutorial/part-2.mdx +++ b/website/src/content/docs/tutorial/part-2.mdx @@ -119,7 +119,7 @@ export default Cloudflare.Worker( main: import.meta.filename, }, Effect.gen(function* () { -+ const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); ++ const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); return { fetch: Effect.gen(function* () { @@ -151,14 +151,14 @@ export default Cloudflare.Worker( "Worker", { main: import.meta.filename }, Effect.gen(function* () { - const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); + const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); return { fetch: Effect.gen(function* () { return HttpServerResponse.text("Hello, world!"); }), }; -+ }).pipe(Effect.provide(Cloudflare.R2BucketBindingLive)), ++ }).pipe(Effect.provide(Cloudflare.R2.ReadWriteBucketBinding)), ); ``` @@ -189,7 +189,7 @@ export default Cloudflare.Worker( "Worker", { main: import.meta.filename }, Effect.gen(function* () { - const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); + const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); return { fetch: Effect.gen(function* () { + const request = yield* HttpServerRequest; @@ -205,7 +205,7 @@ export default Cloudflare.Worker( return HttpServerResponse.text("Hello, world!"); }), }; - }).pipe(Effect.provide(Cloudflare.R2BucketBindingLive)), + }).pipe(Effect.provide(Cloudflare.R2.ReadWriteBucketBinding)), ); ``` @@ -235,7 +235,7 @@ export default Cloudflare.Worker( "Worker", { main: import.meta.filename }, Effect.gen(function* () { - const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); + const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); return { fetch: Effect.gen(function* () { const request = yield* HttpServerRequest; @@ -257,7 +257,7 @@ export default Cloudflare.Worker( + ), + ), }; - }).pipe(Effect.provide(Cloudflare.R2BucketBindingLive)), + }).pipe(Effect.provide(Cloudflare.R2.ReadWriteBucketBinding)), ); ``` @@ -287,7 +287,7 @@ export default Cloudflare.Worker( "Worker", { main: import.meta.filename }, Effect.gen(function* () { - const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); + const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); return { fetch: Effect.gen(function* () { const request = yield* HttpServerRequest; @@ -315,7 +315,7 @@ export default Cloudflare.Worker( ), ), }; - }).pipe(Effect.provide(Cloudflare.R2BucketBindingLive)), + }).pipe(Effect.provide(Cloudflare.R2.ReadWriteBucketBinding)), ); ``` diff --git a/website/src/content/docs/what-is-alchemy.mdx b/website/src/content/docs/what-is-alchemy.mdx index d8998dd68..ea7adbaec 100644 --- a/website/src/content/docs/what-is-alchemy.mdx +++ b/website/src/content/docs/what-is-alchemy.mdx @@ -52,7 +52,7 @@ export default Cloudflare.Worker( "Worker", { main: import.meta.filename }, Effect.gen(function* () { - const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); + const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); return { fetch: Effect.gen(function* () { @@ -122,7 +122,7 @@ export const Bucket = Cloudflare.R2Bucket("Bucket"); ```typescript // src/worker.ts import { Bucket } from "./bucket.ts"; -const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); +const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); ``` ## Bindings @@ -133,7 +133,7 @@ A **Binding** connects a resource to a Worker or Lambda. A single bindings that client needs: ```typescript -const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); +const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); // later, inside fetch: yield* bucket.put("hello.txt", "world"); @@ -219,7 +219,7 @@ export default Cloudflare.Worker( "Worker", { main: import.meta.filename }, Effect.gen(function* () { - const bucket = yield* Cloudflare.R2Bucket.bind(Bucket); + const bucket = yield* Cloudflare.R2.ReadWrite(Bucket); return { fetch: Effect.gen(function* () { // Effect-native runtime code