Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
202 commits
Select commit Hold shift + click to select a range
9a2afe8
Use Effect resource cleanup in schema test
RhysSullivan May 5, 2026
0bbd756
Clean up core OAuth discovery errors
RhysSullivan May 6, 2026
573ed37
Clean up MCP invocation boundaries
RhysSullivan May 6, 2026
0fab457
Clean up MCP manifest option handling
RhysSullivan May 6, 2026
ea3d682
Clean up MCP connection error messages
RhysSullivan May 6, 2026
253b011
Clean up execution engine tagged checks
RhysSullivan May 6, 2026
fe37cf1
Use Effect assertions in connection tests
RhysSullivan May 6, 2026
5393959
Use typed assertions in policy code
RhysSullivan May 6, 2026
74119c1
Use typed failures in OpenAPI multi-scope tests
RhysSullivan May 6, 2026
f9da4f3
Use typed boundaries in cloud MCP e2e
RhysSullivan May 6, 2026
1d616f6
Use typed keychain failures
RhysSullivan May 6, 2026
9c6a5c0
Use Effect assertions in executor tests
RhysSullivan May 6, 2026
83554b7
Keep tool invoker failures typed
RhysSullivan May 6, 2026
2cae07e
Use Effect assertions in OpenAPI plugin tests
RhysSullivan May 6, 2026
ff6dbd3
Use typed OAuth API boundaries
RhysSullivan May 6, 2026
fdb0bcc
Document WorkOS Vault test error boundary
RhysSullivan May 6, 2026
cf41ce1
Use typed boundaries in MCP OAuth tests
RhysSullivan May 6, 2026
33919dd
Use Effect boundaries in release smoke test
RhysSullivan May 6, 2026
cf3f937
Use promiseExit in GraphQL source UI
RhysSullivan May 6, 2026
c1c6430
Use Effect exit for local update check
RhysSullivan May 6, 2026
26475f9
Clarify shared UI and config boundaries
RhysSullivan May 6, 2026
15b056b
Use Effect cleanup in MCP worker transport
RhysSullivan May 6, 2026
f7e6ca4
Simplify tenant isolation assertions
RhysSullivan May 6, 2026
987638b
Use typed errors in postgres storage tests
RhysSullivan May 6, 2026
3e9f3d5
Clarify drizzle generator boundaries
RhysSullivan May 6, 2026
a02b3cc
Use stable blob store error messages
RhysSullivan May 6, 2026
b0d47df
Use inferred keychain extension type
RhysSullivan May 6, 2026
10308ec
Use typed config parse errors
RhysSullivan May 6, 2026
84e64a0
Parse plugin loader config with Schema
RhysSullivan May 6, 2026
58fbda2
Mark core boundary escape hatches
RhysSullivan May 6, 2026
85c78ab
Remove redundant OpenAPI credential casts
RhysSullivan May 6, 2026
4194ac6
Use typed OpenAPI OAuth test failures
RhysSullivan May 6, 2026
2445ee5
Use promiseExit in 1Password settings
RhysSullivan May 6, 2026
8fe0858
Clean daemon state test boundaries
RhysSullivan May 6, 2026
f157b80
Tighten MCP host test boundaries
RhysSullivan May 6, 2026
77e62f3
Clean OpenAPI invoke error handling
RhysSullivan May 6, 2026
868d4d4
Clean MCP probe shape boundaries
RhysSullivan May 6, 2026
d4dfc9d
Clean local server boundary cleanup
RhysSullivan May 6, 2026
4d96793
Parse MCP migration test config with Schema
RhysSullivan May 6, 2026
0545e1e
Clean cloud test worker boundaries
RhysSullivan May 6, 2026
586955b
Clean cloud service boundaries
RhysSullivan May 6, 2026
daa1763
Parse local migration test rows with Schema
RhysSullivan May 6, 2026
9f564b4
Use Effect timeouts in tool invoker tests
RhysSullivan May 6, 2026
976f585
Stabilize GraphQL extraction errors
RhysSullivan May 6, 2026
f534b9d
Clean cloud auth and org test failures
RhysSullivan May 6, 2026
46d1c24
Clean local server tooling boundaries
RhysSullivan May 6, 2026
ffc1374
Parse OpenAPI migration test rows with Schema
RhysSullivan May 6, 2026
3b01c46
Remove MCP cross-user redundant casts
RhysSullivan May 6, 2026
1e26fe2
Normalize WorkOS Vault errors
RhysSullivan May 6, 2026
d401d65
Clean cloud auth and Autumn boundaries
RhysSullivan May 6, 2026
78035f2
Clean local DB migration boundaries
RhysSullivan May 6, 2026
9812c24
Clean core API observability errors
RhysSullivan May 6, 2026
f2f8721
Mark JSON schema adapter boundaries
RhysSullivan May 6, 2026
432ae96
Clean plugin API handler tests
RhysSullivan May 6, 2026
9e84f99
Use a typed chart payload guard
RhysSullivan May 6, 2026
c61c7f0
Fix storage typed boundary lint
RhysSullivan May 6, 2026
110adf1
Parse GraphQL store rows with Schema
RhysSullivan May 6, 2026
de48f2d
Fix Google Discovery SDK test boundaries
RhysSullivan May 6, 2026
1858ea2
Use promiseExit in source UI boundaries
RhysSullivan May 6, 2026
70d01de
Fix core OAuth typed boundaries
RhysSullivan May 6, 2026
23439e6
Use promiseExit in OAuth popup flow
RhysSullivan May 6, 2026
89bc9c9
Fix storage test boundary lint
RhysSullivan May 6, 2026
4256b95
Fix OpenAPI plugin typed boundaries
RhysSullivan May 6, 2026
b91524b
Fix MCP SDK store and test boundaries
RhysSullivan May 6, 2026
418b096
Update OAuth refresh reauth assertion
RhysSullivan May 6, 2026
44315a8
Fix executor typed boundary lint
RhysSullivan May 6, 2026
fcae413
Fix cloud MCP boundary lint
RhysSullivan May 6, 2026
7130b7d
Fix GraphQL plugin boundary lint
RhysSullivan May 6, 2026
81e97c3
Fix OnePassword plugin boundary lint
RhysSullivan May 6, 2026
2aa25fb
Fix core OAuth boundary lint
RhysSullivan May 6, 2026
9494cb4
Fix OpenAPI boundary lint
RhysSullivan May 6, 2026
ed17ac4
Fix Google Discovery boundary lint
RhysSullivan May 6, 2026
9a04145
Fix MCP plugin boundary lint
RhysSullivan May 6, 2026
5ad3094
Fix local MCP host boundary lint
RhysSullivan May 6, 2026
707e5c2
Use typed WorkOS Vault plugin failures
RhysSullivan May 6, 2026
0b34498
Use schema guards for page tagged errors
RhysSullivan May 6, 2026
9352af4
Clean typed test boundary assertions
RhysSullivan May 6, 2026
71332e7
Keep config write failures typed
RhysSullivan May 6, 2026
a995f28
Normalize cloud startup boundaries
RhysSullivan May 6, 2026
ff422a5
Handle all-plugins entrypoint failures in Effect
RhysSullivan May 6, 2026
14060d6
Keep cloud API errors typed
RhysSullivan May 6, 2026
139dd5c
Validate cloud JWT boundaries with schema
RhysSullivan May 6, 2026
489f9f6
Parse file secrets with schema
RhysSullivan May 6, 2026
20c8ac6
Keep keychain boundaries typed
RhysSullivan May 6, 2026
b953a1f
Clean core API typed boundaries
RhysSullivan May 6, 2026
c403f48
Use Effect UI mutation boundaries
RhysSullivan May 6, 2026
025cdf6
Keep kernel core failures typed
RhysSullivan May 6, 2026
e9fd11a
Use schema in OpenAPI preview boundaries
RhysSullivan May 6, 2026
fcd022c
Clean SDK typed test boundaries
RhysSullivan May 6, 2026
e393aed
Mark local app runtime boundaries
RhysSullivan May 6, 2026
df6f374
Clean CLI and host test boundaries
RhysSullivan May 6, 2026
09e9ef6
Clean small package lint boundaries
RhysSullivan May 6, 2026
1476896
Clean remaining cloud lint boundaries
RhysSullivan May 6, 2026
719c5ea
Clean execution runtime lint boundaries
RhysSullivan May 6, 2026
6220d9f
Remove OpenAPI summary casts
RhysSullivan May 6, 2026
7656b2e
Clean MCP per-user auth test boundaries
RhysSullivan May 6, 2026
de10d6d
Use promiseExit in MCP edit source
RhysSullivan May 6, 2026
e8422d2
Document React scope context boundaries
RhysSullivan May 6, 2026
83b84eb
Use typed GraphQL invocation boundaries
RhysSullivan May 6, 2026
72e83f3
Use typed Google Discovery invocation boundaries
RhysSullivan May 6, 2026
8f91606
Add OpenAPI parse boundary tests
RhysSullivan May 6, 2026
dc75098
Merge remote-tracking branch 'origin/codex/effect-lint-cloud-db-schem…
RhysSullivan May 6, 2026
fbcc52b
Merge remote-tracking branch 'origin/codex/effect-lint-core-sdk-oauth…
RhysSullivan May 6, 2026
bec0b0a
Merge remote-tracking branch 'origin/codex/effect-lint-keychain-bound…
RhysSullivan May 6, 2026
4a99018
Merge remote-tracking branch 'origin/codex/effect-lint-mcp-invoke' in…
RhysSullivan May 6, 2026
2d588fc
Merge remote-tracking branch 'origin/codex/effect-lint-mcp-connection…
RhysSullivan May 6, 2026
b9a25b4
Merge remote-tracking branch 'origin/codex/effect-lint-mcp-manifest' …
RhysSullivan May 6, 2026
ed4a5d7
Merge remote-tracking branch 'origin/codex/effect-lint-execution-engi…
RhysSullivan May 6, 2026
f089a2d
Merge remote-tracking branch 'origin/codex/effect-lint-core-connectio…
RhysSullivan May 6, 2026
a92bfe2
Merge remote-tracking branch 'origin/codex/effect-lint-core-policies'…
RhysSullivan May 6, 2026
568373b
Merge remote-tracking branch 'origin/codex/effect-lint-openapi-multis…
RhysSullivan May 6, 2026
c318de5
Merge remote-tracking branch 'origin/codex/effect-lint-cloud-miniflar…
RhysSullivan May 6, 2026
88f874d
Merge remote-tracking branch 'origin/codex/effect-lint-core-executor-…
RhysSullivan May 6, 2026
60627a9
Merge remote-tracking branch 'origin/codex/effect-lint-execution-tool…
RhysSullivan May 6, 2026
eb2f5a4
Merge remote-tracking branch 'origin/codex/effect-lint-openapi-plugin…
RhysSullivan May 6, 2026
e03dd71
Merge remote-tracking branch 'origin/codex/effect-lint-core-api-oauth…
RhysSullivan May 6, 2026
33cce43
Merge remote-tracking branch 'origin/codex/effect-lint-workos-vault-t…
RhysSullivan May 6, 2026
9e61781
Merge remote-tracking branch 'origin/codex/effect-lint-mcp-oauth-test…
RhysSullivan May 6, 2026
75ec915
Merge remote-tracking branch 'origin/codex/effect-lint-release-smoke-…
RhysSullivan May 6, 2026
ab4880f
Merge remote-tracking branch 'origin/codex/effect-lint-graphql-react-…
RhysSullivan May 6, 2026
3ad5333
Merge remote-tracking branch 'origin/codex/effect-lint-local-shell-up…
RhysSullivan May 6, 2026
a8c4b45
Merge remote-tracking branch 'origin/codex/effect-lint-shared-boundar…
RhysSullivan May 6, 2026
85ff2a7
Merge remote-tracking branch 'origin/codex/effect-lint-cloud-mcp-work…
RhysSullivan May 6, 2026
282e7c6
Merge remote-tracking branch 'origin/codex/effect-lint-cloud-tenant-i…
RhysSullivan May 6, 2026
d56487f
Merge remote-tracking branch 'origin/codex/effect-lint-storage-postgr…
RhysSullivan May 6, 2026
28901d9
Merge remote-tracking branch 'origin/codex/effect-lint-cli-drizzle-ge…
RhysSullivan May 6, 2026
1a269ea
Merge remote-tracking branch 'origin/codex/effect-lint-blob-store-err…
RhysSullivan May 6, 2026
45f0734
Merge remote-tracking branch 'origin/codex/effect-lint-keychain-exten…
RhysSullivan May 6, 2026
d296179
Merge remote-tracking branch 'origin/codex/batch10-config-load-error'…
RhysSullivan May 6, 2026
ba6f976
Merge remote-tracking branch 'origin/codex/batch10-config-plugin-load…
RhysSullivan May 6, 2026
83e756b
Merge remote-tracking branch 'origin/codex/batch10-core-boundary-supp…
RhysSullivan May 6, 2026
a1200b1
Merge remote-tracking branch 'origin/codex/batch10-openapi-credential…
RhysSullivan May 6, 2026
86d576a
Merge remote-tracking branch 'origin/codex/batch10-openapi-oauth-test…
RhysSullivan May 6, 2026
9ff4452
Merge remote-tracking branch 'origin/codex/batch10-onepassword-promis…
RhysSullivan May 6, 2026
af548c5
Merge remote-tracking branch 'origin/codex/batch10-daemon-state-bound…
RhysSullivan May 6, 2026
d437854
Merge remote-tracking branch 'origin/codex/batch10-mcp-host-test-boun…
RhysSullivan May 6, 2026
402178c
Merge remote-tracking branch 'origin/codex/batch11-openapi-invoke-err…
RhysSullivan May 6, 2026
b6cb20a
Merge remote-tracking branch 'origin/codex/batch11-mcp-probe-shape-bo…
RhysSullivan May 6, 2026
0237029
Merge remote-tracking branch 'origin/codex/batch11-local-server-bound…
RhysSullivan May 6, 2026
73f312b
Merge remote-tracking branch 'origin/codex/batch11-local-mcp-migratio…
RhysSullivan May 6, 2026
d9ba70c
Merge remote-tracking branch 'origin/codex/batch11-cloud-test-worker-…
RhysSullivan May 6, 2026
a38c46f
Merge remote-tracking branch 'origin/codex/batch12-cloud-service-boun…
RhysSullivan May 6, 2026
fe94822
Merge remote-tracking branch 'origin/codex/batch12-local-migration-sc…
RhysSullivan May 6, 2026
501aabf
Merge remote-tracking branch 'origin/codex/batch12-tool-invoker-timeo…
RhysSullivan May 6, 2026
a81992a
Merge remote-tracking branch 'origin/codex/batch12-graphql-extract-er…
RhysSullivan May 6, 2026
4cc98a6
Merge remote-tracking branch 'origin/codex/batch13-cloud-auth-org-tes…
RhysSullivan May 6, 2026
5b49a1e
Merge remote-tracking branch 'origin/codex/batch13-local-server-tooli…
RhysSullivan May 6, 2026
a41dd4c
Merge remote-tracking branch 'origin/codex/batch13-local-openapi-migr…
RhysSullivan May 6, 2026
f8e48b1
Merge remote-tracking branch 'origin/codex/batch13-mcp-cross-user-cas…
RhysSullivan May 6, 2026
1468797
Merge remote-tracking branch 'origin/codex/batch13-workos-vault-error…
RhysSullivan May 6, 2026
d8abc3d
Merge remote-tracking branch 'origin/codex/batch14-cloud-auth-autumn-…
RhysSullivan May 6, 2026
6a9df08
Merge remote-tracking branch 'origin/codex/batch14-local-db-migrate-c…
RhysSullivan May 6, 2026
4dd070c
Merge remote-tracking branch 'origin/codex/batch14-core-api-observabi…
RhysSullivan May 6, 2026
7dcf5de
Merge remote-tracking branch 'origin/codex/batch14-kernel-json-schema…
RhysSullivan May 6, 2026
9b540fd
Merge remote-tracking branch 'origin/codex/batch14-plugin-api-handler…
RhysSullivan May 6, 2026
abe685b
Merge remote-tracking branch 'origin/codex/batch14-react-chart-payloa…
RhysSullivan May 6, 2026
e609843
Merge remote-tracking branch 'origin/codex/batch15-storage-typed-boun…
RhysSullivan May 6, 2026
635056c
Merge remote-tracking branch 'origin/codex/batch15-graphql-schema-tes…
RhysSullivan May 6, 2026
e9a58c1
Merge remote-tracking branch 'origin/codex/batch15-google-discovery-t…
RhysSullivan May 6, 2026
5d78d34
Merge remote-tracking branch 'origin/codex/batch15-ui-promise-exit-bo…
RhysSullivan May 6, 2026
e5b1196
Merge remote-tracking branch 'origin/codex/batch16-core-oauth-boundar…
RhysSullivan May 6, 2026
2745940
Merge remote-tracking branch 'origin/codex/batch16-react-oauth-popup-…
RhysSullivan May 6, 2026
d516810
Merge remote-tracking branch 'origin/codex/batch16-storage-test-bound…
RhysSullivan May 6, 2026
f887e4c
Merge remote-tracking branch 'origin/codex/batch16-openapi-plugin-bou…
RhysSullivan May 6, 2026
2c0751c
Merge remote-tracking branch 'origin/codex/split-openapi-parse-tests-…
RhysSullivan May 6, 2026
36b20f5
Merge remote-tracking branch 'origin/codex/batch16-mcp-sdk-test-bound…
RhysSullivan May 6, 2026
cd10e67
Merge remote-tracking branch 'origin/codex/batch17-core-executor-boun…
RhysSullivan May 6, 2026
3a85629
Merge remote-tracking branch 'origin/codex/batch17-cloud-mcp-boundari…
RhysSullivan May 6, 2026
0477d35
Merge remote-tracking branch 'origin/codex/batch17-graphql-plugin-bou…
RhysSullivan May 6, 2026
422a424
Merge remote-tracking branch 'origin/codex/batch17-onepassword-bounda…
RhysSullivan May 6, 2026
3f7841a
Merge remote-tracking branch 'origin/codex/batch18-core-oauth-boundar…
RhysSullivan May 6, 2026
7257c89
Merge remote-tracking branch 'origin/codex/batch18-openapi-boundaries…
RhysSullivan May 6, 2026
bb6490c
Merge remote-tracking branch 'origin/codex/batch18-google-discovery-b…
RhysSullivan May 6, 2026
4077874
Merge remote-tracking branch 'origin/codex/batch18-mcp-plugin-boundar…
RhysSullivan May 6, 2026
20ebf61
Merge remote-tracking branch 'origin/codex/batch18-local-host-mcp-bou…
RhysSullivan May 6, 2026
95464b7
Merge remote-tracking branch 'origin/codex/batch19-workos-vault-plugi…
RhysSullivan May 6, 2026
2488306
Merge remote-tracking branch 'origin/codex/batch19-react-tagged-error…
RhysSullivan May 6, 2026
5c899cc
Merge remote-tracking branch 'origin/codex/batch19-test-boundary-clea…
RhysSullivan May 6, 2026
bac06dd
Merge remote-tracking branch 'origin/codex/batch19-config-write-error…
RhysSullivan May 6, 2026
e851713
Merge remote-tracking branch 'origin/codex/batch19-cloud-startup-boun…
RhysSullivan May 6, 2026
86edf61
Merge remote-tracking branch 'origin/codex/batch19-all-plugins-entryp…
RhysSullivan May 6, 2026
2831e92
Merge remote-tracking branch 'origin/codex/batch20-cloud-api-error-bo…
RhysSullivan May 6, 2026
27ec18d
Merge remote-tracking branch 'origin/codex/batch20-cloud-jwt-boundari…
RhysSullivan May 6, 2026
db3ee82
Merge remote-tracking branch 'origin/codex/batch20-file-secrets-schem…
RhysSullivan May 6, 2026
33c2bd1
Merge remote-tracking branch 'origin/codex/batch20-keychain-boundarie…
RhysSullivan May 6, 2026
1423723
Merge remote-tracking branch 'origin/codex/batch20-core-api-boundarie…
RhysSullivan May 6, 2026
ed046ae
Merge remote-tracking branch 'origin/codex/batch20-react-ui-boundarie…
RhysSullivan May 6, 2026
23c5c4e
Merge remote-tracking branch 'origin/codex/batch21-kernel-core-bounda…
RhysSullivan May 6, 2026
995c396
Merge remote-tracking branch 'origin/codex/batch21-openapi-small-boun…
RhysSullivan May 6, 2026
1875c13
Merge remote-tracking branch 'origin/codex/batch21-sdk-test-boundarie…
RhysSullivan May 6, 2026
ad94c1d
Merge remote-tracking branch 'origin/codex/batch21-local-app-boundari…
RhysSullivan May 6, 2026
bd71269
Merge remote-tracking branch 'origin/codex/batch21-cli-host-test-boun…
RhysSullivan May 6, 2026
2b74de9
Merge remote-tracking branch 'origin/codex/batch21-small-package-boun…
RhysSullivan May 6, 2026
8673012
Merge remote-tracking branch 'origin/codex/batch22-cloud-remaining-bo…
RhysSullivan May 6, 2026
39cf6ac
Merge remote-tracking branch 'origin/codex/batch22-execution-runtime-…
RhysSullivan May 6, 2026
01234a3
Merge remote-tracking branch 'origin/codex/batch22-openapi-source-sum…
RhysSullivan May 6, 2026
6c44b34
Merge remote-tracking branch 'origin/codex/batch23-mcp-per-user-auth-…
RhysSullivan May 6, 2026
2194f3d
Merge remote-tracking branch 'origin/codex/split-mcp-edit-source-prom…
RhysSullivan May 6, 2026
ae26cdc
Merge remote-tracking branch 'origin/codex/split-react-scope-context-…
RhysSullivan May 6, 2026
944fd4e
Merge remote-tracking branch 'origin/codex/split-graphql-invoke-bound…
RhysSullivan May 6, 2026
911d156
Merge remote-tracking branch 'origin/codex/split-google-discovery-inv…
RhysSullivan May 6, 2026
eddcffc
Resolve OAuth refresh integration assertion
RhysSullivan May 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 12 additions & 16 deletions apps/cloud/src/api/autumn.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { env } from "cloudflare:workers";
import { Effect } from "effect";
import { Cause, Effect } from "effect";
import {
HttpRouter,
HttpServerRequest,
Expand Down Expand Up @@ -30,13 +30,11 @@ const handler = Effect.gen(function* () {
const session = yield* workos.authenticateRequest(webRequest);

if (!session || !session.organizationId) {
return yield* Effect.fail(
new HttpResponseError({
status: 401,
code: "unauthorized",
message: "Unauthorized",
}),
);
return yield* new HttpResponseError({
status: 401,
code: "unauthorized",
message: "Unauthorized",
});
}

const url = new URL(webRequest.url);
Expand Down Expand Up @@ -74,20 +72,18 @@ const handler = Effect.gen(function* () {

if (statusCode >= 400) {
console.error("[autumn] upstream error:", statusCode, response);
return yield* Effect.fail(
new HttpResponseError({
status: statusCode,
code: "billing_request_failed",
message: "Billing request failed",
}),
);
return yield* new HttpResponseError({
status: statusCode,
code: "billing_request_failed",
message: "Billing request failed",
});
}

return HttpServerResponse.jsonUnsafe(response, { status: statusCode });
}).pipe(
Effect.catchCause((err) => {
if (isServerError(err)) {
console.error("[autumn] request failed:", err instanceof Error ? err.stack : err);
console.error("[autumn] request failed:", Cause.pretty(err));
}
return Effect.succeed(toErrorServerResponse(err));
}),
Expand Down
9 changes: 6 additions & 3 deletions apps/cloud/src/api/error-response.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Cause, Data, Effect, Result } from "effect";
import { Cause, Data, Effect, Predicate, Result } from "effect";
import {
HttpServerRespondable,
HttpServerResponse,
Expand Down Expand Up @@ -38,9 +38,12 @@ const unwrapCause = (error: unknown): unknown => {
return error;
};

const isHttpResponseError = (error: unknown): error is HttpResponseError =>
Predicate.isTagged(error, "HttpResponseError");

const toHttpResponseError = (error: unknown): HttpResponseError => {
const unwrapped = unwrapCause(error);
return unwrapped instanceof HttpResponseError
return isHttpResponseError(unwrapped)
? unwrapped
: new HttpResponseError({
status: 500,
Expand All @@ -62,7 +65,7 @@ export const toErrorServerResponse = (error: unknown): HttpServerResponse.HttpSe
if (mapped.status >= 500) {
console.error(
"[api] toErrorServerResponse error:",
Cause.isCause(error) ? Cause.pretty(error) : error instanceof Error ? error.stack : error,
Cause.isCause(error) ? Cause.pretty(error) : error,
);
captureCause(error);
}
Expand Down
68 changes: 29 additions & 39 deletions apps/cloud/src/api/slack.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { env } from "cloudflare:workers";
import { Effect } from "effect";
import { Cause, Effect } from "effect";
import { HttpRouter, HttpServerRequest, HttpServerResponse } from "effect/unstable/http";

import { SlackService } from "../services/slack";
Expand Down Expand Up @@ -31,20 +31,18 @@ const verifyTurnstile = (token: string, remoteIp: string | null) =>
const json = (await res.json()) as { success: boolean; "error-codes"?: string[] };
return { success: json.success, errorCodes: json["error-codes"] ?? [] };
},
catch: (cause) => ({ success: false as const, fetchError: String(cause) }),
catch: (cause) => ({ success: false as const, fetchError: cause }),
});

const handler = Effect.gen(function* () {
const request = yield* HttpServerRequest.HttpServerRequest;

if (request.method !== "POST") {
return yield* Effect.fail(
new HttpResponseError({
status: 405,
code: "method_not_allowed",
message: "Method not allowed",
}),
);
return yield* new HttpResponseError({
status: 405,
code: "method_not_allowed",
message: "Method not allowed",
});
}

const body = (yield* Effect.mapError(
Expand Down Expand Up @@ -73,54 +71,46 @@ const handler = Effect.gen(function* () {
const turnstileToken = typeof body.turnstileToken === "string" ? body.turnstileToken : "";

if (!isValidEmail(email)) {
return yield* Effect.fail(
new HttpResponseError({
status: 400,
code: "invalid_email",
message: "A valid email is required",
}),
);
return yield* new HttpResponseError({
status: 400,
code: "invalid_email",
message: "A valid email is required",
});
}

if (!turnstileToken) {
return yield* Effect.fail(
new HttpResponseError({
status: 400,
code: "captcha_required",
message: "Captcha verification is required.",
}),
);
return yield* new HttpResponseError({
status: 400,
code: "captcha_required",
message: "Captcha verification is required.",
});
}

const remoteIp = request.headers["cf-connecting-ip"] ?? null;
const verification = yield* verifyTurnstile(turnstileToken, remoteIp);
if (!verification.success) {
console.error("[slack] turnstile verification failed:", verification);
return yield* Effect.fail(
new HttpResponseError({
status: 403,
code: "captcha_failed",
message: "Captcha verification failed. Please try again.",
}),
);
return yield* new HttpResponseError({
status: 403,
code: "captcha_failed",
message: "Captcha verification failed. Please try again.",
});
}

// Global daily channel-creation cap — bounds the worst case if Turnstile is
// bypassed somehow. Per-IP gating belongs at the edge (Cloudflare Rules);
// this binding is a single shared bucket keyed at "global".
const limit = yield* Effect.tryPromise({
try: () => env.SLACK_INVITE_LIMITER.limit({ key: "global" }),
catch: (cause) => ({ success: false as const, fetchError: String(cause) }),
catch: (cause) => ({ success: false as const, fetchError: cause }),
});
if (!limit.success) {
console.error("[slack] global rate limit hit");
return yield* Effect.fail(
new HttpResponseError({
status: 429,
code: "rate_limited",
message: "We're getting more contact requests than usual. Please try again later.",
}),
);
return yield* new HttpResponseError({
status: 429,
code: "rate_limited",
message: "We're getting more contact requests than usual. Please try again later.",
});
}

const slack = yield* SlackService;
Expand All @@ -144,7 +134,7 @@ const handler = Effect.gen(function* () {
}).pipe(
Effect.catchCause((err) => {
if (isServerError(err)) {
console.error("[slack] request failed:", err instanceof Error ? err.stack : err);
console.error("[slack] request failed:", Cause.pretty(err));
}
return Effect.succeed(toErrorServerResponse(err));
}),
Expand Down
15 changes: 11 additions & 4 deletions apps/cloud/src/auth/handlers.node.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { HttpApiBuilder, HttpApi } from "effect/unstable/httpapi";
import { HttpRouter, HttpServer } from "effect/unstable/http";
import { describe, expect, it } from "@effect/vitest";
import { Effect, Layer } from "effect";
import { Data, Effect, Layer } from "effect";
import type { Effect as EffectType } from "effect/Effect";

import { CloudAuthPublicApi } from "./api";
Expand Down Expand Up @@ -31,15 +31,22 @@ const fakeUser: AuthenticateWithCodeResult["user"] = {
metadata: {},
};

class UnstubbedWorkOSMethod extends Data.TaggedError("UnstubbedWorkOSMethod")<{
method: string;
}> {}

const makeAuthFetch = (workos: Partial<WorkOSAuth["Service"]>) => {
const WorkOSTest = Layer.succeed(
WorkOSAuth,
new Proxy(workos as WorkOSAuth["Service"], {
get: (target, prop) => {
if (prop in target) return target[prop as keyof typeof target];
return () => {
throw new Error(`WorkOSAuth.${String(prop)} not stubbed`);
};
return () =>
Effect.fail(
new UnstubbedWorkOSMethod({
method: typeof prop === "string" ? prop : (prop.description ?? "symbol"),
}),
);
},
}),
);
Expand Down
6 changes: 3 additions & 3 deletions apps/cloud/src/auth/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ export const CloudSessionAuthHandlers = HttpApiBuilder.group(
},
);
deleteCookie("wos-session", { path: "/" });
return yield* Effect.fail(new WorkOSError());
return yield* new WorkOSError();
}

setCookie("wos-session", refreshed, COOKIE_OPTIONS);
Expand Down Expand Up @@ -341,7 +341,7 @@ export const CloudSessionAuthHandlers = HttpApiBuilder.group(
yield* Effect.logWarning("acceptInvitation: invitation has no organizationId", {
invitationId: payload.invitationId,
});
return yield* Effect.fail(new WorkOSError());
return yield* new WorkOSError();
}

// Mirror the org locally so domain tables can FK against it.
Expand Down Expand Up @@ -369,7 +369,7 @@ export const CloudSessionAuthHandlers = HttpApiBuilder.group(
},
);
deleteCookie("wos-session", { path: "/" });
return yield* Effect.fail(new WorkOSError());
return yield* new WorkOSError();
}

setCookie("wos-session", refreshed, COOKIE_OPTIONS);
Expand Down
12 changes: 10 additions & 2 deletions apps/cloud/src/auth/workos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,17 @@
// ---------------------------------------------------------------------------

import { env } from "cloudflare:workers";
import { Context, Effect, Layer } from "effect";
import { Context, Data, Effect, Layer } from "effect";
import { GeneratePortalLinkIntent, WorkOS } from "@workos-inc/node/worker";
import { WorkOSError, tryPromiseService, withServiceLogging } from "./errors";

const COOKIE_NAME = "wos-session";
const INVALID_COOKIE_PASSWORD_MESSAGE =
"WORKOS_COOKIE_PASSWORD must be at least 32 characters";

class WorkOSAuthConfigurationError extends Data.TaggedError("WorkOSAuthConfigurationError")<{
readonly message: string;
}> {}

// ---------------------------------------------------------------------------
// Service
Expand All @@ -19,7 +25,9 @@ const make = Effect.gen(function* () {
const cookiePassword = env.WORKOS_COOKIE_PASSWORD;

if (!cookiePassword || cookiePassword.length < 32) {
return yield* Effect.die(new Error("WORKOS_COOKIE_PASSWORD must be at least 32 characters"));
return yield* new WorkOSAuthConfigurationError({
message: INVALID_COOKIE_PASSWORD_MESSAGE,
});
}

const workos = new WorkOS({ apiKey, clientId });
Expand Down
Loading
Loading