Skip to content

Conversation

@akapug
Copy link
Member

@akapug akapug commented Nov 26, 2025

Draft Powered by Pull Request Badge

Summary

Adds the FetchBody interface implementing the Fetch API Body mixin, which provides methods for reading request/response bodies:

  • arrayBuffer() - Read body as ArrayBuffer
  • blob() - Read body as Blob
  • formData() - Read body as FormData
  • json() - Read body as parsed JSON
  • text() - Read body as string

FetchRequest now extends FetchBody to inherit these methods.

⚠️ Draft Status - Need Guidance

This PR is a draft - the implementation is stubbed with TODOs. I need guidance on:

  1. ReadableStream consumption - How to properly consume the body stream and collect bytes
  2. JSON.parse integration - How to invoke the GraalJS JSON.parse from Kotlin
  3. ArrayBuffer intrinsic - How to create ArrayBuffer instances for guest code
  4. Blob intrinsic - How to create Blob instances

@darvld - Would appreciate your guidance on the best approach for these integrations. Happy to implement once I understand the patterns.

Why This Matters

While fetch() works for outbound requests, server-side code receiving requests via the fetch handler pattern needs to parse incoming request bodies:

export default {
  async fetch(request: Request): Promise<Response> {
    // ❌ Currently throws "Unknown identifier: json"
    const body = await request.json();
    
    // Workaround requires manual stream reading + JSON parsing
    // which is error-prone and inconsistent with Fetch API spec
  }
}

This is blocking real-world server applications in elide-showcases.

Changes

  • New: FetchBody.kt - Interface defining Body mixin methods with full JSDoc
  • Modified: FetchRequest.kt - Now extends FetchBody
  • Modified: FetchRequestIntrinsic.kt - Stub implementations with TODOs

Related

Adds the FetchBody interface implementing the Fetch API Body mixin,
which provides methods for reading request/response bodies:
- arrayBuffer() - Read body as ArrayBuffer
- blob() - Read body as Blob
- formData() - Read body as FormData
- json() - Read body as parsed JSON
- text() - Read body as string

FetchRequest now extends FetchBody to inherit these methods.

Implementation is partial - methods are stubbed with TODOs for @darvld
to review and complete the integration with:
- ReadableStream consumption
- JSON.parse intrinsic
- ArrayBuffer intrinsic
- Blob intrinsic

Why this matters:
While fetch() works for outbound requests, server-side code receiving
requests via the fetch handler pattern needs to parse incoming request
bodies. Without request.json(), developers must manually read the body
stream and parse JSON, which is error-prone and inconsistent with the
Fetch API spec.

Refs: Discovered while testing elide-showcases server examples
@akapug akapug requested a review from darvld November 26, 2025 22:10
Implements the Body mixin methods using ReadableStreamDefaultReader
pattern from JsWorkerStreams (thanks @darvld for the pointer!).

- text(): Consumes stream, decodes as UTF-8
- json(): Uses text() then GraalJS JSON.parse
- arrayBuffer(): Consumes stream, returns byte array
- blob()/formData(): Still not implemented (need intrinsics)

Removes TODO(@darvld) comments - no more assigning tasks to humans!
@akapug
Copy link
Member Author

akapug commented Nov 27, 2025

Thanks for the pointer to JsWorkerStreams @darvld! I've implemented the body methods using the ReadableStreamDefaultReader pattern.

Changes pushed to cascade/fetch-body-mixin:

  • text() - Consumes stream, decodes as UTF-8
  • json() - Uses text() then GraalJS JSON.parse
  • arrayBuffer() - Consumes stream, returns byte array
  • blob() - Still needs Blob intrinsic
  • formData() - Still needs multipart parsing

Also removed the TODO(@darvld) comments - no more assigning tasks to humans! 😅

The code compiles successfully. Would you like me to open a new PR with these changes, or should I try to push to this branch?

- Make Blob interface extend BufferAPI.Blob for compatibility
- Make NodeBlob implement the unified Blob interface
- Implement blob() using arrayBuffer() + NodeBlob wrapper
- Content-Type header is preserved in the Blob

formData() still pending - requires multipart parsing implementation.
- arrayBuffer(): Returns NIO ByteBuffer (maps to JS ArrayBuffer)
- blob(): Uses NodeBlob with content-type from headers
- formData(): Supports application/x-www-form-urlencoded
  (multipart/form-data still pending)
- Added FormData class with standard Web API methods

Thanks @darvld for the ByteBuffer hint!
@akapug
Copy link
Member Author

akapug commented Nov 27, 2025

Update: Body mixin implementation complete

All TODO(@darvld) comments have been removed from the PR files. Here's the final status:

Implemented Methods

Method Status Details
text() ✅ Complete Consumes stream, decodes UTF-8
json() ✅ Complete Uses text() + GraalJS JSON.parse
arrayBuffer() ✅ Complete Returns ByteBuffer (maps to JS ArrayBuffer)
blob() ✅ Complete Returns NodeBlob with content-type header
formData() ⚠️ Partial See below

formData() Details

Supported:

  • application/x-www-form-urlencoded - Standard form submissions (key=value&key=value)

Not yet supported:

  • multipart/form-data - File uploads and complex forms (throws NotImplementedError)

Multipart parsing is significantly more complex as it requires:

  1. Parsing boundary from Content-Type header
  2. Splitting body by boundary markers
  3. Parsing headers for each part (Content-Disposition, Content-Type)
  4. Handling file parts vs text parts
  5. Potentially creating File objects for uploaded files

This can be added in a follow-up PR if needed.

Files Changed

  • FetchRequestIntrinsic.kt - Request body methods
  • JsServerRequestExecutionInputs.kt - Server request body methods
  • Blob.kt - Made interface extend BufferAPI.Blob
  • NodeBlob.kt - Implements unified Blob interface
  • FormData.kt - New class with Web API methods

Branch: cascade/fetch-body-mixin

- Added multipart/form-data parsing with boundary detection
- Added FormDataFile class for file uploads (implements Blob)
- Parses Content-Disposition headers for field names and filenames
- Handles both text fields and binary file content
- URL-encoded form data still supported as fallback

All Body mixin methods now fully implemented:
- text() ✅
- json() ✅
- arrayBuffer() ✅
- blob() ✅
- formData() ✅
@akapug
Copy link
Member Author

akapug commented Nov 27, 2025

Update: All Body mixin methods now complete! 🎉

Method Status Implementation
text() Stream → UTF-8 string
json() text() → GraalJS JSON.parse
arrayBuffer() Stream → ByteBuffer (NIO)
blob() arrayBuffer()NodeBlob
formData() Full support (see below)

formData() Implementation

URL-encoded (application/x-www-form-urlencoded):

  • Standard key=value&key=value parsing
  • URL decoding via java.net.URLDecoder

Multipart (multipart/form-data):

  • Boundary extraction from Content-Type header
  • Part splitting by boundary markers
  • Header parsing (Content-Disposition, Content-Type)
  • Text fields → String values
  • File fields → FormDataFile objects (implements Blob)

New Classes

  • FormData - Web API FormData with append, get, getAll, has, set, delete, keys, values, entries
  • FormDataFile - File implementation with name, size, type, lastModified, arrayBuffer(), text(), stream(), slice()

Branch: cascade/fetch-body-mixin

@akapug akapug marked this pull request as ready for review November 27, 2025 23:55
@akapug akapug requested a review from sgammon as a code owner November 27, 2025 23:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants