Skip to content

feat!: upgrade to Effect v4#1288

Draft
juliusmarminge wants to merge 5 commits into
mainfrom
cursor/effect-v4-upgrade-8bf5
Draft

feat!: upgrade to Effect v4#1288
juliusmarminge wants to merge 5 commits into
mainfrom
cursor/effect-v4-upgrade-8bf5

Conversation

@juliusmarminge

@juliusmarminge juliusmarminge commented Jul 7, 2026

Copy link
Copy Markdown
Member

Summary

Upgrades the repo from Effect v3 (effect@3.21.0 + @effect/platform@0.96.0) to Effect v4 (effect@4.0.0-beta.93), following the migration guides in effect-TS/effect-smol (MIGRATION.md and migration/*.md, including the v3→v4 import/API rename maps and the Schema v4 guide).

Live demo

End-to-end upload through examples/backend-adapters running the migrated stack against the live UploadThing API (sea1 region): React client (UploadButton, migrated Effect v4 client code) → Effect v4 HttpRouter server (effect-platform adapter) → real presigned URL generation, ingest upload, dev-mode callback stream, and onUploadComplete:

effect_v4_live_upload_demo.mp4

Upload complete alert after uploading through the Effect v4 stack

The server log confirmed the full dev-mode flow: metadata registered with the ingest server, callback forwarded from the dev stream with a valid hmac-sha256 signature, onUploadComplete executed, and the callback result submitted (200). The live-API SDK surface (uploadFiles, uploadFilesFromUrl, generateSignedURL, getSignedURL, renameFiles, listFiles, deleteFiles) was also exercised against the real API via sdk.live.test.ts and a script using the built package output.

Dependencies

  • effect 3.21.04.0.0-beta.93 (all workspaces)
  • @effect/platform removed — merged into core effect in v4; all HTTP modules now come from effect/unstable/http
  • @effect/vitest 0.29.04.0.0-beta.93, @effect/platform-node 0.106.04.0.0-beta.93, @effect/language-service 0.84.30.86.4
  • .nvmrc bumped 22.1422.23: ini@7.0.0 (transitive dep of effect v4) requires Node ^22.22.2, so pnpm install fails its engine check on older Node 22 releases
  • examples/minimal-expo now uses moduleResolution: "bundler": effect v4 exposes submodules (e.g. effect/Schema) only through package.json exports (no typesVersions fallback), which the legacy node resolution from expo/tsconfig.base cannot follow

@uploadthing/shared

  • effect/Micro was removed in v4; client-side code now uses the core Effect module (tree-shakeable in v4): Micro.TaggedError/Micro.ErrorData.TaggedError/Data.Error, Micro.withTraceEffect.withSpan, Micro.asyncEffect.callback, Micro.fromEitherEffect.fromResult
  • Context.Tag class → Context.Service for FetchContext
  • Predicate.isRecordPredicate.isObject

uploadthing

  • Client (client.ts, client-future.ts, upload-browser.ts, ut-reporter.ts): Micro → Effect, interrupt detection via Cause.hasInterrupts, Cause.squash for error unwrapping
  • Config: ConfigProvider.fromJsonfromUnknown, custom import.meta.env fallback dropped (v4's fromEnv merges process.env and import.meta.env natively, built lazily to observe late env mutations), S.ConfigConfig.string + Schema.decodeUnknownEffect with the same MISSING_ENV / INVALID_SERVER_CONFIG error semantics
  • Logger: custom LogLevel config validator replaced by v4's Config.logLevel (the code comment literally said "Effect 4.0 will change this... then we can remove this"), loggers set via Logger.layer / References.MinimumLogLevel, HttpClientError reworked for the new single-class error with reason/response
  • Runtime: FiberRef-based header redaction → Layer.effect(Headers.CurrentRedactedNames, ...); config provider merged into the runtime context via Layer.provideMerge (v4 references live in the context, not fiber refs); Fetch service falls back to a lazy globalThis.fetch wrapper instead of overriding the reference with undefined
  • Handler: v3's pipeable HttpRouter (removed in v4) replaced with method dispatch on HttpServerRequest; HttpApp.toWebHandlerRuntimeHttpEffect.toWebHandler + Effect.provideContext; Effect.forkDaemonEffect.forkDetach, fiber.awaitFiber.await, Effect.ignoreLogged → local ignoreLogged helper, catchTag("ParseError")catchTag("SchemaError")
  • Schemas: S.Literal(...xs)S.Literals([...]), S.transformS.decodeTo + SchemaTransformation.transform, S.optionalWith({ default })S.withDecodingDefault (+ withConstructorDefault), S.composeS.decodeTo, S.parseJsonS.fromJsonString, S.RedactedS.RedactedFromValue, S.startsWithcheck(S.isStartsWith(...)), S.Record({key, value})S.Record(key, value), Schema.Schema<A, I>Schema.Codec<A, I>
  • SDK: bodyUnsafeJsonbodyJsonUnsafe, Effect.tapBothtap/tapError, Effect.scoped on HTTP calls dropped (v4 responses are not scope-bound), ParseErrorSchemaError
  • effect-platform adapter: now returns a plain Effect<HttpServerResponse, unknown, HttpClient | HttpServerRequest | Scope> usable with the v4 layer-based HttpRouter (see updated example)

Tests & tooling

  • Tests migrated (Layer.setConfigProviderConfigProvider.layer, S.encodeS.encodeEffect, Stream.fromReadableStream options object, updated v4 error shapes/messages)
  • Shared vitest setup file removes vitest browser-mode's broken setImmediate stub (defined but undefined), which crashed Effect v4's scheduler feature detection in browser tests
  • examples/backend-adapters Effect server rewritten to the v4 layer-based router; verified it boots and serves (GET /api, GET /api/uploadthing)
  • Changeset added (major for uploadthing and @uploadthing/shared)

Testing

  • ✅ Live E2E (see demo video): browser upload through the React client + effect-platform (v4 HttpRouter) server against the real UploadThing API, including dev-mode callbacks and serverData round-trip; also verified via the express adapter and curl
  • ✅ Live SDK smoke tests (sdk.live.test.ts with a real token): upload, upload-from-URL, rename by fileKey/customId, delete all pass; the remaining failures are app-tier assumptions baked into the suite (free-tier limitBytes, ACL overrides disabled on the test app), not migration regressions
  • pnpm test@uploadthing/shared 28 passed, uploadthing 120 passed / 10 skipped (incl. chromium browser tests), @uploadthing/react 16 passed / 1 skipped
  • pnpm typecheck — all 37 tasks (packages + examples + playgrounds) successful
  • pnpm turbo run lint --filter "./packages/*"
  • pnpm build — all 10 packages build
  • pnpm manypkg check, pnpm format:check

To show artifacts inline, enable in settings.

Open in Web Open in Cursor 

cursoragent and others added 3 commits July 6, 2026 23:14
Co-authored-by: Julius Marminge <juliusmarminge@users.noreply.github.com>
Co-authored-by: Julius Marminge <juliusmarminge@users.noreply.github.com>
Co-authored-by: Julius Marminge <juliusmarminge@users.noreply.github.com>
@changeset-bot

changeset-bot Bot commented Jul 7, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3a897cc

The changes in this PR will be included in the next version bump.

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jul 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs-uploadthing Ready Ready Preview, Comment Jul 7, 2026 12:32am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
legacy-docs-uploadthing Ignored Ignored Jul 7, 2026 12:32am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b19476fd-6452-43b9-bcd9-7fffcbb2ea0e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/effect-v4-upgrade-8bf5

Comment @coderabbitai help to get the list of available commands.

Co-authored-by: Julius Marminge <juliusmarminge@users.noreply.github.com>
@pkg-pr-new

pkg-pr-new Bot commented Jul 7, 2026

Copy link
Copy Markdown
More templates

npm i https://pkg.pr.new/@uploadthing/shared@1288
npm i https://pkg.pr.new/uploadthing@1288

commit: 3a897cc

@github-actions

github-actions Bot commented Jul 7, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle size comparison

Bundle Size (gzip) Visualization
Main 31.21KB See Treemap 📊
PR (fc0daae) 68.07KB See Treemap 📊
Diff ↑36.85KB

…v4 exports

Co-authored-by: Julius Marminge <juliusmarminge@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants