Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor server to run in all (Node.js compatible) runtimes and meta frameworks #710

Open
wants to merge 7 commits into
base: main
Choose a base branch
from

Conversation

Murderlon
Copy link
Member

@Murderlon Murderlon commented Feb 4, 2025

Closes #461

Refactor the request handlers and hooks to be based on Request (from the fetch API) instead of http.IncomingMessage/http.ServerResponse (which are Node.js specific) as we can turn the Node.js ones into Request/Response but not the other way around. That means we can have a handle(req, res) method for Node.js servers and a handleWeb(req) for other runtimes and meta-frameworks (such as Next.js React Router, SvelteKit, etc) which both use the same core logic.

Changes

  • New getRequest/setResponse utils (in web.ts) to turn http.IncomingMessage/http.ServerResponse into Request/Response
  • Change the handle(req, res) function to use the new utils and pass a Request to a new handler() method
  • The handler() is the new shared request handler
  • Update all handlers to work with Request
  • Introduce handleWeb(req: Request) for runtimes/frameworks based on the fetch api
  • Make BaseHandler.extractHostAndProto() static so we can use it too in handle() as we must construct a valid url to create a Request object.
  • Do not change any of the store implementations. Instead we use the Node.js Readable.fromWeb(req.body) to keep all that logic exactly the same.
  • Breaking change: hooks now get the new Request/Response
  • Breaking change: events now get the new Request/Response
  • Remove addPipableStreamBody test util. It seems no longer needed.
  • Documentation updates

Notes for reviewers

  • I created a Next.js app router project and confirmed handleWeb() works in the app router.
  • Previously we used Node.js req/res lifecycle events (such as finish, close) to cancel the context. We don't have that for Request/Response so we can't easily do that. Now we cancel the context after the handler is done but before the response is send to the client. We don't abort when the request is aborted (potentially problematic) yet no tests are failing.
    • We could pass context to the reader inside getRequest() and cancel context based on the Node.js events there but that means we only deal with that case for handle() and not handleWeb()?

return new ReadableStream({
start(controller) {
req.on('error', (error) => {
cancelled = true
controller.error(error)
})
req.on('end', () => {
if (cancelled) return
controller.close()
})
req.on('data', (chunk) => {
if (cancelled) return
controller.enqueue(chunk)
if (controller.desiredSize === null || controller.desiredSize <= 0) {
req.pause()
}
})
},
pull() {
req.resume()
},
cancel(reason) {
cancelled = true
req.destroy(reason)
},
})
}

  • This requires a new major version for @tus/server but not necessarily for all stores (unless we want to align versions). More breaking changes would be done in other PRs, such as increasing the required Node.js version and removing more deprecated things.

@Murderlon Murderlon self-assigned this Feb 4, 2025
Copy link

changeset-bot bot commented Feb 4, 2025

🦋 Changeset detected

Latest commit: 82e21d9

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

This PR includes changesets to release 1 package
Name Type
@tus/server 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

Copy link

socket-security bot commented Feb 4, 2025

New dependencies detected. Learn more about Socket for GitHub ↗︎

Package New capabilities Transitives Size Publisher
npm/@types/[email protected] None 0 6.99 kB types
npm/[email protected] None 0 16.1 kB nfriedly

View full report↗︎

This comment was marked as resolved.

@Murderlon Murderlon marked this pull request as ready for review February 6, 2025 11:34
@Murderlon Murderlon requested review from Acconut, mitjap and fenos February 6, 2025 11:34
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🔭 Outside diff range comments (3)
packages/server/test/GetHandler.test.ts (1)

1-252: Overall implementation successfully transitions to Web API types.

The test suite has been successfully modernized to use Web API's Request/Response types and CancellationContext, aligning perfectly with the PR's objective of making the server runtime-agnostic. However, there's one area that needs attention:

  1. The skipped test for invalid streams should be addressed to maintain comprehensive test coverage.
packages/server/src/handlers/BaseHandler.ts (2)

179-184: Correct the misuse of stream.pipeline in writeToStore method

The use of stream.pipeline in the writeToStore method is incorrect. The stream.pipeline function expects the last argument to be a destination stream or an array of streams, but an async function is provided instead. This can lead to unexpected behavior or runtime errors.

Apply this diff to fix the usage of stream.pipeline:

- stream
-   .pipeline(data.pipe(proxy), new StreamLimiter(maxFileSize), async (stream) => {
-     return this.store.write(stream as StreamLimiter, upload.id, upload.offset)
-   })
-   .then(resolve)
-   .catch(reject)
-   .finally(() => {
-     context.signal.removeEventListener('abort', onAbort)
-   })
+ try {
+   await stream.pipeline(
+     data.pipe(proxy),
+     new StreamLimiter(maxFileSize),
+     this.store.writeStream(upload.id, upload.offset)
+   )
+   resolve()
+ } catch (err) {
+   reject(err)
+ } finally {
+   context.signal.removeEventListener('abort', onAbort)
+ }

Alternatively, consider refactoring this.store.write to return a writable stream that can be used directly in the pipeline.


110-127: Ensure option callbacks accept Request objects

The methods getLocker, acquireLock, getConfiguredMaxSize, and calculateMaxBodySize have been updated to accept Request objects instead of http.IncomingMessage. Make sure that any custom functions provided in this.options (such as locker, maxSize, etc.) are compatible with the Request object to prevent runtime errors.

Also applies to: 191-196, 203-240

🧹 Nitpick comments (15)
packages/server/test/OptionsHandler.test.ts (2)

28-33: Consider adding tests for abort functionality.

The CancellationContext is set up but its functionality isn't tested. Consider adding test cases to verify the abort behavior.

Example test case:

it('should handle aborted requests', async () => {
  const promise = handler.send(req, context)
  context.abort()
  await assert.rejects(promise, /aborted/)
})

37-66: Consider adding more comprehensive test cases.

While the current tests cover the basic functionality, consider adding:

  1. Error cases (e.g., invalid paths)
  2. Content-Type header verification
  3. Edge cases for extensions

Example additional test:

it('should handle invalid paths', async () => {
  const invalidReq = new Request('https://example.com/invalid', {
    method: 'OPTIONS'
  })
  const res = await handler.send(invalidReq, context)
  assert.equal(res.status, 404)
})
packages/server/test/PatchHandler.test.ts (4)

24-28: Consider extracting common request creation logic into a helper function.

The request creation logic is repeated across multiple tests. Consider creating a helper function to improve maintainability and reduce duplication.

+ function createTestRequest(path: string, options: {
+   method?: string,
+   headers?: Headers,
+   body?: BodyInit | null,
+ } = {}) {
+   return new Request(`https://example.com${path}`, {
+     method: options.method ?? 'PATCH',
+     headers: options.headers ?? new Headers(),
+     duplex: 'half',
+     body: options.body,
+   })
+ }

- req = new Request(`https://example.com${path}/1234`, {
-   method: 'PATCH',
-   headers: new Headers(),
-   duplex: 'half',
- })
+ req = createTestRequest(`${path}/1234`)

185-189: Add more comprehensive response assertions.

The test verifies basic response properties but could benefit from additional assertions to ensure the complete Response object behavior.

  const res = await handler.send(req, context)

  assert.equal(res.headers.get('upload-offset'), '10')
  assert.equal(res.headers.has('Content-Length'), false)
  assert.equal(res.status, 204)
+ assert.equal(res.ok, true)
+ assert.equal(res.statusText, 'No Content')
+ assert.equal(res.body, null)

229-238: Consider using more structured error assertions.

The error object structure verification could be more comprehensive and type-safe.

  try {
    await handler.send(req, context)
    throw new Error('failed test')
  } catch (e) {
-   assert.equal('body' in e, true)
-   assert.equal('status_code' in e, true)
-   assert.equal(e.body, 'Maximum size exceeded\n')
-   assert.equal(e.status_code, 413)
+   type ExpectedError = {
+     body: string
+     status_code: number
+     name: string
+     message: string
+   }
+   assert.equal(Object.prototype.hasOwnProperty.call(e, 'body'), true)
+   assert.equal(Object.prototype.hasOwnProperty.call(e, 'status_code'), true)
+   const error = e as ExpectedError
+   assert.equal(error.body, 'Maximum size exceeded\n')
+   assert.equal(error.status_code, 413)
+   assert.equal(error.name, 'Error')
+   assert.equal(typeof error.message, 'string')
  }

274-331: Add edge cases for stream handling.

While the current test covers the basic streaming scenario, consider adding tests for:

  • Handling very large streams with memory constraints
  • Network interruptions during streaming
  • Malformed stream data

Example test for memory constraints:

it('should handle large streams without excessive memory usage', async () => {
  const handler = new PatchHandler(store, {path, locker: new MemoryLocker()})
  const bodyStream = new PassThrough()
  const req = new Request(`https://example.com${path}/1234`, {
    method: 'PATCH',
    headers: new Headers({
      'Content-Type': 'application/offset+octet-stream',
      'Upload-Offset': '0',
    }),
    duplex: 'half',
    body: bodyStream,
  })

  // Monitor memory usage
  const initialMemory = process.memoryUsage().heapUsed
  let peakMemory = initialMemory

  const interval = setInterval(() => {
    const currentMemory = process.memoryUsage().heapUsed
    peakMemory = Math.max(peakMemory, currentMemory)
  }, 100)

  try {
    // Write 1GB of data in 1MB chunks
    for (let i = 0; i < 1024; i++) {
      bodyStream.write(Buffer.alloc(1024 * 1024)) // 1MB
      await new Promise(resolve => setTimeout(resolve, 10)) // Allow time for processing
    }
    bodyStream.end()

    await handler.send(req, context)

    // Verify memory usage didn't grow linearly with data size
    const memoryGrowth = (peakMemory - initialMemory) / (1024 * 1024) // MB
    assert.ok(memoryGrowth < 100, `Memory growth (${memoryGrowth}MB) exceeded 100MB`)
  } finally {
    clearInterval(interval)
  }
})
packages/server/src/handlers/OptionsHandler.ts (1)

10-10: Use a constant for the 'Tus-Version' header

The 'Tus-Version' header is hardcoded as '1.0.0'. Consider defining a constant or importing the version from a centralized configuration or package metadata. This approach ensures consistency across the codebase and simplifies future updates to the protocol version.

packages/server/src/handlers/HeadHandler.ts (2)

56-56: Confirm necessity of type assertion on file.size

The type assertion (file.size as number).toString() assumes file.size is a number. If file.size is already typed as number, the assertion may be unnecessary. Verify the type of file.size and remove the assertion if it's redundant.


63-63: Check if type assertion on Metadata.stringify is needed

The Metadata.stringify(file.metadata) function should return a string. The type assertion as string might be redundant. Ensure that the return type is correctly inferred to simplify the code.

packages/server/src/web.ts (1)

25-29: Simplify handling of destroyed requests in get_raw_body

When the request is destroyed, the function returns a canceled ReadableStream. Consider simplifying this by returning null or handling it within the main logic, as creating a canceled stream may add unnecessary complexity without significant benefits.

packages/server/src/types.ts (1)

102-105: Consider using more specific response types.

The response types could be more specific to improve type safety:

  • Consider using a union type for status_code to restrict to valid HTTP status codes
  • Consider using a more specific type for headers to ensure valid HTTP header names
  ) => Promise<{
-   status_code?: number
+   status_code?: 200 | 201 | 204 | 400 | 404 | 409 | 412 | 413 | 415 | 460
-   headers?: Record<string, string | number>
+   headers?: {[K in HTTPHeaderName]?: string | number}
    body?: string
  }>

Also applies to: 117-124

packages/server/src/handlers/PostHandler.ts (3)

39-39: Consider making the headers parameter required.

The headers parameter has a default value but is used throughout the method. Making it required would better reflect its importance and usage.

-async send(req: Request, context: CancellationContext, headers = new Headers()) {
+async send(req: Request, context: CancellationContext, headers: Headers) {

60-66: Improve metadata error handling.

The current implementation swallows the original error, making debugging harder. Consider preserving the error details.

if (upload_metadata) {
  try {
    metadata = Metadata.parse(upload_metadata ?? undefined)
-  } catch {
-    throw ERRORS.INVALID_METADATA
+  } catch (error) {
+    throw {
+      ...ERRORS.INVALID_METADATA,
+      details: error instanceof Error ? error.message : 'Unknown parsing error'
+    }
  }
}

98-107: Enhance onUploadCreate hook response handling.

The hook's response handling could be more robust by checking if patch exists before accessing its properties.

if (this.options.onUploadCreate) {
  try {
    const patch = await this.options.onUploadCreate(req, upload)
-    if (patch.metadata) {
+    if (patch?.metadata) {
      upload.metadata = patch.metadata
    }
  } catch (error) {
    log(`onUploadCreate error: ${error.body}`)
    throw error
  }
}
packages/server/test/PostHandler.test.ts (1)

47-53: Add test cases for custom headers.

Consider adding test cases to verify custom headers passed to the send method are properly handled.

it('should handle custom headers', async () => {
  const handler = new PostHandler(store, options)
  const req = new Request(`https://example.com${options.path}`, {
    headers: {'upload-length': '512'}
  })
  const customHeaders = new Headers({'X-Custom': 'test'})
  const res = await handler.send(req, context, customHeaders)
  assert.equal(res.headers.get('X-Custom'), 'test')
})
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2da6a84 and cd5394c.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (25)
  • biome.json (1 hunks)
  • packages/server/package.json (1 hunks)
  • packages/server/src/handlers/BaseHandler.ts (7 hunks)
  • packages/server/src/handlers/DeleteHandler.ts (2 hunks)
  • packages/server/src/handlers/GetHandler.ts (4 hunks)
  • packages/server/src/handlers/HeadHandler.ts (2 hunks)
  • packages/server/src/handlers/OptionsHandler.ts (1 hunks)
  • packages/server/src/handlers/PatchHandler.ts (5 hunks)
  • packages/server/src/handlers/PostHandler.ts (6 hunks)
  • packages/server/src/server.ts (10 hunks)
  • packages/server/src/types.ts (6 hunks)
  • packages/server/src/validators/HeaderValidator.ts (1 hunks)
  • packages/server/src/web.ts (1 hunks)
  • packages/server/test/BaseHandler.test.ts (2 hunks)
  • packages/server/test/DeleteHandler.test.ts (5 hunks)
  • packages/server/test/GetHandler.test.ts (8 hunks)
  • packages/server/test/HeadHandler.test.ts (6 hunks)
  • packages/server/test/OptionsHandler.test.ts (2 hunks)
  • packages/server/test/PatchHandler.test.ts (5 hunks)
  • packages/server/test/PostHandler.test.ts (10 hunks)
  • packages/server/test/Server.test.ts (8 hunks)
  • packages/server/test/utils.ts (0 hunks)
  • packages/utils/src/models/DataStore.ts (2 hunks)
  • test/src/e2e.test.ts (1 hunks)
  • tsconfig.base.json (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/server/test/utils.ts
🔇 Additional comments (45)
packages/server/test/OptionsHandler.test.ts (2)

6-12: LGTM! Clean import organization.

The imports are well-organized and include the necessary types for the new request handling approach.


24-35: LGTM! Modern request handling setup.

The setup correctly uses the fetch API's Request object and includes proper cancellation support, aligning with the PR's objective to move away from Node.js-specific types.

packages/server/test/GetHandler.test.ts (5)

10-10: LGTM! Type declarations align with PR objectives.

The changes correctly transition from Node.js-specific HTTP types to Web API's Request type and introduce CancellationContext for request cancellation handling.

Also applies to: 17-18


21-27: LGTM! Test setup properly initializes Web API constructs.

The beforeEach hook correctly sets up:

  1. A Web API Request object
  2. An AbortController for cancellation
  3. A CancellationContext with proper signal and abort methods

71-82: Investigate and fix the skipped test case.

The test for invalid stream handling has been skipped. This could leave a gap in error handling coverage.

Could you clarify why this test was skipped? If it's still relevant, we should update it to work with the new Web API constructs rather than skipping it.


108-111: LGTM! Response assertions properly updated.

The test correctly validates the Web API Response object's status and headers.


235-236: LGTM! Path handler correctly returns Web API Response.

The registered path handler properly returns a Web API Response object, maintaining consistency with the new API.

packages/server/src/handlers/OptionsHandler.ts (2)

7-23: Refactored 'send' method enhances compatibility and code clarity

The updated send method now accepts a Request, CancellationContext, and Headers, aligning it with the Fetch API standards. This change improves compatibility across various runtimes and meta-frameworks, promoting a more universal handling mechanism. The use of the Headers object to set response headers enhances code modularity and readability.


7-7: Verify the necessity of the unused 'context' parameter

The context parameter in the send method is currently not utilized within the method body. Ensure that this parameter is required, possibly for interface compliance or future expansion. If it's not necessary, consider removing it to simplify the method signature and improve maintainability.

packages/server/src/handlers/HeadHandler.ts (6)

6-6: Method signature updated to use Fetch API's Request

The send method now accepts a Request object instead of Node.js-specific types. This enhances compatibility across different runtimes and meta-frameworks.


39-39: Response instantiated using Fetch API's Response object

The response is now created with new Response('', {status: 200, headers}), aligning with the Fetch API standards and improving cross-environment compatibility.


44-44: Headers set using Fetch API's Headers methods

Headers are now being set using res.headers.set(), which is appropriate for the Headers object in the Fetch API.


47-47: Verify file.offset is defined and correctly typed

Ensure that file.offset is always defined and of type number before calling .toString(). This prevents potential runtime errors if file.offset is undefined or not a number.

If necessary, add validation or default values to handle cases where file.offset might be undefined.


52-52: Set Upload-Defer-Length header appropriately

Setting the Upload-Defer-Length header to '1' when the upload length is deferred is correct and complies with the protocol specifications.


66-66: Return Response object to complete the request handling

Returning the Response object finalizes the request handling using the Fetch API approach, which is appropriate for modern runtimes.

packages/server/src/handlers/PatchHandler.ts (2)

14-14: Updated method signature aligns with modern Request handling

Changing the send method to accept Request, CancellationContext, and Headers enhances compatibility with various runtimes and aligns with the refactoring objectives.


99-104: Ensure Node.js version compatibility for Readable.fromWeb usage

The Readable.fromWeb(req.body) method requires Node.js 17.0.0 or higher. Please verify that the project's minimum Node.js version supports this feature, or implement a fallback for older versions.

packages/server/src/handlers/DeleteHandler.ts (2)

5-5: Confirm the necessity of default parameter for headers

The send method now includes a default parameter headers = new Headers(). Verify if providing a default Headers instance is necessary or if it could lead to unintended side effects. If headers should always be provided by the caller, consider removing the default to enforce explicit passing.


28-28: Verify the write method returns the correct response type

The write method is called and its result assigned to writtenRes. Ensure that this.write correctly returns a Response object compatible with the expected usage in the rest of the method, especially when emitting events and returning the response.

packages/server/test/BaseHandler.test.ts (6)

22-26: Update tests to reflect changes in write method

The test for write() now captures the returned Response object and checks res.status. This aligns the test with the updated implementation of write. The use of assert.equal(res.status, 200) correctly verifies the status code.


31-33: Adjust header assertions to use Response object

The test now uses res.headers.get(header) to assert header values, reflecting the change from a mock response to the native Response object. This ensures that header checks are accurately performed.


36-40: Convert test to async function for body content verification

The test for writing the body has been updated to an async function to await res.text(). This change is appropriate for handling asynchronous operations when reading the response body.


43-46: Use Fetch API's Request in tests for URL parsing

Replacing mock requests with the native Request object in tests ensures consistency with the updated handler methods. The test correctly verifies that getFileIdFromRequest extracts the file ID from the URL.


64-72: Test custom URL generation with updated request object

The test confirms that a custom generateUrl function produces the expected URL when provided with a Request object. The assertion verifies that query parameters are correctly appended.


78-85: Validate custom file ID extraction logic

The test checks that a custom getFileIdFromRequest function correctly extracts and modifies the file ID from the request URL. Using the native Request object ensures that the URL parsing aligns with the actual request handling.

packages/server/src/web.ts (2)

6-65: Review correctness of get_raw_body implementation

The get_raw_body function handles parsing the raw request body from an http.IncomingMessage. Ensure that it correctly manages edge cases, such as missing content types, zero content lengths, and destroyed requests. The use of a ReadableStream and proper event handling appears appropriate.


101-172: Ensure robust error handling in setResponse

The setResponse function includes complex logic for setting headers and streaming the response body, with careful error and cancellation handling. Verify that all potential edge cases and exceptions are properly managed to prevent issues such as unhandled promise rejections or partially sent responses.

packages/server/src/server.ts (2)

130-134: Verify the compatibility of Headers constructor with req.headers

In the handle method, req.headers from http.IncomingMessage is being passed directly to new Headers(req.headers). The Headers constructor may not accept plain objects with string values in all environments. This can lead to issues in header manipulation.

Please ensure that req.headers is properly converted into an array of [name, value] pairs or use an appropriate method to create a Headers instance. You can verify compatibility by reviewing the Node.js and Fetch API documentation.


297-299: Reconsider the removal of the req.on('close') listener

Commenting out the req.on('close', ...) event listener may affect how the server handles aborted requests. This listener is important for cleaning up resources when a client disconnects unexpectedly.

Assess whether the removal impacts request cancellation handling and resource management. If necessary, reintroduce the listener or implement an alternative mechanism to handle request closures.

packages/server/test/DeleteHandler.test.ts (1)

15-15: LGTM! Clean transition to Fetch API.

The changes correctly update the request object type and initialization to use the modern Fetch API, which aligns with the PR's objective of runtime compatibility.

Also applies to: 25-25

packages/server/test/HeadHandler.test.ts (1)

11-11: LGTM! Good URL handling practice.

The addition of a url constant and proper Request initialization improves code clarity and maintainability.

Also applies to: 18-18, 22-24

packages/server/src/validators/HeaderValidator.ts (1)

92-92: LGTM! Type alignment with Fetch API.

The parameter type change from string | undefined to string | null correctly aligns with the Fetch API's Headers.get() return type.

packages/server/src/types.ts (2)

15-15: LGTM! Clean type updates for request handlers.

The type changes correctly modernize the request handling interfaces while maintaining type safety.

Also applies to: 54-56, 62-62, 71-74, 80-80


150-150: LGTM! Clear RouteHandler type definition.

The RouteHandler type is correctly updated to use Request and Response types from the Fetch API.

packages/server/test/Server.test.ts (1)

254-265: Re-enable the HTTP method override test.

This test is currently skipped. Since HTTP method override is an important feature, consider updating and re-enabling it to use the new Request/Response types.

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Search for other tests that might depend on HTTP method override
rg -l "x-http-method-override" .

Length of output: 70


Re-enable the HTTP method override test
The only occurrence of "x-http-method-override" is in packages/server/test/Server.test.ts, confirming that this test is the sole reference. Please remove the .skip (and address the accompanying @ts-expect-error by updating to the new Request/Response types) so that HTTP method overriding is properly validated throughout the codebase.

test/src/e2e.test.ts (1)

1132-1132: Verify the change in locking behavior.

The assertion for a 400 status code has been commented out. Please confirm if this represents an intentional change in how locks are handled when a request is cancelled.

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Search for related lock handling code
rg "acquireLock|releaseLock" .

Length of output: 1252


Locking behavior change appears intentional.
The search results show that the core locking functions (e.g. acquireLock/releaseLock) remain unaltered across the repository. There is no new logic indicating a change in cancellation handling (such as returning a 400 error) in the locker implementation or its usage in request handlers. It seems that the commented-out assertion was deliberately removed to reflect updated test expectations rather than a modification in the underlying locking behavior.

tsconfig.base.json (1)

12-13: Configuration Option Trade-off: "skipLibCheck"
The addition of "skipLibCheck": true can significantly improve build performance by skipping type checks in declaration files. Please ensure that this trade-off is acceptable for your project since it may mask type issues in third-party libraries.

biome.json (2)

7-7: Expanded Ignore Patterns in Files
The updated "ignore": [".git", "node_modules", "./**/dist/**/*"] ensures that common folders not relevant to source code are excluded from processing. This update is standard and should help avoid unnecessary file processing.


13-17: Linter Rule Adjustments for Style and Performance
The changes turning "noParameterAssign": "off" (under the "style" section) and "noDelete": "off" (under the "performance" section) indicate a conscious decision to relax these rules. Please verify that allowing parameter mutation and object property deletion won’t lead to unintended side effects or maintenance challenges in your codebase.

packages/server/package.json (2)

24-25: New Dependency: set-cookie-parser
The dependency "set-cookie-parser": "^2.7.1" has been added to support improved cookie handling with the fetch API’s response processing. Please confirm that this version has been evaluated for compatibility and security within your project’s context.


32-32: Type Definitions for set-cookie-parser
Adding "@types/set-cookie-parser": "^2.4.10" to devDependencies is a necessary adjunct to ensure proper TypeScript type checking for the new package. This change is appropriate for maintaining type safety in your codebase.

packages/server/src/handlers/GetHandler.ts (4)

61-64: Verify that all calls to send are updated to the new signature.

The send method now accepts a Request, CancellationContext, and an optional Headers object instead of the Node.js-specific IncomingMessage and ServerResponse. Please ensure that all invocations of send throughout the codebase are updated to match this new signature to prevent type errors or runtime issues.


68-69: Confirm that all route handlers return a Response object.

The handler functions stored in this.paths are now expected to return a Response object compliant with the Fetch API. Please verify that all registered route handlers have been updated accordingly to ensure proper functionality.


93-93: Verify that acquireLock method supports Request and CancellationContext.

The acquireLock method is now being called with req: Request, id, and context: CancellationContext. Ensure that the acquireLock method has been updated to accept these parameters and that it handles the Request object correctly.


100-100: ⚠️ Potential issue

Ensure compatibility of file_stream with Fetch API Response.

The Response constructor expects the body to be a web ReadableStream, but file_stream is likely a Node.js Readable stream, which is not directly compatible. To prevent runtime issues, convert the Node.js stream to a web ReadableStream using Readable.toWeb() from the stream module:

- return new Response(file_stream, {headers, status: 200})
+ import { Readable } from 'stream'
+ const webStream = Readable.toWeb(file_stream)
+ return new Response(webStream, {headers, status: 200})

Note that Readable.toWeb() is available from Node.js v17.0.0 onwards. If you're targeting earlier versions, consider using a polyfill or a library like web-streams-polyfill to perform the conversion.

To check if Readable.toWeb() is available in your Node.js environment, you can run the following command:

Alternatively, you can search for methods to convert a Node.js Readable stream to a web ReadableStream:

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Check Node.js version
node -v

Length of output: 17


🌐 Web query:

How to convert a Node.js Readable stream to a web ReadableStream for use with Fetch API Response?

💡 Result:

To convert a Node.js `Readable` stream to a web `ReadableStream` for use with the Fetch API `Response`, there are two primary approaches:

---

### 1. Using `readable-stream-node-to-web` Package  
Install via npm:  
```bash
npm install readable-stream-node-to-web
```  

**Implementation**:  
```javascript
const nodeToWebStream = require('readable-stream-node-to-web');
const nodeStream = getNodeReadableStream(); // Your Node.js stream source

// Convert to web ReadableStream
const webStream = nodeToWebStream(nodeStream);

// Use with Fetch API Response
const response = new Response(webStream, { headers: { 'Content-Type': 'text/plain' } });
```  
- **Key Features**:  
  - Works in browsers via bundlers like Webpack/Browserify[1].  
  - ES5-compatible syntax[1].  

---

### 2. Using Native Node.js `stream/web` APIs (v16.5+)  
Node.js natively supports Web Streams via `node:stream/web`:  
```javascript
const { Readable } = require('node:stream');
const { ReadableStream } = require('node:stream/web');

// Convert using Readable.toWeb()
const nodeStream = Readable.from(['Hello', 'World']); // Example source
const webStream = Readable.toWeb(nodeStream);

// Example usage with Fetch API
const response = new Response(webStream);
```  

**Key Differences**:  
| Method                     | Compatibility       | Dependencies | Stream Types Supported          |  
|----------------------------|---------------------|--------------|---------------------------------|  
| `readable-stream-node-to-web` | Older browsers/Node | External     | Classic/Node-specific streams   |  
| Native `Readable.toWeb()`  | Node.js ≥16.5       | None         | Modern Web/Node hybrid streams |  

---

### Common Use Case with Fetch API  
After conversion, use the web `ReadableStream` directly:  
```javascript
// Example: Stream data via Fetch response
fetch('https://example.com')
  .then((response) => {
    const nodeReadable = response.body.pipeThrough(transformStream);
    const webStream = Readable.toWeb(nodeReadable); // or use package
    return new Response(webStream);
  });
```  

For backward compatibility or browser environments, use the package method[1][4]. For modern Node.js applications, prefer native methods[3][9].

Citations:

- 1: https://github.com/xuset/readable-stream-node-to-web
- 2: https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream
- 3: https://www.dennisokeeffe.com/blog/2024-07-08-readable-streams-in-nodejs
- 4: https://2ality.com/2022/06/web-streams-nodejs.html
- 5: https://developer.mozilla.org/en-US/docs/Web/API/Streams_API/Using_readable_streams
- 6: https://css-tricks.com/web-streams-everywhere-and-fetch-for-node-js/
- 7: https://betterstack.com/community/guides/scaling-nodejs/nodejs-streams-vs-web-streams-api/
- 8: https://www.loginradius.com/blog/engineering/guest-post/http-streaming-with-nodejs-and-fetch-api/
- 9: https://nodejs.org/api/webstreams.html
- 10: https://github.com/node-fetch/node-fetch/issues/754

Stream Conversion Required for Compatibility

The Node.js version is v22.9.0, which supports the native conversion using Readable.toWeb(). The web query confirms that converting a Node.js Readable stream to a web ReadableStream with Readable.toWeb() is the recommended approach for use with the Fetch API's Response constructor. Therefore, the suggested code change is valid.

  • File: packages/server/src/handlers/GetHandler.ts
  • Modification: Convert file_stream using Readable.toWeb(file_stream) before passing it into the Response constructor.

@@ -1129,7 +1129,7 @@ describe('EndToEnd', () => {
const [res1, res2] = await Promise.allSettled([req1, req2])
assert.equal(res1.status, 'fulfilled')
assert.equal(res2.status, 'fulfilled')
assert.equal(res1.value.statusCode, 400)
// assert.equal(res1.value.statusCode, 400)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an important e2e locking test. The test with all its assertions still works as expected except that the first request now succeeds with 200 instead of 400. This might be blocking for the merge? 🤔

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels odd, yes, but I don't think it will cause major problems in production.

@Murderlon Murderlon mentioned this pull request Feb 6, 2025
2 tasks
Copy link
Member

@Acconut Acconut left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, that is an incredible work! I left a few minor comments, but I am not sure I fully understood the goal of this PR. It removes the dependency on Node's http module for the main handlers, but adds a dependency on Node's stream module, so the handler can still only be used in Node-compatible runtimes (as the PR title mentions).

What exactly do we gain here now? After this PR, tus-node-server can still only be used in Node-compatible runtimes. Was this not the case before?

Or is this just a stepping stone for future PRs to make the handler completely independent from Node specifics?

@@ -252,7 +251,7 @@ describe('Server', () => {
request(listener).get('/').set('Tus-Resumable', TUS_RESUMABLE).expect(404, {}, done)
})

it('should allow overriding the HTTP method', (done) => {
it.skip('should allow overriding the HTTP method', (done) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this still be implemented?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed HTTP method overriding as it seems very outdated, the reasoning from the code commend being when used in Flash. Do you think we should keep it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tus-java-client might still use method overriding when the used Java SDK does not support the PATCH method. However, I cannot tell you how common this case is right now.

@@ -1129,7 +1129,7 @@ describe('EndToEnd', () => {
const [res1, res2] = await Promise.allSettled([req1, req2])
assert.equal(res1.status, 'fulfilled')
assert.equal(res2.status, 'fulfilled')
assert.equal(res1.value.statusCode, 400)
// assert.equal(res1.value.statusCode, 400)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels odd, yes, but I don't think it will cause major problems in production.

@@ -0,0 +1,176 @@
import type http from 'node:http'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit confused by the name of this file. Its content seems to be about interactions between Node HTTP classes and Fetch API classes. Maybe this can be clarified through a better name?

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (5)
packages/server/src/web.ts (3)

1-1: Consider renaming the file to better reflect its purpose.

The current filename web.ts doesn't clearly indicate that this file contains utilities for converting between Node.js HTTP types and Web API types. Consider renaming it to something more descriptive like http-adapters.ts or request-response-adapters.ts.


6-65: Add JSDoc documentation for the getRawBody function.

This function handles several complex edge cases and stream management scenarios. Adding comprehensive JSDoc documentation would help future maintainers understand:

  • The purpose and return type of the function
  • The handling of different HTTP versions
  • The stream backpressure mechanism
  • The cancellation behavior

Add documentation like this:

+/**
+ * Extracts the raw body from an incoming HTTP request.
+ * 
+ * @param req - The Node.js HTTP incoming message
+ * @returns A ReadableStream of the request body, or null if:
+ *   - Content-Type header is missing
+ *   - Content-Length is 0
+ *   - HTTP/1.1 request with no Content-Length and no Transfer-Encoding
+ */
 function getRawBody(req: http.IncomingMessage) {

164-167: Improve error handling in the stream piping logic.

The catch block could be more specific about the types of errors it handles. Consider categorizing errors and providing different handling strategies based on the error type.

-    } catch (error) {
-      cancel(error instanceof Error ? error : new Error(String(error)))
+    } catch (error) {
+      if (error instanceof TypeError) {
+        // Handle type errors (e.g., stream already closed)
+        cancel(new Error(`Stream error: ${error.message}`))
+      } else if (error instanceof Error) {
+        // Handle known errors
+        cancel(error)
+      } else {
+        // Handle unknown errors
+        cancel(new Error(String(error)))
+      }
packages/server/src/server.ts (2)

31-33: Consider removing the deprecated event.

The POST_RECEIVE event is marked as deprecated in favor of POST_RECEIVE_V2. Since this appears to be a major refactoring, it might be a good time to remove the deprecated event.


208-217: Consider extracting CORS header logic to a separate method.

The CORS header setting logic could be extracted to improve readability and maintainability.

+  private setCorsHeaders(headers: Headers, origin: string | null) {
+    headers.set('Access-Control-Allow-Origin', this.getCorsOrigin(origin))
+    headers.set('Access-Control-Expose-Headers', EXPOSED_HEADERS)
+    if (this.options.allowedCredentials === true) {
+      headers.set('Access-Control-Allow-Credentials', 'true')
+    }
+  }

Then use it in the handler:

-    headers.set(
-      'Access-Control-Allow-Origin',
-      this.getCorsOrigin(req.headers.get('origin'))
-    )
-    headers.set('Access-Control-Expose-Headers', EXPOSED_HEADERS)
-    if (this.options.allowedCredentials === true) {
-      headers.set('Access-Control-Allow-Credentials', 'true')
-    }
+    this.setCorsHeaders(headers, req.headers.get('origin'))
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cd5394c and d7d9817.

📒 Files selected for processing (4)
  • packages/server/src/handlers/PatchHandler.ts (5 hunks)
  • packages/server/src/server.ts (10 hunks)
  • packages/server/src/types.ts (6 hunks)
  • packages/server/src/web.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/server/src/types.ts
🔇 Additional comments (9)
packages/server/src/handlers/PatchHandler.ts (5)

22-22: LGTM! Header validation properly uses the Web standard Headers interface.

The code correctly uses the Headers.get() method and handles null checks appropriately.

Also applies to: 26-26, 29-30, 73-74


113-131: LGTM! Response construction properly handles headers and hook responses.

The code correctly:

  • Merges input headers using Object.fromEntries
  • Handles hook responses by properly merging status, body, and headers

165-168: LGTM! Improved error handling prevents redundant context aborts.

The code now checks if the context is already aborted before attempting to abort it, which is a good practice.


99-104: Consider updating store interface to use Web Streams.

The current implementation converts Web Streams to Node.js streams to maintain compatibility with the store interface. Consider updating the store interface to use Web Streams directly in a future PR.

Let's check if there are any store implementations that might be affected:

✅ Verification successful

🏁 Script executed:

#!/bin/bash
# Search for store implementations
rg -l "implements.*Store" --type ts

Length of output: 215


Store interface update: Maintain current conversion with a note to refactor in the future.

The search confirms that all store implementations (e.g., in FileKvStore.ts, RedisKvStore.ts, IoRedisKvStore.ts, MemoryKvStore.ts) are designed to work with Node.js streams. Thus, the current approach—converting a Web Stream (via Readable.fromWeb(req.body)) into a Node.js stream—is necessary to maintain compatibility. The proposal to update the store interface to accept Web Streams directly is a valid future improvement but would require coordinated changes across these implementations.


2-2: Consider using Web Streams API instead of Node.js Readable.

The PR aims to make the server runtime-agnostic, but the code still depends on Node.js-specific Readable streams. Consider using the Web Streams API (ReadableStream) for better compatibility across different runtimes.

Also applies to: 14-14, 100-100

✅ Verification successful

🌐 Web query:

What is the current browser support for Web Streams API?

💡 Result:

The Web Streams API has achieved widespread adoption across modern browsers with some variations in implementation details and historical support:

Desktop Browser Support

Browser Full Support Versions Partial Support Versions
Chrome ✅ 89+ ◐ 52-88
Edge ✅ 89+ ◐ 14-88
Firefox ✅ 102+ ◐ 65-101
Safari ◐ 10+ (Partial) ❌ <10
Opera ✅ 76+ ◐ 39-75

Mobile Browser Support

Browser Support Status
Chrome for Android ✅ Full (All recent versions)
Safari on iOS ◐ Partial (10.3+)
Firefox for Android ✅ Full (130+)
Samsung Internet ✅ Full (15+)

Key Details:

  1. Core Components:

    • ReadableStream: Available since Chrome 43[2][7], Firefox 65[2][7], Safari 10.1[2][7]
    • WritableStream: Later adoption (e.g., Chrome 59[2], Firefox not fully implemented[2])
  2. Safari Limitations:

    • Supports streams but blocks usage with fetch() without duplex option[3]
    • Partial implementation of transform streams[4][5]
  3. Standardization Milestones:

    • Became stable in Node.js v21[4][8]
    • Supported across all major JS environments (browsers/Node.js/Deno)[4]
  4. Notable Exceptions:

    • No IE11/legacy browser support[1][5]
    • Opera Mini/KaiOS lack support[1]

For production use today:

// Feature detection example
if ('ReadableStream' in window && 'WritableStream' in window) {
  // Safe to use Web Streams API
}

Mobile developers should test target devices due to fragmented iOS/Safari implementation[1][5]. The API now enables advanced use cases like gRPC-web streaming[6] and real-time media processing[9].

Citations:


Switch to Web Streams API for Cross-Runtime Compatibility

The current implementation in packages/server/src/handlers/PatchHandler.ts still relies on the Node.js-specific Readable stream, which contradicts the PR’s goal of runtime-agnosticism. The Web query confirms that the Web Streams API is now widely supported across modern browsers—with full support in Chrome, Edge, and Firefox, and acceptable support in Safari and mobile browsers. To fully achieve runtime-agnostic behavior, consider:

  • Replacing the import {Readable} from 'node:stream' with usage of ReadableStream from the Web Streams API.
  • Reviewing method signatures (e.g., the change at line 14) and corresponding implementations to ensure compatibility.
  • Incorporating fallback mechanisms or polyfills if targeting environments with partial support.
packages/server/src/web.ts (2)

88-89: Address TypeScript error instead of suppressing it.

The code uses @ts-expect-error to suppress a TypeScript error when setting headers. Instead of suppressing the error, consider using proper type casting.


170-172: LGTM!

The implementation correctly uses Node.js's built-in Readable.toWeb utility to convert file streams.

packages/server/src/server.ts (2)

139-141: LGTM!

The new handleWeb method provides a clean interface for frameworks that use the Web API Request type.


244-261: LGTM!

The updated write method cleanly integrates with the Web API by returning a Response object. The implementation handles content length and connection headers appropriately.

@Murderlon
Copy link
Member Author

Murderlon commented Feb 10, 2025

I am not sure I fully understood the goal of this PR. It removes the dependency on Node's http module for the main handlers, but adds a dependency on Node's stream module, so the handler can still only be used in Node-compatible runtimes (as the PR title mentions).

What exactly do we gain here now? After this PR, tus-node-server can still only be used in Node-compatible runtimes. Was this not the case before?

Or is this just a stepping stone for future PRs to make the handler completely independent from Node specifics?

Web standards only cover a limited standardized problem space, such as request/response, files (but not file system interactions), and streams. For our purposes it's therefore impossible to not use some Node.js dependencies. While some other JS runtimes offer their own Node.js equivalents, such as Deno and Bun, all mature runtimes (Cloudflare Workers, AWS Lambda, Deno Deploy, Bun, etc) offer Node.js compatibility. However, to not make all runtimes conform to Node's req/res just because they happen to be first, the WinterTC makes sure we have a standardized way to make basic servers.

So some of the runtimes + all meta frameworks (Next.js, Nuxt, React Router, Svelte, SolidStart, etc) only expose a req: Request to you, not http.IncomingMessage/http.ServerResponse. If we want tus to run there, we need to change the handlers, while the stores can continue to rely on Node.js specifics.

@Acconut
Copy link
Member

Acconut commented Feb 10, 2025

Thanks for the explanation! That helped me a lot understand the purpose of the PR 👍

I didn't know there a runtime that provide just selected modules for Node-compatiblity, such as streams.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
packages/server/src/handlers/GetHandler.ts (1)

95-96: 🛠️ Refactor suggestion

Avoid using @ts-expect-error by updating type definitions.

The use of @ts-expect-error indicates that TypeScript is unable to infer the correct types for this.store.read(id).

Consider updating the store's type definitions to include the optional read method:

interface DataStore {
  read?: (id: string) => Promise<ReadableStream>;
  // ... other methods
}
🧹 Nitpick comments (5)
packages/server/README.md (3)

163-163: Add missing comma for better readability.

Add a comma after "response" to improve sentence clarity.

-If the function returns the (modified) response the upload will be created.
+If the function returns the (modified) response, the upload will be created.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~163-~163: Possible missing comma found.
Context: ... If the function returns the (modified) response the upload will be created. - You can o...

(AI_HYDRA_LEO_MISSING_COMMA)


207-207: Add period after 'etc' in American English.

For consistency with American English style.

-or a meta framework (such as Next.js app router, Nuxt, React Router, SvelteKit, etc)
+or a meta framework (such as Next.js app router, Nuxt, React Router, SvelteKit, etc.)
🧰 Tools
🪛 LanguageTool

[style] ~207-~207: In American English, abbreviations like “etc.” require a period.
Context: ... router, Nuxt, React Router, SvelteKit, etc) or a Node.js compatible runtime based ...

(ETC_PERIOD)


69-70: Consider adding a code example for the maxSize function.

The documentation would benefit from a practical example showing how to implement a dynamic maxSize function.

// Example implementation:
const maxSize = async (req, id) => {
  // Dynamic size based on user role
  const userRole = await getUserRole(req);
  return userRole === 'premium' ? 1024 * 1024 * 1024 : 100 * 1024 * 1024;
};
.changeset/polite-bikes-train.md (2)

5-6: Fix punctuation in meta frameworks list.

Add a period after "etc" to follow proper American English style.

- (such as Next.js, Nuxt, React Router, SvelteKit, etc) and other Node.js compatible runtime environments.
+ (such as Next.js, Nuxt, React Router, SvelteKit, etc.) and other Node.js compatible runtime environments.
🧰 Tools
🪛 LanguageTool

[style] ~6-~6: In American English, abbreviations like “etc.” require a period.
Context: ...Next.js, Nuxt, React Router, SvelteKit, etc) and other Node.js compatible runtime e...

(ETC_PERIOD)


10-13: Fix typo in metadata merge description.

Remove the duplicated word "will".

-    This will will internally merge the existing metadata with the new metadata.
+    This will internally merge the existing metadata with the new metadata.
🧰 Tools
🪛 LanguageTool

[duplication] ~12-~12: Possible typo: you repeated a word.
Context: ...a: Record<string, string> }>`. This will will internally merge the existing metadata ...

(ENGLISH_WORD_REPEAT_RULE)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d7d9817 and 82e21d9.

📒 Files selected for processing (3)
  • .changeset/polite-bikes-train.md (1 hunks)
  • packages/server/README.md (17 hunks)
  • packages/server/src/handlers/GetHandler.ts (4 hunks)
🧰 Additional context used
🪛 LanguageTool
.changeset/polite-bikes-train.md

[style] ~6-~6: In American English, abbreviations like “etc.” require a period.
Context: ...Next.js, Nuxt, React Router, SvelteKit, etc) and other Node.js compatible runtime e...

(ETC_PERIOD)


[duplication] ~12-~12: Possible typo: you repeated a word.
Context: ...a: Record<string, string> }>`. This will will internally merge the existing metadata ...

(ENGLISH_WORD_REPEAT_RULE)

packages/server/README.md

[uncategorized] ~163-~163: Possible missing comma found.
Context: ... If the function returns the (modified) response the upload will be created. - You can o...

(AI_HYDRA_LEO_MISSING_COMMA)


[style] ~207-~207: In American English, abbreviations like “etc.” require a period.
Context: ... router, Nuxt, React Router, SvelteKit, etc) or a Node.js compatible runtime based ...

(ETC_PERIOD)

🔇 Additional comments (9)
packages/server/README.md (2)

199-209: LGTM! Great addition of Web API support.

The addition of handleWeb method alongside the traditional handle method is a significant improvement, enabling better integration with modern frameworks and runtimes that use the Web API's Request/Response model.

🧰 Tools
🪛 LanguageTool

[style] ~207-~207: In American English, abbreviations like “etc.” require a period.
Context: ... router, Nuxt, React Router, SvelteKit, etc) or a Node.js compatible runtime based ...

(ETC_PERIOD)


507-528: LGTM! Well-documented Next.js App Router integration.

The new App Router integration example is clear, concise, and follows Next.js best practices by exporting individual HTTP method handlers.

.changeset/polite-bikes-train.md (2)

1-4: LGTM! Appropriate version bump for breaking changes.

The major version bump is correctly specified given the significant API changes being introduced.


7-9: LGTM! Clear documentation of breaking changes.

The breaking changes are well documented, clearly indicating the transition from Node.js-specific types to web standard Request/Response objects.

packages/server/src/handlers/GetHandler.ts (5)

2-2: LGTM! Import changes align with modern web standards.

The addition of CancellationContext and ERRORS imports from @tus/utils aligns with the PR's objective to modernize the codebase.


60-64: LGTM! Method signature modernization.

The updated signature aligns with web standards by:

  • Using the fetch API's Request type instead of http.IncomingMessage
  • Adding CancellationContext for better concurrency control
  • Using the web standard Headers class with a sensible default

65-65: Ensure that req.url is an absolute URL to prevent TypeError in new URL().

Using new URL(req.url) assumes that req.url is an absolute URL. If req.url is a relative URL, this will throw a TypeError.

#!/bin/bash
# Search for URL construction patterns in the codebase
rg "new URL\(.*?req\.url" -A 2

82-82: Update onIncomingRequest to accept a Request object.

The onIncomingRequest hook is now called with a Request object instead of http.IncomingMessage.

#!/bin/bash
# Search for onIncomingRequest implementations
rg "onIncomingRequest.*?\(" -A 2

93-103: LGTM! Improved concurrency control with proper cleanup.

The lock acquisition and cleanup in a try-finally block ensures proper resource management.

@Murderlon Murderlon added the Breaking change This requires a semver major release label Feb 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Breaking change This requires a semver major release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

@tus/server: support more JS runtimes and metaframeworks
2 participants