Skip to content

Conversation

pcasaretto
Copy link

@pcasaretto pcasaretto commented Aug 17, 2025

Hi there!

I've been learning more about Effect TS and coded this with Claude.
I thought it might be useful so I decided to open a PR.

Summary

Add comprehensive UDP socket support to the Effect platform with proper platform-agnostic design and implementations for Node.js and Bun.

Key Changes

  • Platform-agnostic UDP interface (@effect/platform/UdpSocket)

    • Message-oriented UdpMessage with sender address information
    • Direct send(data, address) and run(handler) methods matching UDP semantics
    • Non-Effect address property for symmetry with other platform interfaces
  • Node.js implementation (@effect/platform-node/NodeUdpSocket)

    • Full UDP socket support using Node.js dgram module
    • Proper resource management with acquireRelease pattern
    • Comprehensive error handling and graceful cleanup
  • Bun implementation (@effect/platform-bun/BunUdpSocket)

    • Leverages Bun's Node.js API compatibility for dgram
    • Same interface as Node.js implementation
  • Comprehensive test coverage

    • Socket creation, message sending/receiving
    • Error handling for invalid addresses
    • Resource cleanup and double-close scenarios

Design Journey & Architecture Decisions

Initial Approach: SocketServer Integration

Initially attempted to make UDP compatible with the existing SocketServer interface by:

  • Adding UdpAddress to the Address union type
  • Creating virtual Socket instances for each UDP message
  • Treating each datagram as a "connection"

Why this felt wrong:

  • Semantic mismatch: UDP is connectionless, but SocketServer assumes persistent connections
  • Virtual connections: Creating Socket instances per message violated UDP's stateless nature
  • RPC incompatibility: Would break RPC protocols expecting persistent, bidirectional streams
  • Forced abstraction: Round peg, square hole - UDP semantics don't match TCP patterns

Final Approach: UDP-Specific Interface

Reverted to a dedicated UDP interface that properly represents UDP semantics:

  • Message-oriented: UdpMessage with discrete datagrams
  • Addressing explicit: Each send() specifies destination
  • Stateless: No connection lifecycle management
  • Platform-agnostic: Same interface works across Node.js, Bun, etc.

Platform Support Matrix

Platform TCP Socket UDP Socket Notes
platform ✅ Interface ✅ Interface Platform-agnostic definitions
platform-node ✅ NodeSocket ✅ NodeUdpSocket Full raw socket support
platform-bun ✅ BunSocket ✅ BunUdpSocket Node.js compatible dgram
platform-browser ✅ BrowserSocket (WebSockets) ❌ Not supported Security limitations

Usage Example

// Platform-agnostic server
const createEchoServer = Effect.gen(function*() {
  const socket = yield* UdpSocket.UdpSocket
  
  yield* socket.run((message) => Effect.gen(function*() {
    const response = `Echo: ${new TextDecoder().decode(message.data)}`
    yield* socket.send(
      new TextEncoder().encode(response), 
      message.remoteAddress
    )
  }))
})

// Node.js
const nodeProgram = createEchoServer.pipe(
  Effect.provide(NodeUdpSocket.layer(address))
)

// Bun  
const bunProgram = createEchoServer.pipe(
  Effect.provide(BunUdpSocket.layer(address))
)

This implementation demonstrates Effect's platform-agnostic design principles while respecting the fundamental differences between TCP and UDP protocols.

🤖 Generated with Claude Code

Co-Authored-By: Claude [email protected]

@github-project-automation github-project-automation bot moved this to Discussion Ongoing in PR Backlog Aug 17, 2025
Copy link

changeset-bot bot commented Aug 17, 2025

🦋 Changeset detected

Latest commit: 2224431

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

This PR includes changesets to release 30 packages
Name Type
@effect/platform-node Major
@effect/platform Minor
@effect/cli Major
@effect/cluster Major
@effect/experimental Major
@effect/sql-clickhouse Major
@effect/sql-drizzle Major
@effect/sql-mssql Major
@effect/sql-sqlite-node Major
@effect/ai-amazon-bedrock Major
@effect/ai-anthropic Major
@effect/ai-google Major
@effect/ai-openai Major
@effect/opentelemetry Major
@effect/platform-browser Major
@effect/platform-bun Major
@effect/platform-node-shared Major
@effect/rpc Major
@effect/sql-d1 Major
@effect/sql-libsql Major
@effect/sql-mysql2 Major
@effect/sql-pg Major
@effect/sql-sqlite-bun Major
@effect/sql Major
@effect/workflow Major
@effect/ai Major
@effect/sql-sqlite-do Major
@effect/sql-sqlite-react-native Major
@effect/sql-sqlite-wasm Major
@effect/sql-kysely Major

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

@pcasaretto pcasaretto marked this pull request as ready for review August 17, 2025 20:12
@pcasaretto pcasaretto requested a review from tim-smart as a code owner August 17, 2025 20:12
@effect-bot effect-bot force-pushed the next-minor branch 22 times, most recently from da6357b to c9516d7 Compare August 24, 2025 08:25
@effect-bot effect-bot force-pushed the next-minor branch 12 times, most recently from b64c6b1 to 5306ee2 Compare September 17, 2025 01:46
@effect-bot effect-bot force-pushed the next-minor branch 13 times, most recently from 6016a0d to 28c8bb7 Compare October 6, 2025 22:02
@effect-bot effect-bot force-pushed the next-minor branch 4 times, most recently from 4489360 to 84b4b65 Compare October 14, 2025 00:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Discussion Ongoing

Development

Successfully merging this pull request may close these issues.

2 participants