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

Add Sec-Fetch-Mode and Sec-Fetch-Dest to @std/http/unstable-headers #6116

Open
csvn opened this issue Oct 13, 2024 · 1 comment
Open

Add Sec-Fetch-Mode and Sec-Fetch-Dest to @std/http/unstable-headers #6116

csvn opened this issue Oct 13, 2024 · 1 comment

Comments

@csvn
Copy link

csvn commented Oct 13, 2024

Is your feature request related to a problem? Please describe.

In many frontend projects I've worked on, I've started relying on ``Sec-Fetch-Modeand/orSec-Fetch-Dest` headers. Because it's very common to return `/index.html` for SPA apps, but returning it for CSS/JS and other unrelated static resources is annoying. Therefore it's very helpful to check that the current request is a `navigate` to `document` before returning `/index.html`, otherwise 404.

I don't know why this header is not included in https://www.iana.org/assignments/http-fields/http-fields.xhtml#field-names, but these headers were part of Baseline 2023, and are supported in all major browsers currently.

Would PR's adding the below be accepted, or does new API's require discussion first in issues?

Describe the solution you'd like

It would be great if the Sec-Fetch-* headers were just included in HEADERS from jsr:@std/http@1/unstable-header. It would be even more awesome if there was a dedicated API for this, like with accepts in negotiation.ts:

import { fetchMode, fetchDestination } from 'jsr:@std/http@1';

const request = new Request('https://example.com/', {
  headers: {
    'Sec-Fetch-Mode': 'navigate',
    'Sec-Fetch-Dest': 'document'
  }
});

fetchMode(req, 'navigate');
// true
fetchMode(req, 'same-origin');
// false
fetchMode(req, 'navigate', 'no-cors');
// true

fetchDestination(req, 'document');
// true
fetchDestination(req, 'iframe');
// false
fetchDestination(req, 'document', 'iframe', 'frame');
// true

The biggest win for this being able to write the values above in a type safe way:

import { serveFile, fethMode, fetchDestination } from '@std/http';

export const spaFallback: Deno.ServeHandler = (req) => {
  if (!isDocumentReq(req)) return new Response(undefined, { status: 404 });
  return serveFile(req, '/index.html');
}

export function isDocumentReq(req: Request) {
  return fethMode(req, 'navigate') && fetchDestination(req, 'document');
}

Describe alternatives you've considered

There current code is not terrible, but using type-safe header values and type safe possible header values means it's easy to for instance misspell 'navigate' to 'navigation'.

export function isDocumentReq(req: Request) {
  const mode = req.headers.get('Sec-Fetch-Mode');
  const dest = req.headers.get('Sec-Fetch-Dest');
  return mode === 'navigate' && dest === 'document';
}
@csvn
Copy link
Author

csvn commented Oct 13, 2024

Perhaps all Sec-Fetch-* params could be combined in one function? Not sure about the best name, but it would probably be very handy if all of these could be provided at once. The mode, dest & site props could take a string or string[], and have a union type of all acceptet string types.

import { secFetch } from 'jsr:@std/http@1/negotiation';

const request = new Request('https://example.com/', {
  headers: {
    'Sec-Fetch-Mode': 'navigate',
    'Sec-Fetch-Dest': 'document',
    'Sec-Fetch-Site': 'none',
    'Sec-Fetch-User': '?1'
  }
});

secFetch({ mode: 'navigate' });
// true
secFetch({ mode: 'navigate', site: 'none', user: true });
// true
secFetch({ user: false });
// false
secFetch({ mode: 'navigate', dest: ['document', 'iframe', 'frame'] });
// true
secFetch({ mode: 'same-origin', dest: 'image' });
// false

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

No branches or pull requests

1 participant