├── CODEOWNERS ├── .github ├── FUNDING.yml ├── workflows │ ├── provenance.yml │ ├── release.yml │ └── ci.yml └── ISSUE_TEMPLATE │ ├── ---feature-suggestion.yml │ └── ---bug-report.yml ├── .gitignore ├── renovate.json ├── pnpm-workspace.yaml ├── playground ├── package.json ├── index.js ├── tsconfig.json └── index.ts ├── eslint.config.js ├── .editorconfig ├── vitest.config.ts ├── src ├── http │ ├── index.ts │ ├── url.ts │ ├── headers │ │ ├── response.ts │ │ └── request.ts │ └── mimes.ts ├── tree.ts ├── index.ts ├── utils.ts ├── fetch.ts ├── serialize.ts └── inference.ts ├── tsconfig.json ├── LICENCE ├── test ├── serialize.test.ts ├── fixture │ └── schema.ts ├── schema.test-d.ts ├── url.test-d.ts └── typed-fetch.test-d.ts ├── package.json ├── CODE_OF_CONDUCT.md └── README.md /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @danielroe 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [danielroe] 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node_modules 3 | coverage 4 | .vscode 5 | .DS_Store 6 | .eslintcache 7 | *.log* 8 | *.env* 9 | 10 | *-2 11 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "github>danielroe/renovate" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - playground 3 | ignoredBuiltDependencies: 4 | - esbuild 5 | - unrs-resolver 6 | onlyBuiltDependencies: 7 | - simple-git-hooks 8 | 9 | -------------------------------------------------------------------------------- /playground/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "type": "module", 3 | "private": true, 4 | "scripts": { 5 | "dev": "node index.js" 6 | }, 7 | "dependencies": { 8 | "fetchdts": "latest" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /playground/index.js: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert' 2 | import * as pkg from 'fetchdts' 3 | 4 | // eslint-disable-next-line no-console 5 | console.log(pkg.welcome()) 6 | 7 | assert.strictEqual(pkg.welcome(), 'hello world') 8 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | import antfu from '@antfu/eslint-config' 2 | 3 | export default antfu() 4 | .append({ 5 | files: ['playground/**'], 6 | rules: { 7 | 'antfu/no-top-level-await': 'off', 8 | }, 9 | }) 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | trim_trailing_whitespace = true 7 | charset = utf-8 8 | 9 | [*.js] 10 | indent_style = space 11 | indent_size = 2 12 | 13 | [{package.json,*.yml,*.cjson}] 14 | indent_style = space 15 | indent_size = 2 16 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { fileURLToPath } from 'node:url' 2 | import { defineConfig } from 'vitest/config' 3 | 4 | export default defineConfig({ 5 | resolve: { 6 | alias: { 7 | fetchdts: fileURLToPath( 8 | new URL('./src/index.ts', import.meta.url).href, 9 | ), 10 | }, 11 | }, 12 | test: { 13 | coverage: { 14 | include: ['src'], 15 | reporter: ['text', 'json', 'html'], 16 | }, 17 | }, 18 | }) 19 | -------------------------------------------------------------------------------- /.github/workflows/provenance.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | permissions: 11 | contents: read 12 | jobs: 13 | check-provenance: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 0 19 | - name: Check provenance downgrades 20 | uses: danielroe/provenance-action@a5a718233ca12eff67651fcf29a030bbbd5b3ca1 # v0.1.0 21 | with: 22 | fail-on-provenance-change: true 23 | -------------------------------------------------------------------------------- /playground/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", 4 | "lib": [ 5 | "DOM", 6 | "es2022" 7 | ], 8 | "moduleDetection": "force", 9 | "module": "preserve", 10 | "resolveJsonModule": true, 11 | "allowJs": true, 12 | "strict": true, 13 | "noImplicitOverride": true, 14 | "noUncheckedIndexedAccess": true, 15 | "noEmit": true, 16 | "esModuleInterop": true, 17 | "forceConsistentCasingInFileNames": true, 18 | "isolatedModules": true, 19 | "verbatimModuleSyntax": true, 20 | "skipLibCheck": true 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/http/index.ts: -------------------------------------------------------------------------------- 1 | import type { TypedHeaders } from '../fetch' 2 | import type { RequestHeaderMap } from './headers/request' 3 | import type { ResponseHeaderMap } from './headers/response' 4 | 5 | export type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' | 'HEAD' | 'CONNECT' | 'TRACE' 6 | 7 | export type { RequestHeaderMap, RequestHeaderName } from './headers/request' 8 | export type { ResponseHeaderMap, ResponseHeaderName } from './headers/response' 9 | 10 | export type { MimeType } from './mimes' 11 | 12 | export type RequestHeaders = TypedHeaders 13 | export type ResponseHeaders = TypedHeaders 14 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | permissions: 4 | contents: write 5 | 6 | on: 7 | push: 8 | tags: 9 | - 'v*' 10 | 11 | jobs: 12 | release: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - uses: actions/checkout@v5 16 | with: 17 | fetch-depth: 0 18 | 19 | - run: corepack enable 20 | - uses: actions/setup-node@v6 21 | with: 22 | node-version: lts/* 23 | cache: pnpm 24 | 25 | - name: 📦 Install dependencies 26 | run: pnpm install 27 | 28 | - run: pnpm changelogithub 29 | env: 30 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 31 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2022", 4 | "lib": [ 5 | "DOM", 6 | "es2022" 7 | ], 8 | "moduleDetection": "force", 9 | "module": "preserve", 10 | "moduleResolution": "bundler", 11 | "resolveJsonModule": true, 12 | "allowJs": true, 13 | "strict": true, 14 | "noImplicitOverride": true, 15 | "noUncheckedIndexedAccess": true, 16 | "noEmit": true, 17 | "esModuleInterop": true, 18 | "forceConsistentCasingInFileNames": true, 19 | "isolatedModules": true, 20 | "verbatimModuleSyntax": true, 21 | "skipLibCheck": true 22 | }, 23 | "include": [ 24 | "src", 25 | "test", 26 | "playground" 27 | ] 28 | } 29 | -------------------------------------------------------------------------------- /src/tree.ts: -------------------------------------------------------------------------------- 1 | import type { HTTPMethod } from './http' 2 | 3 | export type StaticParam = string // for readability only 4 | export const DynamicParam = Symbol.for('dynamic parameter') 5 | export const WildcardParam = Symbol.for('wildcard parameter') 6 | export const Endpoint = Symbol.for('endpoints') 7 | 8 | export interface RouteTree { 9 | [key: string | symbol]: Partial | RouteTree 10 | } 11 | 12 | export interface EndpointMetadata { 13 | query: never | Record 14 | headers: Record 15 | body: never | Record 16 | response: unknown 17 | responseHeaders: Record 18 | } 19 | 20 | export type Endpoints = Record 21 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export type { TypedHeaders, TypedRequest, TypedResponse } from './fetch' 2 | 3 | export type { HTTPMethod, MimeType, RequestHeaderMap, RequestHeaderName, RequestHeaders, ResponseHeaderMap, ResponseHeaderName, ResponseHeaders } from './http' 4 | 5 | export type { TypedURLSearchParams } from './http/url' 6 | 7 | export type { TypedFetchInput, TypedFetchMeta, TypedFetchPath, TypedFetchRequestInit, TypedFetchResponseBody as TypedFetchResponse, TypedFetchResponseBody } from './inference' 8 | 9 | export { serializeRoutes } from './serialize' 10 | 11 | export type { Route } from './serialize' 12 | 13 | export type { Endpoints, RouteTree } from './tree' 14 | 15 | export type { DynamicParam, Endpoint, WildcardParam } from './tree' 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---feature-suggestion.yml: -------------------------------------------------------------------------------- 1 | name: 🆕 Feature suggestion 2 | description: Suggest an idea 3 | labels: [enhancement] 4 | body: 5 | - type: textarea 6 | validations: 7 | required: true 8 | attributes: 9 | label: 🆒 Your use case 10 | description: Add a description of your use case, and how this feature would help you. 11 | placeholder: When I do [...] I would expect to be able to do [...] 12 | - type: textarea 13 | validations: 14 | required: true 15 | attributes: 16 | label: 🆕 The solution you'd like 17 | description: Describe what you want to happen. 18 | - type: textarea 19 | attributes: 20 | label: 🔍 Alternatives you've considered 21 | description: Have you considered any alternative solutions or features? 22 | - type: textarea 23 | attributes: 24 | label: ℹ️ Additional info 25 | description: Is there any other context you think would be helpful to know? 26 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/---bug-report.yml: -------------------------------------------------------------------------------- 1 | name: 🐛 Bug report 2 | description: Something's not working 3 | labels: [bug] 4 | body: 5 | - type: textarea 6 | validations: 7 | required: true 8 | attributes: 9 | label: 🐛 The bug 10 | description: What isn't working? Describe what the bug is. 11 | - type: input 12 | validations: 13 | required: true 14 | attributes: 15 | label: 🛠️ To reproduce 16 | description: A reproduction of the bug via https://stackblitz.com/github/unjs/fetchdts/tree/main/playground 17 | placeholder: https://stackblitz.com/[...] 18 | - type: textarea 19 | validations: 20 | required: true 21 | attributes: 22 | label: 🌈 Expected behaviour 23 | description: What did you expect to happen? Is there a section in the docs about this? 24 | - type: textarea 25 | attributes: 26 | label: ℹ️ Additional context 27 | description: Add any other context about the problem here. 28 | -------------------------------------------------------------------------------- /LICENCE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Daniel Roe 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | push: 8 | branches: 9 | - main 10 | 11 | jobs: 12 | lint: 13 | runs-on: ubuntu-latest 14 | 15 | steps: 16 | - uses: actions/checkout@v5 17 | - run: corepack enable 18 | - uses: actions/setup-node@v6 19 | with: 20 | node-version: lts/* 21 | cache: pnpm 22 | 23 | - name: 📦 Install dependencies 24 | run: pnpm install 25 | 26 | - name: 🔠 Lint project 27 | run: pnpm lint 28 | 29 | - name: ✂️ Knip project 30 | run: pnpm test:knip 31 | 32 | - name: ⚙️ Check package engines 33 | run: pnpm test:versions 34 | 35 | test: 36 | runs-on: ubuntu-latest 37 | 38 | steps: 39 | - uses: actions/checkout@v5 40 | - run: corepack enable 41 | - uses: actions/setup-node@v6 42 | with: 43 | node-version: lts/* 44 | cache: pnpm 45 | 46 | - name: 📦 Install dependencies 47 | run: pnpm install 48 | 49 | - name: 🛠 Build project 50 | run: pnpm build 51 | 52 | - name: 💪 Test types 53 | run: pnpm test:types 54 | 55 | - name: 🧪 Test project 56 | run: pnpm test:unit -- --coverage 57 | 58 | - name: 🟩 Coverage 59 | uses: codecov/codecov-action@v5 60 | -------------------------------------------------------------------------------- /playground/index.ts: -------------------------------------------------------------------------------- 1 | import type { DynamicParam, Endpoint, TypedFetchInput, TypedFetchResponse, WildcardParam } from 'fetchdts' 2 | 3 | interface ExampleSchema { 4 | '/api': { 5 | '/test': { 6 | [Endpoint]: { 7 | POST: { 8 | body: { foo: string } 9 | response: { bar: string } 10 | } 11 | } 12 | } 13 | [DynamicParam]: { 14 | [Endpoint]: { 15 | GET: { 16 | body: { id: boolean } 17 | response: string 18 | } 19 | } 20 | } 21 | } 22 | 'https://jsonplaceholder.typicode.com': { 23 | '/posts': { 24 | [Endpoint]: { 25 | GET: { 26 | body: never 27 | response: { id: number } 28 | } 29 | POST: { 30 | body: { id: number } 31 | response: null 32 | } 33 | } 34 | [DynamicParam]: { 35 | [Endpoint]: { 36 | GET: { 37 | body: { id: boolean } 38 | response: string 39 | } 40 | } 41 | } 42 | } 43 | [WildcardParam]: { 44 | [Endpoint]: { 45 | GET: { 46 | body: { id: boolean } 47 | response: string 48 | } 49 | } 50 | } 51 | } 52 | } 53 | 54 | async function typedFetch>(_input: T) { 55 | return {} as Promise> 56 | } 57 | 58 | const _res = await typedFetch('/api/foo') 59 | -------------------------------------------------------------------------------- /test/serialize.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, expect, it } from 'vitest' 2 | import { serializeRoutes } from '../src' 3 | 4 | describe('serialize', () => { 5 | it('should serialize a route tree', () => { 6 | const tree = serializeRoutes('InternalRoutes', [ 7 | { 8 | path: '/', 9 | metadata: { 10 | GET: { 11 | responseType: '{ type: \'headers\', method: \'POST\' }', 12 | headersType: '{ authorization: \'string\' }', 13 | }, 14 | }, 15 | }, 16 | { 17 | path: '/bob', 18 | type: 'dynamic', 19 | metadata: { 20 | POST: { 21 | responseType: '{ type: \'headers\', method: \'POST\' }', 22 | headersType: '{ authorization: \'string\' }', 23 | }, 24 | }, 25 | }, 26 | ]) 27 | 28 | expect(tree).toMatchInlineSnapshot(` 29 | "import { DynamicParam } from 'fetchdts' 30 | 31 | interface InternalRoutes { 32 | "/": { 33 | "GET": { 34 | "response": { type: 'headers', method: 'POST' } 35 | "headers": { authorization: 'string' } 36 | } 37 | } 38 | "/bob": { 39 | [DynamicParam]: { 40 | "POST": { 41 | "response": { type: 'headers', method: 'POST' } 42 | "headers": { authorization: 'string' } 43 | } 44 | } 45 | } 46 | }" 47 | `) 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | // this is an example native fetch client with full typings 2 | 3 | // import type { HTTPMethod, TypedResponse } from './http' 4 | // import type { RouteTree, TypedFetchInput, TypedFetchRequestInit, TypedFetchResponse } from './inference' 5 | 6 | // export interface TypedFetch { 7 | // , Init extends TypedFetchRequestInit>(input: T, init: Init): Promise>> 8 | // , Init extends TypedFetchRequestInit>(input: T, init?: Init): Promise>> 9 | // /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/fetch) */ 10 | // } 11 | 12 | // export function createFetch() { 13 | // return fetch as unknown as TypedFetch 14 | // } 15 | 16 | export type RespectOptionality = ( 17 | Key extends keyof T 18 | ? T[Key] extends NonNullable 19 | ? { [K in Key]-?: T[Key] } 20 | : { [K in Key]?: T[Key] } 21 | : { [K in Key]?: Fallback } 22 | ) 23 | 24 | type WithoutQuery = T extends `${infer U}?${string}` ? U : T 25 | type WithoutHash = T extends `${infer U}#${string}` ? U : T 26 | type WithoutQueryOrHash = WithoutQuery> 27 | type WithoutTrailingSlash = T extends `${infer U}/` ? U : T 28 | 29 | export type Trimmed = WithoutTrailingSlash> 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fetchdts", 3 | "type": "module", 4 | "version": "0.1.7", 5 | "packageManager": "pnpm@10.21.0", 6 | "description": "A suite of type utilities for building strongly-typed APIs", 7 | "author": { 8 | "name": "Daniel Roe", 9 | "email": "daniel@roe.dev", 10 | "url": "https://roe.dev" 11 | }, 12 | "license": "MIT", 13 | "repository": "unjs/fetchdts", 14 | "sideEffects": false, 15 | "exports": { 16 | ".": "./dist/index.mjs" 17 | }, 18 | "main": "./dist/index.mjs", 19 | "module": "./dist/index.mjs", 20 | "types": "./dist/index.d.mts", 21 | "files": [ 22 | "dist" 23 | ], 24 | "scripts": { 25 | "build": "unbuild", 26 | "dev": "vitest dev", 27 | "lint": "eslint .", 28 | "prepare": "simple-git-hooks", 29 | "prepack": "pnpm build", 30 | "prepublishOnly": "pnpm lint && pnpm test", 31 | "release": "bumpp && pnpm publish", 32 | "test": "pnpm test:unit && pnpm test:types", 33 | "test:unit": "vitest --typecheck", 34 | "test:knip": "knip", 35 | "test:versions": "installed-check -d --no-workspaces", 36 | "test:types": "tsc --noEmit" 37 | }, 38 | "devDependencies": { 39 | "@antfu/eslint-config": "6.2.0", 40 | "@types/node": "latest", 41 | "@vitest/coverage-v8": "4.0.8", 42 | "bumpp": "10.3.1", 43 | "changelogithub": "13.16.1", 44 | "eslint": "9.39.1", 45 | "installed-check": "9.3.0", 46 | "knip": "5.68.0", 47 | "lint-staged": "16.2.6", 48 | "simple-git-hooks": "2.13.1", 49 | "typescript": "5.9.3", 50 | "unbuild": "3.6.1", 51 | "vitest": "4.0.8" 52 | }, 53 | "resolutions": { 54 | "fetchdts": "link:." 55 | }, 56 | "simple-git-hooks": { 57 | "pre-commit": "npx lint-staged" 58 | }, 59 | "lint-staged": { 60 | "*.{js,ts,mjs,cjs,json,.*rc}": [ 61 | "npx eslint --fix" 62 | ] 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/http/url.ts: -------------------------------------------------------------------------------- 1 | export interface TypedURLSearchParams | unknown = Partial>> extends Omit { 2 | /** 3 | * Appends a specified key/value pair as a new search parameter. 4 | * 5 | * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/append) 6 | */ 7 | append: | string & {}> (name: Name, value: Name extends keyof QueryMap ? QueryMap[Name] extends string ? QueryMap[Name] : string : string) => void 8 | /** 9 | * Deletes the given search parameter, and its associated value, from the list of all search parameters. 10 | * 11 | * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/delete) 12 | */ 13 | delete: | string & {}> (name: Name, value?: Name extends keyof QueryMap ? QueryMap[Name] extends string ? QueryMap[Name] : string : string) => void 14 | /** 15 | * Returns the first value associated to the given search parameter. 16 | * 17 | * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/get) 18 | */ 19 | get: | string & {}> (name: Name) => (Name extends keyof QueryMap ? QueryMap[Name] extends string ? QueryMap[Name] : string : string) | null 20 | /** 21 | * Returns all the values association with a given search parameter. 22 | * 23 | * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/getAll) 24 | */ 25 | getAll: | string & {}> (name: Name) => Array 26 | /** 27 | * Returns a Boolean indicating if such a search parameter exists. 28 | * 29 | * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/has) 30 | */ 31 | has: | string & {}> (name: Name, value?: Name extends keyof QueryMap ? QueryMap[Name] extends string ? QueryMap[Name] : string : string) => boolean 32 | /** 33 | * Sets the value associated to a given search parameter to the given value. If there were several values, delete the others. 34 | * 35 | * [MDN Reference](https://developer.mozilla.org/docs/Web/API/URLSearchParams/set) 36 | */ 37 | set: | string & {}> (name: Name, value: Name extends keyof QueryMap ? QueryMap[Name] extends string ? QueryMap[Name] : string : string) => void 38 | forEach: (callbackfn: (value: QueryMap[keyof QueryMap] | string & {}, key: Extract | string & {}, parent: URLSearchParams) => void, thisArg?: any) => void 39 | } 40 | -------------------------------------------------------------------------------- /test/fixture/schema.ts: -------------------------------------------------------------------------------- 1 | import type { DynamicParam, Endpoint, WildcardParam } from '../../src/tree' 2 | 3 | export interface ExampleSchema { 4 | '/methods': { 5 | '/get': { 6 | [Endpoint]: { GET: { response: { method: 'GET' } } } 7 | } 8 | '/post': { 9 | [Endpoint]: { POST: { response: { method: 'POST' } } } 10 | } 11 | } 12 | '/query': { 13 | '/required': { 14 | [Endpoint]: { 15 | POST: { 16 | query: { id: number } 17 | response: { type: 'query', method: 'POST' } 18 | } 19 | } 20 | } 21 | '/optional': { 22 | [Endpoint]: { 23 | POST: { 24 | query?: { id: number } 25 | response: { type: 'query', method: 'POST' } 26 | } 27 | } 28 | } 29 | } 30 | '/body': { 31 | '/required': { 32 | [Endpoint]: { 33 | POST: { 34 | body: { id: number } 35 | response: { type: 'body', method: 'POST' } 36 | } 37 | } 38 | } 39 | '/optional': { 40 | [Endpoint]: { 41 | POST: { 42 | body?: { id: number } 43 | response: { type: 'body', method: 'POST' } 44 | } 45 | } 46 | } 47 | } 48 | '/headers': { 49 | '/request': { 50 | [Endpoint]: { 51 | POST: { 52 | response: { type: 'headers', method: 'POST' } 53 | headers: { authorization: string } 54 | } 55 | } 56 | } 57 | '/response': { 58 | [Endpoint]: { 59 | GET: { 60 | response: { type: 'headers', method: 'POST' } 61 | responseHeaders: { 'content-type': 'application/json' } 62 | } 63 | } 64 | } 65 | } 66 | '/api': { 67 | '/static-options': { 68 | [Endpoint]: { 69 | OPTIONS: { response: { type: 'static', method: 'GET' } } 70 | } 71 | } 72 | '/static': { 73 | [Endpoint]: { 74 | POST: { response: { type: 'static', method: 'POST' } } 75 | } 76 | } 77 | [DynamicParam]: { 78 | [Endpoint]: { 79 | GET: { response: { type: 'dynamic', method: 'GET' } } 80 | POST: { 81 | body: { id: boolean } 82 | response: { type: 'dynamic', method: 'POST' } 83 | } 84 | } 85 | } 86 | } 87 | 'https://jsonplaceholder.typicode.com': { 88 | '/posts': { 89 | [Endpoint]: { 90 | GET: { 91 | body: never 92 | response: { id: number } 93 | } 94 | POST: { 95 | body: { id: number } 96 | response: null 97 | } 98 | } 99 | [DynamicParam]: { 100 | [Endpoint]: { 101 | GET: { 102 | body: { id: boolean } 103 | response: string 104 | } 105 | } 106 | } 107 | } 108 | [WildcardParam]: { 109 | [Endpoint]: { 110 | GET: { 111 | body: { id: boolean } 112 | response: string 113 | } 114 | } 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/fetch.ts: -------------------------------------------------------------------------------- 1 | import type { RequestHeaderMap, ResponseHeaderMap } from './http' 2 | 3 | export interface TypedHeaders | unknown> extends Omit { 4 | /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/append) */ 5 | append: | string & {}> (name: Name, value: Lowercase extends keyof TypedHeaderValues ? TypedHeaderValues[Lowercase] : string) => void 6 | /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/delete) */ 7 | delete: | string & {}> (name: Name) => void 8 | /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/get) */ 9 | get: | string & {}> (name: Name) => (Lowercase extends keyof TypedHeaderValues ? TypedHeaderValues[Lowercase] : string) | null 10 | /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/getSetCookie) */ 11 | getSetCookie: () => string[] 12 | /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/has) */ 13 | has: | string & {}> (name: Name) => boolean 14 | /** [MDN Reference](https://developer.mozilla.org/docs/Web/API/Headers/set) */ 15 | set: | string & {}> (name: Name, value: Lowercase extends keyof TypedHeaderValues ? TypedHeaderValues[Lowercase] : string) => void 16 | forEach: (callbackfn: (value: TypedHeaderValues[keyof TypedHeaderValues] | string & {}, key: Extract | string & {}, parent: TypedHeaders) => void, thisArg?: any) => void 17 | } 18 | 19 | // type TypedHeaderTuples> = { 20 | // [Name in Lowercase>]: [Name, Name extends string ? Lowercase extends keyof TypedHeaderValues ? TypedHeaderValues[Lowercase] : string : string] 21 | // }[Lowercase>][] 22 | 23 | // type TypedHeadersInit> = TypedHeaderTuples | Partial | TypedHeaders | Headers 24 | 25 | // type TypedHeadersClass> = new (init?: TypedHeadersInit) => TypedHeaders 26 | 27 | export interface TypedResponse | unknown = ResponseHeaderMap> extends Omit { 28 | clone: () => TypedResponse 29 | json: () => Promise 30 | headers: TypedHeaders 31 | } 32 | 33 | export interface TypedRequest | unknown = RequestHeaderMap> extends Omit { 34 | clone: () => TypedRequest 35 | json: () => Promise 36 | headers: TypedHeaders 37 | } 38 | -------------------------------------------------------------------------------- /src/serialize.ts: -------------------------------------------------------------------------------- 1 | import type { HTTPMethod } from './http' 2 | import type { EndpointMetadata, RouteTree } from './tree' 3 | import { DynamicParam, WildcardParam } from './tree' 4 | 5 | interface OutputOptions { 6 | /** whether to export the generated interface */ 7 | export?: boolean 8 | } 9 | 10 | export interface Route { 11 | type?: 'static' | 'dynamic' | 'wildcard' 12 | path: string 13 | metadata?: { 14 | [key in HTTPMethod]?: Partial> 15 | } 16 | [key: string]: unknown 17 | } 18 | 19 | export function serializeRoutes(name: string, routes: Route[], options?: OutputOptions): string { 20 | const imports = new Set() 21 | const tree: RouteTree = {} 22 | 23 | // build route tree 24 | for (const route of routes) { 25 | const { segments } = parsePath(route.path) 26 | 27 | let node: RouteTree = tree 28 | for (const segment of segments) { 29 | node[segment] = node[segment] || {} 30 | node = node[segment] as RouteTree 31 | } 32 | 33 | if (route.type === 'dynamic') { 34 | imports.add('DynamicParam') 35 | 36 | node[DynamicParam] = node[DynamicParam] || {} 37 | node = node[DynamicParam] as RouteTree 38 | } 39 | if (route.type === 'wildcard') { 40 | imports.add('WildcardParam') 41 | 42 | node[WildcardParam] = node[WildcardParam] || {} 43 | node = node[WildcardParam] as RouteTree 44 | } 45 | 46 | Object.assign(node, route.metadata) 47 | } 48 | 49 | // stringify resulting tree 50 | return [ 51 | imports.size > 0 ? `import { ${[...imports].join(', ')} } from 'fetchdts'` : undefined, 52 | options?.export ? 'export ' : undefined, 53 | '', 54 | `interface ${name} {\n${stringifyRouteTree(tree)}}`, 55 | ].filter(s => s !== undefined).join('\n') 56 | } 57 | 58 | const symbols = [DynamicParam, WildcardParam] as const 59 | 60 | const keys: Record = { 61 | [DynamicParam]: '[DynamicParam]', 62 | [WildcardParam]: '[WildcardParam]', 63 | } 64 | 65 | function stringifyRouteTree(tree: RouteTree, indent = 2): string { 66 | let properties = '' 67 | const entries = [ 68 | ...Object.entries(tree), 69 | ...symbols.map(symbol => [symbol, tree[symbol]] as const), 70 | ] 71 | for (const [_key, value] of entries) { 72 | if (!value) { 73 | continue 74 | } 75 | const key = keys[_key] || JSON.stringify((_key as string).replace(/Type$/, '')) 76 | if (typeof value === 'string') { 77 | properties += `${' '.repeat(indent)}${key}: ${value}\n` 78 | } 79 | else { 80 | const str = stringifyRouteTree(value as RouteTree, indent + 2) 81 | properties += `${' '.repeat(indent)}${key}: {\n${str}${' '.repeat(indent)}}\n` 82 | } 83 | } 84 | 85 | return properties 86 | } 87 | 88 | function parsePath(path: string): { segments: string[] } { 89 | const url = new URL(path, 'http://localhost') 90 | const segments = url.pathname.split('/').map(s => s.replace(/^\/?/, '/')) 91 | if (segments[0] === '/' && segments.length > 1) { 92 | segments.shift() 93 | } 94 | 95 | segments[segments.length - 1] += url.search + url.hash 96 | 97 | if (!/^https?:\/\/localhost/.test(path) && url.host === 'localhost') { 98 | return { segments } 99 | } 100 | 101 | segments.unshift(url.origin) 102 | 103 | return { segments } 104 | } 105 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | - Using welcoming and inclusive language 12 | - Being respectful of differing viewpoints and experiences 13 | - Gracefully accepting constructive criticism 14 | - Focusing on what is best for the community 15 | - Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | - The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | - Trolling, insulting/derogatory comments, and personal or political attacks 21 | - Public or private harassment 22 | - Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | - Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies within all project spaces, and it also applies when an individual is representing the project or its community in public spaces. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [daniel@roe.dev](mailto:daniel@roe.dev). All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 44 | 45 | [homepage]: https://www.contributor-covenant.org 46 | 47 | For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq 48 | -------------------------------------------------------------------------------- /src/http/headers/response.ts: -------------------------------------------------------------------------------- 1 | import type { HTTPMethod } from '..' 2 | 3 | type AnyString = string & {} 4 | 5 | export type ResponseHeaderName = keyof ResponseHeaderMap | AnyString 6 | 7 | export interface ResponseHeaderMap { 8 | 'Accept-Patch': AnyString 9 | 'Accept-Ranges': 'bytes' | 'none' | AnyString 10 | 'Access-Control-Allow-Credentials': 'true' | AnyString 11 | 'Access-Control-Allow-Headers': '*' | ResponseHeaderName | AnyString 12 | 'Access-Control-Allow-Methods': '*' | HTTPMethod | AnyString 13 | 'Access-Control-Allow-Origin': '*' | 'null' | AnyString 14 | 'Access-Control-Expose-Headers': '*' | ResponseHeaderName | AnyString 15 | 'Access-Control-Max-Age': AnyString 16 | 'Age': AnyString 17 | 'Allow': HTTPMethod | AnyString 18 | 'Alt-Svc': AnyString 19 | 'Alt-Used': AnyString 20 | 'Cache-Control': 21 | | 'no-cache' 22 | | 'no-store' 23 | | 'max-age' 24 | | 'must-revalidate' 25 | | 'public' 26 | | 'private' 27 | | 'proxy-revalidate' 28 | | 's-maxage' 29 | | 'stale-while-revalidate' 30 | | 'stale-if-error' 31 | | AnyString 32 | 'Clear-Site-Data': AnyString 33 | 'Connection': 'keep-alive' | 'close' | AnyString 34 | 'Content-Disposition': AnyString 35 | 'Content-DPR': AnyString 36 | 'Content-Encoding': 37 | | 'gzip' 38 | | 'compress' 39 | | 'deflate' 40 | | 'br' 41 | | 'identity' 42 | | AnyString 43 | 'Content-Language': AnyString 44 | 'Content-Length': AnyString 45 | 'Content-Location': AnyString 46 | 'Content-Range': AnyString 47 | 'Content-Security-Policy': AnyString 48 | 'Content-Security-Policy-Report-Only': AnyString 49 | 'Content-Type': MimeType | AnyString 50 | 'Cross-Origin-Embedder-Policy': 51 | | 'unsafe-none' 52 | | 'require-corp' 53 | | 'credentialless' 54 | | AnyString 55 | 'Cross-Origin-Opener-Policy': 56 | | 'unsafe-none' 57 | | 'same-origin-allow-popups' 58 | | 'same-origin' 59 | | AnyString 60 | 'Cross-Origin-Resource-Policy': 61 | | 'same-site' 62 | | 'same-origin' 63 | | 'cross-origin' 64 | | AnyString 65 | 'Date': AnyString 66 | 'Device-Memory': AnyString 67 | 'Digest': AnyString 68 | 'Downlink': AnyString 69 | 'ECT': 'slow-2g' | '2g' | '3g' | '4g' | AnyString 70 | 'ETag': AnyString 71 | 'Early-Data': '1' | AnyString 72 | 'Expect-CT': AnyString 73 | 'Expires': AnyString 74 | 'Feature-Policy': AnyString 75 | 'Last-Event-ID': AnyString 76 | 'Last-Modified': AnyString 77 | 'Link': AnyString 78 | 'Location': AnyString 79 | 'NEL': AnyString 80 | 'Origin-Agent-Cluster': AnyString 81 | 'Origin-Isolation': AnyString 82 | 'Proxy-Authenticate': AnyString 83 | 'Public-Key-Pins': AnyString 84 | 'Public-Key-Pins-Report-Only': AnyString 85 | 'Refresh': AnyString 86 | 'Report-To': AnyString 87 | 'Retry-After': AnyString 88 | 'Save-Data': AnyString 89 | 'Sec-WebSocket-Accept': AnyString 90 | 'Sec-WebSocket-Extensions': AnyString 91 | 'Sec-WebSocket-Protocol': AnyString 92 | 'Sec-WebSocket-Version': AnyString 93 | 'Server': AnyString 94 | 'Server-Timing': AnyString 95 | 'Service-Worker-Allowed': AnyString 96 | 'Service-Worker-Navigation-Preload': AnyString 97 | 'Set-Cookie': AnyString 98 | 'Signature': AnyString 99 | 'Signed-Headers': AnyString 100 | 'Sourcemap': AnyString 101 | 'Strict-Transport-Security': AnyString 102 | 'Timing-Allow-Origin': AnyString 103 | 'Tk': AnyString 104 | 'Vary': AnyString 105 | 'Via': AnyString 106 | 'WWW-Authenticate': AnyString 107 | 'X-Content-Type-Options': 'nosniff' | AnyString 108 | 'X-DNS-Prefetch-Control': 'on' | 'off' | AnyString 109 | 'X-Frame-Options': 'DENY' | 'SAMEORIGIN' | AnyString 110 | 'X-Permitted-Cross-Domain-Policies': 111 | | 'none' 112 | | 'master-only' 113 | | 'by-content-type' 114 | | 'all' 115 | | AnyString 116 | 'X-Powered-By': AnyString 117 | 'X-Robots-Tag': AnyString 118 | 'X-UA-Compatible': 'IE=edge' | AnyString 119 | 'X-XSS-Protection': '0' | '1' | '1; mode=block' | AnyString 120 | } 121 | -------------------------------------------------------------------------------- /src/inference.ts: -------------------------------------------------------------------------------- 1 | import type { HTTPMethod } from './http' 2 | import type { DynamicParam, Endpoint, StaticParam, WildcardParam } from './tree' 3 | import type { RespectOptionality } from './utils' 4 | 5 | // TODO: support TypedFetchURL | TypedFetchRequest 6 | export type TypedFetchInput = TypedFetchPath 7 | 8 | export type TypedFetchPath = { 9 | [K in keyof Schema]: K extends typeof Endpoint 10 | ? '' extends Method 11 | ? Base // return all possible endpoints 12 | : Method extends keyof Schema[K] 13 | ? Base // filter by method passed in 14 | : never 15 | : K extends StaticParam 16 | ? Schema[K] extends Record 17 | ? TypedFetchPath 18 | : never 19 | : K extends typeof DynamicParam 20 | ? Schema[K] extends Record 21 | ? TypedFetchPath 22 | : never 23 | : K extends typeof WildcardParam 24 | ? Schema[K] extends Record 25 | ? TypedFetchPath 26 | : never 27 | : never 28 | }[keyof Schema] 29 | 30 | // TODO: optimise me 31 | export type TypedFetchRequestInit = { 32 | [K in keyof Omit]?: K extends keyof TypedFetchMeta 33 | ? TypedFetchMeta[K] 34 | : RequestInit[K] 35 | } & ( 36 | 'method' extends keyof TypedFetchMeta 37 | // if GET is a valid method we don't require method to be specified 38 | ? 'GET' extends TypedFetchMeta['method'] 39 | ? { method?: TypedFetchMeta['method'] } 40 | : { method: TypedFetchMeta['method'] } 41 | : { method?: RequestInit['method'] } 42 | ) & RespectOptionality, 'body', RequestInit['body']> 43 | 44 | export type TypedFetchMeta = { 45 | [K in keyof Schema]: K extends typeof Endpoint 46 | ? '' extends Method 47 | ? { [M in keyof Schema[K]]: { method: M } & Schema[K][M] }[keyof Schema[K]] 48 | : Method extends keyof Schema[K] 49 | ? Schema[K][Method] & { method: Method } 50 | : never 51 | : Path extends K 52 | ? TypedFetchMeta 53 | : K extends StaticParam 54 | ? Path extends `${K}${infer Rest}` 55 | ? Rest extends keyof Schema[K] 56 | ? Pattern extends 'dynamic' 57 | ? typeof DynamicParam extends keyof Schema[K] 58 | ? Path extends `${string}${string}/${infer Additional}` 59 | ? TypedFetchMeta | TypedFetchMeta 60 | : never 61 | : typeof WildcardParam extends keyof Schema[K] 62 | ? TypedFetchMeta | TypedFetchMeta 63 | : never 64 | : TypedFetchMeta 65 | : typeof DynamicParam extends keyof Schema[K] 66 | ? Path extends `${string}${string}/${infer Rest}` 67 | ? TypedFetchMeta 68 | : never 69 | : typeof WildcardParam extends keyof Schema[K] 70 | ? TypedFetchMeta 71 | : never 72 | : never 73 | : never 74 | }[keyof Schema] 75 | 76 | export type TypedFetchResponseBody 77 | = TypedFetchMeta extends never 78 | ? 'response' extends keyof TypedFetchMeta 79 | ? TypedFetchMeta['response'] 80 | : never 81 | : 'response' extends keyof TypedFetchMeta 82 | ? TypedFetchMeta['response'] 83 | : never 84 | 85 | export type TypedFetchResponseHeaders 86 | = TypedFetchMeta extends never 87 | ? 'responseHeaders' extends keyof TypedFetchMeta 88 | ? TypedFetchMeta['responseHeaders'] 89 | : never 90 | : 'responseHeaders' extends keyof TypedFetchMeta 91 | ? TypedFetchMeta['responseHeaders'] 92 | : never 93 | -------------------------------------------------------------------------------- /test/schema.test-d.ts: -------------------------------------------------------------------------------- 1 | import type { TypedResponse } from '../src/fetch' 2 | import type { HTTPMethod } from '../src/http' 3 | import type { TypedFetchInput, TypedFetchMeta, TypedFetchRequestInit, TypedFetchResponseBody, TypedFetchResponseHeaders } from '../src/inference' 4 | import type { RespectOptionality, Trimmed } from '../src/utils' 5 | 6 | import type { ExampleSchema } from './fixture/schema' 7 | import { describe, expectTypeOf, it } from 'vitest' 8 | 9 | describe('fetchdts', () => { 10 | it('should fail with untyped inputs in strict mode', () => { 11 | // @ts-expect-error not an endpoint, just a path prefix 12 | infer('/api') 13 | 14 | infer('https://jsonplaceholder.typicode.com/posts') 15 | 16 | // @ts-expect-error not a string 17 | infer(22) 18 | 19 | // TODO: support falling back to untyped fetch in this case 20 | // @ts-expect-error this is an untyped fetch input 21 | infer('http://test.com') 22 | }) 23 | 24 | it.todo('should fall back to untyped fetch in this case when configured') 25 | 26 | it('should type the response', () => { 27 | expectTypeOf(infer('/api/static', { method: 'POST' })).toEqualTypeOf<{ type: 'static', method: 'POST' }>() 28 | 29 | // should fall back to dynamic parameter 30 | expectTypeOf(infer('/api/static')).toEqualTypeOf<{ type: 'dynamic', method: 'GET' }>() 31 | }) 32 | 33 | it('should handle options correctly for GET methods', () => { 34 | expectTypeOf(infer('/methods/get')).toMatchTypeOf<{ method: 'GET' }>() 35 | expectTypeOf(infer('/methods/get', {})).toMatchTypeOf<{ method: 'GET' }>() 36 | }) 37 | 38 | it('should require options for non-GET methods', () => { 39 | // @ts-expect-error requires options for non-GET methods 40 | infer('/methods/post') 41 | 42 | expectTypeOf(infer('/methods/post', { method: 'POST' })).toEqualTypeOf<{ method: 'POST' }>() 43 | }) 44 | 45 | it('should support parameters', () => { 46 | expectTypeOf(infer('/api/dynamic')).toEqualTypeOf<{ type: 'dynamic', method: 'GET' }>() 47 | expectTypeOf(infer('https://jsonplaceholder.typicode.com/posts')).toEqualTypeOf<{ id: number }>() 48 | expectTypeOf(infer('https://jsonplaceholder.typicode.com/posts/test')).toEqualTypeOf() 49 | 50 | // @ts-expect-error dynamic/wildcard parameters require at least one character 51 | infer('/api/') 52 | 53 | // TODO: would be nice to disallow `/` character 54 | infer('/api/foo/bob') 55 | 56 | // TODO: would be nice to disallow `/` character 57 | infer('https://jsonplaceholder.typicode.com/posts/test/foo') 58 | }) 59 | 60 | it('should strip query parameters', () => { 61 | expectTypeOf(infer('https://jsonplaceholder.typicode.com/posts?test')).toEqualTypeOf<{ id: number }>() 62 | }) 63 | 64 | it('can enforce typed headers', async () => { 65 | // @ts-expect-error missing authorization header 66 | infer('/headers/request', { method: 'POST', headers: {} }) 67 | 68 | // @ts-expect-error authorization header should be a string 69 | infer('/headers/request', { method: 'POST', headers: { authorization: 12 } }) 70 | 71 | // requires authorization and also accepts additional headers 72 | infer('/headers/request', { 73 | method: 'POST', 74 | headers: { 75 | authorization: 'my-key', 76 | other: 'foo', 77 | }, 78 | }) 79 | 80 | // types response headers as well 81 | const res = inferResponse('/headers/response') 82 | const header = res.headers.get('content-type') 83 | const data = await res.json() 84 | expectTypeOf(header).toEqualTypeOf<'application/json' | null>() 85 | expectTypeOf(data).toEqualTypeOf<{ type: 'headers', method: 'POST' }>() 86 | }) 87 | 88 | it('can enforce typed body', () => { 89 | // @ts-expect-error missing body 90 | infer('/body/required', { method: 'POST' }) 91 | 92 | // @ts-expect-error missing body 93 | infer('/body/required', { method: 'POST', body: {} }) 94 | 95 | // @ts-expect-error incorrect type 96 | infer('/body/required', { 97 | method: 'POST', 98 | body: { 99 | id: 'bar', 100 | }, 101 | }) 102 | 103 | infer('/body/required', { 104 | method: 'POST', 105 | body: { 106 | id: 200, 107 | }, 108 | }) 109 | 110 | infer('/body/optional', { 111 | method: 'POST', 112 | }) 113 | }) 114 | 115 | it('can enforce typed query', () => { 116 | // @ts-expect-error missing query 117 | infer('/query/required', { method: 'POST' }) 118 | 119 | // @ts-expect-error missing query 120 | infer('/query/required', { method: 'POST', query: {} }) 121 | 122 | // @ts-expect-error incorrect type 123 | infer('/query/required', { 124 | method: 'POST', 125 | query: { 126 | id: 'bar', 127 | }, 128 | }) 129 | 130 | infer('/query/required', { 131 | method: 'POST', 132 | query: { 133 | id: 200, 134 | }, 135 | }) 136 | 137 | infer('/query/optional', { 138 | method: 'POST', 139 | }) 140 | }) 141 | }) 142 | 143 | type CustomTypedFetchRequestInit> = TypedFetchRequestInit & RespectOptionality, 'query', Record> 144 | 145 | function infer, Init extends CustomTypedFetchRequestInit>(_input: T, _init: Init): TypedFetchResponseBody, Init['method'] extends HTTPMethod ? Init['method'] : 'GET'> 146 | 147 | function infer, Init extends CustomTypedFetchRequestInit>(_input: T, _init?: Init): TypedFetchResponseBody, 'GET'> 148 | 149 | function infer(_input: any, _init?: any) { 150 | return {} as any 151 | } 152 | 153 | function inferResponse, Init extends CustomTypedFetchRequestInit>(_input: T, _init: Init): TypedResponse, Init['method'] extends HTTPMethod ? Init['method'] : 'GET'>, TypedFetchResponseHeaders, Init['method'] extends HTTPMethod ? Init['method'] : 'GET'>> 154 | 155 | function inferResponse, Init extends CustomTypedFetchRequestInit>(_input: T, _init?: Init): TypedResponse, 'GET'>, TypedFetchResponseHeaders, 'GET'>> 156 | 157 | function inferResponse(_input: any, _init?: any) { 158 | return {} as any 159 | } 160 | -------------------------------------------------------------------------------- /src/http/headers/request.ts: -------------------------------------------------------------------------------- 1 | import type { HTTPMethod } from '..' 2 | import type { MimeType } from '../mimes' 3 | 4 | type AnyString = string & {} 5 | 6 | export type RequestHeaderName = keyof RequestHeaderMap | AnyString 7 | 8 | export interface RequestHeaderMap { 9 | 'Accept': MimeType | AnyString 10 | 'Accept-Charset': AnyString 11 | 'Accept-Encoding': 12 | | 'gzip' 13 | | 'compress' 14 | | 'deflate' 15 | | 'br' 16 | | 'identity' 17 | | AnyString 18 | 'Accept-Language': AnyString 19 | 'Accept-Ch': 20 | | 'Sec-CH-UA' 21 | | 'Sec-CH-UA-Arch' 22 | | 'Sec-CH-UA-Bitness' 23 | | 'Sec-CH-UA-Full-Version-List' 24 | | 'Sec-CH-UA-Full-Version' 25 | | 'Sec-CH-UA-Mobile' 26 | | 'Sec-CH-UA-Model' 27 | | 'Sec-CH-UA-Platform' 28 | | 'Sec-CH-UA-Platform-Version' 29 | | 'Sec-CH-Prefers-Reduced-Motion' 30 | | 'Sec-CH-Prefers-Color-Scheme' 31 | | 'Device-Memory' 32 | | 'Width' 33 | | 'Viewport-Width' 34 | | 'Save-Data' 35 | | 'Downlink' 36 | | 'ECT' 37 | | 'RTT' 38 | | AnyString 39 | 'Access-Control-Allow-Credentials': 'true' | 'false' | AnyString 40 | 'Access-Control-Allow-Headers': RequestHeaderName | AnyString 41 | 'Access-Control-Allow-Methods': HTTPMethod | AnyString 42 | 'Access-Control-Allow-Origin': '*' | AnyString 43 | 'Access-Control-Expose-Headers': RequestHeaderName | AnyString 44 | 'Access-Control-Max-Age': AnyString 45 | 'Access-Control-Request-Headers': RequestHeaderName | AnyString 46 | 'Access-Control-Request-Method': HTTPMethod | AnyString 47 | 'Age': AnyString 48 | 'Allow': HTTPMethod | AnyString 49 | 'Authorization': AnyString 50 | 'Cache-Control': 51 | | 'no-cache' 52 | | 'no-store' 53 | | 'max-age' 54 | | 'must-revalidate' 55 | | 'public' 56 | | 'private' 57 | | 'proxy-revalidate' 58 | | 's-maxage' 59 | | 'stale-while-revalidate' 60 | | 'stale-if-error' 61 | | AnyString 62 | 'Connection': 'keep-alive' | 'close' | 'upgrade' | AnyString 63 | 'Content-Disposition': AnyString 64 | 'Content-Encoding': 65 | | 'gzip' 66 | | 'compress' 67 | | 'deflate' 68 | | 'br' 69 | | 'identity' 70 | | AnyString 71 | 'Content-Language': AnyString 72 | 'Content-Length': AnyString 73 | 'Content-Location': AnyString 74 | 'Content-Range': AnyString 75 | 'Content-Security-Policy': AnyString 76 | 'Content-Type': MimeType | AnyString 77 | 'Cookie': AnyString 78 | 'Critical-CH': AnyString 79 | 'Date': AnyString 80 | 'Device-Memory': '0.25' | '0.5' | '1' | '2' | '4' | '8' | AnyString 81 | 'Digest': AnyString 82 | 'ETag': AnyString 83 | 'Expect': '100-continue' | AnyString 84 | 'Expires': AnyString 85 | 'Forwarded': AnyString 86 | 'From': AnyString 87 | 'Host': AnyString 88 | 'If-Match': AnyString 89 | 'If-Modified-Since': AnyString 90 | 'If-None-Match': AnyString 91 | 'If-Range': AnyString 92 | 'If-Unmodified-Since': AnyString 93 | 'Keep-Alive': `timeout=${string}, max=${string}` | AnyString 94 | 'Last-Modified': AnyString 95 | 'Link': AnyString 96 | 'Location': AnyString 97 | 'Max-Forwards': AnyString 98 | 'Origin': AnyString 99 | 'Origin-Agent-Cluster': `?1` | `?0` | AnyString 100 | 'Ping-From': AnyString 101 | 'Ping-To': AnyString 102 | 'Pragma': AnyString 103 | 'Proxy-Authenticate': AnyString 104 | 'Proxy-Authorization': AnyString 105 | 'Range': AnyString 106 | 'Referer': AnyString 107 | 'Referrer-Policy': 108 | | 'no-referrer' 109 | | 'no-referrer-when-downgrade' 110 | | 'origin' 111 | | 'origin-when-cross-origin' 112 | | 'same-origin' 113 | | 'strict-origin' 114 | | 'strict-origin-when-cross-origin' 115 | | 'unsafe-url' 116 | | AnyString 117 | 'Retry-After': AnyString 118 | 'Save-Data': `on` | `off` | AnyString 119 | 'Sec-CH-UA': AnyString 120 | 'Sec-CH-UA-Arch': 121 | | 'x86' 122 | | 'ARM' 123 | | '[arm64-v8a, armeabi-v7a, armeabi]' 124 | | AnyString 125 | 'Sec-CH-UA-Bitness': '64' | '32' | AnyString 126 | 'Sec-CH-UA-Full-Version-List': AnyString 127 | 'Sec-CH-UA-Mobile': `?1` | `?0` | AnyString 128 | 'Sec-CH-UA-Model': AnyString 129 | 'Sec-CH-UA-Platform': 130 | | 'Android' 131 | | 'Chrome OS' 132 | | 'Chromium OS' 133 | | 'iOS' 134 | | 'Linux' 135 | | 'macOS' 136 | | 'Windows' 137 | | 'Unknown' 138 | | AnyString 139 | 'Sec-CH-UA-Platform-Version': AnyString 140 | 'Sec-CH-UA-Prefers-Color-Scheme': 'dark' | 'light' | AnyString 141 | 'Sec-CH-UA-Prefers-Reduced-Motion': 'no-preference' | 'reduce' | AnyString 142 | 'Sec-Fetch-Dest': 143 | | 'audio' 144 | | 'audioworklet' 145 | | 'document' 146 | | 'embed' 147 | | 'empty' 148 | | 'font' 149 | | 'frame' 150 | | 'iframe' 151 | | 'image' 152 | | 'manifest' 153 | | 'object' 154 | | 'paintworklet' 155 | | 'report' 156 | | 'script' 157 | | 'serviceworker' 158 | | 'sharedworker' 159 | | 'style' 160 | | 'track' 161 | | 'video' 162 | | 'worker' 163 | | 'xslt' 164 | | AnyString 165 | 'Sec-Fetch-Mode': 166 | | 'cors' 167 | | 'navigate' 168 | | 'no-cors' 169 | | 'same-origin' 170 | | 'websocket' 171 | | AnyString 172 | 'Sec-Fetch-Site': 173 | | 'cross-site' 174 | | 'same-origin' 175 | | 'same-site' 176 | | 'none' 177 | | AnyString 178 | 'Sec-Fetch-User': '?1' | AnyString 179 | 'Sec-Purpose': 'prefetch' | AnyString 180 | 'Sec-WebSocket-Accept': AnyString 181 | 'Sec-WebSocket-Extensions': AnyString 182 | 'Sec-WebSocket-Key': AnyString 183 | 'Sec-WebSocket-Protocol': AnyString 184 | 'Sec-WebSocket-Version': AnyString 185 | 'Server': AnyString 186 | 'Service-Worker-Allowed': AnyString 187 | 'Set-Cookie': AnyString 188 | 'Strict-Transport-Security': AnyString 189 | 'TE': 'trailers' | AnyString 190 | 'Trailer': AnyString 191 | 'Transfer-Encoding': 192 | | 'chunked' 193 | | 'compress' 194 | | 'deflate' 195 | | 'gzip' 196 | | 'identity' 197 | | AnyString 198 | 'Upgrade': AnyString 199 | 'Upgrade-Insecure-Requests': '1' | AnyString 200 | 'User-Agent': AnyString 201 | 'Vary': AnyString 202 | 'Via': AnyString 203 | 'Warning': AnyString 204 | 'WWW-Authenticate': AnyString 205 | 'X-Content-Type-Options': 'nosniff' | AnyString 206 | 'X-DNS-Prefetch-Control': 'on' | 'off' | AnyString 207 | 'X-Forwarded-For': AnyString 208 | 'X-Forwarded-Host': AnyString 209 | 'X-Forwarded-Proto': AnyString 210 | 'X-Frame-Options': 'deny' | 'sameorigin' | AnyString 211 | 'X-Permitted-Cross-Domain-Policies': 212 | | 'none' 213 | | 'master-only' 214 | | 'by-content-type' 215 | | 'all' 216 | | AnyString 217 | 'X-Pingback': AnyString 218 | 'X-Requested-With': AnyString 219 | 'X-XSS-Protection': '0' | '1' | '1; mode=block' | AnyString 220 | } 221 | -------------------------------------------------------------------------------- /test/url.test-d.ts: -------------------------------------------------------------------------------- 1 | import type { TypedURLSearchParams } from '../src/http/url' 2 | import { describe, expectTypeOf, it } from 'vitest' 3 | 4 | describe('TypedURLSearchParams', () => { 5 | interface TestQueryMap { 6 | page: '1' | '2' 7 | limit: string 8 | search: string 9 | } 10 | 11 | it('should preserve URLSearchParams interface properties', () => { 12 | type TestParams = TypedURLSearchParams<{ test: string }> 13 | 14 | // Should have all URLSearchParams properties including size 15 | expectTypeOf().toHaveProperty('size') 16 | expectTypeOf().toEqualTypeOf>() 17 | 18 | // Should have the typed method overrides 19 | expectTypeOf().toHaveProperty('append') 20 | expectTypeOf().toHaveProperty('delete') 21 | expectTypeOf().toHaveProperty('get') 22 | expectTypeOf().toHaveProperty('getAll') 23 | expectTypeOf().toHaveProperty('has') 24 | expectTypeOf().toHaveProperty('set') 25 | expectTypeOf().toHaveProperty('sort') 26 | expectTypeOf().toHaveProperty('toString') 27 | expectTypeOf().toHaveProperty('forEach') 28 | 29 | // Should have all other URLSearchParams properties/methods 30 | expectTypeOf().toEqualTypeOf() 31 | }) 32 | 33 | it('should type append method correctly', () => { 34 | type AppendMethod = TypedURLSearchParams['append'] 35 | 36 | expectTypeOf().parameter(0).toEqualTypeOf() 37 | expectTypeOf().parameter(1).toEqualTypeOf() 38 | expectTypeOf().returns.toEqualTypeOf() 39 | }) 40 | 41 | it('should type delete method correctly', () => { 42 | type DeleteMethod = TypedURLSearchParams['delete'] 43 | 44 | expectTypeOf().parameter(0).toEqualTypeOf() 45 | expectTypeOf().parameter(1).toEqualTypeOf() 46 | expectTypeOf().returns.toEqualTypeOf() 47 | }) 48 | 49 | it('should type get method correctly', () => { 50 | type GetMethod = TypedURLSearchParams['get'] 51 | 52 | expectTypeOf().parameter(0).toEqualTypeOf() 53 | expectTypeOf().returns.toEqualTypeOf() 54 | }) 55 | 56 | it('should type getAll method correctly', () => { 57 | type GetAllMethod = TypedURLSearchParams['getAll'] 58 | 59 | expectTypeOf().parameter(0).toEqualTypeOf() 60 | expectTypeOf().returns.toEqualTypeOf>() 61 | }) 62 | 63 | it('should type has method correctly', () => { 64 | type HasMethod = TypedURLSearchParams['has'] 65 | 66 | expectTypeOf().parameter(0).toEqualTypeOf() 67 | expectTypeOf().parameter(1).toEqualTypeOf() 68 | expectTypeOf().returns.toEqualTypeOf() 69 | }) 70 | 71 | it('should type set method correctly', () => { 72 | type SetMethod = TypedURLSearchParams['set'] 73 | 74 | expectTypeOf().parameter(0).toEqualTypeOf() 75 | expectTypeOf().parameter(1).toEqualTypeOf() 76 | expectTypeOf().returns.toEqualTypeOf() 77 | }) 78 | 79 | it('should type utility methods correctly', () => { 80 | type SortMethod = TypedURLSearchParams['sort'] 81 | type ToStringMethod = TypedURLSearchParams['toString'] 82 | type ForEachMethod = TypedURLSearchParams['forEach'] 83 | 84 | expectTypeOf().returns.toEqualTypeOf() 85 | expectTypeOf().returns.toEqualTypeOf() 86 | expectTypeOf().parameter(0).toEqualTypeOf<(value: string | string & {}, key: keyof TestQueryMap | string & {}, parent: URLSearchParams) => void>() 87 | expectTypeOf().parameter(1).toEqualTypeOf() 88 | expectTypeOf().returns.toEqualTypeOf() 89 | }) 90 | 91 | it('should work with default generic type', () => { 92 | type DefaultParams = TypedURLSearchParams 93 | 94 | expectTypeOf().parameter(0).toEqualTypeOf() 95 | expectTypeOf().returns.toEqualTypeOf() 96 | }) 97 | 98 | it('should work with unknown query map', () => { 99 | type UnknownParams = TypedURLSearchParams 100 | 101 | expectTypeOf().parameter(0).toEqualTypeOf() 102 | expectTypeOf().returns.toEqualTypeOf() 103 | }) 104 | 105 | it('should work with partial record', () => { 106 | type PartialParams = TypedURLSearchParams>> 107 | 108 | expectTypeOf().parameter(0).toEqualTypeOf() 109 | expectTypeOf().returns.toEqualTypeOf() 110 | }) 111 | 112 | it('should handle typed query map with specific values', () => { 113 | interface TypedQueryMap { 114 | status: 'active' | 'inactive' 115 | sort: 'asc' | 'desc' 116 | category: string 117 | } 118 | 119 | type TypedParams = TypedURLSearchParams 120 | 121 | // get method should return conditional types based on the key 122 | expectTypeOf().parameter(0).toEqualTypeOf() 123 | expectTypeOf().returns.toEqualTypeOf<'active' | 'inactive' | 'asc' | 'desc' | string | null>() 124 | 125 | // append/set should accept conditional types based on the key 126 | expectTypeOf().parameter(1).toEqualTypeOf<'active' | 'inactive' | 'asc' | 'desc' | string>() 127 | expectTypeOf().parameter(1).toEqualTypeOf<'active' | 'inactive' | 'asc' | 'desc' | string>() 128 | }) 129 | 130 | it('should preserve URLSearchParams interface methods', () => { 131 | type TestParams = TypedURLSearchParams<{ test: string }> 132 | 133 | // Should have all URLSearchParams properties 134 | expectTypeOf().toHaveProperty('size') 135 | expectTypeOf().toHaveProperty('append') 136 | expectTypeOf().toHaveProperty('delete') 137 | expectTypeOf().toHaveProperty('get') 138 | expectTypeOf().toHaveProperty('getAll') 139 | expectTypeOf().toHaveProperty('has') 140 | expectTypeOf().toHaveProperty('set') 141 | expectTypeOf().toHaveProperty('sort') 142 | expectTypeOf().toHaveProperty('toString') 143 | expectTypeOf().toHaveProperty('forEach') 144 | }) 145 | 146 | it('should handle string & {} pattern for extensibility', () => { 147 | interface QueryMap { 148 | known: string 149 | } 150 | 151 | type TestParams = TypedURLSearchParams 152 | 153 | // Should accept both known keys and arbitrary strings 154 | const params = {} as TestParams 155 | 156 | expectTypeOf(params.get).toBeCallableWith('known') 157 | expectTypeOf(params.get).toBeCallableWith('unknown-param') 158 | expectTypeOf(params.append).toBeCallableWith('known', 'value') 159 | expectTypeOf(params.append).toBeCallableWith('unknown-param', 'value') 160 | }) 161 | 162 | it('should handle forEach callback typing', () => { 163 | interface QueryMap { 164 | test: string 165 | another: string 166 | } 167 | 168 | type TestParams = TypedURLSearchParams 169 | const params = {} as TestParams 170 | 171 | params.forEach((value, key, parent) => { 172 | expectTypeOf(value).toEqualTypeOf() 173 | expectTypeOf(key).toEqualTypeOf<(string & {}) | keyof QueryMap>() 174 | expectTypeOf(parent).toEqualTypeOf() 175 | }) 176 | }) 177 | 178 | it('should constrain query map to string values', () => { 179 | interface InvalidQueryMap { 180 | id: number // This should still work but be treated as unknown 181 | name: string 182 | } 183 | 184 | type InvalidParams = TypedURLSearchParams 185 | 186 | // The interface should still be usable even with non-string types 187 | expectTypeOf().parameter(0).toEqualTypeOf() 188 | }) 189 | 190 | it('should handle string literal types for specific keys', () => { 191 | type TestParams = TypedURLSearchParams 192 | const params = {} as TestParams 193 | 194 | // When using the specific 'page' key, should return the literal types 195 | expectTypeOf(params.get('page')).toEqualTypeOf<'1' | '2' | null>() 196 | expectTypeOf(params.getAll('page')).toEqualTypeOf>() 197 | 198 | // When setting page, should only accept the literal values or string 199 | expectTypeOf(params.append).toBeCallableWith('page', '1') 200 | expectTypeOf(params.append).toBeCallableWith('page', '2') 201 | expectTypeOf(params.set).toBeCallableWith('page', '1') 202 | expectTypeOf(params.set).toBeCallableWith('page', '2') 203 | 204 | // When using other keys with string type, should work with any string 205 | expectTypeOf(params.get('limit')).toEqualTypeOf() 206 | expectTypeOf(params.get('search')).toEqualTypeOf() 207 | 208 | // When using unknown keys, should fallback to string 209 | expectTypeOf(params.get('unknown')).toEqualTypeOf() 210 | }) 211 | }) 212 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fetchdts 2 | 3 | [![npm version][npm-version-src]][npm-version-href] 4 | [![npm downloads][npm-downloads-src]][npm-downloads-href] 5 | [![Github Actions][github-actions-src]][github-actions-href] 6 | [![Codecov][codecov-src]][codecov-href] 7 | 8 | > A suite of type utilities for building strongly-typed APIs 9 | 10 | 🚧 Under active development 11 | 12 | - [▶️  Online playground](https://stackblitz.com/github/unjs/fetchdts/tree/main/playground) 13 | 14 | ## Features 15 | 16 | - 💪 Strongly-typed query, body, headers, response. 17 | - 🗺️ Static path segments, as well as dynamic and wildcard parameters. 18 | - 📦 Exposes core utilities for building typed fetch functions. 19 | 20 | ## Usage 21 | 22 | Install package: 23 | 24 | ```sh 25 | # npm 26 | npm install fetchdts 27 | 28 | # pnpm 29 | pnpm install fetchdts 30 | ``` 31 | 32 | ## Quick Start 33 | 34 | Define your API schema and create a strongly-typed fetch function: 35 | 36 | ```ts 37 | import type { DynamicParam, Endpoint, TypedFetchInput, TypedFetchRequestInit, TypedFetchResponseBody, TypedResponse } from 'fetchdts' 38 | 39 | // Define your API schema 40 | interface APISchema { 41 | '/users': { 42 | [Endpoint]: { 43 | GET: { 44 | response: { id: number, name: string }[] 45 | } 46 | POST: { 47 | body: { name: string, email: string } 48 | response: { id: number, name: string, email: string } 49 | } 50 | } 51 | [DynamicParam]: { // /users/:id 52 | [Endpoint]: { 53 | GET: { 54 | response: { id: number, name: string, email: string } 55 | } 56 | DELETE: { 57 | response: { success: boolean } 58 | } 59 | } 60 | } 61 | } 62 | } 63 | 64 | // Create your typed fetch function 65 | async function api>( 66 | input: T, 67 | init?: TypedFetchRequestInit, 68 | ) { 69 | return fetch(input, init as RequestInit) as unknown as Promise>> 70 | } 71 | 72 | // Use with full type safety 73 | const users = await api('/users').then(r => r.json()) // Type: { id: number; name: string }[] 74 | const user = await api('/users/123').then(r => r.json()) // Type: { id: number; name: string; email: string } 75 | ``` 76 | 77 | ## Core Concepts 78 | 79 | ### Schema Definition 80 | 81 | Your API schema describes the structure of your endpoints using TypeScript interfaces: 82 | 83 | ```ts 84 | interface Schema { 85 | '/path': { 86 | [Endpoint]: { 87 | [HTTPMethod]: { 88 | query?: { param: string } // Query parameters 89 | body?: { data: any } // Request body 90 | headers?: { auth: string } // Required headers 91 | response: { result: any } // Response data 92 | responseHeaders?: { 'x-rate-limit': string } // Response headers 93 | } 94 | } 95 | } 96 | } 97 | ``` 98 | 99 | ### Path Types 100 | 101 | **Static Paths**: Exact string matches 102 | ```ts 103 | interface Schema { 104 | '/api/users': { 105 | [Endpoint]: { 106 | GET: { response: User[] } 107 | } 108 | } 109 | } 110 | ``` 111 | 112 | **Dynamic Parameters**: Single path segments 113 | ```ts 114 | interface Schema { 115 | '/api/users': { 116 | [DynamicParam]: { // matches /api/users/123, /api/users/abc, etc. 117 | [Endpoint]: { 118 | GET: { response: User } 119 | } 120 | } 121 | } 122 | } 123 | ``` 124 | 125 | **Wildcard Parameters**: Multiple path segments 126 | ```ts 127 | interface Schema { 128 | '/api': { 129 | [WildcardParam]: { // matches /api/anything/nested/deep 130 | [Endpoint]: { 131 | GET: { response: any } 132 | } 133 | } 134 | } 135 | } 136 | ``` 137 | 138 | ### Symbols Reference 139 | 140 | fetchdts uses special symbols to define different types of route matching: 141 | 142 | ```ts 143 | import { DynamicParam, Endpoint, WildcardParam } from 'fetchdts' 144 | 145 | interface Schema { 146 | // Endpoint: Marks where HTTP methods are defined 147 | [Endpoint]: { 148 | GET: { response: Data } 149 | POST: { body: Input, response: Data } 150 | } 151 | 152 | // DynamicParam: Matches single path segments (e.g., /users/:id) 153 | [DynamicParam]: { 154 | [Endpoint]: { 155 | GET: { response: User } 156 | } 157 | } 158 | 159 | // WildcardParam: Matches multiple path segments (e.g., /files/*) 160 | [WildcardParam]: { 161 | [Endpoint]: { 162 | GET: { response: File } 163 | } 164 | } 165 | } 166 | ``` 167 | 168 | ### HTTP Methods 169 | 170 | All standard HTTP methods are supported: 171 | - `GET`, `POST`, `PUT`, `DELETE`, `PATCH` 172 | - `OPTIONS`, `HEAD`, `CONNECT`, `TRACE` 173 | 174 | ```ts 175 | interface RESTSchema { 176 | '/api/users': { 177 | [Endpoint]: { 178 | GET: { response: User[] } 179 | POST: { body: CreateUser, response: User } 180 | } 181 | [DynamicParam]: { 182 | [Endpoint]: { 183 | GET: { response: User } 184 | PUT: { body: UpdateUser, response: User } 185 | PATCH: { body: Partial, response: User } 186 | DELETE: { response: { success: boolean } } 187 | } 188 | } 189 | } 190 | } 191 | ``` 192 | 193 | ## API Reference 194 | 195 | ### Core Types 196 | 197 | #### `TypedFetchInput` 198 | Extracts valid URL paths from your schema: 199 | ```ts 200 | type ValidPaths = TypedFetchInput 201 | // Result: '/users' | '/users/${string}' 202 | ``` 203 | 204 | #### `TypedFetchRequestInit` 205 | Provides typed request options for a specific path: 206 | ```ts 207 | // For paths requiring body/headers/query parameters 208 | await api('/users', { 209 | method: 'POST', 210 | body: { name: 'John' }, // ✅ Typed based on schema 211 | headers: { authorization: 'Bearer token' } 212 | }) 213 | ``` 214 | 215 | #### `TypedFetchResponseBody` 216 | Returns the typed response body for a given path: 217 | ```ts 218 | const response = await api('/users/123') 219 | // Type automatically inferred from schema 220 | ``` 221 | 222 | #### `TypedHeaders` 223 | Provides typed header access: 224 | ```ts 225 | const contentType = response.headers.get('content-type') // string | null 226 | const customHeader = response.headers.get('x-custom') // Typed based on schema 227 | ``` 228 | 229 | ### Utilities 230 | 231 | #### `serializeRoutes(name, routes, options?)` 232 | Generate TypeScript schema from route definitions: 233 | 234 | ```ts 235 | import { serializeRoutes } from 'fetchdts' 236 | 237 | const schema = serializeRoutes('APISchema', [ 238 | { 239 | path: '/users', 240 | metadata: { 241 | GET: { 242 | responseType: 'User[]' 243 | }, 244 | POST: { 245 | bodyType: '{ name: string }', 246 | responseType: 'User' 247 | } 248 | } 249 | }, 250 | { 251 | path: '/users/:id', 252 | type: 'dynamic', 253 | metadata: { 254 | GET: { 255 | responseType: 'User' 256 | } 257 | } 258 | } 259 | ]) 260 | 261 | console.log(schema) 262 | // Outputs TypeScript interface definition 263 | ``` 264 | 265 | ## Advanced Examples 266 | 267 | ### Typed Headers and Query Parameters 268 | 269 | ```ts 270 | interface APISchema { 271 | '/search': { 272 | [Endpoint]: { 273 | GET: { 274 | query: { q: string, limit?: number } 275 | headers: { 'x-api-key': string } 276 | response: { results: string[], total: number } 277 | responseHeaders: { 'x-rate-limit-remaining': string } 278 | } 279 | } 280 | } 281 | } 282 | 283 | // Usage with required query and headers 284 | const results = await api('/search', { 285 | query: { q: 'typescript', limit: 10 }, 286 | headers: { 'x-api-key': 'your-key' } 287 | }) 288 | 289 | // Access typed response headers 290 | const rateLimit = results.headers.get('x-rate-limit-remaining') // string | null 291 | ``` 292 | 293 | ### Mixed Static and Dynamic Routes 294 | 295 | ```ts 296 | interface APISchema { 297 | '/api': { 298 | '/health': { 299 | [Endpoint]: { 300 | GET: { response: { status: 'ok' | 'error' } } 301 | } 302 | } 303 | '/users': { 304 | [Endpoint]: { 305 | GET: { response: User[] } 306 | POST: { body: CreateUser, response: User } 307 | } 308 | [DynamicParam]: { 309 | [Endpoint]: { 310 | GET: { response: User } 311 | PUT: { body: UpdateUser, response: User } 312 | DELETE: { response: { deleted: boolean } } 313 | } 314 | '/posts': { 315 | [Endpoint]: { 316 | GET: { response: Post[] } 317 | } 318 | [DynamicParam]: { 319 | [Endpoint]: { 320 | GET: { response: Post } 321 | } 322 | } 323 | } 324 | } 325 | } 326 | } 327 | } 328 | 329 | // All of these are now typed: 330 | await api('/api/health') // { status: 'ok' | 'error' } 331 | await api('/api/users') // User[] 332 | await api('/api/users/123') // User 333 | await api('/api/users/123/posts') // Post[] 334 | await api('/api/users/123/posts/456') // Post 335 | ``` 336 | 337 | ### Cross-Domain API Support 338 | 339 | ```ts 340 | interface Schema { 341 | 'https://api.github.com': { 342 | '/users': { 343 | [DynamicParam]: { 344 | [Endpoint]: { 345 | GET: { response: GitHubUser } 346 | } 347 | '/repos': { 348 | [Endpoint]: { 349 | GET: { response: Repository[] } 350 | } 351 | } 352 | } 353 | } 354 | } 355 | } 356 | 357 | // Works with full URLs 358 | const user = await api('https://api.github.com/users/octocat') 359 | const repos = await api('https://api.github.com/users/octocat/repos') 360 | ``` 361 | 362 | ### Error Handling with Types 363 | 364 | ```ts 365 | interface APISchema { 366 | '/api/users': { 367 | [DynamicParam]: { 368 | [Endpoint]: { 369 | GET: { 370 | response: User | { error: string, code: number } 371 | } 372 | } 373 | } 374 | } 375 | } 376 | 377 | const result = await api('/api/users/123') 378 | // result is typed as: User | { error: string; code: number } 379 | 380 | if ('error' in result) { 381 | console.error(`Error ${result.code}: ${result.error}`) 382 | } 383 | else { 384 | console.log(`User: ${result.name}`) 385 | } 386 | ``` 387 | 388 | ## Best Practices 389 | 390 | ### Schema Organization 391 | 392 | For large APIs, consider organizing your schemas into modules: 393 | 394 | ```ts 395 | // types/api.ts 396 | interface UserAPI { 397 | '/api/users': { 398 | [Endpoint]: { 399 | GET: { response: User[] } 400 | POST: { body: CreateUser, response: User } 401 | } 402 | [DynamicParam]: { 403 | [Endpoint]: { 404 | GET: { response: User } 405 | PUT: { body: UpdateUser, response: User } 406 | DELETE: { response: { success: boolean } } 407 | } 408 | } 409 | } 410 | } 411 | 412 | interface PostAPI { 413 | '/api/posts': { 414 | [Endpoint]: { 415 | GET: { query?: { limit?: number }, response: Post[] } 416 | POST: { body: CreatePost, response: Post } 417 | } 418 | [DynamicParam]: { 419 | [Endpoint]: { 420 | GET: { response: Post } 421 | PUT: { body: UpdatePost, response: Post } 422 | DELETE: { response: { success: boolean } } 423 | } 424 | } 425 | } 426 | } 427 | 428 | // Combine them 429 | type APISchema = UserAPI & PostAPI 430 | ``` 431 | 432 | ### Runtime Validation 433 | 434 | While fetchdts provides compile-time type safety, consider adding runtime validation: 435 | 436 | ```ts 437 | import { z } from 'zod' 438 | 439 | const UserSchema = z.object({ 440 | id: z.number(), 441 | name: z.string(), 442 | email: z.string().email() 443 | }) 444 | 445 | async function api>( 446 | input: T, 447 | init?: TypedFetchRequestInit 448 | ): Promise> { 449 | const response = await fetch(input, init as RequestInit) 450 | const data = await response.json() 451 | 452 | // Runtime validation for critical endpoints 453 | if (input.startsWith('/api/users/') && init?.method !== 'DELETE') { 454 | return UserSchema.parse(data) // Throws if invalid 455 | } 456 | 457 | return data 458 | } 459 | ``` 460 | 461 | ### Error Handling 462 | 463 | Design your schemas with error handling in mind: 464 | 465 | ```ts 466 | interface APISchema { 467 | '/api/users': { 468 | [DynamicParam]: { 469 | [Endpoint]: { 470 | GET: { 471 | response: 472 | | { success: true, data: User } 473 | | { success: false, error: string, code: number } 474 | } 475 | } 476 | } 477 | } 478 | } 479 | 480 | // Usage 481 | const result = await api('/api/users/123') 482 | if (result.success) { 483 | console.log(result.data.name) // ✅ Type-safe access 484 | } 485 | else { 486 | console.error(`Error ${result.code}: ${result.error}`) 487 | } 488 | ``` 489 | 490 | ## Troubleshooting 491 | 492 | ### TypeScript Configuration 493 | 494 | For the best experience, ensure your `tsconfig.json` includes: 495 | 496 | ```json 497 | { 498 | "compilerOptions": { 499 | "strict": true, 500 | "exactOptionalPropertyTypes": true, 501 | "noUncheckedIndexedAccess": true 502 | } 503 | } 504 | ``` 505 | 506 | ## 💻 Development 507 | 508 | I would welcome contributions! Please see the [Code of Conduct](./CODE_OF_CONDUCT.md). 509 | 510 | - Clone this repository 511 | - Enable [Corepack](https://github.com/nodejs/corepack) using `corepack enable` 512 | - Install dependencies using `pnpm install` 513 | - Run interactive tests using `pnpm dev` and type tests with `pnpm test:types` 514 | 515 | ## License 516 | 517 | Made with ❤️ 518 | 519 | Published under [MIT License](./LICENCE). 520 | 521 | 522 | 523 | [npm-version-src]: https://img.shields.io/npm/v/fetchdts?style=flat-square 524 | [npm-version-href]: https://npmjs.com/package/fetchdts 525 | [npm-downloads-src]: https://img.shields.io/npm/dm/fetchdts?style=flat-square 526 | [npm-downloads-href]: https://npm.chart.dev/fetchdts 527 | [github-actions-src]: https://img.shields.io/github/actions/workflow/status/unjs/fetchdts/ci.yml?branch=main&style=flat-square 528 | [github-actions-href]: https://github.com/unjs/fetchdts/actions?query=workflow%3Aci 529 | [codecov-src]: https://img.shields.io/codecov/c/gh/unjs/fetchdts/main?style=flat-square 530 | [codecov-href]: https://codecov.io/gh/unjs/fetchdts 531 | -------------------------------------------------------------------------------- /test/typed-fetch.test-d.ts: -------------------------------------------------------------------------------- 1 | import type { TypedHeaders, TypedRequest, TypedResponse } from '../src/fetch' 2 | import type { RequestHeaderMap, ResponseHeaderMap } from '../src/http' 3 | import { describe, expectTypeOf, it } from 'vitest' 4 | 5 | describe('TypedHeaders', () => { 6 | it('should type header operations correctly', () => { 7 | interface TestHeaderMap { 8 | 'content-type': 'application/json' | 'text/plain' 9 | 'authorization': `Bearer ${string}` 10 | } 11 | 12 | type TestHeaders = TypedHeaders 13 | 14 | // get method should return typed values or null 15 | expectTypeOf().parameter(0).toEqualTypeOf<'content-type' | 'authorization' | string & {}>() 16 | expectTypeOf().returns.toEqualTypeOf() 17 | 18 | // has method should accept header names 19 | expectTypeOf().parameter(0).toEqualTypeOf<'content-type' | 'authorization' | string & {}>() 20 | expectTypeOf().returns.toEqualTypeOf() 21 | 22 | // set method should accept typed values 23 | expectTypeOf().parameter(0).toEqualTypeOf<'content-type' | 'authorization' | string & {}>() 24 | expectTypeOf().parameter(1).toEqualTypeOf() 25 | expectTypeOf().returns.toEqualTypeOf() 26 | 27 | // append method should accept typed values 28 | expectTypeOf().parameter(0).toEqualTypeOf<'content-type' | 'authorization' | string & {}>() 29 | expectTypeOf().parameter(1).toEqualTypeOf<'application/json' | 'text/plain' | string>() 30 | expectTypeOf().returns.toEqualTypeOf() 31 | 32 | // delete method should accept header names 33 | expectTypeOf().parameter(0).toEqualTypeOf<'content-type' | 'authorization' | string & {}>() 34 | expectTypeOf().returns.toEqualTypeOf() 35 | 36 | const a = {} as TestHeaders 37 | a.forEach((value, key, parent) => { 38 | expectTypeOf(parent).toEqualTypeOf() 39 | expectTypeOf(key).toEqualTypeOf<'content-type' | 'authorization' | string & {}>() 40 | if (key === 'content-type') { 41 | expectTypeOf(key).toEqualTypeOf<'content-type' | string & {}>() 42 | expectTypeOf(value).toEqualTypeOf<'application/json' | 'text/plain' | `Bearer ${string}` | string & {}>() 43 | } 44 | }) 45 | }) 46 | 47 | it('should handle case-insensitive header names', () => { 48 | interface TestHeaderMap { 49 | 'Content-Type': 'application/json' 50 | 'X-Custom': string 51 | } 52 | type TestHeaders = TypedHeaders 53 | 54 | // Should accept both cases for known headers 55 | const headers = {} as TestHeaders 56 | 57 | // These should be valid calls (testing that lowercase works) 58 | expectTypeOf(headers.get).toBeCallableWith('content-type') 59 | expectTypeOf(headers.get).toBeCallableWith('Content-Type') 60 | expectTypeOf(headers.get).toBeCallableWith('x-custom') 61 | expectTypeOf(headers.get).toBeCallableWith('X-Custom') 62 | 63 | // Unknown headers should fallback to string 64 | expectTypeOf(headers.get).toBeCallableWith('unknown-header') 65 | }) 66 | 67 | it('should preserve Headers interface methods', () => { 68 | type TestHeaders = TypedHeaders<{ 'content-type': string }> 69 | 70 | // Should have all Headers methods 71 | expectTypeOf().toHaveProperty('getSetCookie') 72 | expectTypeOf().returns.toEqualTypeOf() 73 | 74 | expectTypeOf().toEqualTypeOf() 75 | }) 76 | 77 | it('should work with unknown header types', () => { 78 | type TestHeaders = TypedHeaders 79 | 80 | // Should fallback to string for all operations 81 | expectTypeOf().returns.toEqualTypeOf() 82 | expectTypeOf().parameter(1).toEqualTypeOf() 83 | }) 84 | 85 | it('should work with standard header maps', () => { 86 | type RequestHeaders = TypedHeaders 87 | type ResponseHeaders = TypedHeaders 88 | 89 | // Should accept standard headers 90 | expectTypeOf().toBeCallableWith('authorization') 91 | expectTypeOf().toBeCallableWith('content-type') 92 | expectTypeOf().toBeCallableWith('user-agent') 93 | 94 | expectTypeOf().toBeCallableWith('content-type') 95 | expectTypeOf().toBeCallableWith('cache-control') 96 | expectTypeOf().toBeCallableWith('set-cookie') 97 | }) 98 | }) 99 | 100 | describe('TypedResponse', () => { 101 | it('should type response body correctly', () => { 102 | type UserResponse = TypedResponse<{ id: number, name: string }> 103 | 104 | // json() should return the typed body 105 | expectTypeOf().returns.toEqualTypeOf>() 106 | 107 | // clone() should return TypedResponse with same types 108 | expectTypeOf().returns.toEqualTypeOf() 109 | }) 110 | 111 | it('should type response headers correctly', () => { 112 | interface CustomHeaders { 113 | 'x-rate-limit': string 114 | 'x-total-count': string 115 | } 116 | type CustomResponse = TypedResponse 117 | 118 | // headers should be TypedHeaders with custom header map 119 | expectTypeOf().toEqualTypeOf>() 120 | 121 | // Should be able to get typed headers 122 | const response = {} as CustomResponse 123 | expectTypeOf(response.headers.get).toBeCallableWith('x-rate-limit') 124 | expectTypeOf(response.headers.get).toBeCallableWith('x-total-count') 125 | }) 126 | 127 | it('should preserve Response interface properties', () => { 128 | type TestResponse = TypedResponse 129 | 130 | // Should have all Response properties except the overridden ones 131 | expectTypeOf().toHaveProperty('status') 132 | expectTypeOf().toHaveProperty('statusText') 133 | expectTypeOf().toHaveProperty('ok') 134 | expectTypeOf().toHaveProperty('redirected') 135 | expectTypeOf().toHaveProperty('type') 136 | expectTypeOf().toHaveProperty('url') 137 | expectTypeOf().toHaveProperty('body') 138 | expectTypeOf().toHaveProperty('bodyUsed') 139 | 140 | // Should have response-specific methods 141 | expectTypeOf().toHaveProperty('arrayBuffer') 142 | expectTypeOf().toHaveProperty('blob') 143 | expectTypeOf().toHaveProperty('formData') 144 | expectTypeOf().toHaveProperty('text') 145 | 146 | // Verify types of properties 147 | expectTypeOf().toEqualTypeOf() 148 | expectTypeOf().toEqualTypeOf() 149 | expectTypeOf().toEqualTypeOf() 150 | expectTypeOf().returns.toEqualTypeOf>() 151 | expectTypeOf().returns.toEqualTypeOf>() 152 | }) 153 | 154 | it('should work with different body types', () => { 155 | // String response 156 | type StringResponse = TypedResponse 157 | expectTypeOf().returns.toEqualTypeOf>() 158 | 159 | // Array response 160 | type ArrayResponse = TypedResponse> 161 | expectTypeOf().returns.toEqualTypeOf>>() 162 | 163 | // Union type response 164 | type UnionResponse = TypedResponse<{ success: true, data: string } | { success: false, error: string }> 165 | expectTypeOf().returns.toEqualTypeOf>() 166 | 167 | // null response 168 | type NullResponse = TypedResponse 169 | expectTypeOf().returns.toEqualTypeOf>() 170 | }) 171 | 172 | it('should default to unknown body and ResponseHeaderMap headers', () => { 173 | type DefaultResponse = TypedResponse 174 | 175 | expectTypeOf().returns.toEqualTypeOf>() 176 | expectTypeOf().toEqualTypeOf>() 177 | }) 178 | 179 | it('should work with schema-defined response headers', () => { 180 | interface SchemaHeaders { 181 | 'content-type': 'application/json' 182 | 'x-rate-limit-remaining': string 183 | 'x-rate-limit-reset': string 184 | } 185 | type APIResponse = TypedResponse<{ users: Array<{ id: number }> }, SchemaHeaders> 186 | 187 | const response = {} as APIResponse 188 | 189 | // Should provide typed access to schema headers 190 | expectTypeOf(response.headers.get('content-type')).toEqualTypeOf<'application/json' | null>() 191 | expectTypeOf(response.headers.get('x-rate-limit-remaining')).toEqualTypeOf() 192 | 193 | // json() should return typed body 194 | expectTypeOf(response.json()).toEqualTypeOf }>>() 195 | }) 196 | }) 197 | 198 | describe('TypedRequest', () => { 199 | it('should type request body correctly', () => { 200 | type CreateUserRequest = TypedRequest<{ name: string, email: string }> 201 | 202 | // json() should return the typed body 203 | expectTypeOf().returns.toEqualTypeOf>() 204 | 205 | // clone() should return TypedRequest with same header type 206 | expectTypeOf().returns.toEqualTypeOf>() 207 | }) 208 | 209 | it('should type request headers correctly', () => { 210 | interface CustomHeaders { 211 | 'authorization': `Bearer ${string}` 212 | 'x-api-key': string 213 | 'content-type': 'application/json' 214 | } 215 | type CustomRequest = TypedRequest 216 | 217 | // headers should be TypedHeaders with custom header map 218 | expectTypeOf().toEqualTypeOf>() 219 | 220 | // Should be able to get typed headers 221 | const request = {} as CustomRequest 222 | expectTypeOf(request.headers.get).toBeCallableWith('authorization') 223 | expectTypeOf(request.headers.get).toBeCallableWith('x-api-key') 224 | expectTypeOf(request.headers.get).toBeCallableWith('content-type') 225 | }) 226 | 227 | it('should preserve Request interface properties', () => { 228 | type TestRequest = TypedRequest 229 | 230 | // Should have all Request properties except the overridden ones 231 | expectTypeOf().toHaveProperty('method') 232 | expectTypeOf().toHaveProperty('url') 233 | expectTypeOf().toHaveProperty('body') 234 | expectTypeOf().toHaveProperty('bodyUsed') 235 | expectTypeOf().toHaveProperty('cache') 236 | expectTypeOf().toHaveProperty('credentials') 237 | expectTypeOf().toHaveProperty('destination') 238 | expectTypeOf().toHaveProperty('integrity') 239 | expectTypeOf().toHaveProperty('keepalive') 240 | expectTypeOf().toHaveProperty('mode') 241 | expectTypeOf().toHaveProperty('redirect') 242 | expectTypeOf().toHaveProperty('referrer') 243 | expectTypeOf().toHaveProperty('referrerPolicy') 244 | expectTypeOf().toHaveProperty('signal') 245 | 246 | // Should have request-specific methods 247 | expectTypeOf().toHaveProperty('arrayBuffer') 248 | expectTypeOf().toHaveProperty('blob') 249 | expectTypeOf().toHaveProperty('formData') 250 | expectTypeOf().toHaveProperty('text') 251 | 252 | // Verify types of properties 253 | expectTypeOf().toEqualTypeOf() 254 | expectTypeOf().toEqualTypeOf() 255 | expectTypeOf().returns.toEqualTypeOf>() 256 | expectTypeOf().returns.toEqualTypeOf>() 257 | }) 258 | 259 | it('should work with different body types', () => { 260 | // JSON request body 261 | type JSONRequest = TypedRequest<{ id: number, data: string }> 262 | expectTypeOf().returns.toEqualTypeOf>() 263 | 264 | // Form data (string body) 265 | type FormRequest = TypedRequest 266 | expectTypeOf().returns.toEqualTypeOf>() 267 | 268 | // No body (never) 269 | type NoBodyRequest = TypedRequest 270 | expectTypeOf().returns.toEqualTypeOf>() 271 | 272 | // Binary data 273 | type BinaryRequest = TypedRequest 274 | expectTypeOf().returns.toEqualTypeOf>() 275 | }) 276 | 277 | it('should default to unknown body and ResponseHeaderMap headers', () => { 278 | type DefaultRequest = TypedRequest 279 | 280 | expectTypeOf().returns.toEqualTypeOf>() 281 | expectTypeOf().toEqualTypeOf>() 282 | }) 283 | 284 | it('should work with schema-defined request headers', () => { 285 | interface SchemaHeaders { 286 | 'authorization': `Bearer ${string}` 287 | 'x-api-version': '1' | '2' 288 | 'content-type': 'application/json' | 'application/xml' 289 | } 290 | type APIRequest = TypedRequest<{ action: string }, SchemaHeaders> 291 | 292 | const request = {} as APIRequest 293 | 294 | // Should provide typed access to schema headers 295 | expectTypeOf(request.headers.get('authorization')).toEqualTypeOf<`Bearer ${string}` | null>() 296 | expectTypeOf(request.headers.get('x-api-version')).toEqualTypeOf<'1' | '2' | null>() 297 | 298 | // json() should return typed body 299 | expectTypeOf(request.json()).toEqualTypeOf>() 300 | }) 301 | 302 | it('should have correct clone method signature', () => { 303 | interface CustomHeaders { 'x-test': string } 304 | type CustomRequest = TypedRequest<{ data: string }, CustomHeaders> 305 | 306 | // clone() should return TypedRequest with the Headers type (not Body type) 307 | // This matches the implementation which has clone: () => TypedRequest 308 | expectTypeOf().returns.toEqualTypeOf>() 309 | }) 310 | }) 311 | 312 | describe('Integration with fetchdts inference types', () => { 313 | it('should work with TypedFetchResponseBody and TypedFetchResponseHeaders', () => { 314 | // This test ensures that TypedResponse works well with the inference types 315 | type GetResponse = TypedResponse< 316 | Array<{ id: number, name: string }>, 317 | { 'x-total-count': string } 318 | > 319 | type PostResponse = TypedResponse< 320 | { id: number, name: string, email: string }, 321 | { location: string } 322 | > 323 | 324 | // Should work correctly 325 | expectTypeOf().returns.toEqualTypeOf>>() 326 | expectTypeOf().returns.toEqualTypeOf>() 327 | 328 | const getResponse = {} as GetResponse 329 | const postResponse = {} as PostResponse 330 | 331 | expectTypeOf(getResponse.headers.get('x-total-count')).toEqualTypeOf() 332 | expectTypeOf(postResponse.headers.get('location')).toEqualTypeOf() 333 | }) 334 | }) 335 | -------------------------------------------------------------------------------- /src/http/mimes.ts: -------------------------------------------------------------------------------- 1 | export type MimeType 2 | = | 'application/1d-interleaved-parityfec' 3 | | 'application/3gpdash-qoe-report+xml' 4 | | 'application/3gppHal+json' 5 | | 'application/3gppHalForms+json' 6 | | 'application/3gpp-ims+xml' 7 | | 'application/A2L' 8 | | 'application/ace+cbor' 9 | | 'application/ace+json' 10 | | 'application/activemessage' 11 | | 'application/activity+json' 12 | | 'application/aif+cbor' 13 | | 'application/aif+json' 14 | | 'application/alto-cdni+json' 15 | | 'application/alto-cdnifilter+json' 16 | | 'application/alto-costmap+json' 17 | | 'application/alto-costmapfilter+json' 18 | | 'application/alto-directory+json' 19 | | 'application/alto-endpointprop+json' 20 | | 'application/alto-endpointpropparams+json' 21 | | 'application/alto-endpointcost+json' 22 | | 'application/alto-endpointcostparams+json' 23 | | 'application/alto-error+json' 24 | | 'application/alto-networkmapfilter+json' 25 | | 'application/alto-networkmap+json' 26 | | 'application/alto-propmap+json' 27 | | 'application/alto-propmapparams+json' 28 | | 'application/alto-tips+json' 29 | | 'application/alto-tipsparams+json' 30 | | 'application/alto-updatestreamcontrol+json' 31 | | 'application/alto-updatestreamparams+json' 32 | | 'application/AML' 33 | | 'application/andrew-inset' 34 | | 'application/applefile' 35 | | 'application/at+jwt' 36 | | 'application/ATF' 37 | | 'application/ATFX' 38 | | 'application/atom+xml' 39 | | 'application/atomcat+xml' 40 | | 'application/atomdeleted+xml' 41 | | 'application/atomicmail' 42 | | 'application/atomsvc+xml' 43 | | 'application/atsc-dwd+xml' 44 | | 'application/atsc-dynamic-event-message' 45 | | 'application/atsc-held+xml' 46 | | 'application/atsc-rdt+json' 47 | | 'application/atsc-rsat+xml' 48 | | 'application/ATXML' 49 | | 'application/auth-policy+xml' 50 | | 'application/automationml-aml+xml' 51 | | 'application/automationml-amlx+zip' 52 | | 'application/bacnet-xdd+zip' 53 | | 'application/batch-SMTP' 54 | | 'application/beep+xml' 55 | | 'application/c2pa' 56 | | 'application/calendar+json' 57 | | 'application/calendar+xml' 58 | | 'application/call-completion' 59 | | 'application/CALS-1840' 60 | | 'application/captive+json' 61 | | 'application/cbor' 62 | | 'application/cbor-seq' 63 | | 'application/cccex' 64 | | 'application/ccmp+xml' 65 | | 'application/ccxml+xml' 66 | | 'application/cda+xml' 67 | | 'application/CDFX+XML' 68 | | 'application/cdmi-capability' 69 | | 'application/cdmi-container' 70 | | 'application/cdmi-domain' 71 | | 'application/cdmi-object' 72 | | 'application/cdmi-queue' 73 | | 'application/cdni' 74 | | 'application/CEA' 75 | | 'application/cea-2018+xml' 76 | | 'application/cellml+xml' 77 | | 'application/cfw' 78 | | 'application/cid-edhoc+cbor-seq' 79 | | 'application/city+json' 80 | | 'application/clr' 81 | | 'application/clue_info+xml' 82 | | 'application/clue+xml' 83 | | 'application/cms' 84 | | 'application/cnrp+xml' 85 | | 'application/coap-group+json' 86 | | 'application/coap-payload' 87 | | 'application/commonground' 88 | | 'application/concise-problem-details+cbor' 89 | | 'application/conference-info+xml' 90 | | 'application/cpl+xml' 91 | | 'application/cose' 92 | | 'application/cose-key' 93 | | 'application/cose-key-set' 94 | | 'application/cose-x509' 95 | | 'application/csrattrs' 96 | | 'application/csta+xml' 97 | | 'application/CSTAdata+xml' 98 | | 'application/csvm+json' 99 | | 'application/cwl' 100 | | 'application/cwl+json' 101 | | 'application/cwt' 102 | | 'application/cybercash' 103 | | 'application/dash+xml' 104 | | 'application/dash-patch+xml' 105 | | 'application/dashdelta' 106 | | 'application/davmount+xml' 107 | | 'application/dca-rft' 108 | | 'application/DCD' 109 | | 'application/dec-dx' 110 | | 'application/dialog-info+xml' 111 | | 'application/dicom' 112 | | 'application/dicom+json' 113 | | 'application/dicom+xml' 114 | | 'application/DII' 115 | | 'application/DIT' 116 | | 'application/dns' 117 | | 'application/dns+json' 118 | | 'application/dns-message' 119 | | 'application/dots+cbor' 120 | | 'application/dpop+jwt' 121 | | 'application/dskpp+xml' 122 | | 'application/dssc+der' 123 | | 'application/dssc+xml' 124 | | 'application/dvcs' 125 | | 'application/ecmascript' 126 | | 'application/edhoc+cbor-seq' 127 | | 'application/EDI-consent' 128 | | 'application/EDIFACT' 129 | | 'application/EDI-X12' 130 | | 'application/efi' 131 | | 'application/elm+json' 132 | | 'application/elm+xml' 133 | | 'application/EmergencyCallData.cap+xml' 134 | | 'application/EmergencyCallData.Comment+xml' 135 | | 'application/EmergencyCallData.Control+xml' 136 | | 'application/EmergencyCallData.DeviceInfo+xml' 137 | | 'application/EmergencyCallData.eCall.MSD' 138 | | 'application/EmergencyCallData.LegacyESN+json' 139 | | 'application/EmergencyCallData.ProviderInfo+xml' 140 | | 'application/EmergencyCallData.ServiceInfo+xml' 141 | | 'application/EmergencyCallData.SubscriberInfo+xml' 142 | | 'application/EmergencyCallData.VEDS+xml' 143 | | 'application/emma+xml' 144 | | 'application/emotionml+xml' 145 | | 'application/encaprtp' 146 | | 'application/epp+xml' 147 | | 'application/epub+zip' 148 | | 'application/eshop' 149 | | 'application/example' 150 | | 'application/exi' 151 | | 'application/expect-ct-report+json' 152 | | 'application/express' 153 | | 'application/fastinfoset' 154 | | 'application/fastsoap' 155 | | 'application/fdf' 156 | | 'application/fdt+xml' 157 | | 'application/fhir+json' 158 | | 'application/fhir+xml' 159 | | 'application/fits' 160 | | 'application/flexfec' 161 | | 'application/font-sfnt' 162 | | 'application/font-tdpfr' 163 | | 'application/font-woff' 164 | | 'application/framework-attributes+xml' 165 | | 'application/geo+json' 166 | | 'application/geo+json-seq' 167 | | 'application/geopackage+sqlite3' 168 | | 'application/geoxacml+json' 169 | | 'application/geoxacml+xml' 170 | | 'application/gltf-buffer' 171 | | 'application/gml+xml' 172 | | 'application/gzip' 173 | | 'application/H224' 174 | | 'application/held+xml' 175 | | 'application/hl7v2+xml' 176 | | 'application/http' 177 | | 'application/hyperstudio' 178 | | 'application/ibe-key-request+xml' 179 | | 'application/ibe-pkg-reply+xml' 180 | | 'application/ibe-pp-data' 181 | | 'application/iges' 182 | | 'application/im-iscomposing+xml' 183 | | 'application/index' 184 | | 'application/index.cmd' 185 | | 'application/index.obj' 186 | | 'application/index.response' 187 | | 'application/index.vnd' 188 | | 'application/inkml+xml' 189 | | 'application/IOTP' 190 | | 'application/ipfix' 191 | | 'application/ipp' 192 | | 'application/ISUP' 193 | | 'application/its+xml' 194 | | 'application/java-archive' 195 | | 'application/javascript' 196 | | 'application/jf2feed+json' 197 | | 'application/jose' 198 | | 'application/jose+json' 199 | | 'application/jrd+json' 200 | | 'application/jscalendar+json' 201 | | 'application/jscontact+json' 202 | | 'application/json' 203 | | 'application/json-patch+json' 204 | | 'application/json-seq' 205 | | 'application/jsonpath' 206 | | 'application/jwk+json' 207 | | 'application/jwk-set+json' 208 | | 'application/jwt' 209 | | 'application/kpml-request+xml' 210 | | 'application/kpml-response+xml' 211 | | 'application/ld+json' 212 | | 'application/lgr+xml' 213 | | 'application/link-format' 214 | | 'application/linkset' 215 | | 'application/linkset+json' 216 | | 'application/load-control+xml' 217 | | 'application/logout+jwt' 218 | | 'application/lost+xml' 219 | | 'application/lostsync+xml' 220 | | 'application/lpf+zip' 221 | | 'application/LXF' 222 | | 'application/mac-binhex40' 223 | | 'application/macwriteii' 224 | | 'application/mads+xml' 225 | | 'application/manifest+json' 226 | | 'application/marc' 227 | | 'application/marcxml+xml' 228 | | 'application/mathematica' 229 | | 'application/mathml+xml' 230 | | 'application/mathml-content+xml' 231 | | 'application/mathml-presentation+xml' 232 | | 'application/mbms-associated-procedure-description+xml' 233 | | 'application/mbms-deregister+xml' 234 | | 'application/mbms-envelope+xml' 235 | | 'application/mbms-msk-response+xml' 236 | | 'application/mbms-msk+xml' 237 | | 'application/mbms-protection-description+xml' 238 | | 'application/mbms-reception-report+xml' 239 | | 'application/mbms-register-response+xml' 240 | | 'application/mbms-register+xml' 241 | | 'application/mbms-schedule+xml' 242 | | 'application/mbms-user-service-description+xml' 243 | | 'application/mbox' 244 | | 'application/media_control+xml' 245 | | 'application/media-policy-dataset+xml' 246 | | 'application/mediaservercontrol+xml' 247 | | 'application/merge-patch+json' 248 | | 'application/metalink4+xml' 249 | | 'application/mets+xml' 250 | | 'application/MF4' 251 | | 'application/mikey' 252 | | 'application/mipc' 253 | | 'application/missing-blocks+cbor-seq' 254 | | 'application/mmt-aei+xml' 255 | | 'application/mmt-usd+xml' 256 | | 'application/mods+xml' 257 | | 'application/moss-keys' 258 | | 'application/moss-signature' 259 | | 'application/mosskey-data' 260 | | 'application/mosskey-request' 261 | | 'application/mp21' 262 | | 'application/mp4' 263 | | 'application/mpeg4-generic' 264 | | 'application/mpeg4-iod' 265 | | 'application/mpeg4-iod-xmt' 266 | | 'application/mrb-consumer+xml' 267 | | 'application/mrb-publish+xml' 268 | | 'application/msc-ivr+xml' 269 | | 'application/msc-mixer+xml' 270 | | 'application/msword' 271 | | 'application/mud+json' 272 | | 'application/multipart-core' 273 | | 'application/mxf' 274 | | 'application/n-quads' 275 | | 'application/n-triples' 276 | | 'application/nasdata' 277 | | 'application/news-checkgroups' 278 | | 'application/news-groupinfo' 279 | | 'application/news-transmission' 280 | | 'application/nlsml+xml' 281 | | 'application/node' 282 | | 'application/nss' 283 | | 'application/oauth-authz-req+jwt' 284 | | 'application/oblivious-dns-message' 285 | | 'application/ocsp-request' 286 | | 'application/ocsp-response' 287 | | 'application/octet-stream' 288 | | 'application/ODA' 289 | | 'application/odm+xml' 290 | | 'application/ODX' 291 | | 'application/oebps-package+xml' 292 | | 'application/ogg' 293 | | 'application/ohttp-keys' 294 | | 'application/opc-nodeset+xml' 295 | | 'application/oscore' 296 | | 'application/oxps' 297 | | 'application/p21' 298 | | 'application/p21+zip' 299 | | 'application/p2p-overlay+xml' 300 | | 'application/parityfec' 301 | | 'application/passport' 302 | | 'application/patch-ops-error+xml' 303 | | 'application/pdf' 304 | | 'application/PDX' 305 | | 'application/pem-certificate-chain' 306 | | 'application/pgp-encrypted' 307 | | 'application/pgp-keys' 308 | | 'application/pgp-signature' 309 | | 'application/pidf-diff+xml' 310 | | 'application/pidf+xml' 311 | | 'application/pkcs10' 312 | | 'application/pkcs7-mime' 313 | | 'application/pkcs7-signature' 314 | | 'application/pkcs8' 315 | | 'application/pkcs8-encrypted' 316 | | 'application/pkcs12' 317 | | 'application/pkix-attr-cert' 318 | | 'application/pkix-cert' 319 | | 'application/pkix-crl' 320 | | 'application/pkix-pkipath' 321 | | 'application/pkixcmp' 322 | | 'application/pls+xml' 323 | | 'application/poc-settings+xml' 324 | | 'application/postscript' 325 | | 'application/ppsp-tracker+json' 326 | | 'application/private-token-issuer-directory' 327 | | 'application/private-token-request' 328 | | 'application/private-token-response' 329 | | 'application/problem+json' 330 | | 'application/problem+xml' 331 | | 'application/provenance+xml' 332 | | 'application/prs.alvestrand.titrax-sheet' 333 | | 'application/prs.cww' 334 | | 'application/prs.cyn' 335 | | 'application/prs.hpub+zip' 336 | | 'application/prs.implied-document+xml' 337 | | 'application/prs.implied-executable' 338 | | 'application/prs.implied-object+json' 339 | | 'application/prs.implied-object+json-seq' 340 | | 'application/prs.implied-object+yaml' 341 | | 'application/prs.implied-structure' 342 | | 'application/prs.nprend' 343 | | 'application/prs.plucker' 344 | | 'application/prs.rdf-xml-crypt' 345 | | 'application/prs.vcfbzip2' 346 | | 'application/prs.xsf+xml' 347 | | 'application/pskc+xml' 348 | | 'application/pvd+json' 349 | | 'application/rdf+xml' 350 | | 'application/route-apd+xml' 351 | | 'application/route-s-tsid+xml' 352 | | 'application/route-usd+xml' 353 | | 'application/QSIG' 354 | | 'application/raptorfec' 355 | | 'application/rdap+json' 356 | | 'application/reginfo+xml' 357 | | 'application/relax-ng-compact-syntax' 358 | | 'application/remote-printing' 359 | | 'application/reputon+json' 360 | | 'application/resource-lists-diff+xml' 361 | | 'application/resource-lists+xml' 362 | | 'application/rfc+xml' 363 | | 'application/riscos' 364 | | 'application/rlmi+xml' 365 | | 'application/rls-services+xml' 366 | | 'application/rpki-checklist' 367 | | 'application/rpki-ghostbusters' 368 | | 'application/rpki-manifest' 369 | | 'application/rpki-publication' 370 | | 'application/rpki-roa' 371 | | 'application/rpki-updown' 372 | | 'application/rtf' 373 | | 'application/rtploopback' 374 | | 'application/rtx' 375 | | 'application/samlassertion+xml' 376 | | 'application/samlmetadata+xml' 377 | | 'application/sarif-external-properties+json' 378 | | 'application/sarif+json' 379 | | 'application/sbe' 380 | | 'application/sbml+xml' 381 | | 'application/scaip+xml' 382 | | 'application/scim+json' 383 | | 'application/scvp-cv-request' 384 | | 'application/scvp-cv-response' 385 | | 'application/scvp-vp-request' 386 | | 'application/scvp-vp-response' 387 | | 'application/sdp' 388 | | 'application/secevent+jwt' 389 | | 'application/senml-etch+cbor' 390 | | 'application/senml-etch+json' 391 | | 'application/senml-exi' 392 | | 'application/senml+cbor' 393 | | 'application/senml+json' 394 | | 'application/senml+xml' 395 | | 'application/sensml-exi' 396 | | 'application/sensml+cbor' 397 | | 'application/sensml+json' 398 | | 'application/sensml+xml' 399 | | 'application/sep-exi' 400 | | 'application/sep+xml' 401 | | 'application/session-info' 402 | | 'application/set-payment' 403 | | 'application/set-payment-initiation' 404 | | 'application/set-registration' 405 | | 'application/set-registration-initiation' 406 | | 'application/SGML' 407 | | 'application/sgml-open-catalog' 408 | | 'application/shf+xml' 409 | | 'application/sieve' 410 | | 'application/simple-filter+xml' 411 | | 'application/simple-message-summary' 412 | | 'application/simpleSymbolContainer' 413 | | 'application/sipc' 414 | | 'application/slate' 415 | | 'application/smil' 416 | | 'application/smil+xml' 417 | | 'application/smpte336m' 418 | | 'application/soap+fastinfoset' 419 | | 'application/soap+xml' 420 | | 'application/sparql-query' 421 | | 'application/spdx+json' 422 | | 'application/sparql-results+xml' 423 | | 'application/spirits-event+xml' 424 | | 'application/sql' 425 | | 'application/srgs' 426 | | 'application/srgs+xml' 427 | | 'application/sru+xml' 428 | | 'application/ssml+xml' 429 | | 'application/stix+json' 430 | | 'application/swid+cbor' 431 | | 'application/swid+xml' 432 | | 'application/tamp-apex-update' 433 | | 'application/tamp-apex-update-confirm' 434 | | 'application/tamp-community-update' 435 | | 'application/tamp-community-update-confirm' 436 | | 'application/tamp-error' 437 | | 'application/tamp-sequence-adjust' 438 | | 'application/tamp-sequence-adjust-confirm' 439 | | 'application/tamp-status-query' 440 | | 'application/tamp-status-response' 441 | | 'application/tamp-update' 442 | | 'application/tamp-update-confirm' 443 | | 'application/taxii+json' 444 | | 'application/td+json' 445 | | 'application/tei+xml' 446 | | 'application/TETRA_ISI' 447 | | 'application/thraud+xml' 448 | | 'application/timestamp-query' 449 | | 'application/timestamp-reply' 450 | | 'application/timestamped-data' 451 | | 'application/tlsrpt+gzip' 452 | | 'application/tlsrpt+json' 453 | | 'application/tm+json' 454 | | 'application/tnauthlist' 455 | | 'application/token-introspection+jwt' 456 | | 'application/trickle-ice-sdpfrag' 457 | | 'application/trig' 458 | | 'application/ttml+xml' 459 | | 'application/tve-trigger' 460 | | 'application/tzif' 461 | | 'application/tzif-leap' 462 | | 'application/ulpfec' 463 | | 'application/urc-grpsheet+xml' 464 | | 'application/urc-ressheet+xml' 465 | | 'application/urc-targetdesc+xml' 466 | | 'application/urc-uisocketdesc+xml' 467 | | 'application/vcard+json' 468 | | 'application/vcard+xml' 469 | | 'application/vemmi' 470 | | 'application/vnd.1000minds.decision-model+xml' 471 | | 'application/vnd.1ob' 472 | | 'application/vnd.3gpp.5gnas' 473 | | 'application/vnd.3gpp.access-transfer-events+xml' 474 | | 'application/vnd.3gpp.bsf+xml' 475 | | 'application/vnd.3gpp.crs+xml' 476 | | 'application/vnd.3gpp.current-location-discovery+xml' 477 | | 'application/vnd.3gpp.GMOP+xml' 478 | | 'application/vnd.3gpp.gtpc' 479 | | 'application/vnd.3gpp.interworking-data' 480 | | 'application/vnd.3gpp.lpp' 481 | | 'application/vnd.3gpp.mc-signalling-ear' 482 | | 'application/vnd.3gpp.mcdata-affiliation-command+xml' 483 | | 'application/vnd.3gpp.mcdata-info+xml' 484 | | 'application/vnd.3gpp.mcdata-msgstore-ctrl-request+xml' 485 | | 'application/vnd.3gpp.mcdata-payload' 486 | | 'application/vnd.3gpp.mcdata-regroup+xml' 487 | | 'application/vnd.3gpp.mcdata-service-config+xml' 488 | | 'application/vnd.3gpp.mcdata-signalling' 489 | | 'application/vnd.3gpp.mcdata-ue-config+xml' 490 | | 'application/vnd.3gpp.mcdata-user-profile+xml' 491 | | 'application/vnd.3gpp.mcptt-affiliation-command+xml' 492 | | 'application/vnd.3gpp.mcptt-floor-request+xml' 493 | | 'application/vnd.3gpp.mcptt-info+xml' 494 | | 'application/vnd.3gpp.mcptt-location-info+xml' 495 | | 'application/vnd.3gpp.mcptt-mbms-usage-info+xml' 496 | | 'application/vnd.3gpp.mcptt-regroup+xml' 497 | | 'application/vnd.3gpp.mcptt-service-config+xml' 498 | | 'application/vnd.3gpp.mcptt-signed+xml' 499 | | 'application/vnd.3gpp.mcptt-ue-config+xml' 500 | | 'application/vnd.3gpp.mcptt-ue-init-config+xml' 501 | | 'application/vnd.3gpp.mcptt-user-profile+xml' 502 | | 'application/vnd.3gpp.mcvideo-affiliation-command+xml' 503 | | 'application/vnd.3gpp.mcvideo-affiliation-info+xml' 504 | | 'application/vnd.3gpp.mcvideo-info+xml' 505 | | 'application/vnd.3gpp.mcvideo-location-info+xml' 506 | | 'application/vnd.3gpp.mcvideo-mbms-usage-info+xml' 507 | | 'application/vnd.3gpp.mcvideo-regroup+xml' 508 | | 'application/vnd.3gpp.mcvideo-service-config+xml' 509 | | 'application/vnd.3gpp.mcvideo-transmission-request+xml' 510 | | 'application/vnd.3gpp.mcvideo-ue-config+xml' 511 | | 'application/vnd.3gpp.mcvideo-user-profile+xml' 512 | | 'application/vnd.3gpp.mid-call+xml' 513 | | 'application/vnd.3gpp.ngap' 514 | | 'application/vnd.3gpp.pfcp' 515 | | 'application/vnd.3gpp.pic-bw-large' 516 | | 'application/vnd.3gpp.pic-bw-small' 517 | | 'application/vnd.3gpp.pic-bw-var' 518 | | 'application/vnd.3gpp-prose-pc3a+xml' 519 | | 'application/vnd.3gpp-prose-pc3ach+xml' 520 | | 'application/vnd.3gpp-prose-pc3ch+xml' 521 | | 'application/vnd.3gpp-prose-pc8+xml' 522 | | 'application/vnd.3gpp-prose+xml' 523 | | 'application/vnd.3gpp.s1ap' 524 | | 'application/vnd.3gpp.seal-group-doc+xml' 525 | | 'application/vnd.3gpp.seal-info+xml' 526 | | 'application/vnd.3gpp.seal-location-info+xml' 527 | | 'application/vnd.3gpp.seal-mbms-usage-info+xml' 528 | | 'application/vnd.3gpp.seal-network-QoS-management-info+xml' 529 | | 'application/vnd.3gpp.seal-ue-config-info+xml' 530 | | 'application/vnd.3gpp.seal-unicast-info+xml' 531 | | 'application/vnd.3gpp.seal-user-profile-info+xml' 532 | | 'application/vnd.3gpp.sms' 533 | | 'application/vnd.3gpp.sms+xml' 534 | | 'application/vnd.3gpp.srvcc-ext+xml' 535 | | 'application/vnd.3gpp.SRVCC-info+xml' 536 | | 'application/vnd.3gpp.state-and-event-info+xml' 537 | | 'application/vnd.3gpp.ussd+xml' 538 | | 'application/vnd.3gpp.vae-info+xml' 539 | | 'application/vnd.3gpp-v2x-local-service-information' 540 | | 'application/vnd.3gpp2.bcmcsinfo+xml' 541 | | 'application/vnd.3gpp2.sms' 542 | | 'application/vnd.3gpp2.tcap' 543 | | 'application/vnd.3gpp.v2x' 544 | | 'application/vnd.3lightssoftware.imagescal' 545 | | 'application/vnd.3M.Post-it-Notes' 546 | | 'application/vnd.accpac.simply.aso' 547 | | 'application/vnd.accpac.simply.imp' 548 | | 'application/vnd.acm.addressxfer+json' 549 | | 'application/vnd.acm.chatbot+json' 550 | | 'application/vnd.acucobol' 551 | | 'application/vnd.acucorp' 552 | | 'application/vnd.adobe.flash.movie' 553 | | 'application/vnd.adobe.formscentral.fcdt' 554 | | 'application/vnd.adobe.fxp' 555 | | 'application/vnd.adobe.partial-upload' 556 | | 'application/vnd.adobe.xdp+xml' 557 | | 'application/vnd.aether.imp' 558 | | 'application/vnd.afpc.afplinedata' 559 | | 'application/vnd.afpc.afplinedata-pagedef' 560 | | 'application/vnd.afpc.cmoca-cmresource' 561 | | 'application/vnd.afpc.foca-charset' 562 | | 'application/vnd.afpc.foca-codedfont' 563 | | 'application/vnd.afpc.foca-codepage' 564 | | 'application/vnd.afpc.modca' 565 | | 'application/vnd.afpc.modca-cmtable' 566 | | 'application/vnd.afpc.modca-formdef' 567 | | 'application/vnd.afpc.modca-mediummap' 568 | | 'application/vnd.afpc.modca-objectcontainer' 569 | | 'application/vnd.afpc.modca-overlay' 570 | | 'application/vnd.afpc.modca-pagesegment' 571 | | 'application/vnd.age' 572 | | 'application/vnd.ah-barcode' 573 | | 'application/vnd.ahead.space' 574 | | 'application/vnd.airzip.filesecure.azf' 575 | | 'application/vnd.airzip.filesecure.azs' 576 | | 'application/vnd.amadeus+json' 577 | | 'application/vnd.amazon.mobi8-ebook' 578 | | 'application/vnd.americandynamics.acc' 579 | | 'application/vnd.amiga.ami' 580 | | 'application/vnd.amundsen.maze+xml' 581 | | 'application/vnd.android.ota' 582 | | 'application/vnd.anki' 583 | | 'application/vnd.anser-web-certificate-issue-initiation' 584 | | 'application/vnd.antix.game-component' 585 | | 'application/vnd.apache.arrow.file' 586 | | 'application/vnd.apache.arrow.stream' 587 | | 'application/vnd.apache.thrift.binary' 588 | | 'application/vnd.apache.thrift.compact' 589 | | 'application/vnd.apache.thrift.json' 590 | | 'application/vnd.apexlang' 591 | | 'application/vnd.api+json' 592 | | 'application/vnd.aplextor.warrp+json' 593 | | 'application/vnd.apothekende.reservation+json' 594 | | 'application/vnd.apple.installer+xml' 595 | | 'application/vnd.apple.keynote' 596 | | 'application/vnd.apple.mpegurl' 597 | | 'application/vnd.apple.numbers' 598 | | 'application/vnd.apple.pages' 599 | | 'application/vnd.arastra.swi' 600 | | 'application/vnd.aristanetworks.swi' 601 | | 'application/vnd.artisan+json' 602 | | 'application/vnd.artsquare' 603 | | 'application/vnd.astraea-software.iota' 604 | | 'application/vnd.audiograph' 605 | | 'application/vnd.autopackage' 606 | | 'application/vnd.avalon+json' 607 | | 'application/vnd.avistar+xml' 608 | | 'application/vnd.balsamiq.bmml+xml' 609 | | 'application/vnd.banana-accounting' 610 | | 'application/vnd.bbf.usp.error' 611 | | 'application/vnd.bbf.usp.msg' 612 | | 'application/vnd.bbf.usp.msg+json' 613 | | 'application/vnd.balsamiq.bmpr' 614 | | 'application/vnd.bekitzur-stech+json' 615 | | 'application/vnd.belightsoft.lhzd+zip' 616 | | 'application/vnd.belightsoft.lhzl+zip' 617 | | 'application/vnd.bint.med-content' 618 | | 'application/vnd.biopax.rdf+xml' 619 | | 'application/vnd.blink-idb-value-wrapper' 620 | | 'application/vnd.blueice.multipass' 621 | | 'application/vnd.bluetooth.ep.oob' 622 | | 'application/vnd.bluetooth.le.oob' 623 | | 'application/vnd.bmi' 624 | | 'application/vnd.bpf' 625 | | 'application/vnd.bpf3' 626 | | 'application/vnd.businessobjects' 627 | | 'application/vnd.byu.uapi+json' 628 | | 'application/vnd.bzip3' 629 | | 'application/vnd.cab-jscript' 630 | | 'application/vnd.canon-cpdl' 631 | | 'application/vnd.canon-lips' 632 | | 'application/vnd.capasystems-pg+json' 633 | | 'application/vnd.cendio.thinlinc.clientconf' 634 | | 'application/vnd.century-systems.tcp_stream' 635 | | 'application/vnd.chemdraw+xml' 636 | | 'application/vnd.chess-pgn' 637 | | 'application/vnd.chipnuts.karaoke-mmd' 638 | | 'application/vnd.ciedi' 639 | | 'application/vnd.cinderella' 640 | | 'application/vnd.cirpack.isdn-ext' 641 | | 'application/vnd.citationstyles.style+xml' 642 | | 'application/vnd.claymore' 643 | | 'application/vnd.cloanto.rp9' 644 | | 'application/vnd.clonk.c4group' 645 | | 'application/vnd.cluetrust.cartomobile-config' 646 | | 'application/vnd.cluetrust.cartomobile-config-pkg' 647 | | 'application/vnd.cncf.helm.chart.content.v1.tar+gzip' 648 | | 'application/vnd.cncf.helm.chart.provenance.v1.prov' 649 | | 'application/vnd.cncf.helm.config.v1+json' 650 | | 'application/vnd.coffeescript' 651 | | 'application/vnd.collabio.xodocuments.document' 652 | | 'application/vnd.collabio.xodocuments.document-template' 653 | | 'application/vnd.collabio.xodocuments.presentation' 654 | | 'application/vnd.collabio.xodocuments.presentation-template' 655 | | 'application/vnd.collabio.xodocuments.spreadsheet' 656 | | 'application/vnd.collabio.xodocuments.spreadsheet-template' 657 | | 'application/vnd.collection.doc+json' 658 | | 'application/vnd.collection+json' 659 | | 'application/vnd.collection.next+json' 660 | | 'application/vnd.comicbook-rar' 661 | | 'application/vnd.comicbook+zip' 662 | | 'application/vnd.commerce-battelle' 663 | | 'application/vnd.commonspace' 664 | | 'application/vnd.coreos.ignition+json' 665 | | 'application/vnd.cosmocaller' 666 | | 'application/vnd.contact.cmsg' 667 | | 'application/vnd.crick.clicker' 668 | | 'application/vnd.crick.clicker.keyboard' 669 | | 'application/vnd.crick.clicker.palette' 670 | | 'application/vnd.crick.clicker.template' 671 | | 'application/vnd.crick.clicker.wordbank' 672 | | 'application/vnd.criticaltools.wbs+xml' 673 | | 'application/vnd.cryptii.pipe+json' 674 | | 'application/vnd.crypto-shade-file' 675 | | 'application/vnd.cryptomator.encrypted' 676 | | 'application/vnd.cryptomator.vault' 677 | | 'application/vnd.ctc-posml' 678 | | 'application/vnd.ctct.ws+xml' 679 | | 'application/vnd.cups-pdf' 680 | | 'application/vnd.cups-postscript' 681 | | 'application/vnd.cups-ppd' 682 | | 'application/vnd.cups-raster' 683 | | 'application/vnd.cups-raw' 684 | | 'application/vnd.curl' 685 | | 'application/vnd.cyan.dean.root+xml' 686 | | 'application/vnd.cybank' 687 | | 'application/vnd.cyclonedx+json' 688 | | 'application/vnd.cyclonedx+xml' 689 | | 'application/vnd.d2l.coursepackage1p0+zip' 690 | | 'application/vnd.d3m-dataset' 691 | | 'application/vnd.d3m-problem' 692 | | 'application/vnd.dart' 693 | | 'application/vnd.data-vision.rdz' 694 | | 'application/vnd.datalog' 695 | | 'application/vnd.datapackage+json' 696 | | 'application/vnd.dataresource+json' 697 | | 'application/vnd.dbf' 698 | | 'application/vnd.debian.binary-package' 699 | | 'application/vnd.dece.data' 700 | | 'application/vnd.dece.ttml+xml' 701 | | 'application/vnd.dece.unspecified' 702 | | 'application/vnd.dece.zip' 703 | | 'application/vnd.denovo.fcselayout-link' 704 | | 'application/vnd.desmume.movie' 705 | | 'application/vnd.dir-bi.plate-dl-nosuffix' 706 | | 'application/vnd.dm.delegation+xml' 707 | | 'application/vnd.dna' 708 | | 'application/vnd.document+json' 709 | | 'application/vnd.dolby.mobile.1' 710 | | 'application/vnd.dolby.mobile.2' 711 | | 'application/vnd.doremir.scorecloud-binary-document' 712 | | 'application/vnd.dpgraph' 713 | | 'application/vnd.dreamfactory' 714 | | 'application/vnd.drive+json' 715 | | 'application/vnd.dtg.local' 716 | | 'application/vnd.dtg.local.flash' 717 | | 'application/vnd.dtg.local.html' 718 | | 'application/vnd.dvb.ait' 719 | | 'application/vnd.dvb.dvbisl+xml' 720 | | 'application/vnd.dvb.dvbj' 721 | | 'application/vnd.dvb.esgcontainer' 722 | | 'application/vnd.dvb.ipdcdftnotifaccess' 723 | | 'application/vnd.dvb.ipdcesgaccess' 724 | | 'application/vnd.dvb.ipdcesgaccess2' 725 | | 'application/vnd.dvb.ipdcesgpdd' 726 | | 'application/vnd.dvb.ipdcroaming' 727 | | 'application/vnd.dvb.iptv.alfec-base' 728 | | 'application/vnd.dvb.iptv.alfec-enhancement' 729 | | 'application/vnd.dvb.notif-aggregate-root+xml' 730 | | 'application/vnd.dvb.notif-container+xml' 731 | | 'application/vnd.dvb.notif-generic+xml' 732 | | 'application/vnd.dvb.notif-ia-msglist+xml' 733 | | 'application/vnd.dvb.notif-ia-registration-request+xml' 734 | | 'application/vnd.dvb.notif-ia-registration-response+xml' 735 | | 'application/vnd.dvb.notif-init+xml' 736 | | 'application/vnd.dvb.pfr' 737 | | 'application/vnd.dvb.service' 738 | | 'application/vnd.dxr' 739 | | 'application/vnd.dynageo' 740 | | 'application/vnd.dzr' 741 | | 'application/vnd.easykaraoke.cdgdownload' 742 | | 'application/vnd.ecip.rlp' 743 | | 'application/vnd.ecdis-update' 744 | | 'application/vnd.eclipse.ditto+json' 745 | | 'application/vnd.ecowin.chart' 746 | | 'application/vnd.ecowin.filerequest' 747 | | 'application/vnd.ecowin.fileupdate' 748 | | 'application/vnd.ecowin.series' 749 | | 'application/vnd.ecowin.seriesrequest' 750 | | 'application/vnd.ecowin.seriesupdate' 751 | | 'application/vnd.efi.img' 752 | | 'application/vnd.efi.iso' 753 | | 'application/vnd.eln+zip' 754 | | 'application/vnd.emclient.accessrequest+xml' 755 | | 'application/vnd.enliven' 756 | | 'application/vnd.enphase.envoy' 757 | | 'application/vnd.eprints.data+xml' 758 | | 'application/vnd.epson.esf' 759 | | 'application/vnd.epson.msf' 760 | | 'application/vnd.epson.quickanime' 761 | | 'application/vnd.epson.salt' 762 | | 'application/vnd.epson.ssf' 763 | | 'application/vnd.ericsson.quickcall' 764 | | 'application/vnd.erofs' 765 | | 'application/vnd.espass-espass+zip' 766 | | 'application/vnd.eszigno3+xml' 767 | | 'application/vnd.etsi.aoc+xml' 768 | | 'application/vnd.etsi.asic-s+zip' 769 | | 'application/vnd.etsi.asic-e+zip' 770 | | 'application/vnd.etsi.cug+xml' 771 | | 'application/vnd.etsi.iptvcommand+xml' 772 | | 'application/vnd.etsi.iptvdiscovery+xml' 773 | | 'application/vnd.etsi.iptvprofile+xml' 774 | | 'application/vnd.etsi.iptvsad-bc+xml' 775 | | 'application/vnd.etsi.iptvsad-cod+xml' 776 | | 'application/vnd.etsi.iptvsad-npvr+xml' 777 | | 'application/vnd.etsi.iptvservice+xml' 778 | | 'application/vnd.etsi.iptvsync+xml' 779 | | 'application/vnd.etsi.iptvueprofile+xml' 780 | | 'application/vnd.etsi.mcid+xml' 781 | | 'application/vnd.etsi.mheg5' 782 | | 'application/vnd.etsi.overload-control-policy-dataset+xml' 783 | | 'application/vnd.etsi.pstn+xml' 784 | | 'application/vnd.etsi.sci+xml' 785 | | 'application/vnd.etsi.simservs+xml' 786 | | 'application/vnd.etsi.timestamp-token' 787 | | 'application/vnd.etsi.tsl+xml' 788 | | 'application/vnd.etsi.tsl.der' 789 | | 'application/vnd.eu.kasparian.car+json' 790 | | 'application/vnd.eudora.data' 791 | | 'application/vnd.evolv.ecig.profile' 792 | | 'application/vnd.evolv.ecig.settings' 793 | | 'application/vnd.evolv.ecig.theme' 794 | | 'application/vnd.exstream-empower+zip' 795 | | 'application/vnd.exstream-package' 796 | | 'application/vnd.ezpix-album' 797 | | 'application/vnd.ezpix-package' 798 | | 'application/vnd.f-secure.mobile' 799 | | 'application/vnd.fastcopy-disk-image' 800 | | 'application/vnd.familysearch.gedcom+zip' 801 | | 'application/vnd.fdsn.mseed' 802 | | 'application/vnd.fdsn.seed' 803 | | 'application/vnd.ffsns' 804 | | 'application/vnd.ficlab.flb+zip' 805 | | 'application/vnd.filmit.zfc' 806 | | 'application/vnd.fints' 807 | | 'application/vnd.firemonkeys.cloudcell' 808 | | 'application/vnd.FloGraphIt' 809 | | 'application/vnd.fluxtime.clip' 810 | | 'application/vnd.font-fontforge-sfd' 811 | | 'application/vnd.framemaker' 812 | | 'application/vnd.freelog.comic' 813 | | 'application/vnd.frogans.fnc' 814 | | 'application/vnd.frogans.ltf' 815 | | 'application/vnd.fsc.weblaunch' 816 | | 'application/vnd.fujifilm.fb.docuworks' 817 | | 'application/vnd.fujifilm.fb.docuworks.binder' 818 | | 'application/vnd.fujifilm.fb.docuworks.container' 819 | | 'application/vnd.fujifilm.fb.jfi+xml' 820 | | 'application/vnd.fujitsu.oasys' 821 | | 'application/vnd.fujitsu.oasys2' 822 | | 'application/vnd.fujitsu.oasys3' 823 | | 'application/vnd.fujitsu.oasysgp' 824 | | 'application/vnd.fujitsu.oasysprs' 825 | | 'application/vnd.fujixerox.ART4' 826 | | 'application/vnd.fujixerox.ART-EX' 827 | | 'application/vnd.fujixerox.ddd' 828 | | 'application/vnd.fujixerox.docuworks' 829 | | 'application/vnd.fujixerox.docuworks.binder' 830 | | 'application/vnd.fujixerox.docuworks.container' 831 | | 'application/vnd.fujixerox.HBPL' 832 | | 'application/vnd.fut-misnet' 833 | | 'application/vnd.futoin+cbor' 834 | | 'application/vnd.futoin+json' 835 | | 'application/vnd.fuzzysheet' 836 | | 'application/vnd.genomatix.tuxedo' 837 | | 'application/vnd.genozip' 838 | | 'application/vnd.gentics.grd+json' 839 | | 'application/vnd.gentoo.catmetadata+xml' 840 | | 'application/vnd.gentoo.ebuild' 841 | | 'application/vnd.gentoo.eclass' 842 | | 'application/vnd.gentoo.gpkg' 843 | | 'application/vnd.gentoo.manifest' 844 | | 'application/vnd.gentoo.xpak' 845 | | 'application/vnd.gentoo.pkgmetadata+xml' 846 | | 'application/vnd.geo+json' 847 | | 'application/vnd.geocube+xml' 848 | | 'application/vnd.geogebra.file' 849 | | 'application/vnd.geogebra.slides' 850 | | 'application/vnd.geogebra.tool' 851 | | 'application/vnd.geometry-explorer' 852 | | 'application/vnd.geonext' 853 | | 'application/vnd.geoplan' 854 | | 'application/vnd.geospace' 855 | | 'application/vnd.gerber' 856 | | 'application/vnd.globalplatform.card-content-mgt' 857 | | 'application/vnd.globalplatform.card-content-mgt-response' 858 | | 'application/vnd.gmx' 859 | | 'application/vnd.gnu.taler.exchange+json' 860 | | 'application/vnd.gnu.taler.merchant+json' 861 | | 'application/vnd.google-earth.kml+xml' 862 | | 'application/vnd.google-earth.kmz' 863 | | 'application/vnd.gov.sk.e-form+xml' 864 | | 'application/vnd.gov.sk.e-form+zip' 865 | | 'application/vnd.gov.sk.xmldatacontainer+xml' 866 | | 'application/vnd.gpxsee.map+xml' 867 | | 'application/vnd.grafeq' 868 | | 'application/vnd.gridmp' 869 | | 'application/vnd.groove-account' 870 | | 'application/vnd.groove-help' 871 | | 'application/vnd.groove-identity-message' 872 | | 'application/vnd.groove-injector' 873 | | 'application/vnd.groove-tool-message' 874 | | 'application/vnd.groove-tool-template' 875 | | 'application/vnd.groove-vcard' 876 | | 'application/vnd.hal+json' 877 | | 'application/vnd.hal+xml' 878 | | 'application/vnd.HandHeld-Entertainment+xml' 879 | | 'application/vnd.hbci' 880 | | 'application/vnd.hc+json' 881 | | 'application/vnd.hcl-bireports' 882 | | 'application/vnd.hdt' 883 | | 'application/vnd.heroku+json' 884 | | 'application/vnd.hhe.lesson-player' 885 | | 'application/vnd.hp-HPGL' 886 | | 'application/vnd.hp-hpid' 887 | | 'application/vnd.hp-hps' 888 | | 'application/vnd.hp-jlyt' 889 | | 'application/vnd.hp-PCL' 890 | | 'application/vnd.hp-PCLXL' 891 | | 'application/vnd.hsl' 892 | | 'application/vnd.httphone' 893 | | 'application/vnd.hydrostatix.sof-data' 894 | | 'application/vnd.hyper-item+json' 895 | | 'application/vnd.hyper+json' 896 | | 'application/vnd.hyperdrive+json' 897 | | 'application/vnd.hzn-3d-crossword' 898 | | 'application/vnd.ibm.afplinedata' 899 | | 'application/vnd.ibm.electronic-media' 900 | | 'application/vnd.ibm.MiniPay' 901 | | 'application/vnd.ibm.modcap' 902 | | 'application/vnd.ibm.rights-management' 903 | | 'application/vnd.ibm.secure-container' 904 | | 'application/vnd.iccprofile' 905 | | 'application/vnd.ieee.1905' 906 | | 'application/vnd.igloader' 907 | | 'application/vnd.imagemeter.folder+zip' 908 | | 'application/vnd.imagemeter.image+zip' 909 | | 'application/vnd.immervision-ivp' 910 | | 'application/vnd.immervision-ivu' 911 | | 'application/vnd.ims.imsccv1p1' 912 | | 'application/vnd.ims.imsccv1p2' 913 | | 'application/vnd.ims.imsccv1p3' 914 | | 'application/vnd.ims.lis.v2.result+json' 915 | | 'application/vnd.ims.lti.v2.toolconsumerprofile+json' 916 | | 'application/vnd.ims.lti.v2.toolproxy.id+json' 917 | | 'application/vnd.ims.lti.v2.toolproxy+json' 918 | | 'application/vnd.ims.lti.v2.toolsettings+json' 919 | | 'application/vnd.ims.lti.v2.toolsettings.simple+json' 920 | | 'application/vnd.informedcontrol.rms+xml' 921 | | 'application/vnd.infotech.project' 922 | | 'application/vnd.infotech.project+xml' 923 | | 'application/vnd.informix-visionary' 924 | | 'application/vnd.innopath.wamp.notification' 925 | | 'application/vnd.insors.igm' 926 | | 'application/vnd.intercon.formnet' 927 | | 'application/vnd.intergeo' 928 | | 'application/vnd.intertrust.digibox' 929 | | 'application/vnd.intertrust.nncp' 930 | | 'application/vnd.intu.qbo' 931 | | 'application/vnd.intu.qfx' 932 | | 'application/vnd.ipfs.ipns-record' 933 | | 'application/vnd.ipld.car' 934 | | 'application/vnd.ipld.dag-cbor' 935 | | 'application/vnd.ipld.dag-json' 936 | | 'application/vnd.ipld.raw' 937 | | 'application/vnd.iptc.g2.catalogitem+xml' 938 | | 'application/vnd.iptc.g2.conceptitem+xml' 939 | | 'application/vnd.iptc.g2.knowledgeitem+xml' 940 | | 'application/vnd.iptc.g2.newsitem+xml' 941 | | 'application/vnd.iptc.g2.newsmessage+xml' 942 | | 'application/vnd.iptc.g2.packageitem+xml' 943 | | 'application/vnd.iptc.g2.planningitem+xml' 944 | | 'application/vnd.ipunplugged.rcprofile' 945 | | 'application/vnd.irepository.package+xml' 946 | | 'application/vnd.is-xpr' 947 | | 'application/vnd.isac.fcs' 948 | | 'application/vnd.jam' 949 | | 'application/vnd.iso11783-10+zip' 950 | | 'application/vnd.japannet-directory-service' 951 | | 'application/vnd.japannet-jpnstore-wakeup' 952 | | 'application/vnd.japannet-payment-wakeup' 953 | | 'application/vnd.japannet-registration' 954 | | 'application/vnd.japannet-registration-wakeup' 955 | | 'application/vnd.japannet-setstore-wakeup' 956 | | 'application/vnd.japannet-verification' 957 | | 'application/vnd.japannet-verification-wakeup' 958 | | 'application/vnd.jcp.javame.midlet-rms' 959 | | 'application/vnd.jisp' 960 | | 'application/vnd.joost.joda-archive' 961 | | 'application/vnd.jsk.isdn-ngn' 962 | | 'application/vnd.kahootz' 963 | | 'application/vnd.kde.karbon' 964 | | 'application/vnd.kde.kchart' 965 | | 'application/vnd.kde.kformula' 966 | | 'application/vnd.kde.kivio' 967 | | 'application/vnd.kde.kontour' 968 | | 'application/vnd.kde.kpresenter' 969 | | 'application/vnd.kde.kspread' 970 | | 'application/vnd.kde.kword' 971 | | 'application/vnd.kenameaapp' 972 | | 'application/vnd.kidspiration' 973 | | 'application/vnd.Kinar' 974 | | 'application/vnd.koan' 975 | | 'application/vnd.kodak-descriptor' 976 | | 'application/vnd.las' 977 | | 'application/vnd.las.las+json' 978 | | 'application/vnd.las.las+xml' 979 | | 'application/vnd.laszip' 980 | | 'application/vnd.ldev.productlicensing' 981 | | 'application/vnd.leap+json' 982 | | 'application/vnd.liberty-request+xml' 983 | | 'application/vnd.llamagraphics.life-balance.desktop' 984 | | 'application/vnd.llamagraphics.life-balance.exchange+xml' 985 | | 'application/vnd.logipipe.circuit+zip' 986 | | 'application/vnd.loom' 987 | | 'application/vnd.lotus-1-2-3' 988 | | 'application/vnd.lotus-approach' 989 | | 'application/vnd.lotus-freelance' 990 | | 'application/vnd.lotus-notes' 991 | | 'application/vnd.lotus-organizer' 992 | | 'application/vnd.lotus-screencam' 993 | | 'application/vnd.lotus-wordpro' 994 | | 'application/vnd.macports.portpkg' 995 | | 'application/vnd.mapbox-vector-tile' 996 | | 'application/vnd.marlin.drm.actiontoken+xml' 997 | | 'application/vnd.marlin.drm.conftoken+xml' 998 | | 'application/vnd.marlin.drm.license+xml' 999 | | 'application/vnd.marlin.drm.mdcf' 1000 | | 'application/vnd.mason+json' 1001 | | 'application/vnd.maxar.archive.3tz+zip' 1002 | | 'application/vnd.maxmind.maxmind-db' 1003 | | 'application/vnd.mcd' 1004 | | 'application/vnd.mdl' 1005 | | 'application/vnd.mdl-mbsdf' 1006 | | 'application/vnd.medcalcdata' 1007 | | 'application/vnd.mediastation.cdkey' 1008 | | 'application/vnd.medicalholodeck.recordxr' 1009 | | 'application/vnd.meridian-slingshot' 1010 | | 'application/vnd.mermaid' 1011 | | 'application/vnd.MFER' 1012 | | 'application/vnd.mfmp' 1013 | | 'application/vnd.micro+json' 1014 | | 'application/vnd.micrografx.flo' 1015 | | 'application/vnd.micrografx.igx' 1016 | | 'application/vnd.microsoft.portable-executable' 1017 | | 'application/vnd.microsoft.windows.thumbnail-cache' 1018 | | 'application/vnd.miele+json' 1019 | | 'application/vnd.mif' 1020 | | 'application/vnd.minisoft-hp3000-save' 1021 | | 'application/vnd.mitsubishi.misty-guard.trustweb' 1022 | | 'application/vnd.Mobius.DAF' 1023 | | 'application/vnd.Mobius.DIS' 1024 | | 'application/vnd.Mobius.MBK' 1025 | | 'application/vnd.Mobius.MQY' 1026 | | 'application/vnd.Mobius.MSL' 1027 | | 'application/vnd.Mobius.PLC' 1028 | | 'application/vnd.Mobius.TXF' 1029 | | 'application/vnd.modl' 1030 | | 'application/vnd.mophun.application' 1031 | | 'application/vnd.mophun.certificate' 1032 | | 'application/vnd.motorola.flexsuite' 1033 | | 'application/vnd.motorola.flexsuite.adsi' 1034 | | 'application/vnd.motorola.flexsuite.fis' 1035 | | 'application/vnd.motorola.flexsuite.gotap' 1036 | | 'application/vnd.motorola.flexsuite.kmr' 1037 | | 'application/vnd.motorola.flexsuite.ttc' 1038 | | 'application/vnd.motorola.flexsuite.wem' 1039 | | 'application/vnd.motorola.iprm' 1040 | | 'application/vnd.mozilla.xul+xml' 1041 | | 'application/vnd.ms-artgalry' 1042 | | 'application/vnd.ms-asf' 1043 | | 'application/vnd.ms-cab-compressed' 1044 | | 'application/vnd.ms-3mfdocument' 1045 | | 'application/vnd.ms-excel' 1046 | | 'application/vnd.ms-excel.addin.macroEnabled.12' 1047 | | 'application/vnd.ms-excel.sheet.binary.macroEnabled.12' 1048 | | 'application/vnd.ms-excel.sheet.macroEnabled.12' 1049 | | 'application/vnd.ms-excel.template.macroEnabled.12' 1050 | | 'application/vnd.ms-fontobject' 1051 | | 'application/vnd.ms-htmlhelp' 1052 | | 'application/vnd.ms-ims' 1053 | | 'application/vnd.ms-lrm' 1054 | | 'application/vnd.ms-office.activeX+xml' 1055 | | 'application/vnd.ms-officetheme' 1056 | | 'application/vnd.ms-playready.initiator+xml' 1057 | | 'application/vnd.ms-powerpoint' 1058 | | 'application/vnd.ms-powerpoint.addin.macroEnabled.12' 1059 | | 'application/vnd.ms-powerpoint.presentation.macroEnabled.12' 1060 | | 'application/vnd.ms-powerpoint.slide.macroEnabled.12' 1061 | | 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12' 1062 | | 'application/vnd.ms-powerpoint.template.macroEnabled.12' 1063 | | 'application/vnd.ms-PrintDeviceCapabilities+xml' 1064 | | 'application/vnd.ms-PrintSchemaTicket+xml' 1065 | | 'application/vnd.ms-project' 1066 | | 'application/vnd.ms-tnef' 1067 | | 'application/vnd.ms-windows.devicepairing' 1068 | | 'application/vnd.ms-windows.nwprinting.oob' 1069 | | 'application/vnd.ms-windows.printerpairing' 1070 | | 'application/vnd.ms-windows.wsd.oob' 1071 | | 'application/vnd.ms-wmdrm.lic-chlg-req' 1072 | | 'application/vnd.ms-wmdrm.lic-resp' 1073 | | 'application/vnd.ms-wmdrm.meter-chlg-req' 1074 | | 'application/vnd.ms-wmdrm.meter-resp' 1075 | | 'application/vnd.ms-word.document.macroEnabled.12' 1076 | | 'application/vnd.ms-word.template.macroEnabled.12' 1077 | | 'application/vnd.ms-works' 1078 | | 'application/vnd.ms-wpl' 1079 | | 'application/vnd.ms-xpsdocument' 1080 | | 'application/vnd.msa-disk-image' 1081 | | 'application/vnd.mseq' 1082 | | 'application/vnd.msign' 1083 | | 'application/vnd.multiad.creator' 1084 | | 'application/vnd.multiad.creator.cif' 1085 | | 'application/vnd.musician' 1086 | | 'application/vnd.music-niff' 1087 | | 'application/vnd.muvee.style' 1088 | | 'application/vnd.mynfc' 1089 | | 'application/vnd.nacamar.ybrid+json' 1090 | | 'application/vnd.nato.bindingdataobject+cbor' 1091 | | 'application/vnd.nato.bindingdataobject+json' 1092 | | 'application/vnd.nato.bindingdataobject+xml' 1093 | | 'application/vnd.nato.openxmlformats-package.iepd+zip' 1094 | | 'application/vnd.ncd.control' 1095 | | 'application/vnd.ncd.reference' 1096 | | 'application/vnd.nearst.inv+json' 1097 | | 'application/vnd.nebumind.line' 1098 | | 'application/vnd.nervana' 1099 | | 'application/vnd.netfpx' 1100 | | 'application/vnd.neurolanguage.nlu' 1101 | | 'application/vnd.nimn' 1102 | | 'application/vnd.nintendo.snes.rom' 1103 | | 'application/vnd.nintendo.nitro.rom' 1104 | | 'application/vnd.nitf' 1105 | | 'application/vnd.noblenet-directory' 1106 | | 'application/vnd.noblenet-sealer' 1107 | | 'application/vnd.noblenet-web' 1108 | | 'application/vnd.nokia.catalogs' 1109 | | 'application/vnd.nokia.conml+wbxml' 1110 | | 'application/vnd.nokia.conml+xml' 1111 | | 'application/vnd.nokia.iptv.config+xml' 1112 | | 'application/vnd.nokia.iSDS-radio-presets' 1113 | | 'application/vnd.nokia.landmark+wbxml' 1114 | | 'application/vnd.nokia.landmark+xml' 1115 | | 'application/vnd.nokia.landmarkcollection+xml' 1116 | | 'application/vnd.nokia.ncd' 1117 | | 'application/vnd.nokia.n-gage.ac+xml' 1118 | | 'application/vnd.nokia.n-gage.data' 1119 | | 'application/vnd.nokia.n-gage.symbian.install' 1120 | | 'application/vnd.nokia.pcd+wbxml' 1121 | | 'application/vnd.nokia.pcd+xml' 1122 | | 'application/vnd.nokia.radio-preset' 1123 | | 'application/vnd.nokia.radio-presets' 1124 | | 'application/vnd.novadigm.EDM' 1125 | | 'application/vnd.novadigm.EDX' 1126 | | 'application/vnd.novadigm.EXT' 1127 | | 'application/vnd.ntt-local.content-share' 1128 | | 'application/vnd.ntt-local.file-transfer' 1129 | | 'application/vnd.ntt-local.ogw_remote-access' 1130 | | 'application/vnd.ntt-local.sip-ta_remote' 1131 | | 'application/vnd.ntt-local.sip-ta_tcp_stream' 1132 | | 'application/vnd.oai.workflows' 1133 | | 'application/vnd.oai.workflows+json' 1134 | | 'application/vnd.oai.workflows+yaml' 1135 | | 'application/vnd.oasis.opendocument.base' 1136 | | 'application/vnd.oasis.opendocument.chart' 1137 | | 'application/vnd.oasis.opendocument.chart-template' 1138 | | 'application/vnd.oasis.opendocument.database' 1139 | | 'application/vnd.oasis.opendocument.formula' 1140 | | 'application/vnd.oasis.opendocument.formula-template' 1141 | | 'application/vnd.oasis.opendocument.graphics' 1142 | | 'application/vnd.oasis.opendocument.graphics-template' 1143 | | 'application/vnd.oasis.opendocument.image' 1144 | | 'application/vnd.oasis.opendocument.image-template' 1145 | | 'application/vnd.oasis.opendocument.presentation' 1146 | | 'application/vnd.oasis.opendocument.presentation-template' 1147 | | 'application/vnd.oasis.opendocument.spreadsheet' 1148 | | 'application/vnd.oasis.opendocument.spreadsheet-template' 1149 | | 'application/vnd.oasis.opendocument.text' 1150 | | 'application/vnd.oasis.opendocument.text-master' 1151 | | 'application/vnd.oasis.opendocument.text-master-template' 1152 | | 'application/vnd.oasis.opendocument.text-template' 1153 | | 'application/vnd.oasis.opendocument.text-web' 1154 | | 'application/vnd.obn' 1155 | | 'application/vnd.ocf+cbor' 1156 | | 'application/vnd.oci.image.manifest.v1+json' 1157 | | 'application/vnd.oftn.l10n+json' 1158 | | 'application/vnd.oipf.contentaccessdownload+xml' 1159 | | 'application/vnd.oipf.contentaccessstreaming+xml' 1160 | | 'application/vnd.oipf.cspg-hexbinary' 1161 | | 'application/vnd.oipf.dae.svg+xml' 1162 | | 'application/vnd.oipf.dae.xhtml+xml' 1163 | | 'application/vnd.oipf.mippvcontrolmessage+xml' 1164 | | 'application/vnd.oipf.pae.gem' 1165 | | 'application/vnd.oipf.spdiscovery+xml' 1166 | | 'application/vnd.oipf.spdlist+xml' 1167 | | 'application/vnd.oipf.ueprofile+xml' 1168 | | 'application/vnd.oipf.userprofile+xml' 1169 | | 'application/vnd.olpc-sugar' 1170 | | 'application/vnd.oma.bcast.associated-procedure-parameter+xml' 1171 | | 'application/vnd.oma.bcast.drm-trigger+xml' 1172 | | 'application/vnd.oma.bcast.imd+xml' 1173 | | 'application/vnd.oma.bcast.ltkm' 1174 | | 'application/vnd.oma.bcast.notification+xml' 1175 | | 'application/vnd.oma.bcast.provisioningtrigger' 1176 | | 'application/vnd.oma.bcast.sgboot' 1177 | | 'application/vnd.oma.bcast.sgdd+xml' 1178 | | 'application/vnd.oma.bcast.sgdu' 1179 | | 'application/vnd.oma.bcast.simple-symbol-container' 1180 | | 'application/vnd.oma.bcast.smartcard-trigger+xml' 1181 | | 'application/vnd.oma.bcast.sprov+xml' 1182 | | 'application/vnd.oma.bcast.stkm' 1183 | | 'application/vnd.oma.cab-address-book+xml' 1184 | | 'application/vnd.oma.cab-feature-handler+xml' 1185 | | 'application/vnd.oma.cab-pcc+xml' 1186 | | 'application/vnd.oma.cab-subs-invite+xml' 1187 | | 'application/vnd.oma.cab-user-prefs+xml' 1188 | | 'application/vnd.oma.dcd' 1189 | | 'application/vnd.oma.dcdc' 1190 | | 'application/vnd.oma.dd2+xml' 1191 | | 'application/vnd.oma.drm.risd+xml' 1192 | | 'application/vnd.oma.group-usage-list+xml' 1193 | | 'application/vnd.oma.lwm2m+cbor' 1194 | | 'application/vnd.oma.lwm2m+json' 1195 | | 'application/vnd.oma.lwm2m+tlv' 1196 | | 'application/vnd.oma.pal+xml' 1197 | | 'application/vnd.oma.poc.detailed-progress-report+xml' 1198 | | 'application/vnd.oma.poc.final-report+xml' 1199 | | 'application/vnd.oma.poc.groups+xml' 1200 | | 'application/vnd.oma.poc.invocation-descriptor+xml' 1201 | | 'application/vnd.oma.poc.optimized-progress-report+xml' 1202 | | 'application/vnd.oma.push' 1203 | | 'application/vnd.oma.scidm.messages+xml' 1204 | | 'application/vnd.oma.xcap-directory+xml' 1205 | | 'application/vnd.omads-email+xml' 1206 | | 'application/vnd.omads-file+xml' 1207 | | 'application/vnd.omads-folder+xml' 1208 | | 'application/vnd.omaloc-supl-init' 1209 | | 'application/vnd.oma-scws-config' 1210 | | 'application/vnd.oma-scws-http-request' 1211 | | 'application/vnd.oma-scws-http-response' 1212 | | 'application/vnd.onepager' 1213 | | 'application/vnd.onepagertamp' 1214 | | 'application/vnd.onepagertamx' 1215 | | 'application/vnd.onepagertat' 1216 | | 'application/vnd.onepagertatp' 1217 | | 'application/vnd.onepagertatx' 1218 | | 'application/vnd.onvif.metadata' 1219 | | 'application/vnd.openblox.game-binary' 1220 | | 'application/vnd.openblox.game+xml' 1221 | | 'application/vnd.openeye.oeb' 1222 | | 'application/vnd.openstreetmap.data+xml' 1223 | | 'application/vnd.opentimestamps.ots' 1224 | | 'application/vnd.openxmlformats-officedocument.custom-properties+xml' 1225 | | 'application/vnd.openxmlformats-officedocument.customXmlProperties+xml' 1226 | | 'application/vnd.openxmlformats-officedocument.drawing+xml' 1227 | | 'application/vnd.openxmlformats-officedocument.drawingml.chart+xml' 1228 | | 'application/vnd.openxmlformats-officedocument.drawingml.chartshapes+xml' 1229 | | 'application/vnd.openxmlformats-officedocument.drawingml.diagramColors+xml' 1230 | | 'application/vnd.openxmlformats-officedocument.drawingml.diagramData+xml' 1231 | | 'application/vnd.openxmlformats-officedocument.drawingml.diagramLayout+xml' 1232 | | 'application/vnd.openxmlformats-officedocument.drawingml.diagramStyle+xml' 1233 | | 'application/vnd.openxmlformats-officedocument.extended-properties+xml' 1234 | | 'application/vnd.openxmlformats-officedocument.presentationml.commentAuthors+xml' 1235 | | 'application/vnd.openxmlformats-officedocument.presentationml.comments+xml' 1236 | | 'application/vnd.openxmlformats-officedocument.presentationml.handoutMaster+xml' 1237 | | 'application/vnd.openxmlformats-officedocument.presentationml.notesMaster+xml' 1238 | | 'application/vnd.openxmlformats-officedocument.presentationml.notesSlide+xml' 1239 | | 'application/vnd.openxmlformats-officedocument.presentationml.presentation' 1240 | | 'application/vnd.openxmlformats-officedocument.presentationml.presentation.main+xml' 1241 | | 'application/vnd.openxmlformats-officedocument.presentationml.presProps+xml' 1242 | | 'application/vnd.openxmlformats-officedocument.presentationml.slide' 1243 | | 'application/vnd.openxmlformats-officedocument.presentationml.slide+xml' 1244 | | 'application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml' 1245 | | 'application/vnd.openxmlformats-officedocument.presentationml.slideMaster+xml' 1246 | | 'application/vnd.openxmlformats-officedocument.presentationml.slideshow' 1247 | | 'application/vnd.openxmlformats-officedocument.presentationml.slideshow.main+xml' 1248 | | 'application/vnd.openxmlformats-officedocument.presentationml.slideUpdateInfo+xml' 1249 | | 'application/vnd.openxmlformats-officedocument.presentationml.tableStyles+xml' 1250 | | 'application/vnd.openxmlformats-officedocument.presentationml.tags+xml' 1251 | | 'application/vnd.openxmlformats-officedocument.presentationml.template' 1252 | | 'application/vnd.openxmlformats-officedocument.presentationml.template.main+xml' 1253 | | 'application/vnd.openxmlformats-officedocument.presentationml.viewProps+xml' 1254 | | 'application/vnd.openxmlformats-officedocument.spreadsheetml.calcChain+xml' 1255 | | 'application/vnd.openxmlformats-officedocument.spreadsheetml.chartsheet+xml' 1256 | | 'application/vnd.openxmlformats-officedocument.spreadsheetml.comments+xml' 1257 | | 'application/vnd.openxmlformats-officedocument.spreadsheetml.connections+xml' 1258 | | 'application/vnd.openxmlformats-officedocument.spreadsheetml.dialogsheet+xml' 1259 | | 'application/vnd.openxmlformats-officedocument.spreadsheetml.externalLink+xml' 1260 | | 'application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheDefinition+xml' 1261 | | 'application/vnd.openxmlformats-officedocument.spreadsheetml.pivotCacheRecords+xml' 1262 | | 'application/vnd.openxmlformats-officedocument.spreadsheetml.pivotTable+xml' 1263 | | 'application/vnd.openxmlformats-officedocument.spreadsheetml.queryTable+xml' 1264 | | 'application/vnd.openxmlformats-officedocument.spreadsheetml.revisionHeaders+xml' 1265 | | 'application/vnd.openxmlformats-officedocument.spreadsheetml.revisionLog+xml' 1266 | | 'application/vnd.openxmlformats-officedocument.spreadsheetml.sharedStrings+xml' 1267 | | 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' 1268 | | 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml' 1269 | | 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheetMetadata+xml' 1270 | | 'application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml' 1271 | | 'application/vnd.openxmlformats-officedocument.spreadsheetml.table+xml' 1272 | | 'application/vnd.openxmlformats-officedocument.spreadsheetml.tableSingleCells+xml' 1273 | | 'application/vnd.openxmlformats-officedocument.spreadsheetml.template' 1274 | | 'application/vnd.openxmlformats-officedocument.spreadsheetml.template.main+xml' 1275 | | 'application/vnd.openxmlformats-officedocument.spreadsheetml.userNames+xml' 1276 | | 'application/vnd.openxmlformats-officedocument.spreadsheetml.volatileDependencies+xml' 1277 | | 'application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml' 1278 | | 'application/vnd.openxmlformats-officedocument.theme+xml' 1279 | | 'application/vnd.openxmlformats-officedocument.themeOverride+xml' 1280 | | 'application/vnd.openxmlformats-officedocument.vmlDrawing' 1281 | | 'application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml' 1282 | | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' 1283 | | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document.glossary+xml' 1284 | | 'application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml' 1285 | | 'application/vnd.openxmlformats-officedocument.wordprocessingml.endnotes+xml' 1286 | | 'application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml' 1287 | | 'application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml' 1288 | | 'application/vnd.openxmlformats-officedocument.wordprocessingml.footnotes+xml' 1289 | | 'application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml' 1290 | | 'application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml' 1291 | | 'application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml' 1292 | | 'application/vnd.openxmlformats-officedocument.wordprocessingml.template' 1293 | | 'application/vnd.openxmlformats-officedocument.wordprocessingml.template.main+xml' 1294 | | 'application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml' 1295 | | 'application/vnd.openxmlformats-package.core-properties+xml' 1296 | | 'application/vnd.openxmlformats-package.digital-signature-xmlsignature+xml' 1297 | | 'application/vnd.openxmlformats-package.relationships+xml' 1298 | | 'application/vnd.oracle.resource+json' 1299 | | 'application/vnd.orange.indata' 1300 | | 'application/vnd.osa.netdeploy' 1301 | | 'application/vnd.osgeo.mapguide.package' 1302 | | 'application/vnd.osgi.bundle' 1303 | | 'application/vnd.osgi.dp' 1304 | | 'application/vnd.osgi.subsystem' 1305 | | 'application/vnd.otps.ct-kip+xml' 1306 | | 'application/vnd.oxli.countgraph' 1307 | | 'application/vnd.pagerduty+json' 1308 | | 'application/vnd.palm' 1309 | | 'application/vnd.panoply' 1310 | | 'application/vnd.paos.xml' 1311 | | 'application/vnd.patentdive' 1312 | | 'application/vnd.patientecommsdoc' 1313 | | 'application/vnd.pawaafile' 1314 | | 'application/vnd.pcos' 1315 | | 'application/vnd.pg.format' 1316 | | 'application/vnd.pg.osasli' 1317 | | 'application/vnd.piaccess.application-licence' 1318 | | 'application/vnd.picsel' 1319 | | 'application/vnd.pmi.widget' 1320 | | 'application/vnd.poc.group-advertisement+xml' 1321 | | 'application/vnd.pocketlearn' 1322 | | 'application/vnd.powerbuilder6' 1323 | | 'application/vnd.powerbuilder6-s' 1324 | | 'application/vnd.powerbuilder7' 1325 | | 'application/vnd.powerbuilder75' 1326 | | 'application/vnd.powerbuilder75-s' 1327 | | 'application/vnd.powerbuilder7-s' 1328 | | 'application/vnd.preminet' 1329 | | 'application/vnd.previewsystems.box' 1330 | | 'application/vnd.proteus.magazine' 1331 | | 'application/vnd.psfs' 1332 | | 'application/vnd.pt.mundusmundi' 1333 | | 'application/vnd.publishare-delta-tree' 1334 | | 'application/vnd.pvi.ptid1' 1335 | | 'application/vnd.pwg-multiplexed' 1336 | | 'application/vnd.pwg-xhtml-print+xml' 1337 | | 'application/vnd.qualcomm.brew-app-res' 1338 | | 'application/vnd.quarantainenet' 1339 | | 'application/vnd.Quark.QuarkXPress' 1340 | | 'application/vnd.quobject-quoxdocument' 1341 | | 'application/vnd.radisys.moml+xml' 1342 | | 'application/vnd.radisys.msml-audit-conf+xml' 1343 | | 'application/vnd.radisys.msml-audit-conn+xml' 1344 | | 'application/vnd.radisys.msml-audit-dialog+xml' 1345 | | 'application/vnd.radisys.msml-audit-stream+xml' 1346 | | 'application/vnd.radisys.msml-audit+xml' 1347 | | 'application/vnd.radisys.msml-conf+xml' 1348 | | 'application/vnd.radisys.msml-dialog-base+xml' 1349 | | 'application/vnd.radisys.msml-dialog-fax-detect+xml' 1350 | | 'application/vnd.radisys.msml-dialog-fax-sendrecv+xml' 1351 | | 'application/vnd.radisys.msml-dialog-group+xml' 1352 | | 'application/vnd.radisys.msml-dialog-speech+xml' 1353 | | 'application/vnd.radisys.msml-dialog-transform+xml' 1354 | | 'application/vnd.radisys.msml-dialog+xml' 1355 | | 'application/vnd.radisys.msml+xml' 1356 | | 'application/vnd.rainstor.data' 1357 | | 'application/vnd.rapid' 1358 | | 'application/vnd.rar' 1359 | | 'application/vnd.realvnc.bed' 1360 | | 'application/vnd.recordare.musicxml' 1361 | | 'application/vnd.recordare.musicxml+xml' 1362 | | 'application/vnd.relpipe' 1363 | | 'application/vnd.RenLearn.rlprint' 1364 | | 'application/vnd.resilient.logic' 1365 | | 'application/vnd.restful+json' 1366 | | 'application/vnd.rig.cryptonote' 1367 | | 'application/vnd.route66.link66+xml' 1368 | | 'application/vnd.rs-274x' 1369 | | 'application/vnd.ruckus.download' 1370 | | 'application/vnd.s3sms' 1371 | | 'application/vnd.sailingtracker.track' 1372 | | 'application/vnd.sar' 1373 | | 'application/vnd.sbm.cid' 1374 | | 'application/vnd.sbm.mid2' 1375 | | 'application/vnd.scribus' 1376 | | 'application/vnd.sealed.3df' 1377 | | 'application/vnd.sealed.csf' 1378 | | 'application/vnd.sealed.doc' 1379 | | 'application/vnd.sealed.eml' 1380 | | 'application/vnd.sealed.mht' 1381 | | 'application/vnd.sealed.net' 1382 | | 'application/vnd.sealed.ppt' 1383 | | 'application/vnd.sealed.tiff' 1384 | | 'application/vnd.sealed.xls' 1385 | | 'application/vnd.sealedmedia.softseal.html' 1386 | | 'application/vnd.sealedmedia.softseal.pdf' 1387 | | 'application/vnd.seemail' 1388 | | 'application/vnd.seis+json' 1389 | | 'application/vnd.sema' 1390 | | 'application/vnd.semd' 1391 | | 'application/vnd.semf' 1392 | | 'application/vnd.shade-save-file' 1393 | | 'application/vnd.shana.informed.formdata' 1394 | | 'application/vnd.shana.informed.formtemplate' 1395 | | 'application/vnd.shana.informed.interchange' 1396 | | 'application/vnd.shana.informed.package' 1397 | | 'application/vnd.shootproof+json' 1398 | | 'application/vnd.shopkick+json' 1399 | | 'application/vnd.shp' 1400 | | 'application/vnd.shx' 1401 | | 'application/vnd.sigrok.session' 1402 | | 'application/vnd.SimTech-MindMapper' 1403 | | 'application/vnd.siren+json' 1404 | | 'application/vnd.smaf' 1405 | | 'application/vnd.smart.notebook' 1406 | | 'application/vnd.smart.teacher' 1407 | | 'application/vnd.smintio.portals.archive' 1408 | | 'application/vnd.snesdev-page-table' 1409 | | 'application/vnd.software602.filler.form+xml' 1410 | | 'application/vnd.software602.filler.form-xml-zip' 1411 | | 'application/vnd.solent.sdkm+xml' 1412 | | 'application/vnd.spotfire.dxp' 1413 | | 'application/vnd.spotfire.sfs' 1414 | | 'application/vnd.sqlite3' 1415 | | 'application/vnd.sss-cod' 1416 | | 'application/vnd.sss-dtf' 1417 | | 'application/vnd.sss-ntf' 1418 | | 'application/vnd.stepmania.package' 1419 | | 'application/vnd.stepmania.stepchart' 1420 | | 'application/vnd.street-stream' 1421 | | 'application/vnd.sun.wadl+xml' 1422 | | 'application/vnd.sus-calendar' 1423 | | 'application/vnd.svd' 1424 | | 'application/vnd.swiftview-ics' 1425 | | 'application/vnd.sybyl.mol2' 1426 | | 'application/vnd.sycle+xml' 1427 | | 'application/vnd.syft+json' 1428 | | 'application/vnd.syncml.dm.notification' 1429 | | 'application/vnd.syncml.dmddf+xml' 1430 | | 'application/vnd.syncml.dmtnds+wbxml' 1431 | | 'application/vnd.syncml.dmtnds+xml' 1432 | | 'application/vnd.syncml.dmddf+wbxml' 1433 | | 'application/vnd.syncml.dm+wbxml' 1434 | | 'application/vnd.syncml.dm+xml' 1435 | | 'application/vnd.syncml.ds.notification' 1436 | | 'application/vnd.syncml+xml' 1437 | | 'application/vnd.tableschema+json' 1438 | | 'application/vnd.tao.intent-module-archive' 1439 | | 'application/vnd.tcpdump.pcap' 1440 | | 'application/vnd.think-cell.ppttc+json' 1441 | | 'application/vnd.tml' 1442 | | 'application/vnd.tmd.mediaflex.api+xml' 1443 | | 'application/vnd.tmobile-livetv' 1444 | | 'application/vnd.tri.onesource' 1445 | | 'application/vnd.trid.tpt' 1446 | | 'application/vnd.triscape.mxs' 1447 | | 'application/vnd.trueapp' 1448 | | 'application/vnd.truedoc' 1449 | | 'application/vnd.ubisoft.webplayer' 1450 | | 'application/vnd.ufdl' 1451 | | 'application/vnd.uiq.theme' 1452 | | 'application/vnd.umajin' 1453 | | 'application/vnd.unity' 1454 | | 'application/vnd.uoml+xml' 1455 | | 'application/vnd.uplanet.alert' 1456 | | 'application/vnd.uplanet.alert-wbxml' 1457 | | 'application/vnd.uplanet.bearer-choice' 1458 | | 'application/vnd.uplanet.bearer-choice-wbxml' 1459 | | 'application/vnd.uplanet.cacheop' 1460 | | 'application/vnd.uplanet.cacheop-wbxml' 1461 | | 'application/vnd.uplanet.channel' 1462 | | 'application/vnd.uplanet.channel-wbxml' 1463 | | 'application/vnd.uplanet.list' 1464 | | 'application/vnd.uplanet.listcmd' 1465 | | 'application/vnd.uplanet.listcmd-wbxml' 1466 | | 'application/vnd.uplanet.list-wbxml' 1467 | | 'application/vnd.uri-map' 1468 | | 'application/vnd.uplanet.signal' 1469 | | 'application/vnd.valve.source.material' 1470 | | 'application/vnd.vcx' 1471 | | 'application/vnd.vd-study' 1472 | | 'application/vnd.vectorworks' 1473 | | 'application/vnd.vel+json' 1474 | | 'application/vnd.verimatrix.vcas' 1475 | | 'application/vnd.veritone.aion+json' 1476 | | 'application/vnd.veryant.thin' 1477 | | 'application/vnd.ves.encrypted' 1478 | | 'application/vnd.vidsoft.vidconference' 1479 | | 'application/vnd.visio' 1480 | | 'application/vnd.visionary' 1481 | | 'application/vnd.vividence.scriptfile' 1482 | | 'application/vnd.vsf' 1483 | | 'application/vnd.wap.sic' 1484 | | 'application/vnd.wap.slc' 1485 | | 'application/vnd.wap.wbxml' 1486 | | 'application/vnd.wap.wmlc' 1487 | | 'application/vnd.wap.wmlscriptc' 1488 | | 'application/vnd.wasmflow.wafl' 1489 | | 'application/vnd.webturbo' 1490 | | 'application/vnd.wfa.dpp' 1491 | | 'application/vnd.wfa.p2p' 1492 | | 'application/vnd.wfa.wsc' 1493 | | 'application/vnd.windows.devicepairing' 1494 | | 'application/vnd.wmc' 1495 | | 'application/vnd.wmf.bootstrap' 1496 | | 'application/vnd.wolfram.mathematica' 1497 | | 'application/vnd.wolfram.mathematica.package' 1498 | | 'application/vnd.wolfram.player' 1499 | | 'application/vnd.wordlift' 1500 | | 'application/vnd.wordperfect' 1501 | | 'application/vnd.wqd' 1502 | | 'application/vnd.wrq-hp3000-labelled' 1503 | | 'application/vnd.wt.stf' 1504 | | 'application/vnd.wv.csp+xml' 1505 | | 'application/vnd.wv.csp+wbxml' 1506 | | 'application/vnd.wv.ssp+xml' 1507 | | 'application/vnd.xacml+json' 1508 | | 'application/vnd.xara' 1509 | | 'application/vnd.xecrets-encrypted' 1510 | | 'application/vnd.xfdl' 1511 | | 'application/vnd.xfdl.webform' 1512 | | 'application/vnd.xmi+xml' 1513 | | 'application/vnd.xmpie.cpkg' 1514 | | 'application/vnd.xmpie.dpkg' 1515 | | 'application/vnd.xmpie.plan' 1516 | | 'application/vnd.xmpie.ppkg' 1517 | | 'application/vnd.xmpie.xlim' 1518 | | 'application/vnd.yamaha.hv-dic' 1519 | | 'application/vnd.yamaha.hv-script' 1520 | | 'application/vnd.yamaha.hv-voice' 1521 | | 'application/vnd.yamaha.openscoreformat.osfpvg+xml' 1522 | | 'application/vnd.yamaha.openscoreformat' 1523 | | 'application/vnd.yamaha.remote-setup' 1524 | | 'application/vnd.yamaha.smaf-audio' 1525 | | 'application/vnd.yamaha.smaf-phrase' 1526 | | 'application/vnd.yamaha.through-ngn' 1527 | | 'application/vnd.yamaha.tunnel-udpencap' 1528 | | 'application/vnd.yaoweme' 1529 | | 'application/vnd.yellowriver-custom-menu' 1530 | | 'application/vnd.youtube.yt' 1531 | | 'application/vnd.zul' 1532 | | 'application/vnd.zzazz.deck+xml' 1533 | | 'application/voicexml+xml' 1534 | | 'application/voucher-cms+json' 1535 | | 'application/vq-rtcpxr' 1536 | | 'application/wasm' 1537 | | 'application/watcherinfo+xml' 1538 | | 'application/webpush-options+json' 1539 | | 'application/whoispp-query' 1540 | | 'application/whoispp-response' 1541 | | 'application/widget' 1542 | | 'application/wita' 1543 | | 'application/wordperfect5.1' 1544 | | 'application/wsdl+xml' 1545 | | 'application/wspolicy+xml' 1546 | | 'application/x-pki-message' 1547 | | 'application/x-www-form-urlencoded' 1548 | | 'application/x-x509-ca-cert' 1549 | | 'application/x-x509-ca-ra-cert' 1550 | | 'application/x-x509-next-ca-cert' 1551 | | 'application/x400-bp' 1552 | | 'application/xacml+xml' 1553 | | 'application/xcap-att+xml' 1554 | | 'application/xcap-caps+xml' 1555 | | 'application/xcap-diff+xml' 1556 | | 'application/xcap-el+xml' 1557 | | 'application/xcap-error+xml' 1558 | | 'application/xcap-ns+xml' 1559 | | 'application/xcon-conference-info-diff+xml' 1560 | | 'application/xcon-conference-info+xml' 1561 | | 'application/xenc+xml' 1562 | | 'application/xfdf' 1563 | | 'application/xhtml+xml' 1564 | | 'application/xliff+xml' 1565 | | 'application/xml' 1566 | | 'application/xml-dtd' 1567 | | 'application/xml-external-parsed-entity' 1568 | | 'application/xml-patch+xml' 1569 | | 'application/xmpp+xml' 1570 | | 'application/xop+xml' 1571 | | 'application/xslt+xml' 1572 | | 'application/xv+xml' 1573 | | 'application/yaml' 1574 | | 'application/yang' 1575 | | 'application/yang-data+cbor' 1576 | | 'application/yang-data+json' 1577 | | 'application/yang-data+xml' 1578 | | 'application/yang-patch+json' 1579 | | 'application/yang-patch+xml' 1580 | | 'application/yin+xml' 1581 | | 'application/zip' 1582 | | 'application/zlib' 1583 | | 'application/zstd' 1584 | | 'audio/1d-interleaved-parityfec' 1585 | | 'audio/32kadpcm' 1586 | | 'audio/3gpp' 1587 | | 'audio/3gpp2' 1588 | | 'audio/aac' 1589 | | 'audio/ac3' 1590 | | 'audio/AMR' 1591 | | 'audio/AMR-WB' 1592 | | 'audio/amr-wb+' 1593 | | 'audio/aptx' 1594 | | 'audio/asc' 1595 | | 'audio/ATRAC-ADVANCED-LOSSLESS' 1596 | | 'audio/ATRAC-X' 1597 | | 'audio/ATRAC3' 1598 | | 'audio/basic' 1599 | | 'audio/BV16' 1600 | | 'audio/BV32' 1601 | | 'audio/clearmode' 1602 | | 'audio/CN' 1603 | | 'audio/DAT12' 1604 | | 'audio/dls' 1605 | | 'audio/dsr-es201108' 1606 | | 'audio/dsr-es202050' 1607 | | 'audio/dsr-es202211' 1608 | | 'audio/dsr-es202212' 1609 | | 'audio/DV' 1610 | | 'audio/DVI4' 1611 | | 'audio/eac3' 1612 | | 'audio/encaprtp' 1613 | | 'audio/EVRC' 1614 | | 'audio/EVRC-QCP' 1615 | | 'audio/EVRC0' 1616 | | 'audio/EVRC1' 1617 | | 'audio/EVRCB' 1618 | | 'audio/EVRCB0' 1619 | | 'audio/EVRCB1' 1620 | | 'audio/EVRCNW' 1621 | | 'audio/EVRCNW0' 1622 | | 'audio/EVRCNW1' 1623 | | 'audio/EVRCWB' 1624 | | 'audio/EVRCWB0' 1625 | | 'audio/EVRCWB1' 1626 | | 'audio/EVS' 1627 | | 'audio/example' 1628 | | 'audio/flexfec' 1629 | | 'audio/fwdred' 1630 | | 'audio/G711-0' 1631 | | 'audio/G719' 1632 | | 'audio/G7221' 1633 | | 'audio/G722' 1634 | | 'audio/G723' 1635 | | 'audio/G726-16' 1636 | | 'audio/G726-24' 1637 | | 'audio/G726-32' 1638 | | 'audio/G726-40' 1639 | | 'audio/G728' 1640 | | 'audio/G729' 1641 | | 'audio/G7291' 1642 | | 'audio/G729D' 1643 | | 'audio/G729E' 1644 | | 'audio/GSM' 1645 | | 'audio/GSM-EFR' 1646 | | 'audio/GSM-HR-08' 1647 | | 'audio/iLBC' 1648 | | 'audio/ip-mr_v2.5' 1649 | | 'audio/L8' 1650 | | 'audio/L16' 1651 | | 'audio/L20' 1652 | | 'audio/L24' 1653 | | 'audio/LPC' 1654 | | 'audio/matroska' 1655 | | 'audio/MELP' 1656 | | 'audio/MELP600' 1657 | | 'audio/MELP1200' 1658 | | 'audio/MELP2400' 1659 | | 'audio/mhas' 1660 | | 'audio/mobile-xmf' 1661 | | 'audio/MPA' 1662 | | 'audio/mp4' 1663 | | 'audio/MP4A-LATM' 1664 | | 'audio/mpa-robust' 1665 | | 'audio/mpeg' 1666 | | 'audio/mpeg4-generic' 1667 | | 'audio/ogg' 1668 | | 'audio/opus' 1669 | | 'audio/parityfec' 1670 | | 'audio/PCMA' 1671 | | 'audio/PCMA-WB' 1672 | | 'audio/PCMU' 1673 | | 'audio/PCMU-WB' 1674 | | 'audio/prs.sid' 1675 | | 'audio/QCELP' 1676 | | 'audio/raptorfec' 1677 | | 'audio/RED' 1678 | | 'audio/rtp-enc-aescm128' 1679 | | 'audio/rtploopback' 1680 | | 'audio/rtp-midi' 1681 | | 'audio/rtx' 1682 | | 'audio/scip' 1683 | | 'audio/SMV' 1684 | | 'audio/SMV0' 1685 | | 'audio/SMV-QCP' 1686 | | 'audio/sofa' 1687 | | 'audio/sp-midi' 1688 | | 'audio/speex' 1689 | | 'audio/t140c' 1690 | | 'audio/t38' 1691 | | 'audio/telephone-event' 1692 | | 'audio/TETRA_ACELP' 1693 | | 'audio/TETRA_ACELP_BB' 1694 | | 'audio/tone' 1695 | | 'audio/TSVCIS' 1696 | | 'audio/UEMCLIP' 1697 | | 'audio/ulpfec' 1698 | | 'audio/usac' 1699 | | 'audio/VDVI' 1700 | | 'audio/VMR-WB' 1701 | | 'audio/vnd.3gpp.iufp' 1702 | | 'audio/vnd.4SB' 1703 | | 'audio/vnd.audiokoz' 1704 | | 'audio/vnd.CELP' 1705 | | 'audio/vnd.cisco.nse' 1706 | | 'audio/vnd.cmles.radio-events' 1707 | | 'audio/vnd.cns.anp1' 1708 | | 'audio/vnd.cns.inf1' 1709 | | 'audio/vnd.dece.audio' 1710 | | 'audio/vnd.digital-winds' 1711 | | 'audio/vnd.dlna.adts' 1712 | | 'audio/vnd.dolby.heaac.1' 1713 | | 'audio/vnd.dolby.heaac.2' 1714 | | 'audio/vnd.dolby.mlp' 1715 | | 'audio/vnd.dolby.mps' 1716 | | 'audio/vnd.dolby.pl2' 1717 | | 'audio/vnd.dolby.pl2x' 1718 | | 'audio/vnd.dolby.pl2z' 1719 | | 'audio/vnd.dolby.pulse.1' 1720 | | 'audio/vnd.dra' 1721 | | 'audio/vnd.dts' 1722 | | 'audio/vnd.dts.hd' 1723 | | 'audio/vnd.dts.uhd' 1724 | | 'audio/vnd.dvb.file' 1725 | | 'audio/vnd.everad.plj' 1726 | | 'audio/vnd.hns.audio' 1727 | | 'audio/vnd.lucent.voice' 1728 | | 'audio/vnd.ms-playready.media.pya' 1729 | | 'audio/vnd.nokia.mobile-xmf' 1730 | | 'audio/vnd.nortel.vbk' 1731 | | 'audio/vnd.nuera.ecelp4800' 1732 | | 'audio/vnd.nuera.ecelp7470' 1733 | | 'audio/vnd.nuera.ecelp9600' 1734 | | 'audio/vnd.octel.sbc' 1735 | | 'audio/vnd.presonus.multitrack' 1736 | | 'audio/vnd.qcelp' 1737 | | 'audio/vnd.rhetorex.32kadpcm' 1738 | | 'audio/vnd.rip' 1739 | | 'audio/vnd.sealedmedia.softseal.mpeg' 1740 | | 'audio/vnd.vmx.cvsd' 1741 | | 'audio/vorbis' 1742 | | 'audio/vorbis-config' 1743 | | 'font/collection' 1744 | | 'font/otf' 1745 | | 'font/sfnt' 1746 | | 'font/ttf' 1747 | | 'font/woff' 1748 | | 'font/woff2' 1749 | | 'image/aces' 1750 | | 'image/apng' 1751 | | 'image/avci' 1752 | | 'image/avcs' 1753 | | 'image/avif' 1754 | | 'image/bmp' 1755 | | 'image/cgm' 1756 | | 'image/dicom-rle' 1757 | | 'image/dpx' 1758 | | 'image/emf' 1759 | | 'image/example' 1760 | | 'image/fits' 1761 | | 'image/g3fax' 1762 | | 'image/heic' 1763 | | 'image/heic-sequence' 1764 | | 'image/heif' 1765 | | 'image/heif-sequence' 1766 | | 'image/hej2k' 1767 | | 'image/hsj2' 1768 | | 'image/j2c' 1769 | | 'image/jls' 1770 | | 'image/jp2' 1771 | | 'image/jph' 1772 | | 'image/jphc' 1773 | | 'image/jpm' 1774 | | 'image/jpx' 1775 | | 'image/jxr' 1776 | | 'image/jxrA' 1777 | | 'image/jxrS' 1778 | | 'image/jxs' 1779 | | 'image/jxsc' 1780 | | 'image/jxsi' 1781 | | 'image/jxss' 1782 | | 'image/ktx' 1783 | | 'image/ktx2' 1784 | | 'image/naplps' 1785 | | 'image/png' 1786 | | 'image/prs.btif' 1787 | | 'image/prs.pti' 1788 | | 'image/pwg-raster' 1789 | | 'image/svg+xml' 1790 | | 'image/t38' 1791 | | 'image/tiff' 1792 | | 'image/tiff-fx' 1793 | | 'image/vnd.adobe.photoshop' 1794 | | 'image/vnd.airzip.accelerator.azv' 1795 | | 'image/vnd.cns.inf2' 1796 | | 'image/vnd.dece.graphic' 1797 | | 'image/vnd.djvu' 1798 | | 'image/vnd.dwg' 1799 | | 'image/vnd.dxf' 1800 | | 'image/vnd.dvb.subtitle' 1801 | | 'image/vnd.fastbidsheet' 1802 | | 'image/vnd.fpx' 1803 | | 'image/vnd.fst' 1804 | | 'image/vnd.fujixerox.edmics-mmr' 1805 | | 'image/vnd.fujixerox.edmics-rlc' 1806 | | 'image/vnd.globalgraphics.pgb' 1807 | | 'image/vnd.microsoft.icon' 1808 | | 'image/vnd.mix' 1809 | | 'image/vnd.ms-modi' 1810 | | 'image/vnd.mozilla.apng' 1811 | | 'image/vnd.net-fpx' 1812 | | 'image/vnd.pco.b16' 1813 | | 'image/vnd.radiance' 1814 | | 'image/vnd.sealed.png' 1815 | | 'image/vnd.sealedmedia.softseal.gif' 1816 | | 'image/vnd.sealedmedia.softseal.jpg' 1817 | | 'image/vnd.svf' 1818 | | 'image/vnd.tencent.tap' 1819 | | 'image/vnd.valve.source.texture' 1820 | | 'image/vnd.wap.wbmp' 1821 | | 'image/vnd.xiff' 1822 | | 'image/vnd.zbrush.pcx' 1823 | | 'image/webp' 1824 | | 'image/wmf' 1825 | | 'image/emf' 1826 | | 'image/wmf' 1827 | | 'message/bhttp' 1828 | | 'message/CPIM' 1829 | | 'message/delivery-status' 1830 | | 'message/disposition-notification' 1831 | | 'message/example' 1832 | | 'message/feedback-report' 1833 | | 'message/global' 1834 | | 'message/global-delivery-status' 1835 | | 'message/global-disposition-notification' 1836 | | 'message/global-headers' 1837 | | 'message/http' 1838 | | 'message/imdn+xml' 1839 | | 'message/mls' 1840 | | 'message/news' 1841 | | 'message/ohttp-req' 1842 | | 'message/ohttp-res' 1843 | | 'message/s-http' 1844 | | 'message/sip' 1845 | | 'message/sipfrag' 1846 | | 'message/tracking-status' 1847 | | 'message/vnd.si.simp' 1848 | | 'message/vnd.wfa.wsc' 1849 | | 'model/3mf' 1850 | | 'model/e57' 1851 | | 'model/example' 1852 | | 'model/gltf-binary' 1853 | | 'model/gltf+json' 1854 | | 'model/JT' 1855 | | 'model/iges' 1856 | | 'model/mtl' 1857 | | 'model/obj' 1858 | | 'model/prc' 1859 | | 'model/step' 1860 | | 'model/step+xml' 1861 | | 'model/step+zip' 1862 | | 'model/step-xml+zip' 1863 | | 'model/stl' 1864 | | 'model/u3d' 1865 | | 'model/vnd.bary' 1866 | | 'model/vnd.cld' 1867 | | 'model/vnd.collada+xml' 1868 | | 'model/vnd.dwf' 1869 | | 'model/vnd.flatland.3dml' 1870 | | 'model/vnd.gdl' 1871 | | 'model/vnd.gs-gdl' 1872 | | 'model/vnd.gtw' 1873 | | 'model/vnd.moml+xml' 1874 | | 'model/vnd.mts' 1875 | | 'model/vnd.opengex' 1876 | | 'model/vnd.parasolid.transmit.binary' 1877 | | 'model/vnd.parasolid.transmit.text' 1878 | | 'model/vnd.pytha.pyox' 1879 | | 'model/vnd.rosette.annotated-data-model' 1880 | | 'model/vnd.sap.vds' 1881 | | 'model/vnd.usda' 1882 | | 'model/vnd.usdz+zip' 1883 | | 'model/vnd.valve.source.compiled-map' 1884 | | 'model/vnd.vtu' 1885 | | 'model/x3d-vrml' 1886 | | 'model/x3d+fastinfoset' 1887 | | 'model/x3d+xml' 1888 | | 'multipart/appledouble' 1889 | | 'multipart/byteranges' 1890 | | 'multipart/encrypted' 1891 | | 'multipart/example' 1892 | | 'multipart/form-data' 1893 | | 'multipart/header-set' 1894 | | 'multipart/multilingual' 1895 | | 'multipart/related' 1896 | | 'multipart/report' 1897 | | 'multipart/signed' 1898 | | 'multipart/vnd.bint.med-plus' 1899 | | 'multipart/voice-message' 1900 | | 'multipart/x-mixed-replace' 1901 | | 'text/1d-interleaved-parityfec' 1902 | | 'text/cache-manifest' 1903 | | 'text/calendar' 1904 | | 'text/cql' 1905 | | 'text/cql-expression' 1906 | | 'text/cql-identifier' 1907 | | 'text/css' 1908 | | 'text/csv' 1909 | | 'text/csv-schema' 1910 | | 'text/directory' 1911 | | 'text/dns' 1912 | | 'text/ecmascript' 1913 | | 'text/encaprtp' 1914 | | 'text/example' 1915 | | 'text/fhirpath' 1916 | | 'text/flexfec' 1917 | | 'text/fwdred' 1918 | | 'text/gff3' 1919 | | 'text/grammar-ref-list' 1920 | | 'text/hl7v2' 1921 | | 'text/html' 1922 | | 'text/javascript' 1923 | | 'text/jcr-cnd' 1924 | | 'text/markdown' 1925 | | 'text/mizar' 1926 | | 'text/n3' 1927 | | 'text/parameters' 1928 | | 'text/parityfec' 1929 | | 'text/provenance-notation' 1930 | | 'text/prs.fallenstein.rst' 1931 | | 'text/prs.lines.tag' 1932 | | 'text/prs.prop.logic' 1933 | | 'text/prs.texi' 1934 | | 'text/raptorfec' 1935 | | 'text/RED' 1936 | | 'text/rfc822-headers' 1937 | | 'text/rtf' 1938 | | 'text/rtp-enc-aescm128' 1939 | | 'text/rtploopback' 1940 | | 'text/rtx' 1941 | | 'text/SGML' 1942 | | 'text/shaclc' 1943 | | 'text/shex' 1944 | | 'text/spdx' 1945 | | 'text/strings' 1946 | | 'text/t140' 1947 | | 'text/tab-separated-values' 1948 | | 'text/troff' 1949 | | 'text/turtle' 1950 | | 'text/ulpfec' 1951 | | 'text/uri-list' 1952 | | 'text/vcard' 1953 | | 'text/vnd.a' 1954 | | 'text/vnd.abc' 1955 | | 'text/vnd.ascii-art' 1956 | | 'text/vnd.curl' 1957 | | 'text/vnd.debian.copyright' 1958 | | 'text/vnd.DMClientScript' 1959 | | 'text/vnd.dvb.subtitle' 1960 | | 'text/vnd.esmertec.theme-descriptor' 1961 | | 'text/vnd.exchangeable' 1962 | | 'text/vnd.familysearch.gedcom' 1963 | | 'text/vnd.ficlab.flt' 1964 | | 'text/vnd.fly' 1965 | | 'text/vnd.fmi.flexstor' 1966 | | 'text/vnd.gml' 1967 | | 'text/vnd.graphviz' 1968 | | 'text/vnd.hans' 1969 | | 'text/vnd.hgl' 1970 | | 'text/vnd.in3d.3dml' 1971 | | 'text/vnd.in3d.spot' 1972 | | 'text/vnd.IPTC.NewsML' 1973 | | 'text/vnd.IPTC.NITF' 1974 | | 'text/vnd.latex-z' 1975 | | 'text/vnd.motorola.reflex' 1976 | | 'text/vnd.ms-mediapackage' 1977 | | 'text/vnd.net2phone.commcenter.command' 1978 | | 'text/vnd.radisys.msml-basic-layout' 1979 | | 'text/vnd.senx.warpscript' 1980 | | 'text/vnd.si.uricatalogue' 1981 | | 'text/vnd.sun.j2me.app-descriptor' 1982 | | 'text/vnd.sosi' 1983 | | 'text/vnd.trolltech.linguist' 1984 | | 'text/vnd.wap.si' 1985 | | 'text/vnd.wap.sl' 1986 | | 'text/vnd.wap.wml' 1987 | | 'text/vnd.wap.wmlscript' 1988 | | 'text/vtt' 1989 | | 'text/wgsl' 1990 | | 'text/xml' 1991 | | 'text/xml-external-parsed-entity' 1992 | | 'video/1d-interleaved-parityfec' 1993 | | 'video/3gpp' 1994 | | 'video/3gpp2' 1995 | | 'video/3gpp-tt' 1996 | | 'video/AV1' 1997 | | 'video/BMPEG' 1998 | | 'video/BT656' 1999 | | 'video/CelB' 2000 | | 'video/DV' 2001 | | 'video/encaprtp' 2002 | | 'video/example' 2003 | | 'video/FFV1' 2004 | | 'video/flexfec' 2005 | | 'video/H261' 2006 | | 'video/H263' 2007 | | 'video/H263-1998' 2008 | | 'video/H263-2000' 2009 | | 'video/H264' 2010 | | 'video/H264-RCDO' 2011 | | 'video/H264-SVC' 2012 | | 'video/H265' 2013 | | 'video/H266' 2014 | | 'video/iso.segment' 2015 | | 'video/JPEG' 2016 | | 'video/jpeg2000' 2017 | | 'video/jxsv' 2018 | | 'video/matroska' 2019 | | 'video/matroska-3d' 2020 | | 'video/mj2' 2021 | | 'video/MP1S' 2022 | | 'video/MP2P' 2023 | | 'video/MP2T' 2024 | | 'video/mp4' 2025 | | 'video/MP4V-ES' 2026 | | 'video/MPV' 2027 | | 'video/mpeg4-generic' 2028 | | 'video/nv' 2029 | | 'video/ogg' 2030 | | 'video/parityfec' 2031 | | 'video/pointer' 2032 | | 'video/quicktime' 2033 | | 'video/raptorfec' 2034 | | 'video/raw' 2035 | | 'video/rtp-enc-aescm128' 2036 | | 'video/rtploopback' 2037 | | 'video/rtx' 2038 | | 'video/scip' 2039 | | 'video/smpte291' 2040 | | 'video/SMPTE292M' 2041 | | 'video/ulpfec' 2042 | | 'video/vc1' 2043 | | 'video/vc2' 2044 | | 'video/vnd.CCTV' 2045 | | 'video/vnd.dece.hd' 2046 | | 'video/vnd.dece.mobile' 2047 | | 'video/vnd.dece.mp4' 2048 | | 'video/vnd.dece.pd' 2049 | | 'video/vnd.dece.sd' 2050 | | 'video/vnd.dece.video' 2051 | | 'video/vnd.directv.mpeg' 2052 | | 'video/vnd.directv.mpeg-tts' 2053 | | 'video/vnd.dlna.mpeg-tts' 2054 | | 'video/vnd.dvb.file' 2055 | | 'video/vnd.fvt' 2056 | | 'video/vnd.hns.video' 2057 | | 'video/vnd.iptvforum.1dparityfec-1010' 2058 | | 'video/vnd.iptvforum.1dparityfec-2005' 2059 | | 'video/vnd.iptvforum.2dparityfec-1010' 2060 | | 'video/vnd.iptvforum.2dparityfec-2005' 2061 | | 'video/vnd.iptvforum.ttsavc' 2062 | | 'video/vnd.iptvforum.ttsmpeg2' 2063 | | 'video/vnd.motorola.video' 2064 | | 'video/vnd.motorola.videop' 2065 | | 'video/vnd.mpegurl' 2066 | | 'video/vnd.ms-playready.media.pyv' 2067 | | 'video/vnd.nokia.interleaved-multimedia' 2068 | | 'video/vnd.nokia.mp4vr' 2069 | | 'video/vnd.nokia.videovoip' 2070 | | 'video/vnd.objectvideo' 2071 | | 'video/vnd.radgamettools.bink' 2072 | | 'video/vnd.radgamettools.smacker' 2073 | | 'video/vnd.sealed.mpeg1' 2074 | | 'video/vnd.sealed.mpeg4' 2075 | | 'video/vnd.sealed.swf' 2076 | | 'video/vnd.sealedmedia.softseal.mov' 2077 | | 'video/vnd.uvvu.mp4' 2078 | | 'video/vnd.youtube.yt' 2079 | | 'video/vnd.vivo' 2080 | | 'video/VP8' 2081 | | 'video/VP9' 2082 | | string & {} 2083 | --------------------------------------------------------------------------------