Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 20 additions & 0 deletions packages/fetch-router/.changes/minor.plain-headers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
BREAKING CHANGE: `RequestContext.headers` now returns a standard `Headers` instance instead of the `SuperHeaders`/`Headers` subclass from `@remix-run/headers`. As a result, the `@remix-run/headers` peer dependency has now been removed.

If you were relying on the type-safe property accessors on `RequestContext.headers`, you should use the new parse functions from `@remix-run/headers` instead:

```ts
// Before:
router.get('/api/users', (context) => {
let acceptsJson = context.headers.accept.accepts('application/json')
// ...
})

// After:
import { Accept } from '@remix-run/headers'

router.get('/api/users', (context) => {
let accept = Accept.from(context.headers.get('accept'))
let acceptsJson = accept.accepts('application/json')
// ...
})
```
2 changes: 1 addition & 1 deletion packages/fetch-router/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ router.get('/posts/:id', ({ request, url, params, storage }) => {

#### Content Negotiation

- use `headers.accept.accepts()` to serve different responses based on the client's `Accept` header
- use `Accept.from()` from `@remix-run/headers` to serve different responses based on the client's `Accept` header
- maybe put this on `context.accepts()` for convenience?

#### Sessions
Expand Down
1 change: 0 additions & 1 deletion packages/fetch-router/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
"@typescript/native-preview": "catalog:"
},
"peerDependencies": {
"@remix-run/headers": "workspace:^",
"@remix-run/route-pattern": "workspace:^",
"@remix-run/session": "workspace:^"
},
Expand Down
28 changes: 20 additions & 8 deletions packages/fetch-router/src/lib/request-context.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,33 @@ import assert from 'node:assert/strict'
import { RequestContext } from './request-context.ts'

describe('new RequestContext()', () => {
it('has a header object that is SuperHeaders', () => {
it('provides access to request headers', () => {
let req = new Request('https://remix.run/test', {
headers: {
'Content-Type': 'application/json',
},
})
let context = new RequestContext(req)

assert.equal('contentType' in context.headers, true)
assert.equal('contentType' in context.request.headers, false)
assert.equal(context.headers.contentType.toString(), 'application/json')
assert.equal(
context.headers.contentType.toString(),
context.request.headers.get('content-type'),
)
assert.equal(context.headers.get('content-type'), 'application/json')
})

it('provides a copy of request headers that can be mutated independently', () => {
let req = new Request('https://remix.run/test', {
headers: { 'X-Original': 'value' },
})
let context = new RequestContext(req)

context.headers.set('X-New', 'new-value')
context.headers.delete('X-Original')

// context.headers was mutated
assert.equal(context.headers.get('X-New'), 'new-value')
assert.equal(context.headers.get('X-Original'), null)

// original request.headers unchanged
assert.equal(req.headers.get('X-Original'), 'value')
assert.equal(req.headers.get('X-New'), null)
})

it('does not provide formData on GET requests', () => {
Expand Down
12 changes: 2 additions & 10 deletions packages/fetch-router/src/lib/request-context.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import SuperHeaders from '@remix-run/headers'
import { createSession, type Session } from '@remix-run/session'

import { AppStorage } from './app-storage.ts'
Expand All @@ -20,6 +19,7 @@ export class RequestContext<
* @param request The incoming request
*/
constructor(request: Request) {
this.headers = new Headers(request.headers)
this.method = request.method.toUpperCase() as RequestMethod
this.params = {} as params
this.request = request
Expand Down Expand Up @@ -71,15 +71,7 @@ export class RequestContext<
/**
* The headers of the request.
*/
get headers(): SuperHeaders {
if (this.#headers == null) {
this.#headers = new SuperHeaders(this.request.headers)
}

return this.#headers
}

#headers?: SuperHeaders
headers: Headers

/**
* The request method. This may differ from `request.method` when using the `methodOverride`
Expand Down
65 changes: 65 additions & 0 deletions packages/headers/.changes/minor.remove-super-headers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
BREAKING CHANGE: Removed `Headers`/`SuperHeaders` class and default export. Use the native `Headers` class with the static `from()` method on each header class instead.

New individual header `.from()` methods:

- `Accept.from()`
- `AcceptEncoding.from()`
- `AcceptLanguage.from()`
- `CacheControl.from()`
- `ContentDisposition.from()`
- `ContentRange.from()`
- `ContentType.from()`
- `Cookie.from()`
- `IfMatch.from()`
- `IfNoneMatch.from()`
- `IfRange.from()`
- `Range.from()`
- `SetCookie.from()`
- `Vary.from()`

New raw header utilities added:

- `parse()`
- `stringify()`

Migration example:

```ts
// Before:
import SuperHeaders from '@remix-run/headers'
let headers = new SuperHeaders(request.headers)
let mediaType = headers.contentType.mediaType

// After:
import { ContentType } from '@remix-run/headers'
let contentType = ContentType.from(request.headers.get('content-type'))
let mediaType = contentType.mediaType
```

If you were using the `Headers` constructor to parse raw HTTP header strings, use `parse()` instead:

```ts
// Before:
import SuperHeaders from '@remix-run/headers'
let headers = new SuperHeaders('Content-Type: text/html\r\nCache-Control: no-cache')

// After:
import { parse } from '@remix-run/headers'
let headers = parse('Content-Type: text/html\r\nCache-Control: no-cache')
```

If you were using `headers.toString()` to convert headers to raw format, use `stringify()` instead:

```ts
// Before:
import SuperHeaders from '@remix-run/headers'
let headers = new SuperHeaders()
headers.set('Content-Type', 'text/html')
let rawHeaders = headers.toString()

// After:
import { stringify } from '@remix-run/headers'
let headers = new Headers()
headers.set('Content-Type', 'text/html')
let rawHeaders = stringify(headers)
```
Loading