Full Code of fastify/fastify-sensible for AI

main 3d0ec2bf01fd cached
28 files
65.6 KB
18.3k tokens
39 symbols
1 requests
Download .txt
Repository: fastify/fastify-sensible
Branch: main
Commit: 3d0ec2bf01fd
Files: 28
Total size: 65.6 KB

Directory structure:
gitextract__ix3j9hb/

├── .borp.yaml
├── .gitattributes
├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── ci.yml
│       └── lock-threads.yml
├── .gitignore
├── .npmrc
├── LICENSE
├── README.md
├── eslint.config.js
├── index.js
├── lib/
│   ├── assert.js
│   ├── cache-control.js
│   ├── httpError.d.ts
│   ├── httpErrors.js
│   └── vary.js
├── package.json
├── test/
│   ├── assert.test.js
│   ├── cache-control.test.js
│   ├── forwarded.test.js
│   ├── httpErrors.test.js
│   ├── httpErrorsReply.test.js
│   ├── is.test.js
│   ├── schema.test.js
│   ├── to.test.js
│   └── vary.test.js
└── types/
    ├── index.d.ts
    └── index.test-d.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: .borp.yaml
================================================
files:
  - 'test/**/*.test.js'


================================================
FILE: .gitattributes
================================================
# Set the default behavior, in case people don't have core.autocrlf set
* text=auto

# Require Unix line endings
* text eol=lf


================================================
FILE: .github/dependabot.yml
================================================
version: 2
updates:
  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "monthly"
    open-pull-requests-limit: 10

  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "monthly"
    open-pull-requests-limit: 10


================================================
FILE: .github/workflows/ci.yml
================================================
name: CI

on:
  push:
    branches:
     - main
     - next
     - 'v*'
    paths-ignore:
      - 'docs/**'
      - '*.md'
  pull_request:
    paths-ignore:
      - 'docs/**'
      - '*.md'

# This allows a subsequently queued workflow run to interrupt previous runs
concurrency:
    group: "${{ github.workflow }}-${{ github.event.pull_request.head.label || github.head_ref || github.ref }}"
    cancel-in-progress: true

permissions:
  contents: read

jobs:
  test:
    permissions:
      contents: write
      pull-requests: write
    uses: fastify/workflows/.github/workflows/plugins-ci.yml@v6
    with:
      license-check: true
      lint: true


================================================
FILE: .github/workflows/lock-threads.yml
================================================
name: Lock Threads

on:
  schedule:
    - cron: '0 0 1 * *'
  workflow_dispatch:

concurrency:
  group: lock

permissions:
  contents: read

jobs:
  lock-threads:
    permissions:
      issues: write
      pull-requests: write
    uses: fastify/workflows/.github/workflows/lock-threads.yml@v6


================================================
FILE: .gitignore
================================================
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
.pnpm-debug.log*

# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json

# Runtime data
pids
*.pid
*.seed
*.pid.lock

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
*.lcov

# nyc test coverage
.nyc_output

# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# Bower dependency directory (https://bower.io/)
bower_components

# node-waf configuration
.lock-wscript

# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules/
jspm_packages/

# Snowpack dependency directory (https://snowpack.dev/)
web_modules/

# TypeScript cache
*.tsbuildinfo

# Optional npm cache directory
.npm

# Optional eslint cache
.eslintcache

# Optional stylelint cache
.stylelintcache

# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/

# Optional REPL history
.node_repl_history

# Output of 'npm pack'
*.tgz

# Yarn Integrity file
.yarn-integrity

# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local

# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache

# Next.js build output
.next
out

# Nuxt.js build / generate output
.nuxt
dist

# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public

# vuepress build output
.vuepress/dist

# vuepress v2.x temp and cache directory
.temp
.cache

# Docusaurus cache and generated files
.docusaurus

# Serverless directories
.serverless/

# FuseBox cache
.fusebox/

# DynamoDB Local files
.dynamodb/

# TernJS port file
.tern-port

# Stores VSCode versions used for testing VSCode extensions
.vscode-test

# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

# Vim swap files
*.swp

# macOS files
.DS_Store

# Clinic
.clinic

# lock files
bun.lockb
package-lock.json
pnpm-lock.yaml
yarn.lock

# editor files
.vscode
.idea

#tap files
.tap/


================================================
FILE: .npmrc
================================================
ignore-scripts=true
package-lock=false



================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2018-present Tomas Della Vedova and The Fastify team <https://github.com/fastify/fastify#team>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.


================================================
FILE: README.md
================================================
# @fastify/sensible

[![CI](https://github.com/fastify/fastify-sensible/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/fastify/fastify-sensible/actions/workflows/ci.yml)
[![NPM version](https://img.shields.io/npm/v/@fastify/sensible.svg?style=flat)](https://www.npmjs.com/package/@fastify/sensible)
[![neostandard javascript style](https://img.shields.io/badge/code_style-neostandard-brightgreen?style=flat)](https://github.com/neostandard/neostandard)

Defaults for Fastify that everyone can agree on™.<br>
This plugin adds some useful utilities to your Fastify instance, see the API section to learn more.

*Why are these APIs here and not included with Fastify?<br>
Because Fastify aims to be as small and focused as possible, every utility that is not essential should be shipped as a standalone plugin.*

## Install
```
npm i @fastify/sensible
```

### Compatibility

| Plugin version | Fastify version |
| -------------- | --------------- |
| `>=6.x`        | `^5.x`          |
| `^5.x`         | `^4.x`          |
| `^4.x`         | `^3.x`          |
| `>=2.x <4.x`   | `^2.x`          |
| `^1.x`         | `^1.x`          |


Please note that if a Fastify version is out of support, then so are the corresponding versions of this plugin
in the table above.
See [Fastify's LTS policy](https://github.com/fastify/fastify/blob/main/docs/Reference/LTS.md) for more details.

## Usage
```js
const fastify = require('fastify')()
fastify.register(require('@fastify/sensible'))

fastify.get('/', (req, reply) => {
  reply.notFound()
})

fastify.get('/async', async (req, reply) => {
  throw fastify.httpErrors.notFound()
})

fastify.get('/async-return', async (req, reply) => {
  return reply.notFound()
})

fastify.listen({ port: 3000 })
```

## Shared JSON Schema for HTTP errors
If you set the `sharedSchemaId` option, a shared JSON Schema is added and can be used in your routes.
```js
const fastify = require('fastify')()
fastify.register(require('@fastify/sensible'), {
  sharedSchemaId: 'HttpError'
})

fastify.get('/async', {
  schema: {
    response: {
      404: { $ref: 'HttpError' }
    }
  },
  handler: async (req, reply) => {
    return reply.notFound()
  }
})

fastify.listen({ port: 3000 })
```

## API
#### `fastify.httpErrors`
Object that exposes `createError` and all of the `4xx` and `5xx` error constructors.

Use of `4xx` and `5xx` error constructors follows the same structure as [`new createError[code || name]([msg]))`](https://github.com/jshttp/http-errors#new-createerrorcode--namemsg) in [http-errors](https://github.com/jshttp/http-errors):

```js
 // the custom message is optional
const notFoundErr = fastify.httpErrors.notFound('custom message')
```

`4xx`
- <code>fastify.httpErrors.<b>badRequest()</b></code>
- <code>fastify.httpErrors.<b>unauthorized()</b></code>
- <code>fastify.httpErrors.<b>paymentRequired()</b></code>
- <code>fastify.httpErrors.<b>forbidden()</b></code>
- <code>fastify.httpErrors.<b>notFound()</b></code>
- <code>fastify.httpErrors.<b>methodNotAllowed()</b></code>
- <code>fastify.httpErrors.<b>notAcceptable()</b></code>
- <code>fastify.httpErrors.<b>proxyAuthenticationRequired()</b></code>
- <code>fastify.httpErrors.<b>requestTimeout()</b></code>
- <code>fastify.httpErrors.<b>conflict()</b></code>
- <code>fastify.httpErrors.<b>gone()</b></code>
- <code>fastify.httpErrors.<b>lengthRequired()</b></code>
- <code>fastify.httpErrors.<b>preconditionFailed()</b></code>
- <code>fastify.httpErrors.<b>payloadTooLarge()</b></code>
- <code>fastify.httpErrors.<b>uriTooLong()</b></code>
- <code>fastify.httpErrors.<b>unsupportedMediaType()</b></code>
- <code>fastify.httpErrors.<b>rangeNotSatisfiable()</b></code>
- <code>fastify.httpErrors.<b>expectationFailed()</b></code>
- <code>fastify.httpErrors.<b>imateapot()</b></code>
- <code>fastify.httpErrors.<b>misdirectedRequest()</b></code>
- <code>fastify.httpErrors.<b>unprocessableEntity()</b></code>
- <code>fastify.httpErrors.<b>locked()</b></code>
- <code>fastify.httpErrors.<b>failedDependency()</b></code>
- <code>fastify.httpErrors.<b>tooEarly()</b></code>
- <code>fastify.httpErrors.<b>upgradeRequired()</b></code>
- <code>fastify.httpErrors.<b>preconditionRequired()</b></code>
- <code>fastify.httpErrors.<b>tooManyRequests()</b></code>
- <code>fastify.httpErrors.<b>requestHeaderFieldsTooLarge()</b></code>
- <code>fastify.httpErrors.<b>unavailableForLegalReasons()</b></code>

`5xx`
- <code>fastify.httpErrors.<b>internalServerError()</b></code>
- <code>fastify.httpErrors.<b>notImplemented()</b></code>
- <code>fastify.httpErrors.<b>badGateway()</b></code>
- <code>fastify.httpErrors.<b>serviceUnavailable()</b></code>
- <code>fastify.httpErrors.<b>gatewayTimeout()</b></code>
- <code>fastify.httpErrors.<b>httpVersionNotSupported()</b></code>
- <code>fastify.httpErrors.<b>variantAlsoNegotiates()</b></code>
- <code>fastify.httpErrors.<b>insufficientStorage()</b></code>
- <code>fastify.httpErrors.<b>loopDetected()</b></code>
- <code>fastify.httpErrors.<b>bandwidthLimitExceeded()</b></code>
- <code>fastify.httpErrors.<b>notExtended()</b></code>
- <code>fastify.httpErrors.<b>networkAuthenticationRequired()</b></code>

`createError`

Use of `createError` follows the same structure as [`createError([status], [message], [properties])`](https://github.com/jshttp/http-errors#createerrorstatus-message-properties) in [http-errors](https://github.com/jshttp/http-errors):

```js
const err = fastify.httpErrors.createError(404, 'This video does not exist!')
```

#### `reply.[httpError]`
The `reply` interface is decorated with all of the functions declared above, using it is easy:
```js
fastify.get('/', (req, reply) => {
  reply.notFound()
})
```

#### `reply.vary`
The `reply` interface is decorated with [`jshttp/vary`](https://github.com/jshttp/vary), the API is the same, but you do not need to pass the res object.
```js
fastify.get('/', (req, reply) => {
  reply.vary('Accept')
  reply.send('ok')
})
```

#### `reply.cacheControl`
The `reply` interface is decorated with a helper to configure cache control response headers.
```js
// configure a single type
fastify.get('/', (req, reply) => {
  reply.cacheControl('public')
  reply.send('ok')
})

// configure multiple types
fastify.get('/', (req, reply) => {
  reply.cacheControl('public')
  reply.cacheControl('immutable')
  reply.send('ok')
})

// configure a type time
fastify.get('/', (req, reply) => {
  reply.cacheControl('max-age', 42)
  reply.send('ok')
})

// the time can be defined as string
fastify.get('/', (req, reply) => {
  // all the formats of github.com/vercel/ms are supported
  reply.cacheControl('max-age', '1d') // will set to 'max-age=86400'
  reply.send('ok')
})
```

#### `reply.preventCache`
The `reply` interface is decorated with a helper to set the cache control header to a no caching configuration.
```js
fastify.get('/', (req, reply) => {
  // will set cache-control to 'no-store, max-age=0, private'
  // and for HTTP/1.0 compatibility
  // will set pragma to 'no-cache' and expires to 0
  reply.preventCache()
  reply.send('ok')
})
```

#### `reply.revalidate`
The `reply` interface is decorated with a helper to set the cache control header to a no caching configuration.
```js
fastify.get('/', (req, reply) => {
  reply.revalidate() // will set to 'max-age=0, must-revalidate'
  reply.send('ok')
})
```

#### `reply.staticCache`
The `reply` interface is decorated with a helper to set the cache control header to a public and immutable configuration.
```js
fastify.get('/', (req, reply) => {
  // the time can be defined as a string
  reply.staticCache(42) // will set to 'public, max-age=42, immutable'
  reply.send('ok')
})
```

#### `reply.stale`
The `reply` interface is decorated with a helper to set the cache control header for [stale content](https://tools.ietf.org/html/rfc5861).
```js
fastify.get('/', (req, reply) => {
  // the time can be defined as a string
  reply.stale('while-revalidate', 42)
  reply.stale('if-error', 1)
  reply.send('ok')
})
```

#### `reply.maxAge`
The `reply` interface is decorated with a helper to set max age of the response. It can be used in conjunction with `reply.stale`, see [here](https://web.dev/stale-while-revalidate/).
```js
fastify.get('/', (req, reply) => {
  // the time can be defined as a string
  reply.maxAge(86400)
  reply.stale('while-revalidate', 42)
  reply.send('ok')
})
```

#### `request.forwarded`
The `request` interface is decorated with [`jshttp/forwarded`](https://github.com/jshttp/forwarded), the API is the same, but you do not need to pass the request object:
```js
fastify.get('/', (req, reply) => {
  reply.send(req.forwarded())
})
```

#### `request.is`
The `request` interface is decorated with [`jshttp/type-is`](https://github.com/jshttp/type-is), the API is the same but you do not need to pass the request object:
```js
fastify.get('/', (req, reply) => {
  reply.send(req.is(['html', 'json']))
})
```

#### `assert`
Verify if a given condition is true, if not it throws the specified http error.<br> Useful if you work with *async* routes:
```js
// the custom message is optional
fastify.assert(
  req.headers.authorization, 400, 'Missing authorization header'
)
```
The `assert` API also exposes the following methods:
- <code>fastify.assert.<b>ok()</b></code>
- <code>fastify.assert.<b>equal()</b></code>
- <code>fastify.assert.<b>notEqual()</b></code>
- <code>fastify.assert.<b>strictEqual()</b></code>
- <code>fastify.assert.<b>notStrictEqual()</b></code>
- <code>fastify.assert.<b>deepEqual()</b></code>
- <code>fastify.assert.<b>notDeepEqual()</b></code>

#### `to`
Async await wrapper for easy error handling without try-catch, inspired by [`await-to-js`](https://github.com/scopsy/await-to-js):

```js
const [err, user] = await fastify.to(
  db.findOne({ user: 'tyrion' })
)
```

## Contributing
Do you feel there is some utility that *everyone can agree on* that is not present?<br>
Open an issue and let's discuss it! Even better a pull request!

## Acknowledgments

The project name is inspired by [`vim-sensible`](https://github.com/tpope/vim-sensible), an awesome package that if you use vim you should use too.

## License

Licensed under [MIT](./LICENSE).


================================================
FILE: eslint.config.js
================================================
'use strict'

module.exports = require('neostandard')({
  ignores: require('neostandard').resolveIgnoresFromGitignore(),
  ts: true
})


================================================
FILE: index.js
================================================
'use strict'

const fp = require('fastify-plugin')
// External utilities
const forwarded = require('forwarded')
const typeis = require('type-is')
// Internals Utilities
const httpErrors = require('./lib/httpErrors')
const assert = require('./lib/assert')
const vary = require('./lib/vary')
const cache = require('./lib/cache-control')

/** @type {typeof import('./types/index').fastifySensible} */
function fastifySensible (fastify, opts, next) {
  fastify.decorate('httpErrors', httpErrors)
  fastify.decorate('assert', assert)
  fastify.decorate('to', to)

  fastify.decorateRequest('forwarded', function requestForwarded () {
    return forwarded(this.raw)
  })

  fastify.decorateRequest('is', function requestIs (types) {
    return typeis(this.raw, Array.isArray(types) ? types : [types])
  })

  fastify.decorateReply('vary', vary)
  fastify.decorateReply('cacheControl', cache.cacheControl)
  fastify.decorateReply('preventCache', cache.preventCache)
  fastify.decorateReply('revalidate', cache.revalidate)
  fastify.decorateReply('staticCache', cache.staticCache)
  fastify.decorateReply('stale', cache.stale)
  fastify.decorateReply('maxAge', cache.maxAge)

  const httpErrorsKeys = Object.keys(httpErrors)
  const httpErrorsKeysLength = httpErrorsKeys.length
  for (let i = 0; i < httpErrorsKeysLength; ++i) {
    const httpError = httpErrorsKeys[i]

    switch (httpError) {
      case 'HttpError':
        // skip abstract class constructor
        break
      case 'getHttpError':
        fastify.decorateReply('getHttpError', function replyGetHttpError (errorCode, message) {
          this.send(httpErrors.getHttpError(errorCode, message))
          return this
        })
        break
      default: {
        const capitalizedMethodName = httpError.replace(/(?:^|\s)\S/gu, a => a.toUpperCase())
        const replyMethodName = 'sensible' + capitalizedMethodName
        fastify.decorateReply(httpError, {
          [replyMethodName]: function (message) {
            this.send(httpErrors[httpError](message))
            return this
          }
        }[replyMethodName])
      }
    }
  }

  if (opts?.sharedSchemaId) {
    // The schema must be the same as:
    // https://github.com/fastify/fastify/blob/c08b67e0bfedc9935b51c787ae4cd6b250ad303c/build/build-error-serializer.js#L8-L16
    fastify.addSchema({
      $id: opts.sharedSchemaId,
      type: 'object',
      properties: {
        statusCode: { type: 'number' },
        code: { type: 'string' },
        error: { type: 'string' },
        message: { type: 'string' }
      }
    })
  }

  /**
   * Wraps a promise for easier error handling without try/catch.
   * @template T
   * @param {Promise<T>} promise - The promise to wrap.
   * @returns {Promise<[Error, undefined] | [null, T]>} A promise that resolves to a tuple containing either an error or the resolved data.
   */
  function to (promise) {
    return promise.then(data => [null, data], err => [err, undefined])
  }

  next()
}

module.exports = fp(fastifySensible, {
  name: '@fastify/sensible',
  fastify: '5.x'
})
module.exports.default = fastifySensible
module.exports.fastifySensible = fastifySensible
module.exports.httpErrors = httpErrors
module.exports.HttpError = httpErrors.HttpError


================================================
FILE: lib/assert.js
================================================
/* eslint-disable eqeqeq */
'use strict'

const { dequal: deepEqual } = require('dequal')
const { getHttpError } = require('./httpErrors')

function assert (condition, code, message) {
  if (condition) return
  throw getHttpError(code, message)
}

assert.ok = assert

assert.equal = function (a, b, code, message) {
  assert(a == b, code, message)
}

assert.notEqual = function (a, b, code, message) {
  assert(a != b, code, message)
}

assert.strictEqual = function (a, b, code, message) {
  assert(a === b, code, message)
}

assert.notStrictEqual = function (a, b, code, message) {
  assert(a !== b, code, message)
}

assert.deepEqual = function (a, b, code, message) {
  assert(deepEqual(a, b), code, message)
}

assert.notDeepEqual = function (a, b, code, message) {
  assert(!deepEqual(a, b), code, message)
}

module.exports = assert


================================================
FILE: lib/cache-control.js
================================================
'use strict'

// Cache control header utilities, for more info see:
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control
// Useful reads:
// - https://odino.org/http-cache-101-scaling-the-web/
// - https://web.dev/stale-while-revalidate/
// - https://csswizardry.com/2019/03/cache-control-for-civilians/
// - https://jakearchibald.com/2016/caching-best-practices/

const assert = require('node:assert')
const ms = require('@lukeed/ms').parse

const validSingletimes = [
  'must-revalidate',
  'no-cache',
  'no-store',
  'no-transform',
  'public',
  'private',
  'proxy-revalidate',
  'immutable'
]

const validMultitimes = [
  'max-age',
  's-maxage',
  'stale-while-revalidate',
  'stale-if-error'
]

function cacheControl (type, time) {
  const previoustime = this.getHeader('Cache-Control')
  if (time == null) {
    assert(validSingletimes.indexOf(type) !== -1, `Invalid Cache Control type: ${type}`)
    this.header('Cache-Control', previoustime ? `${previoustime}, ${type}` : type)
  } else {
    if (typeof time === 'string') {
      time = ms(time) / 1000
    }
    assert(validMultitimes.indexOf(type) !== -1, `Invalid Cache Control type: ${type}`)
    assert(typeof time === 'number', 'The cache control time should be a number')
    this.header('Cache-Control', previoustime ? `${previoustime}, ${type}=${time}` : `${type}=${time}`)
  }
  return this
}

function preventCache () {
  this
    .header('Cache-Control', 'no-store, max-age=0, private')
    // compatibility support for HTTP/1.0
    // see: https://owasp.org/www-community/OWASP_Application_Security_FAQ#how-do-i-ensure-that-sensitive-pages-are-not-cached-on-the-users-browser
    .header('Pragma', 'no-cache')
    .header('Expires', 0)

  return this
}

function maxAge (time) {
  return this.cacheControl('max-age', time)
}

function revalidate () {
  this.header('Cache-Control', 'max-age=0, must-revalidate')
  return this
}

function staticCache (time) {
  if (typeof time === 'string') {
    time = ms(time) / 1000
  }
  assert(typeof time === 'number', 'The cache control time should be a number')
  this.header('Cache-Control', `public, max-age=${time}, immutable`)
  return this
}

function stale (type, time) {
  if (type === 'while-revalidate') {
    return this.cacheControl('stale-while-revalidate', time)
  } else if (type === 'if-error') {
    return this.cacheControl('stale-if-error', time)
  } else {
    throw new Error(`Invalid cache control stale time ${time}`)
  }
}

module.exports = {
  cacheControl,
  preventCache,
  revalidate,
  staticCache,
  stale,
  maxAge
}


================================================
FILE: lib/httpError.d.ts
================================================
export declare class HttpError<N extends number = number> extends Error {
  status: N
  statusCode: N
  expose: boolean
  message: string
  headers?: {
    [key: string]: string;
  };

  [key: string]: any;
}

type UnknownError = Error | string | number | { [key: string]: any }

export type HttpErrorTypes = {
  badRequest: 400,
  unauthorized: 401,
  paymentRequired: 402,
  forbidden: 403,
  notFound: 404,
  methodNotAllowed: 405,
  notAcceptable: 406,
  proxyAuthenticationRequired: 407,
  requestTimeout: 408,
  conflict: 409,
  gone: 410,
  lengthRequired: 411,
  preconditionFailed: 412,
  payloadTooLarge: 413,
  uriTooLong: 414,
  unsupportedMediaType: 415,
  rangeNotSatisfiable: 416,
  expectationFailed: 417,
  imateapot: 418,
  misdirectedRequest: 421,
  unprocessableEntity: 422,
  locked: 423,
  failedDependency: 424,
  tooEarly: 425,
  upgradeRequired: 426,
  preconditionRequired: 428,
  tooManyRequests: 429,
  requestHeaderFieldsTooLarge: 431,
  unavailableForLegalReasons: 451,
  internalServerError: 500,
  notImplemented: 501,
  badGateway: 502,
  serviceUnavailable: 503,
  gatewayTimeout: 504,
  httpVersionNotSupported: 505,
  variantAlsoNegotiates: 506,
  insufficientStorage: 507,
  loopDetected: 508,
  bandwidthLimitExceeded: 509,
  notExtended: 510
  networkAuthenticationRequired: 511
}

type ValueOf<ObjectType, ValueType extends keyof ObjectType = keyof ObjectType> = ObjectType[ValueType]

export type HttpErrorNames = keyof HttpErrorTypes
export type HttpErrorCodes = ValueOf<HttpErrorTypes>
// Permissive type for getHttpError lookups
export type HttpErrorCodesLoose = HttpErrorCodes | `${HttpErrorCodes}`
// Helper to go from stringified error codes back to numeric
type AsCode<T> = T extends `${infer N extends HttpErrorCodes}` ? N : never

export type HttpErrors = {
  HttpError: typeof HttpError;
  getHttpError: <T extends HttpErrorCodesLoose>(code: T, message?: string) => HttpError<AsCode<T>>;
  createError: (...args: UnknownError[]) => HttpError;
} & {
  [Property in keyof HttpErrorTypes]: (...args: UnknownError[]) => HttpError<HttpErrorTypes[Property]>
}

// eslint-disable-next-line @typescript-eslint/no-redeclare
declare const HttpErrors: HttpErrors
export default HttpErrors


================================================
FILE: lib/httpErrors.js
================================================
'use strict'

const createError = require('http-errors')
const statusCodes = require('node:http').STATUS_CODES

const statusCodesMap = Object.assign({}, statusCodes)
Object.keys(statusCodesMap).forEach(code => {
  statusCodesMap[code] = normalize(code, statusCodesMap[code])
})

function normalize (code, msg) {
  if (code === '414') return 'uriTooLong'
  if (code === '505') return 'httpVersionNotSupported'
  msg = msg.split(' ').join('').replace(/'/g, '')
  msg = msg[0].toLowerCase() + msg.slice(1)
  return msg
}

const httpErrors = {
  badRequest: function badRequest (message) {
    return new createError.BadRequest(message)
  },

  unauthorized: function unauthorized (message) {
    return new createError.Unauthorized(message)
  },

  paymentRequired: function paymentRequired (message) {
    return new createError.PaymentRequired(message)
  },

  forbidden: function forbidden (message) {
    return new createError.Forbidden(message)
  },

  notFound: function notFound (message) {
    return new createError.NotFound(message)
  },

  methodNotAllowed: function methodNotAllowed (message) {
    return new createError.MethodNotAllowed(message)
  },

  notAcceptable: function notAcceptable (message) {
    return new createError.NotAcceptable(message)
  },

  proxyAuthenticationRequired: function proxyAuthenticationRequired (message) {
    return new createError.ProxyAuthenticationRequired(message)
  },

  requestTimeout: function requestTimeout (message) {
    return new createError.RequestTimeout(message)
  },

  conflict: function conflict (message) {
    return new createError.Conflict(message)
  },

  gone: function gone (message) {
    return new createError.Gone(message)
  },

  lengthRequired: function lengthRequired (message) {
    return new createError.LengthRequired(message)
  },

  preconditionFailed: function preconditionFailed (message) {
    return new createError.PreconditionFailed(message)
  },

  payloadTooLarge: function payloadTooLarge (message) {
    return new createError.PayloadTooLarge(message)
  },

  uriTooLong: function uriTooLong (message) {
    return new createError.URITooLong(message)
  },

  unsupportedMediaType: function unsupportedMediaType (message) {
    return new createError.UnsupportedMediaType(message)
  },

  rangeNotSatisfiable: function rangeNotSatisfiable (message) {
    return new createError.RangeNotSatisfiable(message)
  },

  expectationFailed: function expectationFailed (message) {
    return new createError.ExpectationFailed(message)
  },

  imateapot: function imateapot (message) {
    return new createError.ImATeapot(message)
  },

  misdirectedRequest: function misdirectedRequest (message) {
    return new createError.MisdirectedRequest(message)
  },

  unprocessableEntity: function unprocessableEntity (message) {
    return new createError.UnprocessableEntity(message)
  },

  locked: function locked (message) {
    return new createError.Locked(message)
  },

  failedDependency: function failedDependency (message) {
    return new createError.FailedDependency(message)
  },

  tooEarly: function tooEarly (message) {
    return new createError.TooEarly(message)
  },

  upgradeRequired: function upgradeRequired (message) {
    return new createError.UpgradeRequired(message)
  },

  preconditionRequired: function preconditionRequired (message) {
    return new createError.PreconditionRequired(message)
  },

  tooManyRequests: function tooManyRequests (message) {
    return new createError.TooManyRequests(message)
  },

  requestHeaderFieldsTooLarge: function requestHeaderFieldsTooLarge (message) {
    return new createError.RequestHeaderFieldsTooLarge(message)
  },

  unavailableForLegalReasons: function unavailableForLegalReasons (message) {
    return new createError.UnavailableForLegalReasons(message)
  },

  internalServerError: function internalServerError (message) {
    return new createError.InternalServerError(message)
  },

  notImplemented: function notImplemented (message) {
    return new createError.NotImplemented(message)
  },

  badGateway: function badGateway (message) {
    return new createError.BadGateway(message)
  },

  serviceUnavailable: function serviceUnavailable (message) {
    return new createError.ServiceUnavailable(message)
  },

  gatewayTimeout: function gatewayTimeout (message) {
    return new createError.GatewayTimeout(message)
  },

  httpVersionNotSupported: function httpVersionNotSupported (message) {
    return new createError.HTTPVersionNotSupported(message)
  },

  variantAlsoNegotiates: function variantAlsoNegotiates (message) {
    return new createError.VariantAlsoNegotiates(message)
  },

  insufficientStorage: function insufficientStorage (message) {
    return new createError.InsufficientStorage(message)
  },

  loopDetected: function loopDetected (message) {
    return new createError.LoopDetected(message)
  },

  bandwidthLimitExceeded: function bandwidthLimitExceeded (message) {
    return new createError.BandwidthLimitExceeded(message)
  },

  notExtended: function notExtended (message) {
    return new createError.NotExtended(message)
  },

  networkAuthenticationRequired: function networkAuthenticationRequired (message) {
    return new createError.NetworkAuthenticationRequired(message)
  }
}

function getHttpError (code, message) {
  return httpErrors[statusCodesMap[code + '']](message)
}

module.exports = httpErrors
module.exports.getHttpError = getHttpError
module.exports.HttpError = createError.HttpError
module.exports.createError = createError


================================================
FILE: lib/vary.js
================================================
'use strict'

const append = require('vary').append

// Same implementation of https://github.com/jshttp/vary
// but adapted to the Fastify API
function vary (field) {
  let value = this.getHeader('Vary') || ''
  const header = Array.isArray(value)
    ? value.join(', ')
    : String(value)

  // set new header
  value = append(header, field)
  this.header('Vary', value)
}

module.exports = vary
module.exports.append = append


================================================
FILE: package.json
================================================
{
  "name": "@fastify/sensible",
  "version": "6.0.4",
  "description": "Defaults for Fastify that everyone can agree on",
  "main": "index.js",
  "type": "commonjs",
  "types": "types/index.d.ts",
  "scripts": {
    "lint": "eslint",
    "lint:fix": "eslint --fix",
    "test": "npm run test:unit && npm run test:typescript",
    "test:typescript": "tsd",
    "test:unit": "borp -C --check-coverage --reporter=@jsumners/line-reporter"
  },
  "repository": {
    "type": "git",
    "url": "git+https://github.com/fastify/fastify-sensible.git"
  },
  "keywords": [
    "fastify",
    "http",
    "defaults",
    "helper"
  ],
  "author": "Tomas Della Vedova - @delvedor (http://delved.org)",
  "contributors": [
    {
      "name": "Matteo Collina",
      "email": "hello@matteocollina.com"
    },
    {
      "name": "Manuel Spigolon",
      "email": "behemoth89@gmail.com"
    },
    {
      "name": "Cemre Mengu",
      "email": "cemremengu@gmail.com"
    },
    {
      "name": "Frazer Smith",
      "email": "frazer.dev@icloud.com",
      "url": "https://github.com/fdawgs"
    }
  ],
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/fastify/fastify-sensible/issues"
  },
  "homepage": "https://github.com/fastify/fastify-sensible#readme",
  "funding": [
    {
      "type": "github",
      "url": "https://github.com/sponsors/fastify"
    },
    {
      "type": "opencollective",
      "url": "https://opencollective.com/fastify"
    }
  ],
  "devDependencies": {
    "@jsumners/line-reporter": "^1.0.1",
    "@types/node": "^25.0.3",
    "borp": "^1.0.0",
    "eslint": "^9.17.0",
    "fastify": "^5.0.0",
    "neostandard": "^0.13.0",
    "tsd": "^0.33.0"
  },
  "dependencies": {
    "@lukeed/ms": "^2.0.2",
    "dequal": "^2.0.3",
    "fastify-plugin": "^5.0.0",
    "forwarded": "^0.2.0",
    "http-errors": "^2.0.0",
    "type-is": "^2.0.1",
    "vary": "^1.1.2"
  },
  "publishConfig": {
    "access": "public"
  }
}


================================================
FILE: test/assert.test.js
================================================
'use strict'

const { test } = require('node:test')

const Fastify = require('fastify')
const Sensible = require('../index')

test('Should support basic assert', (t, done) => {
  t.plan(2)
  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.ready(err => {
    t.assert.ifError(err)
    try {
      fastify.assert.ok(true)
      t.assert.ok('Works correctly')
    } catch (err) {
      t.assert.fail(err)
    }
    done()
  })
})

test('Should support ok assert', (t, done) => {
  t.plan(2)
  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.ready(err => {
    t.assert.ifError(err)
    try {
      fastify.assert.ok(true)
      t.assert.ok('Works correctly')
    } catch (err) {
      t.assert.fail(err)
    }
    done()
  })
})

test('Should support equal assert', (t, done) => {
  t.plan(2)
  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.ready(err => {
    t.assert.ifError(err)
    try {
      fastify.assert.equal(1, '1')
      t.assert.ok('Works correctly')
    } catch (err) {
      t.assert.fail(err)
    }
    done()
  })
})

test('Should support not equal assert', (t, done) => {
  t.plan(2)
  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.ready(err => {
    t.assert.ifError(err)
    try {
      fastify.assert.notEqual(1, '2')
      t.assert.ok('Works correctly')
    } catch (err) {
      t.assert.fail(err)
    }
    done()
  })
})

test('Should support strict equal assert', (t, done) => {
  t.plan(2)
  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.ready(err => {
    t.assert.ifError(err)
    try {
      fastify.assert.strictEqual(1, 1)
      t.assert.ok('Works correctly')
    } catch (err) {
      t.assert.fail(err)
    }
    done()
  })
})

test('Should support not strict equal assert', (t, done) => {
  t.plan(2)
  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.ready(err => {
    t.assert.ifError(err)
    try {
      fastify.assert.notStrictEqual(1, 2)
      t.assert.ok('Works correctly')
    } catch (err) {
      t.assert.fail(err)
    }
    done()
  })
})

test('Should support deep equal assert', (t, done) => {
  t.plan(2)
  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.ready(err => {
    t.assert.ifError(err)
    try {
      fastify.assert.deepEqual({ a: 1 }, { a: 1 })
      t.assert.ok('Works correctly')
    } catch (err) {
      t.assert.fail(err)
    }
    done()
  })
})

test('Should support not deep equal assert', (t, done) => {
  t.plan(2)
  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.ready(err => {
    t.assert.ifError(err)
    try {
      fastify.assert.notDeepEqual({ hello: 'world' }, { hello: 'dlrow' })
      t.assert.ok('Works correctly')
    } catch (err) {
      t.assert.fail(err)
    }
    done()
  })
})

test('Should support basic assert (throw)', (t, done) => {
  t.plan(2)
  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.ready(err => {
    t.assert.ifError(err)
    try {
      fastify.assert(false)
      t.assert.fail('Should throw')
    } catch (err) {
      t.assert.ok(err)
    }
    done()
  })
})

test('Should support equal assert (throw)', (t, done) => {
  t.plan(2)
  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.ready(err => {
    t.assert.ifError(err)
    try {
      fastify.assert.equal(1, '2')
      t.assert.fail('Should throw')
    } catch (err) {
      t.assert.ok(err)
    }
    done()
  })
})

test('Should support not equal assert (throw)', (t, done) => {
  t.plan(2)
  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.ready(err => {
    t.assert.ifError(err)
    try {
      fastify.assert.notEqual(1, '1')
      t.assert.fail('Should throw')
    } catch (err) {
      t.assert.ok(err)
    }
    done()
  })
})

test('Should support strict equal assert (throw)', (t, done) => {
  t.plan(2)
  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.ready(err => {
    t.assert.ifError(err)
    try {
      fastify.assert.equal(1, 2)
      t.assert.fail('Should throw')
    } catch (err) {
      t.assert.ok(err)
    }
    done()
  })
})

test('Should support not strict equal assert (throw)', (t, done) => {
  t.plan(2)
  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.ready(err => {
    t.assert.ifError(err)
    try {
      fastify.assert.notStrictEqual(1, 1)
      t.assert.fail('Should throw')
    } catch (err) {
      t.assert.ok(err)
    }
    done()
  })
})

test('Should support deep equal assert (throw)', (t, done) => {
  t.plan(2)
  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.ready(err => {
    t.assert.ifError(err)
    try {
      fastify.assert.deepEqual({ hello: 'world' }, { hello: 'dlrow' })
      t.assert.fail('Should throw')
    } catch (err) {
      t.assert.ok(err)
    }
    done()
  })
})

test('Should support not deep equal assert (throw)', (t, done) => {
  t.plan(2)
  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.ready(err => {
    t.assert.ifError(err)
    try {
      fastify.assert.notDeepEqual({ hello: 'world' }, { hello: 'world' })
      t.assert.fail('Should throw')
    } catch (err) {
      t.assert.ok(err)
    }
    done()
  })
})

test('Should generate the correct http error', (t, done) => {
  t.plan(4)
  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.ready(err => {
    t.assert.ifError(err)
    try {
      fastify.assert(false, 400, 'Wrong!')
      t.assert.fail('Should throw')
    } catch (err) {
      t.assert.strictEqual(err.message, 'Wrong!')
      t.assert.strictEqual(err.name, 'BadRequestError')
      t.assert.strictEqual(err.statusCode, 400)
    }
    done()
  })
})


================================================
FILE: test/cache-control.test.js
================================================
'use strict'

const { test } = require('node:test')
const Fastify = require('fastify')
const Sensible = require('../index')

test('reply.cacheControl API', (t, done) => {
  t.plan(4)

  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.get('/', (_req, reply) => {
    reply.cacheControl('public')
    reply.send('ok')
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, (err, res) => {
    t.assert.ifError(err)
    t.assert.strictEqual(res.statusCode, 200)
    t.assert.strictEqual(res.headers['cache-control'], 'public')
    t.assert.strictEqual(res.payload, 'ok')
    done()
  })
})

test('reply.cacheControl API (multiple values)', (t, done) => {
  t.plan(4)

  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.get('/', (_req, reply) => {
    reply
      .cacheControl('public')
      .cacheControl('max-age', 604800)
      .cacheControl('immutable')
      .send('ok')
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, (err, res) => {
    t.assert.ifError(err)
    t.assert.strictEqual(res.statusCode, 200)
    t.assert.strictEqual(res.headers['cache-control'], 'public, max-age=604800, immutable')
    t.assert.strictEqual(res.payload, 'ok')
    done()
  })
})

test('reply.preventCache API', (t, done) => {
  t.plan(6)

  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.get('/', (_req, reply) => {
    reply.preventCache().send('ok')
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, (err, res) => {
    t.assert.ifError(err)
    t.assert.strictEqual(res.statusCode, 200)
    t.assert.strictEqual(res.headers['cache-control'], 'no-store, max-age=0, private')
    t.assert.strictEqual(res.headers.pragma, 'no-cache')
    t.assert.strictEqual(res.headers.expires, '0')
    t.assert.strictEqual(res.payload, 'ok')
    done()
  })
})

test('reply.stale API', (t, done) => {
  t.plan(4)

  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.get('/', (_req, reply) => {
    reply.stale('while-revalidate', 42).send('ok')
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, (err, res) => {
    t.assert.ifError(err)
    t.assert.strictEqual(res.statusCode, 200)
    t.assert.strictEqual(res.headers['cache-control'], 'stale-while-revalidate=42')
    t.assert.strictEqual(res.payload, 'ok')
    done()
  })
})

test('reply.stale API (multiple values)', (t, done) => {
  t.plan(4)

  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.get('/', (_req, reply) => {
    reply
      .stale('while-revalidate', 42)
      .stale('if-error', 1)
      .send('ok')
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, (err, res) => {
    t.assert.ifError(err)
    t.assert.strictEqual(res.statusCode, 200)
    t.assert.strictEqual(res.headers['cache-control'], 'stale-while-revalidate=42, stale-if-error=1')
    t.assert.strictEqual(res.payload, 'ok')
    done()
  })
})

test('reply.stale API (bad value)', (t, done) => {
  t.plan(5)

  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.get('/', (_req, reply) => {
    try {
      reply.stale('foo', 42).send('ok')
      t.assert.fail('Should throw')
    } catch (err) {
      t.assert.ok(err)
      reply.send('ok')
    }
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, (err, res) => {
    t.assert.ifError(err)
    t.assert.strictEqual(res.statusCode, 200)
    t.assert.ok(!res.headers['cache-control'])
    t.assert.strictEqual(res.payload, 'ok')
    done()
  })
})

test('reply.revalidate API', (t, done) => {
  t.plan(4)

  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.get('/', (_req, reply) => {
    reply.revalidate().send('ok')
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, (err, res) => {
    t.assert.ifError(err)
    t.assert.strictEqual(res.statusCode, 200)
    t.assert.strictEqual(res.headers['cache-control'], 'max-age=0, must-revalidate')
    t.assert.strictEqual(res.payload, 'ok')
    done()
  })
})

test('reply.staticCache API', (t, done) => {
  t.plan(4)

  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.get('/', (_req, reply) => {
    reply.staticCache(42).send('ok')
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, (err, res) => {
    t.assert.ifError(err)
    t.assert.strictEqual(res.statusCode, 200)
    t.assert.strictEqual(res.headers['cache-control'], 'public, max-age=42, immutable')
    t.assert.strictEqual(res.payload, 'ok')
    done()
  })
})

test('reply.staticCache API (as string)', (t, done) => {
  t.plan(4)

  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.get('/', (_req, reply) => {
    reply.staticCache('42s').send('ok')
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, (err, res) => {
    t.assert.ifError(err)
    t.assert.strictEqual(res.statusCode, 200)
    t.assert.strictEqual(res.headers['cache-control'], 'public, max-age=42, immutable')
    t.assert.strictEqual(res.payload, 'ok')
    done()
  })
})

test('reply.maxAge and reply.stale API', (t, done) => {
  t.plan(4)

  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.get('/', (_req, reply) => {
    reply
      .maxAge(42)
      .stale('while-revalidate', 3)
      .send('ok')
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, (err, res) => {
    t.assert.ifError(err)
    t.assert.strictEqual(res.statusCode, 200)
    t.assert.strictEqual(res.headers['cache-control'], 'max-age=42, stale-while-revalidate=3')
    t.assert.strictEqual(res.payload, 'ok')
    done()
  })
})

test('reply.cacheControl API (string time)', (t, done) => {
  t.plan(4)

  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.get('/', (_req, reply) => {
    reply.cacheControl('max-age', '1d')
    reply.send('ok')
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, (err, res) => {
    t.assert.ifError(err)
    t.assert.strictEqual(res.statusCode, 200)
    t.assert.strictEqual(res.headers['cache-control'], 'max-age=86400')
    t.assert.strictEqual(res.payload, 'ok')
    done()
  })
})


================================================
FILE: test/forwarded.test.js
================================================
'use strict'

const { test } = require('node:test')
const Fastify = require('fastify')
const Sensible = require('../index')

test('request.forwarded API', (t, done) => {
  t.plan(3)

  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.get('/', (req, reply) => {
    reply.send(req.forwarded())
  })

  fastify.inject({
    method: 'GET',
    url: '/',
    headers: {
      'x-forwarded-for': '10.0.0.2, 10.0.0.1'
    }
  }, (err, res) => {
    t.assert.ifError(err)
    t.assert.strictEqual(res.statusCode, 200)
    t.assert.deepStrictEqual(
      JSON.parse(res.payload),
      ['127.0.0.1', '10.0.0.1', '10.0.0.2']
    )
    done()
  })
})

test('request.forwarded API (without header)', (t, done) => {
  t.plan(3)

  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.get('/', (req, reply) => {
    reply.send(req.forwarded())
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, (err, res) => {
    t.assert.ifError(err)
    t.assert.strictEqual(res.statusCode, 200)
    t.assert.deepStrictEqual(
      JSON.parse(res.payload),
      ['127.0.0.1']
    )
    done()
  })
})


================================================
FILE: test/httpErrors.test.js
================================================
'use strict'

const { test } = require('node:test')
const createError = require('http-errors')
const statusCodes = require('node:http').STATUS_CODES
const Fastify = require('fastify')
const Sensible = require('../index')
const HttpError = require('../lib/httpErrors').HttpError

test('Should generate the correct http error', (t, done) => {
  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.ready(err => {
    t.assert.ifError(err)

    Object.keys(statusCodes).forEach(code => {
      if (Number(code) < 400) return
      const name = normalize(code, statusCodes[code])
      const err = fastify.httpErrors[name]()
      t.assert.ok(err instanceof HttpError)
      // `statusCodes` uses the capital T
      if (err.message === 'I\'m a Teapot') {
        t.assert.strictEqual(err.statusCode, 418)
      } else {
        t.assert.strictEqual(err.message, statusCodes[code])
      }
      t.assert.strictEqual(typeof err.name, 'string')
      t.assert.strictEqual(err.statusCode, Number(code))
    })

    done()
  })
})

test('Should expose the createError method from http-errors', (t, done) => {
  t.plan(2)
  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.ready(err => {
    t.assert.ifError(err)
    t.assert.strictEqual(fastify.httpErrors.createError, createError)
    done()
  })
})

test('Should generate the correct error using the properties given', (t, done) => {
  t.plan(5)
  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.ready(err => {
    t.assert.ifError(err)
    const customError = fastify.httpErrors.createError(404, 'This video does not exist!')
    t.assert.ok(customError instanceof HttpError)
    t.assert.strictEqual(customError.message, 'This video does not exist!')
    t.assert.strictEqual(typeof customError.name, 'string')
    t.assert.strictEqual(customError.statusCode, 404)
    done()
  })
})

test('Should generate the correct http error (with custom message)', (t, done) => {
  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.ready(err => {
    t.assert.ifError(err)

    Object.keys(statusCodes).forEach(code => {
      if (Number(code) < 400) return
      const name = normalize(code, statusCodes[code])
      const err = fastify.httpErrors[name]('custom')
      t.assert.ok(err instanceof HttpError)
      t.assert.strictEqual(err.message, 'custom')
      t.assert.strictEqual(typeof err.name, 'string')
      t.assert.strictEqual(err.statusCode, Number(code))
    })

    done()
  })
})

test('should throw error', (t) => {
  const err = Sensible.httpErrors.conflict('custom')
  t.assert.strictEqual(err.message, 'custom')
})

function normalize (code, msg) {
  if (code === '414') return 'uriTooLong'
  if (code === '418') return 'imateapot'
  if (code === '505') return 'httpVersionNotSupported'
  msg = msg.split(' ').join('').replace(/'/g, '')
  msg = msg[0].toLowerCase() + msg.slice(1)
  return msg
}


================================================
FILE: test/httpErrorsReply.test.js
================================================
'use strict'

const { test } = require('node:test')
const statusCodes = require('node:http').STATUS_CODES
const Fastify = require('fastify')
const Sensible = require('../index')

test('Should generate the correct http error', (t, rootDone) => {
  const codes = Object.keys(statusCodes).filter(code => Number(code) >= 400 && code !== '418')
  let completedTests = 0

  codes.forEach(code => {
    t.test(code, (t, done) => {
      t.plan(4)
      const fastify = Fastify()
      fastify.register(Sensible)

      fastify.get('/', (_req, reply) => {
        const name = normalize(code, statusCodes[code])
        t.assert.strictEqual(reply[name](), reply)
      })

      fastify.inject({
        method: 'GET',
        url: '/'
      }, (err, res) => {
        t.assert.ifError(err)
        t.assert.strictEqual(res.statusCode, Number(code))
        if (code === '425') {
          t.assert.deepStrictEqual(JSON.parse(res.payload), {
            error: 'Too Early',
            message: 'Too Early',
            statusCode: 425
          })
        } else {
          t.assert.deepStrictEqual(JSON.parse(res.payload), {
            error: statusCodes[code],
            message: statusCodes[code],
            statusCode: Number(code)
          })
        }
        done()
        completedTests++

        if (completedTests === codes.length) {
          rootDone()
        }
      })
    })
  })
})

test('Should generate the correct http error using getter', (t, rootDone) => {
  const codes = Object.keys(statusCodes).filter(code => Number(code) >= 400 && code !== '418')
  let completedTests = 0

  codes.forEach(code => {
    t.test(code, (t, done) => {
      t.plan(4)
      const fastify = Fastify()
      fastify.register(Sensible)

      fastify.get('/', (_req, reply) => {
        t.assert.strictEqual(reply.getHttpError(code), reply)
      })

      fastify.inject({
        method: 'GET',
        url: '/'
      }, (err, res) => {
        t.assert.ifError(err)
        t.assert.strictEqual(res.statusCode, Number(code))
        t.assert.deepStrictEqual(JSON.parse(res.payload), {
          error: statusCodes[code],
          message: statusCodes[code],
          statusCode: Number(code)
        })
        done()
        completedTests++

        if (completedTests === codes.length) {
          rootDone()
        }
      })
    })
  })
})

test('Should generate the correct http error (with custom message)', (t, rootDone) => {
  const codes = Object.keys(statusCodes).filter(code => Number(code) >= 400 && code !== '418')
  let completedTests = 0

  codes.forEach(code => {
    t.test(code, (t, done) => {
      t.plan(3)
      const fastify = Fastify()
      fastify.register(Sensible)

      fastify.get('/', (_req, reply) => {
        const name = normalize(code, statusCodes[code])
        reply[name]('custom')
      })

      fastify.inject({
        method: 'GET',
        url: '/'
      }, (err, res) => {
        t.assert.ifError(err)
        t.assert.strictEqual(res.statusCode, Number(code))
        t.assert.deepStrictEqual(JSON.parse(res.payload), {
          error: statusCodes[code],
          message: 'custom',
          statusCode: Number(code)
        })
        done()
        completedTests++

        if (completedTests === codes.length) {
          rootDone()
        }
      })
    })
  })
})

function normalize (code, msg) {
  if (code === '414') return 'uriTooLong'
  if (code === '505') return 'httpVersionNotSupported'
  msg = msg.split(' ').join('').replace(/'/g, '')
  msg = msg[0].toLowerCase() + msg.slice(1)
  return msg
}


================================================
FILE: test/is.test.js
================================================
'use strict'

const { test } = require('node:test')
const Fastify = require('fastify')
const Sensible = require('../index')

test('request.is API', (t, done) => {
  t.plan(3)

  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.get('/', (req, reply) => {
    reply.send(req.is('json'))
  })

  fastify.inject({
    method: 'GET',
    url: '/',
    payload: { foo: 'bar' }
  }, (err, res) => {
    t.assert.ifError(err)
    t.assert.strictEqual(res.statusCode, 200)
    t.assert.deepStrictEqual(
      res.payload,
      'json'
    )
    done()
  })
})

test('request.is API (with array)', (t, done) => {
  t.plan(3)

  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.get('/', (req, reply) => {
    reply.send(req.is(['html', 'json']))
  })

  fastify.inject({
    method: 'GET',
    url: '/',
    payload: { foo: 'bar' }
  }, (err, res) => {
    t.assert.ifError(err)
    t.assert.strictEqual(res.statusCode, 200)
    t.assert.deepStrictEqual(
      res.payload,
      'json'
    )
    done()
  })
})


================================================
FILE: test/schema.test.js
================================================
'use strict'

const { test } = require('node:test')
const statusCodes = require('node:http').STATUS_CODES
const Fastify = require('fastify')
const Sensible = require('../index')

test('Should add shared schema', (t, done) => {
  t.plan(3)

  const fastify = Fastify()
  fastify.register(Sensible, { sharedSchemaId: 'myError' })

  fastify.get('/', {
    schema: {
      response: {
        400: { $ref: 'myError' }
      }
    },
    handler: (_req, reply) => {
      reply.badRequest()
    }
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, (err, res) => {
    t.assert.ifError(err)
    t.assert.strictEqual(res.statusCode, 400)
    t.assert.deepStrictEqual(JSON.parse(res.payload), {
      error: statusCodes[400],
      message: statusCodes[400],
      statusCode: 400
    })
    done()
  })
})


================================================
FILE: test/to.test.js
================================================
'use strict'

const { test } = require('node:test')
const Fastify = require('fastify')
const Sensible = require('../index')

test('Should nicely wrap promises (resolve)', (t, done) => {
  t.plan(4)

  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.ready(err => {
    t.assert.ifError(err)

    fastify.to(promise(true))
      .then(val => {
        t.assert.ok(Array.isArray(val))
        t.assert.ok(!val[0])
        t.assert.ok(val[1])
        done()
      })
  })
})

test('Should nicely wrap promises (reject)', (t, done) => {
  t.plan(4)

  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.ready(err => {
    t.assert.ifError(err)

    fastify.to(promise(false))
      .then(val => {
        t.assert.ok(Array.isArray(val))
        t.assert.ok(val[0])
        t.assert.ok(!val[1])
        done()
      })
  })
})

function promise (bool) {
  return new Promise((resolve, reject) => {
    if (bool) {
      resolve(true)
    } else {
      reject(new Error('kaboom'))
    }
  })
}


================================================
FILE: test/vary.test.js
================================================
'use strict'

const { test, describe } = require('node:test')
const Fastify = require('fastify')
const Sensible = require('../index')

describe('reply.vary API', () => {
  test('accept string', (t, done) => {
    t.plan(4)

    const fastify = Fastify()
    fastify.register(Sensible)

    fastify.get('/', (_req, reply) => {
      reply.vary('Accept')
      reply.vary('Origin')
      reply.vary('User-Agent')
      reply.send('ok')
    })

    fastify.inject({
      method: 'GET',
      url: '/'
    }, (err, res) => {
      t.assert.ifError(err)
      t.assert.strictEqual(res.statusCode, 200)
      t.assert.strictEqual(res.headers.vary, 'Accept, Origin, User-Agent')
      t.assert.strictEqual(res.payload, 'ok')
      done()
    })
  })

  test('accept array of strings', (t, done) => {
    t.plan(4)

    const fastify = Fastify()
    fastify.register(Sensible)

    fastify.get('/', (_req, reply) => {
      reply.header('Vary', ['Accept', 'Origin'])
      reply.vary('User-Agent')
      reply.send('ok')
    })

    fastify.inject({
      method: 'GET',
      url: '/'
    }, (err, res) => {
      t.assert.ifError(err)
      t.assert.strictEqual(res.statusCode, 200)
      t.assert.strictEqual(res.headers.vary, 'Accept, Origin, User-Agent')
      t.assert.strictEqual(res.payload, 'ok')
      done()
    })
  })
})

test('reply.vary.append API', (t, done) => {
  t.plan(4)

  const fastify = Fastify()
  fastify.register(Sensible)

  fastify.get('/', (_req, reply) => {
    t.assert.strictEqual(
      reply.vary.append('', ['Accept', 'Accept-Language']), 'Accept, Accept-Language'
    )
    reply.send('ok')
  })

  fastify.inject({
    method: 'GET',
    url: '/'
  }, (err, res) => {
    t.assert.ifError(err)
    t.assert.strictEqual(res.statusCode, 200)
    t.assert.strictEqual(res.payload, 'ok')
    done()
  })
})


================================================
FILE: types/index.d.ts
================================================
import { FastifyPluginCallback, FastifyReply } from 'fastify'
import { HttpErrors, HttpError } from '../lib/httpError'
import * as Errors from '../lib/httpError'

type FastifySensible = FastifyPluginCallback<fastifySensible.FastifySensibleOptions>

type singleValueTypes =
  | 'must-revalidate'
  | 'no-cache'
  | 'no-store'
  | 'no-transform'
  | 'public'
  | 'private'
  | 'proxy-revalidate'
  | 'immutable'

type multiValueTypes =
  | 'max-age'
  | 's-maxage'
  | 'stale-while-revalidate'
  | 'stale-if-error'

type staleTypes = 'while-revalidate' | 'if-error'

declare module 'fastify' {
  namespace SensibleTypes {
    type ToType<T> = [Error, T]
  }

  interface Assert {
    (condition: unknown, code: number, message?: string): asserts condition;
    ok(condition: unknown, code: number, message?: string): asserts condition;
    equal(a: unknown, b: unknown, code: number, message?: string): void;
    notEqual(a: unknown, b: unknown, code: number, message?: string): void;
    strictEqual<T>(a: unknown, b: T, code: number, message?: string): asserts a is T;
    notStrictEqual(a: unknown, b: unknown, code: number, message?: string): void;
    deepEqual(a: unknown, b: unknown, code: number, message?: string): void;
    notDeepEqual(a: unknown, b: unknown, code: number, message?: string): void;
  }

  interface FastifyInstance {
    assert: Assert;
    to<T>(to: Promise<T>): Promise<SensibleTypes.ToType<T>>;
    httpErrors: HttpErrors;
  }

  interface FastifyReply extends fastifySensible.HttpErrorReplys {
    vary: {
      (field: string | string[]): void;
      append: (header: string, field: string | string[]) => string;
    };
    cacheControl(type: singleValueTypes): this
    cacheControl(type: multiValueTypes, time: number | string): this
    preventCache(): this
    maxAge(type: number | string): this
    revalidate(): this
    staticCache(time: number | string): this
    stale(type: staleTypes, time: number | string): this
  }

  interface FastifyRequest {
    forwarded(): string[];
    is(types: Array<string>): string | false | null;
    is(...types: Array<string>): string | false | null;
  }
}

declare namespace fastifySensible {
  export interface FastifySensibleOptions {
    /**
     * This option registers a shared JSON Schema to be used by all response schemas.
     *
     * @example
     * ```js
     * fastify.register(require('@fastify/sensible'), {
     *   sharedSchemaId: 'HttpError'
     * })
     *
     * fastify.get('/async', {
     *   schema: {
     *     response: { 404: { $ref: 'HttpError' } }
     *   }
     *   handler: async (req, reply) => {
     *     return reply.notFound()
     *   }
     * })
     * ```
     */
    sharedSchemaId?: string | undefined;
  }

  export { HttpError }

  export type HttpErrors = Errors.HttpErrors
  export type HttpErrorCodes = Errors.HttpErrorCodes
  export type HttpErrorCodesLoose = Errors.HttpErrorCodesLoose
  export type HttpErrorNames = Errors.HttpErrorNames
  export type HttpErrorTypes = Errors.HttpErrorTypes

  export const httpErrors: typeof Errors.default

  export type HttpErrorReplys = {
    getHttpError: (code: HttpErrorCodesLoose, message?: string) => FastifyReply;
  } & {
    [Property in keyof HttpErrorTypes]: (msg?: string) => FastifyReply
  }

  export const fastifySensible: FastifySensible
  export { fastifySensible as default }
}

declare function fastifySensible (...params: Parameters<FastifySensible>): ReturnType<FastifySensible>
export = fastifySensible


================================================
FILE: types/index.test-d.ts
================================================
import { expectType, expectAssignable, expectError, expectNotAssignable } from 'tsd'
import fastify from 'fastify'
import fastifySensible, { FastifySensibleOptions, httpErrors, HttpError } from '..'

const app = fastify()

app.register(fastifySensible)

expectAssignable<FastifySensibleOptions>({})
expectAssignable<FastifySensibleOptions>({ sharedSchemaId: 'HttpError' })
expectAssignable<FastifySensibleOptions>({ sharedSchemaId: undefined })
expectNotAssignable<FastifySensibleOptions>({ notSharedSchemaId: 'HttpError' })

app.get('/', (_req, reply) => {
  expectAssignable<typeof reply>(reply.badRequest())
  expectAssignable<typeof reply>(reply.unauthorized())
  expectAssignable<typeof reply>(reply.paymentRequired())
  expectAssignable<typeof reply>(reply.forbidden())
  expectAssignable<typeof reply>(reply.notFound())
  expectAssignable<typeof reply>(reply.methodNotAllowed())
  expectAssignable<typeof reply>(reply.notAcceptable())
  expectAssignable<typeof reply>(reply.proxyAuthenticationRequired())
  expectAssignable<typeof reply>(reply.requestTimeout())
  expectAssignable<typeof reply>(reply.gone())
  expectAssignable<typeof reply>(reply.lengthRequired())
  expectAssignable<typeof reply>(reply.preconditionFailed())
  expectAssignable<typeof reply>(reply.payloadTooLarge())
  expectAssignable<typeof reply>(reply.uriTooLong())
  expectAssignable<typeof reply>(reply.unsupportedMediaType())
  expectAssignable<typeof reply>(reply.rangeNotSatisfiable())
  expectAssignable<typeof reply>(reply.expectationFailed())
  expectAssignable<typeof reply>(reply.imateapot())
  expectAssignable<typeof reply>(reply.unprocessableEntity())
  expectAssignable<typeof reply>(reply.locked())
  expectAssignable<typeof reply>(reply.failedDependency())
  expectAssignable<typeof reply>(reply.tooEarly())
  expectAssignable<typeof reply>(reply.upgradeRequired())
  expectAssignable<typeof reply>(reply.preconditionFailed())
  expectAssignable<typeof reply>(reply.tooManyRequests())
  expectAssignable<typeof reply>(reply.requestHeaderFieldsTooLarge())
  expectAssignable<typeof reply>(reply.unavailableForLegalReasons())
  expectAssignable<typeof reply>(reply.internalServerError())
  expectAssignable<typeof reply>(reply.notImplemented())
  expectAssignable<typeof reply>(reply.badGateway())
  expectAssignable<typeof reply>(reply.serviceUnavailable())
  expectAssignable<typeof reply>(reply.gatewayTimeout())
  expectAssignable<typeof reply>(reply.httpVersionNotSupported())
  expectAssignable<typeof reply>(reply.variantAlsoNegotiates())
  expectAssignable<typeof reply>(reply.insufficientStorage())
  expectAssignable<typeof reply>(reply.loopDetected())
  expectAssignable<typeof reply>(reply.bandwidthLimitExceeded())
  expectAssignable<typeof reply>(reply.notExtended())
  expectAssignable<typeof reply>(reply.networkAuthenticationRequired())
})

app.get('/', (_req, reply) => {
  expectAssignable<typeof reply>(reply.getHttpError(405, 'Method Not Allowed'))
  expectAssignable<typeof reply>(reply.getHttpError('405', 'Method Not Allowed'))
})

app.get('/', () => {
  expectAssignable<HttpError>(app.httpErrors.createError(405, 'Method Not Allowed'))
})

app.get('/', () => {
  expectAssignable<HttpError>(
    app.httpErrors.createError(405, 'Method Not Allowed')
  )
  expectAssignable<HttpError>(
    app.httpErrors.createError(405, 'Method Not Allowed')
  )
  expectAssignable<HttpError<400>>(app.httpErrors.badRequest())
})

app.get('/', async () => {
  expectAssignable<HttpError<400>>(app.httpErrors.badRequest())
  expectAssignable<HttpError<401>>(app.httpErrors.unauthorized())
  expectAssignable<HttpError<402>>(app.httpErrors.paymentRequired())
  expectAssignable<HttpError<403>>(app.httpErrors.forbidden())
  expectAssignable<HttpError<404>>(app.httpErrors.notFound())
  expectAssignable<HttpError<405>>(app.httpErrors.methodNotAllowed())
  expectAssignable<HttpError<406>>(app.httpErrors.notAcceptable())
  expectAssignable<HttpError<407>>(app.httpErrors.proxyAuthenticationRequired())
  expectAssignable<HttpError<408>>(app.httpErrors.requestTimeout())
  expectAssignable<HttpError<410>>(app.httpErrors.gone())
  expectAssignable<HttpError<411>>(app.httpErrors.lengthRequired())
  expectAssignable<HttpError<412>>(app.httpErrors.preconditionFailed())
  expectAssignable<HttpError<413>>(app.httpErrors.payloadTooLarge())
  expectAssignable<HttpError<414>>(app.httpErrors.uriTooLong())
  expectAssignable<HttpError<415>>(app.httpErrors.unsupportedMediaType())
  expectAssignable<HttpError<416>>(app.httpErrors.rangeNotSatisfiable())
  expectAssignable<HttpError<417>>(app.httpErrors.expectationFailed())
  expectAssignable<HttpError<418>>(app.httpErrors.imateapot())
  expectAssignable<HttpError<422>>(app.httpErrors.unprocessableEntity())
  expectAssignable<HttpError<423>>(app.httpErrors.locked())
  expectAssignable<HttpError<424>>(app.httpErrors.failedDependency())
  expectAssignable<HttpError<425>>(app.httpErrors.tooEarly())
  expectAssignable<HttpError<426>>(app.httpErrors.upgradeRequired())
  expectAssignable<HttpError<429>>(app.httpErrors.tooManyRequests())
  expectAssignable<HttpError<431>>(app.httpErrors.requestHeaderFieldsTooLarge())
  expectAssignable<HttpError<451>>(app.httpErrors.unavailableForLegalReasons())
  expectAssignable<HttpError<500>>(app.httpErrors.internalServerError())
  expectAssignable<HttpError<501>>(app.httpErrors.notImplemented())
  expectAssignable<HttpError<502>>(app.httpErrors.badGateway())
  expectAssignable<HttpError<503>>(app.httpErrors.serviceUnavailable())
  expectAssignable<HttpError<504>>(app.httpErrors.gatewayTimeout())
  expectAssignable<HttpError<505>>(app.httpErrors.httpVersionNotSupported())
  expectAssignable<HttpError<506>>(app.httpErrors.variantAlsoNegotiates())
  expectAssignable<HttpError<507>>(app.httpErrors.insufficientStorage())
  expectAssignable<HttpError<508>>(app.httpErrors.loopDetected())
  expectAssignable<HttpError<509>>(app.httpErrors.bandwidthLimitExceeded())
  expectAssignable<HttpError<510>>(app.httpErrors.notExtended())
  expectAssignable<HttpError<511>>(app.httpErrors.networkAuthenticationRequired())
})

app.get('/', async () => {
  expectError(app.assert(true))
  expectType<void>(app.assert(1, 400, 'Bad number'))
  expectType<void>(app.assert.ok(true, 400))
  expectType<void>(app.assert.equal(1, 1, 400))
  expectType<void>(app.assert.notEqual(1, 2, 400))
  expectType<void>(app.assert.strictEqual(1, 1, 400))
  expectType<void>(app.assert.notStrictEqual(1, 2, 400))
  expectType<void>(app.assert.deepEqual({}, {}, 400))
  expectType<void>(app.assert.notDeepEqual({}, { a: 1 }, 400))
})

app.get('/', async () => {
  expectType<Promise<[Error, void]>>(app.to<void>(new Promise(resolve => resolve())))
})

app.get('/', (_req, reply) => {
  expectAssignable<typeof reply>(reply.cacheControl('public'))
})

app.get('/', (_req, reply) => {
  expectAssignable<typeof reply>(reply.preventCache())
})

app.get('/', (_req, reply) => {
  expectAssignable<typeof reply>(reply.cacheControl('max-age', 42))
})

app.get('/', (_req, reply) => {
  expectError(reply.cacheControl('foobar'))
})

app.get('/', (_req, reply) => {
  expectAssignable<typeof reply>(reply.stale('while-revalidate', 42))
})

app.get('/', async (_req, reply) => {
  expectType<void>(reply.vary('test'))
  expectType<void>(reply.vary(['test']))
  expectType<string>(reply.vary.append('X-Header', 'field1'))
  expectType<string>(reply.vary.append('X-Header', ['field1']))
})

app.get('/', async (req) => {
  expectType<string[]>(req.forwarded())
  expectType<string | false | null>(req.is(['foo', 'bar']))
  expectType<string | false | null>(req.is('foo', 'bar'))
})

httpErrors.forbidden('This type should be also available')
httpErrors.createError('MyError')
Download .txt
gitextract__ix3j9hb/

├── .borp.yaml
├── .gitattributes
├── .github/
│   ├── dependabot.yml
│   └── workflows/
│       ├── ci.yml
│       └── lock-threads.yml
├── .gitignore
├── .npmrc
├── LICENSE
├── README.md
├── eslint.config.js
├── index.js
├── lib/
│   ├── assert.js
│   ├── cache-control.js
│   ├── httpError.d.ts
│   ├── httpErrors.js
│   └── vary.js
├── package.json
├── test/
│   ├── assert.test.js
│   ├── cache-control.test.js
│   ├── forwarded.test.js
│   ├── httpErrors.test.js
│   ├── httpErrorsReply.test.js
│   ├── is.test.js
│   ├── schema.test.js
│   ├── to.test.js
│   └── vary.test.js
└── types/
    ├── index.d.ts
    └── index.test-d.ts
Download .txt
SYMBOL INDEX (39 symbols across 10 files)

FILE: index.js
  function fastifySensible (line 14) | function fastifySensible (fastify, opts, next) {

FILE: lib/assert.js
  function assert (line 7) | function assert (condition, code, message) {

FILE: lib/cache-control.js
  function cacheControl (line 32) | function cacheControl (type, time) {
  function preventCache (line 48) | function preventCache () {
  function maxAge (line 59) | function maxAge (time) {
  function revalidate (line 63) | function revalidate () {
  function staticCache (line 68) | function staticCache (time) {
  function stale (line 77) | function stale (type, time) {

FILE: lib/httpError.d.ts
  class HttpError (line 1) | class HttpError<N extends number = number> extends Error {
  type UnknownError (line 13) | type UnknownError = Error | string | number | { [key: string]: any }
  type HttpErrorTypes (line 15) | type HttpErrorTypes = {
  type ValueOf (line 59) | type ValueOf<ObjectType, ValueType extends keyof ObjectType = keyof Obje...
  type HttpErrorNames (line 61) | type HttpErrorNames = keyof HttpErrorTypes
  type HttpErrorCodes (line 62) | type HttpErrorCodes = ValueOf<HttpErrorTypes>
  type HttpErrorCodesLoose (line 64) | type HttpErrorCodesLoose = HttpErrorCodes | `${HttpErrorCodes}`
  type AsCode (line 66) | type AsCode<T> = T extends `${infer N extends HttpErrorCodes}` ? N : never
  type HttpErrors (line 68) | type HttpErrors = {

FILE: lib/httpErrors.js
  function normalize (line 11) | function normalize (code, msg) {
  function getHttpError (line 185) | function getHttpError (code, message) {

FILE: lib/vary.js
  function vary (line 7) | function vary (field) {

FILE: test/httpErrors.test.js
  function normalize (line 90) | function normalize (code, msg) {

FILE: test/httpErrorsReply.test.js
  function normalize (line 126) | function normalize (code, msg) {

FILE: test/to.test.js
  function promise (line 45) | function promise (bool) {

FILE: types/index.d.ts
  type FastifySensible (line 5) | type FastifySensible = FastifyPluginCallback<fastifySensible.FastifySens...
  type singleValueTypes (line 7) | type singleValueTypes =
  type multiValueTypes (line 17) | type multiValueTypes =
  type staleTypes (line 23) | type staleTypes = 'while-revalidate' | 'if-error'
  type ToType (line 27) | type ToType<T> = [Error, T]
  type Assert (line 30) | interface Assert {
  type FastifyInstance (line 41) | interface FastifyInstance {
  type FastifyReply (line 47) | interface FastifyReply extends fastifySensible.HttpErrorReplys {
  type FastifyRequest (line 61) | interface FastifyRequest {
  type FastifySensibleOptions (line 69) | interface FastifySensibleOptions {
  type HttpErrors (line 94) | type HttpErrors = Errors.HttpErrors
  type HttpErrorCodes (line 95) | type HttpErrorCodes = Errors.HttpErrorCodes
  type HttpErrorCodesLoose (line 96) | type HttpErrorCodesLoose = Errors.HttpErrorCodesLoose
  type HttpErrorNames (line 97) | type HttpErrorNames = Errors.HttpErrorNames
  type HttpErrorTypes (line 98) | type HttpErrorTypes = Errors.HttpErrorTypes
  type HttpErrorReplys (line 102) | type HttpErrorReplys = {
Condensed preview — 28 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (71K chars).
[
  {
    "path": ".borp.yaml",
    "chars": 31,
    "preview": "files:\n  - 'test/**/*.test.js'\n"
  },
  {
    "path": ".gitattributes",
    "chars": 127,
    "preview": "# Set the default behavior, in case people don't have core.autocrlf set\n* text=auto\n\n# Require Unix line endings\n* text "
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 274,
    "preview": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n "
  },
  {
    "path": ".github/workflows/ci.yml",
    "chars": 651,
    "preview": "name: CI\n\non:\n  push:\n    branches:\n     - main\n     - next\n     - 'v*'\n    paths-ignore:\n      - 'docs/**'\n      - '*.m"
  },
  {
    "path": ".github/workflows/lock-threads.yml",
    "chars": 293,
    "preview": "name: Lock Threads\n\non:\n  schedule:\n    - cron: '0 0 1 * *'\n  workflow_dispatch:\n\nconcurrency:\n  group: lock\n\npermission"
  },
  {
    "path": ".gitignore",
    "chars": 2229,
    "preview": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\nlerna-debug.log*\n.pnpm-debug.log*\n\n# Diagnostic reports"
  },
  {
    "path": ".npmrc",
    "chars": 40,
    "preview": "ignore-scripts=true\npackage-lock=false\n\n"
  },
  {
    "path": "LICENSE",
    "chars": 1146,
    "preview": "MIT License\n\nCopyright (c) 2018-present Tomas Della Vedova and The Fastify team <https://github.com/fastify/fastify#team"
  },
  {
    "path": "README.md",
    "chars": 10189,
    "preview": "# @fastify/sensible\n\n[![CI](https://github.com/fastify/fastify-sensible/actions/workflows/ci.yml/badge.svg?branch=main)]"
  },
  {
    "path": "eslint.config.js",
    "chars": 135,
    "preview": "'use strict'\n\nmodule.exports = require('neostandard')({\n  ignores: require('neostandard').resolveIgnoresFromGitignore(),"
  },
  {
    "path": "index.js",
    "chars": 3240,
    "preview": "'use strict'\n\nconst fp = require('fastify-plugin')\n// External utilities\nconst forwarded = require('forwarded')\nconst ty"
  },
  {
    "path": "lib/assert.js",
    "chars": 840,
    "preview": "/* eslint-disable eqeqeq */\n'use strict'\n\nconst { dequal: deepEqual } = require('dequal')\nconst { getHttpError } = requi"
  },
  {
    "path": "lib/cache-control.js",
    "chars": 2587,
    "preview": "'use strict'\n\n// Cache control header utilities, for more info see:\n// https://developer.mozilla.org/en-US/docs/Web/HTTP"
  },
  {
    "path": "lib/httpError.d.ts",
    "chars": 2229,
    "preview": "export declare class HttpError<N extends number = number> extends Error {\n  status: N\n  statusCode: N\n  expose: boolean\n"
  },
  {
    "path": "lib/httpErrors.js",
    "chars": 5550,
    "preview": "'use strict'\n\nconst createError = require('http-errors')\nconst statusCodes = require('node:http').STATUS_CODES\n\nconst st"
  },
  {
    "path": "lib/vary.js",
    "chars": 430,
    "preview": "'use strict'\n\nconst append = require('vary').append\n\n// Same implementation of https://github.com/jshttp/vary\n// but ada"
  },
  {
    "path": "package.json",
    "chars": 1943,
    "preview": "{\n  \"name\": \"@fastify/sensible\",\n  \"version\": \"6.0.4\",\n  \"description\": \"Defaults for Fastify that everyone can agree on"
  },
  {
    "path": "test/assert.test.js",
    "chars": 5711,
    "preview": "'use strict'\n\nconst { test } = require('node:test')\n\nconst Fastify = require('fastify')\nconst Sensible = require('../ind"
  },
  {
    "path": "test/cache-control.test.js",
    "chars": 6031,
    "preview": "'use strict'\n\nconst { test } = require('node:test')\nconst Fastify = require('fastify')\nconst Sensible = require('../inde"
  },
  {
    "path": "test/forwarded.test.js",
    "chars": 1117,
    "preview": "'use strict'\n\nconst { test } = require('node:test')\nconst Fastify = require('fastify')\nconst Sensible = require('../inde"
  },
  {
    "path": "test/httpErrors.test.js",
    "chars": 2918,
    "preview": "'use strict'\n\nconst { test } = require('node:test')\nconst createError = require('http-errors')\nconst statusCodes = requi"
  },
  {
    "path": "test/httpErrorsReply.test.js",
    "chars": 3575,
    "preview": "'use strict'\n\nconst { test } = require('node:test')\nconst statusCodes = require('node:http').STATUS_CODES\nconst Fastify "
  },
  {
    "path": "test/is.test.js",
    "chars": 1035,
    "preview": "'use strict'\n\nconst { test } = require('node:test')\nconst Fastify = require('fastify')\nconst Sensible = require('../inde"
  },
  {
    "path": "test/schema.test.js",
    "chars": 811,
    "preview": "'use strict'\n\nconst { test } = require('node:test')\nconst statusCodes = require('node:http').STATUS_CODES\nconst Fastify "
  },
  {
    "path": "test/to.test.js",
    "chars": 1021,
    "preview": "'use strict'\n\nconst { test } = require('node:test')\nconst Fastify = require('fastify')\nconst Sensible = require('../inde"
  },
  {
    "path": "test/vary.test.js",
    "chars": 1834,
    "preview": "'use strict'\n\nconst { test, describe } = require('node:test')\nconst Fastify = require('fastify')\nconst Sensible = requir"
  },
  {
    "path": "types/index.d.ts",
    "chars": 3490,
    "preview": "import { FastifyPluginCallback, FastifyReply } from 'fastify'\nimport { HttpErrors, HttpError } from '../lib/httpError'\ni"
  },
  {
    "path": "types/index.test-d.ts",
    "chars": 7729,
    "preview": "import { expectType, expectAssignable, expectError, expectNotAssignable } from 'tsd'\nimport fastify from 'fastify'\nimpor"
  }
]

About this extraction

This page contains the full source code of the fastify/fastify-sensible GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 28 files (65.6 KB), approximately 18.3k tokens, and a symbol index with 39 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!