Auto-commit 2026-04-29 16:31

This commit is contained in:
2026-04-29 16:31:27 -04:00
parent e8687bb6b2
commit 0495ee5bd2
19691 changed files with 3272886 additions and 138 deletions

8
node_modules/@fastify/cors/.editorconfig generated vendored Normal file
View File

@@ -0,0 +1,8 @@
root = true
[*]
indent_style = space
indent_size = 2
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = false

5
node_modules/@fastify/cors/.gitattributes generated vendored Normal file
View File

@@ -0,0 +1,5 @@
# Set the default behavior, in case people don't have core.autocrlf set
* text=auto
# Require Unix line endings
* text eol=lf

13
node_modules/@fastify/cors/.github/dependabot.yml generated vendored Normal file
View File

@@ -0,0 +1,13 @@
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

21
node_modules/@fastify/cors/.github/stale.yml generated vendored Normal file
View File

@@ -0,0 +1,21 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 15
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- "discussion"
- "feature request"
- "bug"
- "help wanted"
- "plugin suggestion"
- "good first issue"
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

33
node_modules/@fastify/cors/.github/workflows/ci.yml generated vendored Normal file
View File

@@ -0,0 +1,33 @@
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@v5
with:
license-check: true
lint: true

23
node_modules/@fastify/cors/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,23 @@
MIT License
Copyright (c) 2018-present The Fastify team
The Fastify team members are listed at 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.

212
node_modules/@fastify/cors/README.md generated vendored Normal file
View File

@@ -0,0 +1,212 @@
# @fastify/cors
[![CI](https://github.com/fastify/fastify-cors/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/fastify/fastify-cors/actions/workflows/ci.yml)
[![NPM version](https://img.shields.io/npm/v/@fastify/cors.svg?style=flat)](https://www.npmjs.com/package/@fastify/cors)
[![neostandard javascript style](https://img.shields.io/badge/code_style-neostandard-brightgreen?style=flat)](https://github.com/neostandard/neostandard)
`@fastify/cors` enables the use of [CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) in a Fastify application.
## Install
```
npm i @fastify/cors
```
### Compatibility
| Plugin version | Fastify version |
| ---------------|-----------------|
| `^11.x` | `^5.x` |
| `^10.x` | `^5.x` |
| `^8.x` | `^4.x` |
| `^7.x` | `^3.x` |
| `>=3.x <7.x` | `^2.x` |
| `>=1.x <3.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
Require `@fastify/cors` and register it as any other plugin. It adds an `onRequest` hook and a [wildcard options route](https://github.com/fastify/fastify/issues/326#issuecomment-411360862).
```js
import Fastify from 'fastify'
import cors from '@fastify/cors'
const fastify = Fastify()
await fastify.register(cors, {
// put your options here
})
fastify.get('/', (req, reply) => {
reply.send({ hello: 'world' })
})
await fastify.listen({ port: 3000 })
```
You can use it as is without passing any option or you can configure it as explained below.
### Options
* `origin`: Configures the **Access-Control-Allow-Origin** CORS header. The value of origin can be:
- `Boolean`: Set to `true` to reflect the [request origin](http://tools.ietf.org/html/draft-abarth-origin-09), or `false` to disable CORS.
- `String`: Set to a specific origin (e.g., `"http://example.com"`). The special `*` value (default) allows any origin.
- `RegExp`: Set to a regular expression pattern to test the request origin. If it matches, the request origin is reflected (e.g., `/example\.com$/` returns the origin only if it ends with `example.com`).
- `Array`: Set to an array of valid origins, each being a `String` or `RegExp` (e.g., `["http://example1.com", /\.example2\.com$/]`).
- `Function`: Set to a function with custom logic. The function takes the request origin as the first parameter and a callback as the second (signature `err [Error | null], origin`). *Async-await* and promises are supported. The Fastify instance is bound to the function call and can be accessed via `this`. For example:
```js
origin: (origin, cb) => {
const hostname = new URL(origin).hostname
if(hostname === "localhost"){
// Request from localhost will pass
cb(null, true)
return
}
// Generate an error on other origins, disabling access
cb(new Error("Not allowed"), false)
}
```
* `methods`: Configures the **Access-Control-Allow-Methods** CORS header. Expects a comma-delimited string (e.g., 'GET,HEAD,POST') or an array (e.g., `['GET', 'HEAD', 'POST']`). Default: [CORS-safelisted methods](https://fetch.spec.whatwg.org/#methods) `GET,HEAD,POST`.
* `hook`: See [Custom Fastify hook name](#custom-fastify-hook-name). Default: `onRequest`.
* `allowedHeaders`: Configures the **Access-Control-Allow-Headers** CORS header. Expects a comma-delimited string (e.g., `'Content-Type,Authorization'`) or an array (e.g., `['Content-Type', 'Authorization']`). Defaults to reflecting the headers specified in the request's **Access-Control-Request-Headers** header if not specified.
* `exposedHeaders`: Configures the **Access-Control-Expose-Headers** CORS header. Expects a comma-delimited string (e.g., `'Content-Range,X-Content-Range'`) or an array (e.g., `['Content-Range', 'X-Content-Range']`). No custom headers are exposed if not specified.
* `credentials`: Configures the **Access-Control-Allow-Credentials** CORS header. Set to `true` to pass the header; otherwise, it is omitted.
* `maxAge`: Configures the **Access-Control-Max-Age** CORS header in seconds. Set to an integer to pass the header; otherwise, it is omitted.
* `cacheControl`: Configures the **Cache-Control** header for CORS preflight responses. Set to an integer to pass the header as `Cache-Control: max-age=${cacheControl}`, or set to a string to pass the header as `Cache-Control: ${cacheControl}`. Otherwise, the header is omitted.
* `preflightContinue`: Passes the CORS preflight response to the route handler. Default: `false`.
* `optionsSuccessStatus`: Provides a status code for successful `OPTIONS` requests, as some legacy browsers (IE11, various SmartTVs) choke on `204`.
* `preflight`: Disables preflight by passing `false`. Default: `true`.
* `strictPreflight`: Enforces strict requirements for the CORS preflight request headers (**Access-Control-Request-Method** and **Origin**) as defined by the [W3C CORS specification](https://www.w3.org/TR/2020/SPSD-cors-20200602/#resource-preflight-requests). Preflight requests without the required headers result in 400 errors when set to `true`. Default: `true`.
* `hideOptionsRoute`: Hides the options route from documentation built using [@fastify/swagger](https://github.com/fastify/fastify-swagger). Default: `true`.
* `logLevel`: Sets the Fastify log level **only** for the internal CORS pre-flight `OPTIONS *` route.
Pass `'silent'` to suppress these requests in your logs, or any valid Fastify
log level (`'trace'`, `'debug'`, `'info'`, `'warn'`, `'error'`, `'fatal'`).
Default: inherits Fastifys global log level.
#### :warning: DoS attacks
Using `RegExp` or a `function` for the `origin` parameter may enable Denial of Service attacks.
Craft with extreme care.
### Configuring CORS Asynchronously
```js
const fastify = require('fastify')()
fastify.register(require('@fastify/cors'), (instance) => {
return (req, callback) => {
const corsOptions = {
// This is NOT recommended for production as it enables reflection exploits
origin: true
};
// do not include CORS headers for requests from localhost
if (/^localhost$/m.test(req.headers.origin)) {
corsOptions.origin = false
}
// callback expects two parameters: error and options
callback(null, corsOptions)
}
})
fastify.register(async function (fastify) {
fastify.get('/', (req, reply) => {
reply.send({ hello: 'world' })
})
})
fastify.listen({ port: 3000 })
```
### Route-Level CORS Overrides
It is possible to override the CORS plugin options provided during registration on a per-route basis using the `config.cors` option.
```js
const fastify = require('fastify')()
fastify.register(require('@fastify/cors'), { origin: 'https://example.com' })
fastify.get('/cors-enabled', (_req, reply) => {
reply.send('CORS headers applied')
})
fastify.get('/cors-allow-all', {
config: {
cors: {
origin: '*', // Allow all origins for this route
},
},
}, (_req, reply) => {
reply.send('Custom CORS headers applied')
})
fastify.get('/cors-disabled', {
config: {
cors: false, // Disable CORS for this route
},
}, (_req, reply) => {
reply.send('No CORS headers')
})
fastify.listen({ port: 3000 })
```
### Custom Fastify hook name
By default, `@fastify/cors` adds an `onRequest` hook for validation and header injection. This can be customized by passing `hook` in the options. Valid values are `onRequest`, `preParsing`, `preValidation`, `preHandler`, `preSerialization`, and `onSend`.
```js
import Fastify from 'fastify'
import cors from '@fastify/cors'
const fastify = Fastify()
await fastify.register(cors, {
hook: 'preHandler',
})
fastify.get('/', (req, reply) => {
reply.send({ hello: 'world' })
})
await fastify.listen({ port: 3000 })
```
To configure CORS asynchronously, provide an object with the `delegator` key:
```js
const fastify = require('fastify')()
fastify.register(require('@fastify/cors'), {
hook: 'preHandler',
delegator: (req, callback) => {
const corsOptions = {
// This is NOT recommended for production as it enables reflection exploits
origin: true
};
// do not include CORS headers for requests from localhost
if (/^localhost$/m.test(req.headers.origin)) {
corsOptions.origin = false
}
// callback expects two parameters: error and options
callback(null, corsOptions)
},
})
fastify.register(async function (fastify) {
fastify.get('/', (req, reply) => {
reply.send({ hello: 'world' })
})
})
fastify.listen({ port: 3000 })
```
## Acknowledgments
The code is a port for Fastify of [`expressjs/cors`](https://github.com/expressjs/cors).
## License
Licensed under [MIT](./LICENSE).<br/>
[`expressjs/cors` license](https://github.com/expressjs/cors/blob/master/LICENSE)

17
node_modules/@fastify/cors/bench.js generated vendored Normal file
View File

@@ -0,0 +1,17 @@
'use strict'
const fastify = require('fastify')()
fastify.register((instance, _opts, next) => {
instance.register(require('./index'))
instance.get('/fastify', (_req, reply) => reply.send('ok'))
next()
})
fastify.register((instance, _opts, next) => {
instance.use(require('cors')())
instance.get('/express', (_req, reply) => reply.send('ok'))
next()
})
fastify.listen({ port: 3000 })

15
node_modules/@fastify/cors/benchmark/package.json generated vendored Normal file
View File

@@ -0,0 +1,15 @@
{
"name": "benchmark",
"version": "1.0.0",
"description": "",
"main": "vary.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"benchmark": "^2.1.4",
"vary": "^1.1.2"
}
}

34
node_modules/@fastify/cors/benchmark/vary.js generated vendored Normal file
View File

@@ -0,0 +1,34 @@
'use strict'
const benchmark = require('benchmark')
const vary = require('vary')
const createAddFieldnameToVary = require('../vary').createAddFieldnameToVary
const replyMock = (header) => ({
getHeader () { return header },
setHeader () { },
header () { }
})
const addAcceptToVary = createAddFieldnameToVary('Accept')
const addWildcardToVary = createAddFieldnameToVary('*')
const addAcceptEncodingToVary = createAddFieldnameToVary('Accept-Encoding')
const addXFooToVary = createAddFieldnameToVary('X-Foo')
new benchmark.Suite()
.add('vary - field to undefined', function () { vary(replyMock(undefined), 'Accept-Encoding') }, { minSamples: 100 })
.add('vary - field to *', function () { vary(replyMock('*'), 'Accept-Encoding') }, { minSamples: 100 })
.add('vary - * to field', function () { vary(replyMock('Accept-Encoding'), '*') }, { minSamples: 100 })
.add('vary - field to empty', function () { vary(replyMock(''), 'Accept-Encoding') }, { minSamples: 100 })
.add('vary - fields string to empty', function () { vary(replyMock(''), 'Accept') }, { minSamples: 100 })
.add('vary - field to fields', function () { vary(replyMock('Accept, Accept-Encoding, Accept-Language'), 'X-Foo') }, { minSamples: 100 })
.add('cors - field to undefined', function () { addAcceptEncodingToVary(replyMock(undefined)) }, { minSamples: 100 })
.add('cors - field to *', function () { addAcceptEncodingToVary(replyMock('*')) }, { minSamples: 100 })
.add('cors - * to field', function () { addWildcardToVary(replyMock('Accept-Encoding')) }, { minSamples: 100 })
.add('cors - field to empty', function () { addAcceptEncodingToVary(replyMock('')) }, { minSamples: 100 })
.add('cors - fields string to empty', function () { addAcceptToVary(replyMock('')) }, { minSamples: 100 })
.add('cors - field to fields', function () { addXFooToVary(replyMock('Accept, Accept-Encoding, Accept-Language')) }, { minSamples: 100 })
.on('cycle', function onCycle (event) { console.log(String(event.target)) })
.run({ async: false })

6
node_modules/@fastify/cors/eslint.config.js generated vendored Normal file
View File

@@ -0,0 +1,6 @@
'use strict'
module.exports = require('neostandard')({
ignores: require('neostandard').resolveIgnoresFromGitignore(),
ts: true
})

318
node_modules/@fastify/cors/index.js generated vendored Normal file
View File

@@ -0,0 +1,318 @@
'use strict'
const fp = require('fastify-plugin')
const {
addAccessControlRequestHeadersToVaryHeader,
addOriginToVaryHeader
} = require('./vary')
const defaultOptions = {
origin: '*',
methods: 'GET,HEAD,POST',
hook: 'onRequest',
preflightContinue: false,
optionsSuccessStatus: 204,
credentials: false,
exposedHeaders: null,
allowedHeaders: null,
maxAge: null,
preflight: true,
strictPreflight: true
}
const validHooks = [
'onRequest',
'preParsing',
'preValidation',
'preHandler',
'preSerialization',
'onSend'
]
const hookWithPayload = [
'preSerialization',
'preParsing',
'onSend'
]
function validateHook (value, next) {
if (validHooks.indexOf(value) !== -1) {
return
}
next(new TypeError('@fastify/cors: Invalid hook option provided.'))
}
function fastifyCors (fastify, opts, next) {
fastify.decorateRequest('corsPreflightEnabled', false)
let hideOptionsRoute = true
let logLevel
if (typeof opts === 'function') {
handleCorsOptionsDelegator(opts, fastify, { hook: defaultOptions.hook }, next)
} else if (opts.delegator) {
const { delegator, ...options } = opts
handleCorsOptionsDelegator(delegator, fastify, options, next)
} else {
const corsOptions = normalizeCorsOptions(opts)
validateHook(corsOptions.hook, next)
if (hookWithPayload.indexOf(corsOptions.hook) !== -1) {
fastify.addHook(corsOptions.hook, function handleCors (req, reply, _payload, next) {
addCorsHeadersHandler(fastify, corsOptions, req, reply, next)
})
} else {
fastify.addHook(corsOptions.hook, function handleCors (req, reply, next) {
addCorsHeadersHandler(fastify, corsOptions, req, reply, next)
})
}
}
if (opts.logLevel !== undefined) logLevel = opts.logLevel
if (opts.hideOptionsRoute !== undefined) hideOptionsRoute = opts.hideOptionsRoute
// The preflight reply must occur in the hook. This allows fastify-cors to reply to
// preflight requests BEFORE possible authentication plugins. If the preflight reply
// occurred in this handler, other plugins may deny the request since the browser will
// remove most headers (such as the Authentication header).
//
// This route simply enables fastify to accept preflight requests.
fastify.options('*', { schema: { hide: hideOptionsRoute }, logLevel }, (req, reply) => {
if (!req.corsPreflightEnabled) {
// Do not handle preflight requests if the origin option disabled CORS
reply.callNotFound()
return
}
reply.send()
})
next()
}
function handleCorsOptionsDelegator (optionsResolver, fastify, opts, next) {
const hook = opts?.hook || defaultOptions.hook
validateHook(hook, next)
if (optionsResolver.length === 2) {
if (hookWithPayload.indexOf(hook) !== -1) {
fastify.addHook(hook, function handleCors (req, reply, _payload, next) {
handleCorsOptionsCallbackDelegator(optionsResolver, fastify, req, reply, next)
})
} else {
fastify.addHook(hook, function handleCors (req, reply, next) {
handleCorsOptionsCallbackDelegator(optionsResolver, fastify, req, reply, next)
})
}
} else {
if (hookWithPayload.indexOf(hook) !== -1) {
// handle delegator based on Promise
fastify.addHook(hook, function handleCors (req, reply, _payload, next) {
const ret = optionsResolver(req)
if (ret && typeof ret.then === 'function') {
ret.then(options => addCorsHeadersHandler(fastify, normalizeCorsOptions(options, true), req, reply, next)).catch(next)
return
}
next(new Error('Invalid CORS origin option'))
})
} else {
// handle delegator based on Promise
fastify.addHook(hook, function handleCors (req, reply, next) {
const ret = optionsResolver(req)
if (ret && typeof ret.then === 'function') {
ret.then(options => addCorsHeadersHandler(fastify, normalizeCorsOptions(options, true), req, reply, next)).catch(next)
return
}
next(new Error('Invalid CORS origin option'))
})
}
}
}
function handleCorsOptionsCallbackDelegator (optionsResolver, fastify, req, reply, next) {
optionsResolver(req, (err, options) => {
if (err) {
next(err)
} else {
addCorsHeadersHandler(fastify, normalizeCorsOptions(options, true), req, reply, next)
}
})
}
/**
* @param {import('./types').FastifyCorsOptions} opts
*/
function normalizeCorsOptions (opts, dynamic) {
const corsOptions = { ...defaultOptions, ...opts }
if (Array.isArray(opts.origin) && opts.origin.indexOf('*') !== -1) {
corsOptions.origin = '*'
}
if (Number.isInteger(corsOptions.cacheControl)) {
// integer numbers are formatted this way
corsOptions.cacheControl = `max-age=${corsOptions.cacheControl}`
} else if (typeof corsOptions.cacheControl !== 'string') {
// strings are applied directly and any other value is ignored
corsOptions.cacheControl = null
}
corsOptions.dynamic = dynamic || false
return corsOptions
}
function addCorsHeadersHandler (fastify, globalOptions, req, reply, next) {
const options = { ...globalOptions, ...req.routeOptions.config?.cors }
if ((typeof options.origin !== 'string' && options.origin !== false) || options.dynamic) {
// Always set Vary header for non-static origin option
// https://fetch.spec.whatwg.org/#cors-protocol-and-http-caches
addOriginToVaryHeader(reply)
}
const resolveOriginOption = typeof options.origin === 'function' ? resolveOriginWrapper(fastify, options.origin) : (_, cb) => cb(null, options.origin)
resolveOriginOption(req, (error, resolvedOriginOption) => {
if (error !== null) {
return next(error)
}
// Disable CORS and preflight if false
if (resolvedOriginOption === false) {
return next()
}
// Allow routes to disable CORS individually
if (req.routeOptions.config?.cors === false) {
return next()
}
// Falsy values are invalid
if (!resolvedOriginOption) {
return next(new Error('Invalid CORS origin option'))
}
addCorsHeaders(req, reply, resolvedOriginOption, options)
if (req.raw.method === 'OPTIONS' && options.preflight === true) {
// Strict mode enforces the required headers for preflight
if (options.strictPreflight === true && (!req.headers.origin || !req.headers['access-control-request-method'])) {
reply.status(400).type('text/plain').send('Invalid Preflight Request')
return
}
req.corsPreflightEnabled = true
addPreflightHeaders(req, reply, options)
if (!options.preflightContinue) {
// Do not call the hook callback and terminate the request
// Safari (and potentially other browsers) need content-length 0,
// for 204 or they just hang waiting for a body
reply
.code(options.optionsSuccessStatus)
.header('Content-Length', '0')
.send()
return
}
}
return next()
})
}
function addCorsHeaders (req, reply, originOption, corsOptions) {
const origin = getAccessControlAllowOriginHeader(req.headers.origin, originOption)
// In the case of origin not allowed the header is not
// written in the response.
// https://github.com/fastify/fastify-cors/issues/127
if (origin) {
reply.header('Access-Control-Allow-Origin', origin)
}
if (corsOptions.credentials) {
reply.header('Access-Control-Allow-Credentials', 'true')
}
if (corsOptions.exposedHeaders !== null) {
reply.header(
'Access-Control-Expose-Headers',
Array.isArray(corsOptions.exposedHeaders) ? corsOptions.exposedHeaders.join(', ') : corsOptions.exposedHeaders
)
}
}
function addPreflightHeaders (req, reply, corsOptions) {
reply.header(
'Access-Control-Allow-Methods',
Array.isArray(corsOptions.methods) ? corsOptions.methods.join(', ') : corsOptions.methods
)
if (corsOptions.allowedHeaders === null) {
addAccessControlRequestHeadersToVaryHeader(reply)
const reqAllowedHeaders = req.headers['access-control-request-headers']
if (reqAllowedHeaders !== undefined) {
reply.header('Access-Control-Allow-Headers', reqAllowedHeaders)
}
} else {
reply.header(
'Access-Control-Allow-Headers',
Array.isArray(corsOptions.allowedHeaders) ? corsOptions.allowedHeaders.join(', ') : corsOptions.allowedHeaders
)
}
if (corsOptions.maxAge !== null) {
reply.header('Access-Control-Max-Age', String(corsOptions.maxAge))
}
if (corsOptions.cacheControl) {
reply.header('Cache-Control', corsOptions.cacheControl)
}
}
function resolveOriginWrapper (fastify, origin) {
return function (req, cb) {
const result = origin.call(fastify, req.headers.origin, cb)
// Allow for promises
if (result && typeof result.then === 'function') {
result.then(res => cb(null, res), cb)
}
}
}
function getAccessControlAllowOriginHeader (reqOrigin, originOption) {
if (typeof originOption === 'string') {
// fixed or any origin ('*')
return originOption
}
// reflect origin
return isRequestOriginAllowed(reqOrigin, originOption) ? reqOrigin : false
}
function isRequestOriginAllowed (reqOrigin, allowedOrigin) {
if (Array.isArray(allowedOrigin)) {
for (let i = 0; i < allowedOrigin.length; ++i) {
if (isRequestOriginAllowed(reqOrigin, allowedOrigin[i])) {
return true
}
}
return false
} else if (typeof allowedOrigin === 'string') {
return reqOrigin === allowedOrigin
} else if (allowedOrigin instanceof RegExp) {
allowedOrigin.lastIndex = 0
return allowedOrigin.test(reqOrigin)
} else {
return !!allowedOrigin
}
}
const _fastifyCors = fp(fastifyCors, {
fastify: '5.x',
name: '@fastify/cors'
})
/**
* These export configurations enable JS and TS developers
* to consumer fastify in whatever way best suits their needs.
*/
module.exports = _fastifyCors
module.exports.fastifyCors = _fastifyCors
module.exports.default = _fastifyCors

View File

@@ -0,0 +1,2 @@
# Set default behavior to automatically convert line endings
* text=auto eol=lf

View File

@@ -0,0 +1,13 @@
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

View File

@@ -0,0 +1,21 @@
# Number of days of inactivity before an issue becomes stale
daysUntilStale: 15
# Number of days of inactivity before a stale issue is closed
daysUntilClose: 7
# Issues with these labels will never be considered stale
exemptLabels:
- "discussion"
- "feature request"
- "bug"
- "help wanted"
- "plugin suggestion"
- "good first issue"
# Label to use when marking an issue as stale
staleLabel: stale
# Comment to post when marking an issue as stale. Set to `false` to disable
markComment: >
This issue has been automatically marked as stale because it has not had
recent activity. It will be closed if no further activity occurs. Thank you
for your contributions.
# Comment to post when closing a stale issue. Set to `false` to disable
closeComment: false

View File

@@ -0,0 +1,28 @@
name: CI
on:
push:
branches:
- main
- next
- 'v*'
paths-ignore:
- 'docs/**'
- '*.md'
pull_request:
paths-ignore:
- 'docs/**'
- '*.md'
permissions:
contents: read
jobs:
test:
permissions:
contents: write
pull-requests: write
uses: fastify/workflows/.github/workflows/plugins-ci.yml@v5
with:
license-check: true
lint: true

View File

@@ -0,0 +1,23 @@
MIT License
Copyright (c) 2017-present The Fastify team
The Fastify team members are listed at 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.

View File

@@ -0,0 +1,188 @@
# fastify-plugin
[![CI](https://github.com/fastify/fastify-plugin/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/fastify/fastify-plugin/actions/workflows/ci.yml)
[![NPM version](https://img.shields.io/npm/v/fastify-plugin.svg?style=flat)](https://www.npmjs.com/package/fastify-plugin)
[![neostandard javascript style](https://img.shields.io/badge/code_style-neostandard-brightgreen?style=flat)](https://github.com/neostandard/neostandard)
`fastify-plugin` is a plugin helper for [Fastify](https://github.com/fastify/fastify).
When you build plugins for Fastify and you want them to be accessible in the same context where you require them, you have two ways:
1. Use the `skip-override` hidden property
2. Use this module
__Note: the v4.x series of this module covers Fastify v4__
__Note: the v2.x & v3.x series of this module covers Fastify v3. For Fastify v2 support, refer to the v1.x series.__
## Install
```sh
npm i fastify-plugin
```
## Usage
`fastify-plugin` can do three things for you:
- Add the `skip-override` hidden property
- Check the bare-minimum version of Fastify
- Pass some custom metadata of the plugin to Fastify
Example using a callback:
```js
const fp = require('fastify-plugin')
module.exports = fp(function (fastify, opts, done) {
// your plugin code
done()
})
```
Example using an [async](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) function:
```js
const fp = require('fastify-plugin')
// A callback function param is not required for async functions
module.exports = fp(async function (fastify, opts) {
// Wait for an async function to fulfill promise before proceeding
await exampleAsyncFunction()
})
```
## Metadata
In addition, if you use this module when creating new plugins, you can declare the dependencies, the name, and the expected Fastify version that your plugin needs.
#### Fastify version
If you need to set a bare-minimum version of Fastify for your plugin, just add the [semver](https://semver.org/) range that you need:
```js
const fp = require('fastify-plugin')
module.exports = fp(function (fastify, opts, done) {
// your plugin code
done()
}, { fastify: '5.x' })
```
If you need to check the Fastify version only, you can pass just the version string.
You can check [here](https://github.com/npm/node-semver#ranges) how to define a `semver` range.
#### Name
Fastify uses this option to validate the dependency graph, allowing it to ensure that no name collisions occur and making it possible to perform [dependency checks](https://github.com/fastify/fastify-plugin#dependencies).
```js
const fp = require('fastify-plugin')
function plugin (fastify, opts, done) {
// your plugin code
done()
}
module.exports = fp(plugin, {
fastify: '5.x',
name: 'your-plugin-name'
})
```
#### Dependencies
You can also check if the `plugins` and `decorators` that your plugin intend to use are present in the dependency graph.
> *Note:* This is the point where registering `name` of the plugins become important, because you can reference `plugin` dependencies by their [name](https://github.com/fastify/fastify-plugin#name).
```js
const fp = require('fastify-plugin')
function plugin (fastify, opts, done) {
// your plugin code
done()
}
module.exports = fp(plugin, {
fastify: '5.x',
decorators: {
fastify: ['plugin1', 'plugin2'],
reply: ['compress']
},
dependencies: ['plugin1-name', 'plugin2-name']
})
```
#### Encapsulate
By default, `fastify-plugin` breaks the [encapsulation](https://github.com/fastify/fastify/blob/HEAD/docs/Reference/Encapsulation.md) but you can optionally keep the plugin encapsulated.
This allows you to set the plugin's name and validate its dependencies without making the plugin accessible.
```js
const fp = require('fastify-plugin')
function plugin (fastify, opts, done) {
// the decorator is not accessible outside this plugin
fastify.decorate('util', function() {})
done()
}
module.exports = fp(plugin, {
name: 'my-encapsulated-plugin',
fastify: '5.x',
decorators: {
fastify: ['plugin1', 'plugin2'],
reply: ['compress']
},
dependencies: ['plugin1-name', 'plugin2-name'],
encapsulate: true
})
```
#### Bundlers and Typescript
`fastify-plugin` adds a `.default` and `[name]` property to the passed in function.
The type definition would have to be updated to leverage this.
## Known Issue: TypeScript Contextual Inference
[Documentation Reference](https://www.typescriptlang.org/docs/handbook/functions.html#inferring-the-types)
It is common for developers to inline their plugin with fastify-plugin such as:
```js
fp((fastify, opts, done) => { done() })
fp(async (fastify, opts) => { return })
```
TypeScript can sometimes infer the types of the arguments for these functions. Plugins in Fastify are recommended to be typed using either `FastifyPluginCallback` or `FastifyPluginAsync`. These two definitions only differ in two ways:
1. The third argument `done` (the callback part)
2. The return type `FastifyPluginCallback` or `FastifyPluginAsync`
At this time, TypeScript inference is not smart enough to differentiate by definition argument length alone.
Thus, if you are a TypeScript developer please use on the following patterns instead:
```ts
// Callback
// Assign type directly
const pluginCallback: FastifyPluginCallback = (fastify, options, done) => { }
fp(pluginCallback)
// or define your own function declaration that satisfies the existing definitions
const pluginCallbackWithTypes = (fastify: FastifyInstance, options: FastifyPluginOptions, done: (error?: FastifyError) => void): void => { }
fp(pluginCallbackWithTypes)
// or inline
fp((fastify: FastifyInstance, options: FastifyPluginOptions, done: (error?: FastifyError) => void): void => { })
// Async
// Assign type directly
const pluginAsync: FastifyPluginAsync = async (fastify, options) => { }
fp(pluginAsync)
// or define your own function declaration that satisfies the existing definitions
const pluginAsyncWithTypes = async (fastify: FastifyInstance, options: FastifyPluginOptions): Promise<void> => { }
fp(pluginAsyncWithTypes)
// or inline
fp(async (fastify: FastifyInstance, options: FastifyPluginOptions): Promise<void> => { })
```
## Acknowledgments
This project is kindly sponsored by:
- [nearForm](https://nearform.com)
- [LetzDoIt](https://www.letzdoitapp.com/)
## License
Licensed under [MIT](./LICENSE).

View File

@@ -0,0 +1,6 @@
'use strict'
module.exports = require('neostandard')({
ignores: require('neostandard').resolveIgnoresFromGitignore(),
ts: true
})

View File

@@ -0,0 +1,25 @@
'use strict'
const fpStackTracePattern = /at\s(?:.*\.)?plugin\s.*\n\s*(.*)/
const fileNamePattern = /(\w*(\.\w*)*)\..*/
module.exports = function getPluginName (fn) {
if (fn.name.length > 0) return fn.name
const stackTraceLimit = Error.stackTraceLimit
Error.stackTraceLimit = 10
try {
throw new Error('anonymous function')
} catch (e) {
Error.stackTraceLimit = stackTraceLimit
return extractPluginName(e.stack)
}
}
function extractPluginName (stack) {
const m = stack.match(fpStackTracePattern)
// get last section of path and match for filename
return m ? m[1].split(/[/\\]/).slice(-1)[0].match(fileNamePattern)[1] : 'anonymous'
}
module.exports.extractPluginName = extractPluginName

View File

@@ -0,0 +1,10 @@
'use strict'
module.exports = function toCamelCase (name) {
if (name[0] === '@') {
name = name.slice(1).replace('/', '-')
}
return name.replace(/-(.)/g, function (match, g1) {
return g1.toUpperCase()
})
}

View File

@@ -0,0 +1,70 @@
{
"name": "fastify-plugin",
"version": "5.1.0",
"description": "Plugin helper for Fastify",
"main": "plugin.js",
"type": "commonjs",
"types": "types/plugin.d.ts",
"scripts": {
"lint": "eslint",
"lint:fix": "eslint --fix",
"test": "npm run test:unit && npm run test:typescript",
"test:unit": "c8 --100 node --test",
"test:coverage": "c8 node --test && c8 report --reporter=html",
"test:typescript": "tsd"
},
"repository": {
"type": "git",
"url": "git+https://github.com/fastify/fastify-plugin.git"
},
"keywords": [
"plugin",
"helper",
"fastify"
],
"author": "Tomas Della Vedova - @delvedor (http://delved.org)",
"contributors": [
{
"name": "Matteo Collina",
"email": "hello@matteocollina.com"
},
{
"name": "Manuel Spigolon",
"email": "behemoth89@gmail.com"
},
{
"name": "Aras Abbasi",
"email": "aras.abbasi@gmail.com"
},
{
"name": "Frazer Smith",
"email": "frazer.dev@icloud.com",
"url": "https://github.com/fdawgs"
}
],
"license": "MIT",
"bugs": {
"url": "https://github.com/fastify/fastify-plugin/issues"
},
"homepage": "https://github.com/fastify/fastify-plugin#readme",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fastify"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fastify"
}
],
"devDependencies": {
"@fastify/type-provider-typebox": "^5.1.0",
"@types/node": "^24.0.8",
"c8": "^10.1.2",
"eslint": "^9.17.0",
"fastify": "^5.0.0",
"neostandard": "^0.12.0",
"proxyquire": "^2.1.3",
"tsd": "^0.33.0"
}
}

View File

@@ -0,0 +1,67 @@
'use strict'
const getPluginName = require('./lib/getPluginName')
const toCamelCase = require('./lib/toCamelCase')
let count = 0
function plugin (fn, options = {}) {
let autoName = false
if (fn.default !== undefined) {
// Support for 'export default' behaviour in transpiled ECMAScript module
fn = fn.default
}
if (typeof fn !== 'function') {
throw new TypeError(
`fastify-plugin expects a function, instead got a '${typeof fn}'`
)
}
if (typeof options === 'string') {
options = {
fastify: options
}
}
if (
typeof options !== 'object' ||
Array.isArray(options) ||
options === null
) {
throw new TypeError('The options object should be an object')
}
if (options.fastify !== undefined && typeof options.fastify !== 'string') {
throw new TypeError(`fastify-plugin expects a version string, instead got '${typeof options.fastify}'`)
}
if (!options.name) {
autoName = true
options.name = getPluginName(fn) + '-auto-' + count++
}
fn[Symbol.for('skip-override')] = options.encapsulate !== true
fn[Symbol.for('fastify.display-name')] = options.name
fn[Symbol.for('plugin-meta')] = options
// Faux modules support
if (!fn.default) {
fn.default = fn
}
// TypeScript support for named imports
// See https://github.com/fastify/fastify/issues/2404 for more details
// The type definitions would have to be update to match this.
const camelCase = toCamelCase(options.name)
if (!autoName && !fn[camelCase]) {
fn[camelCase] = fn
}
return fn
}
module.exports = plugin
module.exports.default = plugin
module.exports.fastifyPlugin = plugin

View File

@@ -0,0 +1,110 @@
'use strict'
const { test } = require('node:test')
const fp = require('../plugin')
test('webpack removes require.main.filename', t => {
const filename = require.main.filename
const info = console.info
t.after(() => {
require.main.filename = filename
console.info = info
})
require.main.filename = null
console.info = function (msg) {
t.assert.fail('logged: ' + msg)
}
fp((_fastify, _opts, next) => {
next()
}, {
fastify: '^5.0.0'
})
})
test('support faux modules', (t) => {
const plugin = fp((_fastify, _opts, next) => {
next()
})
t.assert.strictEqual(plugin.default, plugin)
})
test('support faux modules does not override existing default field in babel module', (t) => {
const module = {
default: (_fastify, _opts, next) => next()
}
module.default.default = 'Existing default field'
const plugin = fp(module)
t.assert.strictEqual(plugin.default, 'Existing default field')
})
test('support ts named imports', (t) => {
const plugin = fp((_fastify, _opts, next) => {
next()
}, {
name: 'hello'
})
t.assert.strictEqual(plugin.hello, plugin)
})
test('from kebab-case to camelCase', (t) => {
const plugin = fp((_fastify, _opts, next) => {
next()
}, {
name: 'hello-world'
})
t.assert.strictEqual(plugin.helloWorld, plugin)
})
test('from @-prefixed named imports', (t) => {
const plugin = fp((_fastify, _opts, next) => {
next()
}, {
name: '@hello/world'
})
t.assert.strictEqual(plugin.helloWorld, plugin)
})
test('from @-prefixed named kebab-case to camelCase', (t) => {
const plugin = fp((_fastify, _opts, next) => {
next()
}, {
name: '@hello/my-world'
})
t.assert.strictEqual(plugin.helloMyWorld, plugin)
})
test('from kebab-case to camelCase multiple words', (t) => {
const plugin = fp((_fastify, _opts, next) => {
next()
}, {
name: 'hello-long-world'
})
t.assert.strictEqual(plugin.helloLongWorld, plugin)
})
test('from kebab-case to camelCase multiple words does not override', (t) => {
const fn = (_fastify, _opts, next) => {
next()
}
const foobar = {}
fn.helloLongWorld = foobar
const plugin = fp(fn, {
name: 'hello-long-world'
})
t.assert.strictEqual(plugin.helloLongWorld, foobar)
})

View File

@@ -0,0 +1,67 @@
'use strict'
const { test } = require('node:test')
const fp = require('../plugin')
test('checkVersion having require.main.filename', (t) => {
const info = console.info
t.assert.ok(require.main.filename)
t.after(() => {
console.info = info
})
console.info = function (msg) {
t.assert.fail('logged: ' + msg)
}
fp((_fastify, _opts, next) => {
next()
}, {
fastify: '^5.0.0'
})
})
test('checkVersion having no require.main.filename but process.argv[1]', (t) => {
const filename = require.main.filename
const info = console.info
t.after(() => {
require.main.filename = filename
console.info = info
})
require.main.filename = null
console.info = function (msg) {
t.assert.fail('logged: ' + msg)
}
fp((_fastify, _opts, next) => {
next()
}, {
fastify: '^5.0.0'
})
})
test('checkVersion having no require.main.filename and no process.argv[1]', (t) => {
const filename = require.main.filename
const argv = process.argv
const info = console.info
t.after(() => {
require.main.filename = filename
process.argv = argv
console.info = info
})
require.main.filename = null
process.argv[1] = null
console.info = function (msg) {
t.assert.fail('logged: ' + msg)
}
fp((_fastify, _opts, next) => {
next()
}, {
fastify: '^5.0.0'
})
})

View File

@@ -0,0 +1,14 @@
'use strict'
const { test } = require('node:test')
const fp = require('../plugin')
test('anonymous function should be named composite.test0', (t) => {
t.plan(2)
const fn = fp((_fastify, _opts, next) => {
next()
})
t.assert.strictEqual(fn[Symbol.for('plugin-meta')].name, 'composite.test-auto-0')
t.assert.strictEqual(fn[Symbol.for('fastify.display-name')], 'composite.test-auto-0')
})

View File

@@ -0,0 +1,11 @@
import { test } from 'node:test'
import fp from '../../plugin.js'
test('esm base support', (t) => {
fp((_fastify, _opts, next) => {
next()
}, {
fastify: '^5.0.0'
})
t.assert.ok(true, 'fp function called without throwing an error')
})

View File

@@ -0,0 +1,11 @@
'use strict'
// Node v8 throw a `SyntaxError: Unexpected token import`
// even if this branch is never touch in the code,
// by using `eval` we can avoid this issue.
// eslint-disable-next-line
new Function('module', 'return import(module)')('./esm.mjs').catch((err) => {
process.nextTick(() => {
throw err
})
})

View File

@@ -0,0 +1,49 @@
'use strict'
const { test } = require('node:test')
const extractPluginName = require('../lib/getPluginName').extractPluginName
const winStack = `Error: anonymous function
at checkName (C:\\Users\\leonardo.davinci\\Desktop\\fastify-plugin\\index.js:43:11)
at plugin (C:\\Users\\leonardo.davinci\\Desktop\\fastify-plugin\\index.js:24:20)
at Test.test (C:\\Users\\leonardo.davinci\\Desktop\\fastify-plugin\\test\\hello.test.js:9:14)
at bound (domain.js:396:14)
at Test.runBound (domain.js:409:12)
at ret (C:\\Users\\leonardo.davinci\\Desktop\\fastify-plugin\\node_modules\\tap\\lib\\test.js:278:21)
at Test.main (C:\\Users\\leonardo.davinci\\Desktop\\fastify-plugin\\node_modules\\tap\\lib\\test.js:282:7)
at writeSubComment (C:\\Users\\leonardo.davinci\\Desktop\\fastify-plugin\\node_modules\\tap\\lib\\test.js:371:13)
at TAP.writeSubComment (C:\\Users\\leonardo.davinci\\Desktop\\fastify-plugin\\node_modules\\tap\\lib\\test.js:403:5)
at Test.runBeforeEach (C:\\Users\\leonardo.davinci\\Desktop\\fastify-plugin\\node_modules\\tap\\lib\\test.js:370:14)
at loop (C:\\Users\\leonardo.davinci\\Desktop\\fastify-plugin\\node_modules\\function-loop\\index.js:35:15)
at TAP.runBeforeEach (C:\\Users\\leonardo.davinci\\Desktop\\fastify-plugin\\node_modules\\tap\\lib\\test.js:683:7)
at TAP.processSubtest (C:\\Users\\leonardo.davinci\\Desktop\\fastify-plugin\\node_modules\\tap\\lib\\test.js:369:12)
at TAP.process (C:\\Users\\leonardo.davinci\\Desktop\\fastify-plugin\\node_modules\\tap\\lib\\test.js:306:14)
at TAP.sub (C:\\Users\\leonardo.davinci\\Desktop\\fastify-plugin\\node_modules\\tap\\lib\\test.js:185:10)
at TAP.test (C:\\Users\\leonardo.davinci\\Desktop\\fastify-plugin\\node_modules\\tap\\lib\\test.js:209:17)`
const nixStack = `Error: anonymous function
at checkName (/home/leonardo/desktop/fastify-plugin/index.js:43:11)
at plugin (/home/leonardo/desktop/fastify-plugin/index.js:24:20)
at Test.test (/home/leonardo/desktop/fastify-plugin/test/this.is.a.test.js:9:14)
at bound (domain.js:396:14)
at Test.runBound (domain.js:409:12)
at ret (/home/leonardo/desktop/fastify-plugin/node_modules/tap/lib/test.js:278:21)
at Test.main (/home/leonardo/desktop/fastify-plugin/node_modules/tap/lib/test.js:282:7)
at writeSubComment (/home/leonardo/desktop/fastify-plugin/node_modules/tap/lib/test.js:371:13)
at TAP.writeSubComment (/home/leonardo/desktop/fastify-plugin/node_modules/tap/lib/test.js:403:5)
at Test.runBeforeEach (/home/leonardo/desktop/fastify-plugin/node_modules/tap/lib/test.js:370:14)
at loop (/home/leonardo/desktop/fastify-plugin/node_modules/function-loop/index.js:35:15)
at TAP.runBeforeEach (/home/leonardo/desktop/fastify-plugin/node_modules/tap/lib/test.js:683:7)
at TAP.processSubtest (/home/leonardo/desktop/fastify-plugin/node_modules/tap/lib/test.js:369:12)
at TAP.process (/home/leonardo/desktop/fastify-plugin/node_modules/tap/lib/test.js:306:14)
at TAP.sub (/home/leonardo/desktop/fastify-plugin/node_modules/tap/lib/test.js:185:10)
at TAP.test (/home/leonardo/desktop/fastify-plugin/node_modules/tap/lib/test.js:209:17)`
const anonymousStack = 'Unable to parse this'
test('extractPluginName tests', (t) => {
t.plan(3)
t.assert.strictEqual(extractPluginName(winStack), 'hello.test')
t.assert.strictEqual(extractPluginName(nixStack), 'this.is.a.test')
t.assert.strictEqual(extractPluginName(anonymousStack), 'anonymous')
})

View File

@@ -0,0 +1,15 @@
'use strict'
const { test } = require('node:test')
const fp = require('../plugin')
test('anonymous function should be named mu1tip1e.composite.test', (t) => {
t.plan(2)
const fn = fp((_fastify, _opts, next) => {
next()
})
t.assert.strictEqual(fn[Symbol.for('plugin-meta')].name, 'mu1tip1e.composite.test-auto-0')
t.assert.strictEqual(fn[Symbol.for('fastify.display-name')], 'mu1tip1e.composite.test-auto-0')
})

View File

@@ -0,0 +1,396 @@
'use strict'
const { test } = require('node:test')
const proxyquire = require('proxyquire')
const fp = require('../plugin')
const Fastify = require('fastify')
const pkg = require('../package.json')
test('fastify-plugin is a function', (t) => {
t.plan(1)
t.assert.ok(typeof fp === 'function')
})
test('should return the function with the skip-override Symbol', (t) => {
t.plan(1)
function plugin (_fastify, _opts, next) {
next()
}
fp(plugin)
t.assert.ok(plugin[Symbol.for('skip-override')])
})
test('should support "default" function from babel module', (t) => {
t.plan(1)
const plugin = {
default: () => { }
}
try {
fp(plugin)
t.assert.ok(true)
} catch (e) {
t.assert.strictEqual(e.message, 'fastify-plugin expects a function, instead got a \'object\'')
}
})
test('should throw if the plugin is not a function', (t) => {
t.plan(1)
try {
fp('plugin')
t.assert.fail()
} catch (e) {
t.assert.strictEqual(e.message, 'fastify-plugin expects a function, instead got a \'string\'')
}
})
test('should check the fastify version', (t) => {
t.plan(1)
function plugin (_fastify, _opts, next) {
next()
}
try {
fp(plugin, { fastify: '>=0.10.0' })
t.assert.ok(true)
} catch {
t.assert.fail()
}
})
test('should check the fastify version', (t) => {
t.plan(1)
function plugin (_fastify, _opts, next) {
next()
}
try {
fp(plugin, '>=0.10.0')
t.assert.ok(true)
} catch {
t.assert.fail()
}
})
test('the options object should be an object', (t) => {
t.plan(2)
try {
fp(() => { }, null)
t.assert.fail()
} catch (e) {
t.assert.strictEqual(e.message, 'The options object should be an object')
}
try {
fp(() => { }, [])
t.assert.fail()
} catch (e) {
t.assert.strictEqual(e.message, 'The options object should be an object')
}
})
test('should throw if the version number is not a string', (t) => {
t.plan(1)
try {
fp(() => { }, { fastify: 12 })
t.assert.fail()
} catch (e) {
t.assert.strictEqual(e.message, 'fastify-plugin expects a version string, instead got \'number\'')
}
})
test('Should accept an option object', (t) => {
t.plan(2)
const opts = { hello: 'world' }
function plugin (_fastify, _opts, next) {
next()
}
fp(plugin, opts)
t.assert.ok(plugin[Symbol.for('skip-override')], 'skip-override symbol should be present')
t.assert.deepStrictEqual(plugin[Symbol.for('plugin-meta')], opts, 'plugin-meta should match opts')
})
test('Should accept an option object and checks the version', (t) => {
t.plan(2)
const opts = { hello: 'world', fastify: '>=0.10.0' }
function plugin (_fastify, _opts, next) {
next()
}
fp(plugin, opts)
t.assert.ok(plugin[Symbol.for('skip-override')])
t.assert.deepStrictEqual(plugin[Symbol.for('plugin-meta')], opts)
})
test('should set anonymous function name to file it was called from with a counter', (t) => {
const fp = proxyquire('../plugin.js', { stubs: {} })
const fn = fp((_fastify, _opts, next) => {
next()
})
t.assert.strictEqual(fn[Symbol.for('plugin-meta')].name, 'test-auto-0')
t.assert.strictEqual(fn[Symbol.for('fastify.display-name')], 'test-auto-0')
const fn2 = fp((_fastify, _opts, next) => {
next()
})
t.assert.strictEqual(fn2[Symbol.for('plugin-meta')].name, 'test-auto-1')
t.assert.strictEqual(fn2[Symbol.for('fastify.display-name')], 'test-auto-1')
})
test('should set function name if Error.stackTraceLimit is set to 0', (t) => {
const stackTraceLimit = Error.stackTraceLimit = 0
const fp = proxyquire('../plugin.js', { stubs: {} })
const fn = fp((_fastify, _opts, next) => {
next()
})
t.assert.strictEqual(fn[Symbol.for('plugin-meta')].name, 'test-auto-0')
t.assert.strictEqual(fn[Symbol.for('fastify.display-name')], 'test-auto-0')
const fn2 = fp((_fastify, _opts, next) => {
next()
})
t.assert.strictEqual(fn2[Symbol.for('plugin-meta')].name, 'test-auto-1')
t.assert.strictEqual(fn2[Symbol.for('fastify.display-name')], 'test-auto-1')
Error.stackTraceLimit = stackTraceLimit
})
test('should set display-name to meta name', (t) => {
t.plan(2)
const functionName = 'superDuperSpecialFunction'
const fn = fp((_fastify, _opts, next) => next(), {
name: functionName
})
t.assert.strictEqual(fn[Symbol.for('plugin-meta')].name, functionName)
t.assert.strictEqual(fn[Symbol.for('fastify.display-name')], functionName)
})
test('should preserve fastify version in meta', (t) => {
t.plan(1)
const opts = { hello: 'world', fastify: '>=0.10.0' }
const fn = fp((_fastify, _opts, next) => next(), opts)
t.assert.strictEqual(fn[Symbol.for('plugin-meta')].fastify, '>=0.10.0')
})
test('should check fastify dependency graph - plugin', async (t) => {
t.plan(1)
const fastify = Fastify()
fastify.register(fp((_fastify, _opts, next) => next(), {
fastify: '5.x',
name: 'plugin1-name'
}))
fastify.register(fp((_fastify, _opts, next) => next(), {
fastify: '5.x',
name: 'test',
dependencies: ['plugin1-name', 'plugin2-name']
}))
await t.assert.rejects(fastify.ready(), { message: "The dependency 'plugin2-name' of plugin 'test' is not registered" })
})
test('should check fastify dependency graph - decorate', async (t) => {
t.plan(1)
const fastify = Fastify()
fastify.decorate('plugin1', fp((_fastify, _opts, next) => next(), {
fastify: '5.x',
name: 'plugin1-name'
}))
fastify.register(fp((_fastify, _opts, next) => next(), {
fastify: '5.x',
name: 'test',
decorators: { fastify: ['plugin1', 'plugin2'] }
}))
await t.assert.rejects(fastify.ready(), { message: "The decorator 'plugin2' required by 'test' is not present in Fastify" })
})
test('should check fastify dependency graph - decorateReply', async (t) => {
t.plan(1)
const fastify = Fastify()
fastify.decorateReply('plugin1', fp((_fastify, _opts, next) => next(), {
fastify: '5.x',
name: 'plugin1-name'
}))
fastify.register(fp((_fastify, _opts, next) => next(), {
fastify: '5.x',
name: 'test',
decorators: { reply: ['plugin1', 'plugin2'] }
}))
await t.assert.rejects(fastify.ready(), { message: "The decorator 'plugin2' required by 'test' is not present in Reply" })
})
test('should accept an option to encapsulate', async (t) => {
t.plan(3)
const fastify = Fastify()
fastify.register(fp((fastify, _opts, next) => {
fastify.decorate('accessible', true)
next()
}, {
name: 'accessible-plugin'
}))
fastify.register(fp((fastify, _opts, next) => {
fastify.decorate('alsoAccessible', true)
next()
}, {
name: 'accessible-plugin2',
encapsulate: false
}))
fastify.register(fp((fastify, _opts, next) => {
fastify.decorate('encapsulated', true)
next()
}, {
name: 'encapsulated-plugin',
encapsulate: true
}))
await fastify.ready()
t.assert.ok(fastify.hasDecorator('accessible'))
t.assert.ok(fastify.hasDecorator('alsoAccessible'))
t.assert.ok(!fastify.hasDecorator('encapsulated'))
})
test('should check dependencies when encapsulated', async (t) => {
t.plan(1)
const fastify = Fastify()
fastify.register(fp((_fastify, _opts, next) => next(), {
name: 'test',
dependencies: ['missing-dependency-name'],
encapsulate: true
}))
await t.assert.rejects(fastify.ready(), { message: "The dependency 'missing-dependency-name' of plugin 'test' is not registered" })
})
test(
'should check version when encapsulated',
{ skip: /\d-.+/.test(pkg.devDependencies.fastify) },
async (t) => {
t.plan(1)
const fastify = Fastify()
fastify.register(fp((_fastify, _opts, next) => next(), {
name: 'test',
fastify: '<=2.10.0',
encapsulate: true
}))
await t.assert.rejects(fastify.ready(), { message: /fastify-plugin: test - expected '<=2.10.0' fastify version, '\d.\d+.\d+' is installed/ })
}
)
test('should check decorators when encapsulated', async (t) => {
t.plan(1)
const fastify = Fastify()
fastify.decorate('plugin1', 'foo')
fastify.register(fp((_fastify, _opts, next) => next(), {
fastify: '5.x',
name: 'test',
encapsulate: true,
decorators: { fastify: ['plugin1', 'plugin2'] }
}))
await t.assert.rejects(fastify.ready(), { message: "The decorator 'plugin2' required by 'test' is not present in Fastify" })
})
test('plugin name when encapsulated', async (t) => {
t.plan(6)
const fastify = Fastify()
fastify.register(function plugin (_instance, _opts, next) {
next()
})
fastify.register(fp(getFn('hello'), {
fastify: '5.x',
name: 'hello',
encapsulate: true
}))
fastify.register(function plugin (fastify, _opts, next) {
fastify.register(fp(getFn('deep'), {
fastify: '5.x',
name: 'deep',
encapsulate: true
}))
fastify.register(fp(function genericPlugin (fastify, _opts, next) {
t.assert.strictEqual(fastify.pluginName, 'deep-deep', 'should be deep-deep')
fastify.register(fp(getFn('deep-deep-deep'), {
fastify: '5.x',
name: 'deep-deep-deep',
encapsulate: true
}))
fastify.register(fp(getFn('deep-deep -> not-encapsulated-2'), {
fastify: '5.x',
name: 'not-encapsulated-2'
}))
next()
}, {
fastify: '5.x',
name: 'deep-deep',
encapsulate: true
}))
fastify.register(fp(getFn('plugin -> not-encapsulated'), {
fastify: '5.x',
name: 'not-encapsulated'
}))
next()
})
await fastify.ready()
function getFn (expectedName) {
return function genericPlugin (fastify, _opts, next) {
t.assert.strictEqual(fastify.pluginName, expectedName, `should be ${expectedName}`)
next()
}
}
})

View File

@@ -0,0 +1,24 @@
'use strict'
const { test } = require('node:test')
const toCamelCase = require('../lib/toCamelCase')
test('from kebab-case to camelCase', (t) => {
t.plan(1)
t.assert.strictEqual(toCamelCase('hello-world'), 'helloWorld')
})
test('from @-prefixed named imports', (t) => {
t.plan(1)
t.assert.strictEqual(toCamelCase('@hello/world'), 'helloWorld')
})
test('from @-prefixed named kebab-case to camelCase', (t) => {
t.plan(1)
t.assert.strictEqual(toCamelCase('@hello/my-world'), 'helloMyWorld')
})
test('from kebab-case to camelCase multiple words', (t) => {
t.plan(1)
t.assert.strictEqual(toCamelCase('hello-long-world'), 'helloLongWorld')
})

View File

@@ -0,0 +1,19 @@
import { FastifyPluginAsync } from 'fastify'
type FastifyExampleAsync = FastifyPluginAsync<fastifyExampleAsync.FastifyExampleAsyncOptions>
declare namespace fastifyExampleAsync {
export interface FastifyExampleAsyncOptions {
foo?: 'bar'
}
export interface FastifyExampleAsyncPluginOptions extends FastifyExampleAsyncOptions {
}
export const fastifyExampleAsync: FastifyExampleAsync
export { fastifyExampleAsync as default }
}
declare function fastifyExampleAsync (...params: Parameters<FastifyExampleAsync>): ReturnType<FastifyExampleAsync>
export default fastifyExampleAsync

View File

@@ -0,0 +1,19 @@
import { FastifyPluginCallback } from 'fastify'
type FastifyExampleCallback = FastifyPluginCallback<fastifyExampleCallback.FastifyExampleCallbackOptions>
declare namespace fastifyExampleCallback {
export interface FastifyExampleCallbackOptions {
foo?: 'bar'
}
export interface FastifyExampleCallbackPluginOptions extends FastifyExampleCallbackOptions {
}
export const fastifyExampleCallback: FastifyExampleCallback
export { fastifyExampleCallback as default }
}
declare function fastifyExampleCallback (...params: Parameters<FastifyExampleCallback>): ReturnType<FastifyExampleCallback>
export default fastifyExampleCallback

View File

@@ -0,0 +1,61 @@
/// <reference types="fastify" />
import {
FastifyPluginCallback,
FastifyPluginAsync,
FastifyPluginOptions,
RawServerBase,
RawServerDefault,
FastifyTypeProvider,
FastifyTypeProviderDefault,
FastifyBaseLogger,
} from 'fastify'
type FastifyPlugin = typeof fastifyPlugin
declare namespace fastifyPlugin {
export interface PluginMetadata {
/** Bare-minimum version of Fastify for your plugin, just add the semver range that you need. */
fastify?: string,
name?: string,
/** Decorator dependencies for this plugin */
decorators?: {
fastify?: (string | symbol)[],
reply?: (string | symbol)[],
request?: (string | symbol)[]
},
/** The plugin dependencies */
dependencies?: string[],
encapsulate?: boolean
}
// Exporting PluginOptions for backward compatibility after renaming it to PluginMetadata
/**
* @deprecated Use PluginMetadata instead
*/
export interface PluginOptions extends PluginMetadata {}
export const fastifyPlugin: FastifyPlugin
export { fastifyPlugin as default }
}
/**
* This function does three things for you:
* 1. Add the `skip-override` hidden property
* 2. Check bare-minimum version of Fastify
* 3. Pass some custom metadata of the plugin to Fastify
* @param fn Fastify plugin function
* @param options Optional plugin options
*/
declare function fastifyPlugin<
Options extends FastifyPluginOptions = Record<never, never>,
RawServer extends RawServerBase = RawServerDefault,
TypeProvider extends FastifyTypeProvider = FastifyTypeProviderDefault,
Logger extends FastifyBaseLogger = FastifyBaseLogger,
Fn extends FastifyPluginCallback<Options, RawServer, TypeProvider, Logger> | FastifyPluginAsync<Options, RawServer, TypeProvider, Logger> = FastifyPluginCallback<Options, RawServer, TypeProvider, Logger>
> (
fn: Fn extends unknown ? Fn extends (...args: any) => Promise<any> ? FastifyPluginAsync<Options, RawServer, TypeProvider, Logger> : FastifyPluginCallback<Options, RawServer, TypeProvider, Logger> : Fn,
options?: fastifyPlugin.PluginMetadata | string
): Fn
export = fastifyPlugin

View File

@@ -0,0 +1,166 @@
import fastifyPlugin from '..'
import fastify, { FastifyPluginCallback, FastifyPluginAsync, FastifyError, FastifyInstance, FastifyPluginOptions, RawServerDefault, FastifyTypeProviderDefault, FastifyBaseLogger } from 'fastify'
import { expectAssignable, expectError, expectNotType, expectType } from 'tsd'
import { Server } from 'node:https'
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
import fastifyExampleCallback from './example-callback.test-d'
import fastifyExampleAsync from './example-async.test-d'
interface Options {
foo: string
}
const testSymbol = Symbol('foobar')
// Callback
const pluginCallback: FastifyPluginCallback = (_fastify, _options, _next) => { }
expectType<FastifyPluginCallback>(fastifyPlugin(pluginCallback))
const pluginCallbackWithTypes = (_fastify: FastifyInstance, _options: FastifyPluginOptions, _next: (error?: FastifyError) => void): void => { }
expectAssignable<FastifyPluginCallback>(fastifyPlugin(pluginCallbackWithTypes))
expectNotType<any>(fastifyPlugin(pluginCallbackWithTypes))
expectAssignable<FastifyPluginCallback>(fastifyPlugin((_fastify: FastifyInstance, _options: FastifyPluginOptions, _next: (error?: FastifyError) => void): void => { }))
expectNotType<any>(fastifyPlugin((_fastify: FastifyInstance, _options: FastifyPluginOptions, _next: (error?: FastifyError) => void): void => { }))
expectType<FastifyPluginCallback>(fastifyPlugin(pluginCallback, ''))
expectType<FastifyPluginCallback>(fastifyPlugin(pluginCallback, {
fastify: '',
name: '',
decorators: {
fastify: ['', testSymbol],
reply: ['', testSymbol],
request: ['', testSymbol]
},
dependencies: [''],
encapsulate: true
}))
const pluginCallbackWithOptions: FastifyPluginCallback<Options> = (_fastify, options, _next) => {
expectType<string>(options.foo)
}
expectType<FastifyPluginCallback<Options>>(fastifyPlugin(pluginCallbackWithOptions))
const pluginCallbackWithServer: FastifyPluginCallback<Options, Server> = (fastify, _options, _next) => {
expectType<Server>(fastify.server)
}
expectType<FastifyPluginCallback<Options, Server>>(fastifyPlugin(pluginCallbackWithServer))
const pluginCallbackWithTypeProvider: FastifyPluginCallback<Options, Server, TypeBoxTypeProvider> = (_fastify, _options, _next) => { }
expectType<FastifyPluginCallback<Options, Server, TypeBoxTypeProvider>>(fastifyPlugin(pluginCallbackWithTypeProvider))
// Async
const pluginAsync: FastifyPluginAsync = async (_fastify, _options) => { }
expectType<FastifyPluginAsync>(fastifyPlugin(pluginAsync))
const pluginAsyncWithTypes = async (_fastify: FastifyInstance, _options: FastifyPluginOptions): Promise<void> => { }
expectType<FastifyPluginAsync<FastifyPluginOptions, RawServerDefault, FastifyTypeProviderDefault>>(fastifyPlugin(pluginAsyncWithTypes))
expectType<FastifyPluginAsync<FastifyPluginOptions, RawServerDefault, FastifyTypeProviderDefault>>(fastifyPlugin(async (_fastify: FastifyInstance, _options: FastifyPluginOptions): Promise<void> => { }))
expectType<FastifyPluginAsync>(fastifyPlugin(pluginAsync, ''))
expectType<FastifyPluginAsync>(fastifyPlugin(pluginAsync, {
fastify: '',
name: '',
decorators: {
fastify: ['', testSymbol],
reply: ['', testSymbol],
request: ['', testSymbol]
},
dependencies: [''],
encapsulate: true
}))
const pluginAsyncWithOptions: FastifyPluginAsync<Options> = async (_fastify, options) => {
expectType<string>(options.foo)
}
expectType<FastifyPluginAsync<Options>>(fastifyPlugin(pluginAsyncWithOptions))
const pluginAsyncWithServer: FastifyPluginAsync<Options, Server> = async (fastify, _options) => {
expectType<Server>(fastify.server)
}
expectType<FastifyPluginAsync<Options, Server>>(fastifyPlugin(pluginAsyncWithServer))
const pluginAsyncWithTypeProvider: FastifyPluginAsync<Options, Server, TypeBoxTypeProvider> = async (_fastify, _options) => { }
expectType<FastifyPluginAsync<Options, Server, TypeBoxTypeProvider>>(fastifyPlugin(pluginAsyncWithTypeProvider))
// Fastify register
const server = fastify()
server.register(fastifyPlugin(pluginCallback))
server.register(fastifyPlugin(pluginCallbackWithTypes), { foo: 'bar' })
server.register(fastifyPlugin(pluginCallbackWithOptions), { foo: 'bar' })
server.register(fastifyPlugin(pluginCallbackWithServer), { foo: 'bar' })
server.register(fastifyPlugin(pluginCallbackWithTypeProvider), { foo: 'bar' })
server.register(fastifyPlugin(pluginAsync))
server.register(fastifyPlugin(pluginAsyncWithTypes), { foo: 'bar' })
server.register(fastifyPlugin(pluginAsyncWithOptions), { foo: 'bar' })
server.register(fastifyPlugin(pluginAsyncWithServer), { foo: 'bar' })
server.register(fastifyPlugin(pluginAsyncWithTypeProvider), { foo: 'bar' })
// properly handling callback and async
fastifyPlugin(function (fastify, options, next) {
expectType<FastifyInstance>(fastify)
expectType<Record<never, never>>(options)
expectType<(err?: Error) => void>(next)
})
fastifyPlugin<Options>(function (fastify, options, next) {
expectType<FastifyInstance>(fastify)
expectType<Options>(options)
expectType<(err?: Error) => void>(next)
})
fastifyPlugin<Options>(async function (fastify, options) {
expectType<FastifyInstance>(fastify)
expectType<Options>(options)
})
expectAssignable<FastifyPluginAsync<Options, RawServerDefault, FastifyTypeProviderDefault, FastifyBaseLogger>>(fastifyPlugin(async function (_fastify: FastifyInstance, _options: Options) { }))
expectNotType<any>(fastifyPlugin(async function (_fastify: FastifyInstance, _options: Options) { }))
fastifyPlugin(async function (fastify, options: Options) {
expectType<FastifyInstance>(fastify)
expectType<Options>(options)
})
fastifyPlugin(async function (fastify, options) {
expectType<FastifyInstance>(fastify)
expectType<Record<never, never>>(options)
})
expectError(
fastifyPlugin(async function (fastify, options: Options, _next) {
expectType<FastifyInstance>(fastify)
expectType<Options>(options)
})
)
expectAssignable<FastifyPluginCallback<Options>>(fastifyPlugin(function (_fastify, _options, _next) { }))
expectNotType<any>(fastifyPlugin(function (_fastify, _options, _next) { }))
fastifyPlugin(function (fastify, options: Options, next) {
expectType<FastifyInstance>(fastify)
expectType<Options>(options)
expectType<(err?: Error) => void>(next)
})
expectError(
fastifyPlugin(function (fastify, options: Options, _next) {
expectType<FastifyInstance>(fastify)
expectType<Options>(options)
return Promise.resolve()
})
)
server.register(fastifyExampleCallback, { foo: 'bar' })
expectError(server.register(fastifyExampleCallback, { foo: 'baz' }))
server.register(fastifyExampleAsync, { foo: 'bar' })
expectError(server.register(fastifyExampleAsync, { foo: 'baz' }))

81
node_modules/@fastify/cors/package.json generated vendored Normal file
View File

@@ -0,0 +1,81 @@
{
"name": "@fastify/cors",
"version": "11.2.0",
"description": "Fastify CORS",
"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": "c8 --100 node --test"
},
"keywords": [
"fastify",
"cors",
"headers",
"access",
"control"
],
"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",
"repository": {
"type": "git",
"url": "git+https://github.com/fastify/fastify-cors.git"
},
"bugs": {
"url": "https://github.com/fastify/fastify-cors/issues"
},
"homepage": "https://github.com/fastify/fastify-cors#readme",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/fastify"
},
{
"type": "opencollective",
"url": "https://opencollective.com/fastify"
}
],
"devDependencies": {
"@types/node": "^24.0.8",
"c8": "^10.1.2",
"cors": "^2.8.5",
"eslint": "^9.17.0",
"fastify": "^5.0.0",
"neostandard": "^0.12.0",
"tsd": "^0.33.0",
"typescript": "~5.9.2"
},
"dependencies": {
"fastify-plugin": "^5.0.0",
"toad-cache": "^3.7.0"
},
"tsd": {
"directory": "test"
},
"publishConfig": {
"access": "public"
}
}

1124
node_modules/@fastify/cors/test/cors.test.js generated vendored Normal file

File diff suppressed because it is too large Load Diff

787
node_modules/@fastify/cors/test/hooks.test.js generated vendored Normal file
View File

@@ -0,0 +1,787 @@
'use strict'
const { test } = require('node:test')
const Fastify = require('fastify')
const kFastifyContext = require('fastify/lib/symbols').kRouteContext
const cors = require('..')
const { setTimeout: sleep } = require('node:timers/promises')
test('Should error on invalid hook option', async (t) => {
t.plan(3)
const fastify = Fastify()
await t.assert.rejects(
async () => fastify.register(cors, { hook: 'invalid' }),
(err) => {
t.assert.strictEqual(err.name, 'TypeError')
t.assert.strictEqual(err.message, '@fastify/cors: Invalid hook option provided.')
return true
}
)
})
test('Should set hook onRequest if hook option is not set', async (t) => {
t.plan(10)
const fastify = Fastify()
fastify.register(cors)
fastify.addHook('onResponse', (request, _reply, done) => {
t.assert.strictEqual(request[kFastifyContext].onError, null)
t.assert.strictEqual(request[kFastifyContext].onRequest.length, 1)
t.assert.strictEqual(request[kFastifyContext].onSend, null)
t.assert.strictEqual(request[kFastifyContext].preHandler, null)
t.assert.strictEqual(request[kFastifyContext].preParsing, null)
t.assert.strictEqual(request[kFastifyContext].preSerialization, null)
t.assert.strictEqual(request[kFastifyContext].preValidation, null)
done()
})
fastify.get('/', (_req, reply) => {
reply.send('ok')
})
await fastify.ready()
const res = await fastify.inject({
method: 'GET',
url: '/'
})
delete res.headers.date
t.assert.strictEqual(res.statusCode, 200)
t.assert.strictEqual(res.payload, 'ok')
const actualHeader = {
'access-control-allow-origin': res.headers['access-control-allow-origin']
}
t.assert.deepStrictEqual(actualHeader, {
'access-control-allow-origin': '*'
})
})
test('Should set hook onRequest if hook option is set to onRequest', async (t) => {
t.plan(10)
const fastify = Fastify()
fastify.register(cors, {
hook: 'onRequest'
})
fastify.addHook('onResponse', (request, _reply, done) => {
t.assert.strictEqual(request[kFastifyContext].onError, null)
t.assert.strictEqual(request[kFastifyContext].onRequest.length, 1)
t.assert.strictEqual(request[kFastifyContext].onSend, null)
t.assert.strictEqual(request[kFastifyContext].preHandler, null)
t.assert.strictEqual(request[kFastifyContext].preParsing, null)
t.assert.strictEqual(request[kFastifyContext].preSerialization, null)
t.assert.strictEqual(request[kFastifyContext].preValidation, null)
done()
})
fastify.get('/', (_req, reply) => {
reply.send('ok')
})
await fastify.ready()
const res = await fastify.inject({
method: 'GET',
url: '/'
})
delete res.headers.date
t.assert.strictEqual(res.statusCode, 200)
t.assert.strictEqual(res.payload, 'ok')
const actualHeader = {
'access-control-allow-origin': res.headers['access-control-allow-origin']
}
t.assert.deepStrictEqual(actualHeader, {
'access-control-allow-origin': '*'
})
})
test('Should set hook preParsing if hook option is set to preParsing', async (t) => {
t.plan(11)
const fastify = Fastify()
fastify.register(cors, {
hook: 'preParsing'
})
fastify.addHook('onResponse', (request, _reply, done) => {
t.assert.strictEqual(request[kFastifyContext].onError, null)
t.assert.strictEqual(request[kFastifyContext].onRequest, null)
t.assert.strictEqual(request[kFastifyContext].onSend, null)
t.assert.strictEqual(request[kFastifyContext].preHandler, null)
t.assert.strictEqual(request[kFastifyContext].preParsing.length, 1)
t.assert.strictEqual(request[kFastifyContext].preSerialization, null)
t.assert.strictEqual(request[kFastifyContext].preValidation, null)
done()
})
fastify.get('/', (_req, reply) => {
reply.send('ok')
})
await fastify.ready()
const res = await fastify.inject({
method: 'GET',
url: '/'
})
delete res.headers.date
t.assert.strictEqual(res.statusCode, 200)
t.assert.strictEqual(res.payload, 'ok')
const actualHeader = {
'access-control-allow-origin': res.headers['access-control-allow-origin']
}
t.assert.deepStrictEqual(actualHeader, {
'access-control-allow-origin': '*'
})
t.assert.notStrictEqual(res.headers.vary, 'Origin')
})
test('Should set hook preValidation if hook option is set to preValidation', async (t) => {
t.plan(11)
const fastify = Fastify()
fastify.register(cors, {
hook: 'preValidation'
})
fastify.addHook('onResponse', (request, _reply, done) => {
t.assert.strictEqual(request[kFastifyContext].onError, null)
t.assert.strictEqual(request[kFastifyContext].onRequest, null)
t.assert.strictEqual(request[kFastifyContext].onSend, null)
t.assert.strictEqual(request[kFastifyContext].preHandler, null)
t.assert.strictEqual(request[kFastifyContext].preParsing, null)
t.assert.strictEqual(request[kFastifyContext].preSerialization, null)
t.assert.strictEqual(request[kFastifyContext].preValidation.length, 1)
done()
})
fastify.get('/', (_req, reply) => {
reply.send('ok')
})
await fastify.ready()
const res = await fastify.inject({
method: 'GET',
url: '/'
})
delete res.headers.date
t.assert.strictEqual(res.statusCode, 200)
t.assert.strictEqual(res.payload, 'ok')
const actualHeader = {
'access-control-allow-origin': res.headers['access-control-allow-origin']
}
t.assert.deepStrictEqual(actualHeader, {
'access-control-allow-origin': '*'
})
t.assert.notStrictEqual(res.headers.vary, 'Origin')
})
test('Should set hook preParsing if hook option is set to preParsing', async (t) => {
t.plan(11)
const fastify = Fastify()
fastify.register(cors, {
hook: 'preParsing'
})
fastify.addHook('onResponse', (request, _reply, done) => {
t.assert.strictEqual(request[kFastifyContext].onError, null)
t.assert.strictEqual(request[kFastifyContext].onRequest, null)
t.assert.strictEqual(request[kFastifyContext].onSend, null)
t.assert.strictEqual(request[kFastifyContext].preHandler, null)
t.assert.strictEqual(request[kFastifyContext].preParsing.length, 1)
t.assert.strictEqual(request[kFastifyContext].preSerialization, null)
t.assert.strictEqual(request[kFastifyContext].preValidation, null)
done()
})
fastify.get('/', (_req, reply) => {
reply.send('ok')
})
await fastify.ready()
const res = await fastify.inject({
method: 'GET',
url: '/'
})
delete res.headers.date
t.assert.strictEqual(res.statusCode, 200)
t.assert.strictEqual(res.payload, 'ok')
const actualHeader = {
'access-control-allow-origin': res.headers['access-control-allow-origin']
}
t.assert.deepStrictEqual(actualHeader, {
'access-control-allow-origin': '*'
})
t.assert.notStrictEqual(res.headers.vary, 'Origin')
})
test('Should set hook preHandler if hook option is set to preHandler', async (t) => {
t.plan(11)
const fastify = Fastify()
fastify.register(cors, {
hook: 'preHandler'
})
fastify.addHook('onResponse', (request, _reply, done) => {
t.assert.strictEqual(request[kFastifyContext].onError, null)
t.assert.strictEqual(request[kFastifyContext].onRequest, null)
t.assert.strictEqual(request[kFastifyContext].onSend, null)
t.assert.strictEqual(request[kFastifyContext].preHandler.length, 1)
t.assert.strictEqual(request[kFastifyContext].preParsing, null)
t.assert.strictEqual(request[kFastifyContext].preSerialization, null)
t.assert.strictEqual(request[kFastifyContext].preValidation, null)
done()
})
fastify.get('/', (_req, reply) => {
reply.send('ok')
})
await fastify.ready()
const res = await fastify.inject({
method: 'GET',
url: '/'
})
delete res.headers.date
t.assert.strictEqual(res.statusCode, 200)
t.assert.strictEqual(res.payload, 'ok')
const actualHeader = {
'access-control-allow-origin': res.headers['access-control-allow-origin']
}
t.assert.deepStrictEqual(actualHeader, {
'access-control-allow-origin': '*'
})
t.assert.notStrictEqual(res.headers.vary, 'Origin')
})
test('Should set hook onSend if hook option is set to onSend', async (t) => {
t.plan(11)
const fastify = Fastify()
fastify.register(cors, {
hook: 'onSend'
})
fastify.addHook('onResponse', (request, _reply, done) => {
t.assert.strictEqual(request[kFastifyContext].onError, null)
t.assert.strictEqual(request[kFastifyContext].onRequest, null)
t.assert.strictEqual(request[kFastifyContext].onSend.length, 1)
t.assert.strictEqual(request[kFastifyContext].preHandler, null)
t.assert.strictEqual(request[kFastifyContext].preParsing, null)
t.assert.strictEqual(request[kFastifyContext].preSerialization, null)
t.assert.strictEqual(request[kFastifyContext].preValidation, null)
done()
})
fastify.get('/', (_req, reply) => {
reply.send('ok')
})
await fastify.ready()
const res = await fastify.inject({
method: 'GET',
url: '/'
})
delete res.headers.date
t.assert.strictEqual(res.statusCode, 200)
t.assert.strictEqual(res.payload, 'ok')
const actualHeader = {
'access-control-allow-origin': res.headers['access-control-allow-origin']
}
t.assert.deepStrictEqual(actualHeader, {
'access-control-allow-origin': '*'
})
t.assert.notStrictEqual(res.headers.vary, 'Origin')
})
test('Should set hook preSerialization if hook option is set to preSerialization', async (t) => {
t.plan(11)
const fastify = Fastify()
fastify.register(cors, {
hook: 'preSerialization'
})
fastify.addHook('onResponse', (request, _reply, done) => {
t.assert.strictEqual(request[kFastifyContext].onError, null)
t.assert.strictEqual(request[kFastifyContext].onRequest, null)
t.assert.strictEqual(request[kFastifyContext].onSend, null)
t.assert.strictEqual(request[kFastifyContext].preHandler, null)
t.assert.strictEqual(request[kFastifyContext].preParsing, null)
t.assert.strictEqual(request[kFastifyContext].preSerialization.length, 1)
t.assert.strictEqual(request[kFastifyContext].preValidation, null)
done()
})
fastify.get('/', (_req, reply) => {
reply.send({ nonString: true })
})
await fastify.ready()
const res = await fastify.inject({
method: 'GET',
url: '/'
})
delete res.headers.date
t.assert.strictEqual(res.statusCode, 200)
t.assert.strictEqual(res.payload, '{"nonString":true}')
const actualHeader = {
'access-control-allow-origin': res.headers['access-control-allow-origin']
}
t.assert.deepStrictEqual(actualHeader, {
'access-control-allow-origin': '*'
})
t.assert.notStrictEqual(res.headers.vary, 'Origin')
})
test('Should support custom hook with dynamic config', async t => {
t.plan(16)
const configs = [{
origin: 'example.com',
methods: 'GET',
credentials: true,
exposedHeaders: ['foo', 'bar'],
allowedHeaders: ['baz', 'woo'],
maxAge: 123
}, {
origin: 'sample.com',
methods: 'GET',
credentials: true,
exposedHeaders: ['zoo', 'bar'],
allowedHeaders: ['baz', 'foo'],
maxAge: 321
}]
const fastify = Fastify()
let requestId = 0
const configDelegation = async function (req) {
// request should have id
t.assert.ok(req.id)
// request should not have send
t.assert.ifError(req.send)
const config = configs[requestId]
requestId++
if (config) {
return Promise.resolve(config)
} else {
return Promise.reject(new Error('ouch'))
}
}
await fastify.register(cors, {
hook: 'preHandler',
delegator: configDelegation
})
fastify.get('/', (_req, reply) => {
reply.send('ok')
})
let res = await fastify.inject({
method: 'GET',
url: '/'
})
t.assert.ok(res)
delete res.headers.date
t.assert.strictEqual(res.statusCode, 200)
t.assert.strictEqual(res.payload, 'ok')
let actualHeaders = {
'access-control-allow-origin': res.headers['access-control-allow-origin'],
'access-control-allow-credentials': res.headers['access-control-allow-credentials'],
'access-control-expose-headers': res.headers['access-control-expose-headers'],
'content-length': res.headers['content-length'],
vary: res.headers.vary
}
t.assert.deepStrictEqual(actualHeaders, {
'access-control-allow-origin': 'example.com',
'access-control-allow-credentials': 'true',
'access-control-expose-headers': 'foo, bar',
'content-length': '2',
vary: 'Origin'
})
res = await fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
})
t.assert.ok(res)
delete res.headers.date
t.assert.strictEqual(res.statusCode, 204)
t.assert.strictEqual(res.payload, '')
actualHeaders = {
'access-control-allow-origin': res.headers['access-control-allow-origin'],
'access-control-allow-credentials': res.headers['access-control-allow-credentials'],
'access-control-expose-headers': res.headers['access-control-expose-headers'],
'access-control-allow-methods': res.headers['access-control-allow-methods'],
'access-control-allow-headers': res.headers['access-control-allow-headers'],
'access-control-max-age': res.headers['access-control-max-age'],
'content-length': res.headers['content-length'],
vary: res.headers.vary
}
t.assert.deepStrictEqual(actualHeaders, {
'access-control-allow-origin': 'sample.com',
'access-control-allow-credentials': 'true',
'access-control-expose-headers': 'zoo, bar',
'access-control-allow-methods': 'GET',
'access-control-allow-headers': 'baz, foo',
'access-control-max-age': '321',
'content-length': '0',
vary: 'Origin'
})
res = await fastify.inject({
method: 'GET',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
})
t.assert.ok(res)
t.assert.strictEqual(res.statusCode, 500)
})
test('Should support custom hook with dynamic config (callback)', async t => {
t.plan(16)
const configs = [{
origin: 'example.com',
methods: 'GET',
credentials: true,
exposedHeaders: ['foo', 'bar'],
allowedHeaders: ['baz', 'woo'],
maxAge: 123
}, {
origin: 'sample.com',
methods: 'GET',
credentials: true,
exposedHeaders: ['zoo', 'bar'],
allowedHeaders: ['baz', 'foo'],
maxAge: 321
}]
const fastify = Fastify()
let requestId = 0
const configDelegation = function (req, cb) {
// request should have id
t.assert.ok(req.id)
// request should not have send
t.assert.ifError(req.send)
const config = configs[requestId]
requestId++
if (config) {
cb(null, config)
} else {
cb(new Error('ouch'))
}
}
fastify.register(cors, {
hook: 'preParsing',
delegator: configDelegation
})
fastify.get('/', (_req, reply) => {
reply.send('ok')
})
fastify.inject({
method: 'GET',
url: '/'
}, (err, res) => {
t.assert.ifError(err)
delete res.headers.date
t.assert.strictEqual(res.statusCode, 200)
t.assert.strictEqual(res.payload, 'ok')
const actualHeaders = {
'access-control-allow-origin': res.headers['access-control-allow-origin'],
'access-control-allow-credentials': res.headers['access-control-allow-credentials'],
'access-control-expose-headers': res.headers['access-control-expose-headers'],
'content-length': res.headers['content-length'],
vary: res.headers.vary
}
t.assert.deepStrictEqual(actualHeaders, {
'access-control-allow-origin': 'example.com',
'access-control-allow-credentials': 'true',
'access-control-expose-headers': 'foo, bar',
'content-length': '2',
vary: 'Origin'
})
})
fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.assert.ifError(err)
delete res.headers.date
t.assert.strictEqual(res.statusCode, 204)
t.assert.strictEqual(res.payload, '')
const actualHeaders = {
'access-control-allow-origin': res.headers['access-control-allow-origin'],
'access-control-allow-credentials': res.headers['access-control-allow-credentials'],
'access-control-expose-headers': res.headers['access-control-expose-headers'],
'access-control-allow-methods': res.headers['access-control-allow-methods'],
'access-control-allow-headers': res.headers['access-control-allow-headers'],
'access-control-max-age': res.headers['access-control-max-age'],
'content-length': res.headers['content-length'],
vary: res.headers.vary
}
t.assert.deepStrictEqual(actualHeaders, {
'access-control-allow-origin': 'sample.com',
'access-control-allow-credentials': 'true',
'access-control-expose-headers': 'zoo, bar',
'access-control-allow-methods': 'GET',
'access-control-allow-headers': 'baz, foo',
'access-control-max-age': '321',
'content-length': '0',
vary: 'Origin'
})
})
fastify.inject({
method: 'GET',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
}, (err, res) => {
t.assert.ifError(err)
t.assert.strictEqual(res.statusCode, 500)
})
await sleep()
})
test('Should support custom hook with dynamic config (Promise)', async t => {
t.plan(16)
const configs = [{
origin: 'example.com',
methods: 'GET',
credentials: true,
exposedHeaders: ['foo', 'bar'],
allowedHeaders: ['baz', 'woo'],
maxAge: 123
}, {
origin: 'sample.com',
methods: 'GET',
credentials: true,
exposedHeaders: ['zoo', 'bar'],
allowedHeaders: ['baz', 'foo'],
maxAge: 321
}]
const fastify = Fastify()
let requestId = 0
const configDelegation = async function (req) {
// request should have id
t.assert.ok(req.id)
// request should not have send
t.assert.ifError(req.send)
const config = configs[requestId]
requestId++
if (config) {
return Promise.resolve(config)
} else {
return Promise.reject(new Error('ouch'))
}
}
await fastify.register(cors, {
hook: 'preParsing',
delegator: configDelegation
})
fastify.get('/', (_req, reply) => {
reply.send('ok')
})
let res = await fastify.inject({
method: 'GET',
url: '/'
})
t.assert.ok(res)
delete res.headers.date
t.assert.strictEqual(res.statusCode, 200)
t.assert.strictEqual(res.payload, 'ok')
let actualHeaders = {
'access-control-allow-origin': res.headers['access-control-allow-origin'],
'access-control-allow-credentials': res.headers['access-control-allow-credentials'],
'access-control-expose-headers': res.headers['access-control-expose-headers'],
'content-length': res.headers['content-length'],
vary: res.headers.vary
}
t.assert.deepStrictEqual(actualHeaders, {
'access-control-allow-origin': 'example.com',
'access-control-allow-credentials': 'true',
'access-control-expose-headers': 'foo, bar',
'content-length': '2',
vary: 'Origin'
})
res = await fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
})
t.assert.ok(res)
delete res.headers.date
t.assert.strictEqual(res.statusCode, 204)
t.assert.strictEqual(res.payload, '')
actualHeaders = {
'access-control-allow-origin': res.headers['access-control-allow-origin'],
'access-control-allow-credentials': res.headers['access-control-allow-credentials'],
'access-control-expose-headers': res.headers['access-control-expose-headers'],
'access-control-allow-methods': res.headers['access-control-allow-methods'],
'access-control-allow-headers': res.headers['access-control-allow-headers'],
'access-control-max-age': res.headers['access-control-max-age'],
'content-length': res.headers['content-length'],
vary: res.headers.vary
}
t.assert.deepStrictEqual(actualHeaders, {
'access-control-allow-origin': 'sample.com',
'access-control-allow-credentials': 'true',
'access-control-expose-headers': 'zoo, bar',
'access-control-allow-methods': 'GET',
'access-control-allow-headers': 'baz, foo',
'access-control-max-age': '321',
'content-length': '0',
vary: 'Origin'
})
res = await fastify.inject({
method: 'GET',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
})
t.assert.ok(res)
t.assert.strictEqual(res.statusCode, 500)
})
test('Should support custom hook with dynamic config (Promise), but should error /1', async t => {
t.plan(6)
const fastify = Fastify()
const configDelegation = function () {
return false
}
await fastify.register(cors, {
hook: 'preParsing',
delegator: configDelegation
})
fastify.get('/', (_req, reply) => {
reply.send('ok')
})
let res = await fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
})
t.assert.ok(res)
delete res.headers.date
t.assert.strictEqual(res.statusCode, 500)
t.assert.strictEqual(res.payload, '{"statusCode":500,"error":"Internal Server Error","message":"Invalid CORS origin option"}')
const actualHeaders = {
'content-length': res.headers['content-length']
}
t.assert.deepStrictEqual(actualHeaders, {
'content-length': '89'
})
res = await fastify.inject({
method: 'GET',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
})
t.assert.ok(res)
t.assert.strictEqual(res.statusCode, 500)
})
test('Should support custom hook with dynamic config (Promise), but should error /2', async t => {
t.plan(6)
const fastify = Fastify()
const configDelegation = function () {
return false
}
await fastify.register(cors, {
delegator: configDelegation
})
fastify.get('/', (_req, reply) => {
reply.send('ok')
})
let res = await fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
})
t.assert.ok(res)
delete res.headers.date
t.assert.strictEqual(res.statusCode, 500)
t.assert.strictEqual(res.payload, '{"statusCode":500,"error":"Internal Server Error","message":"Invalid CORS origin option"}')
const actualHeaders = {
'content-length': res.headers['content-length']
}
t.assert.deepStrictEqual(actualHeaders, {
'content-length': '89'
})
res = await fastify.inject({
method: 'GET',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
})
t.assert.ok(res)
t.assert.strictEqual(res.statusCode, 500)
})

590
node_modules/@fastify/cors/test/preflight.test.js generated vendored Normal file
View File

@@ -0,0 +1,590 @@
'use strict'
const { test } = require('node:test')
const Fastify = require('fastify')
const cors = require('../')
test('Should reply to preflight requests', async t => {
t.plan(4)
const fastify = Fastify()
await fastify.register(cors)
const res = await fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
})
t.assert.ok(res)
delete res.headers.date
t.assert.strictEqual(res.statusCode, 204)
t.assert.strictEqual(res.payload, '')
const actualHeaders = {
'access-control-allow-origin': res.headers['access-control-allow-origin'],
'access-control-allow-methods': res.headers['access-control-allow-methods'],
vary: res.headers.vary,
'content-length': res.headers['content-length']
}
t.assert.deepStrictEqual(actualHeaders, {
'access-control-allow-origin': '*',
'access-control-allow-methods': 'GET,HEAD,POST',
vary: 'Access-Control-Request-Headers',
'content-length': '0'
})
})
test('Should add access-control-allow-headers to response if preflight req has access-control-request-headers', async t => {
t.plan(4)
const fastify = Fastify()
await fastify.register(cors)
const res = await fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-headers': 'x-requested-with',
'access-control-request-method': 'GET',
origin: 'example.com'
}
})
t.assert.ok(res)
delete res.headers.date
t.assert.strictEqual(res.statusCode, 204)
t.assert.strictEqual(res.payload, '')
const actualHeaders = {
'access-control-allow-origin': res.headers['access-control-allow-origin'],
'access-control-allow-methods': res.headers['access-control-allow-methods'],
'access-control-allow-headers': res.headers['access-control-allow-headers'],
vary: res.headers.vary,
'content-length': res.headers['content-length']
}
t.assert.deepStrictEqual(actualHeaders, {
'access-control-allow-origin': '*',
'access-control-allow-methods': 'GET,HEAD,POST',
'access-control-allow-headers': 'x-requested-with',
vary: 'Access-Control-Request-Headers',
'content-length': '0'
})
})
test('Should reply to preflight requests with custom status code', async t => {
t.plan(4)
const fastify = Fastify()
await fastify.register(cors, { optionsSuccessStatus: 200 })
const res = await fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
})
t.assert.ok(res)
delete res.headers.date
t.assert.strictEqual(res.statusCode, 200)
t.assert.strictEqual(res.payload, '')
const actualHeaders = {
'access-control-allow-origin': res.headers['access-control-allow-origin'],
'access-control-allow-methods': res.headers['access-control-allow-methods'],
vary: res.headers.vary,
'content-length': res.headers['content-length']
}
t.assert.deepStrictEqual(actualHeaders, {
'access-control-allow-origin': '*',
'access-control-allow-methods': 'GET,HEAD,POST',
vary: 'Access-Control-Request-Headers',
'content-length': '0'
})
})
test('Should be able to override preflight response with a route', async t => {
t.plan(5)
const fastify = Fastify()
await fastify.register(cors, { preflightContinue: true })
fastify.options('/', (_req, reply) => {
reply.send('ok')
})
const res = await fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
})
t.assert.ok(res)
delete res.headers.date
t.assert.strictEqual(res.statusCode, 200)
t.assert.strictEqual(res.payload, 'ok')
const actualHeaders = {
'access-control-allow-origin': res.headers['access-control-allow-origin']
}
t.assert.deepStrictEqual(actualHeaders, {
// Only the base cors headers and no preflight headers
'access-control-allow-origin': '*'
})
t.assert.notStrictEqual(res.headers.vary, 'Origin')
})
test('Should reply to all options requests', async t => {
t.plan(4)
const fastify = Fastify()
await fastify.register(cors)
const res = await fastify.inject({
method: 'OPTIONS',
url: '/hello',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
})
t.assert.ok(res)
delete res.headers.date
t.assert.strictEqual(res.statusCode, 204)
t.assert.strictEqual(res.payload, '')
const actualHeaders = {
'access-control-allow-origin': res.headers['access-control-allow-origin'],
'access-control-allow-methods': res.headers['access-control-allow-methods'],
vary: res.headers.vary,
'content-length': res.headers['content-length']
}
t.assert.deepStrictEqual(actualHeaders, {
'access-control-allow-origin': '*',
'access-control-allow-methods': 'GET,HEAD,POST',
vary: 'Access-Control-Request-Headers',
'content-length': '0'
})
})
test('Should support a prefix for preflight requests', async t => {
t.plan(6)
const fastify = Fastify()
await fastify.register((instance, _opts, next) => {
instance.register(cors)
next()
}, { prefix: '/subsystem' })
let res = await fastify.inject({
method: 'OPTIONS',
url: '/hello'
})
t.assert.ok(res)
t.assert.strictEqual(res.statusCode, 404)
res = await fastify.inject({
method: 'OPTIONS',
url: '/subsystem/hello',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
})
t.assert.ok(res)
delete res.headers.date
t.assert.strictEqual(res.statusCode, 204)
t.assert.strictEqual(res.payload, '')
const actualHeaders = {
'access-control-allow-origin': res.headers['access-control-allow-origin'],
'access-control-allow-methods': res.headers['access-control-allow-methods'],
vary: res.headers.vary,
'content-length': res.headers['content-length']
}
t.assert.deepStrictEqual(actualHeaders, {
'access-control-allow-origin': '*',
'access-control-allow-methods': 'GET,HEAD,POST',
vary: 'Access-Control-Request-Headers',
'content-length': '0'
})
})
test('hide options route by default', async t => {
t.plan(2)
const fastify = Fastify()
fastify.addHook('onRoute', (route) => {
if (route.method === 'OPTIONS' && route.url === '*') {
t.assert.strictEqual(route.schema.hide, true)
}
})
await fastify.register(cors)
const ready = await fastify.ready()
t.assert.ok(ready)
})
test('show options route', async t => {
t.plan(2)
const fastify = Fastify()
fastify.addHook('onRoute', (route) => {
if (route.method === 'OPTIONS' && route.url === '*') {
t.assert.strictEqual(route.schema.hide, false)
}
})
await fastify.register(cors, { hideOptionsRoute: false })
const ready = await fastify.ready()
t.assert.ok(ready)
})
test('Allow only request from with specific methods', async t => {
t.plan(4)
const fastify = Fastify()
await fastify.register(cors, { methods: ['GET', 'POST'] })
const res = await fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
})
t.assert.ok(res)
delete res.headers.date
t.assert.strictEqual(res.statusCode, 204)
const actualHeaders = {
'access-control-allow-methods': res.headers['access-control-allow-methods']
}
t.assert.deepStrictEqual(actualHeaders, {
'access-control-allow-methods': 'GET, POST'
})
t.assert.notStrictEqual(res.headers.vary, 'Origin')
})
test('Should reply with 400 error to OPTIONS requests missing origin header when default strictPreflight', async t => {
t.plan(3)
const fastify = Fastify()
await fastify.register(cors)
const res = await fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET'
}
})
t.assert.ok(res)
t.assert.strictEqual(res.statusCode, 400)
t.assert.strictEqual(res.payload, 'Invalid Preflight Request')
})
test('Should reply with 400 to OPTIONS requests when missing Access-Control-Request-Method header when default strictPreflight', async t => {
t.plan(3)
const fastify = Fastify()
await fastify.register(cors, {
strictPreflight: true
})
const res = await fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
origin: 'example.com'
}
})
t.assert.ok(res)
t.assert.strictEqual(res.statusCode, 400)
t.assert.strictEqual(res.payload, 'Invalid Preflight Request')
})
test('Should reply to all preflight requests when strictPreflight is disabled', async t => {
t.plan(4)
const fastify = Fastify()
await fastify.register(cors, { strictPreflight: false })
const res = await fastify.inject({
method: 'OPTIONS',
url: '/'
// No access-control-request-method or origin headers
})
t.assert.ok(res)
delete res.headers.date
t.assert.strictEqual(res.statusCode, 204)
t.assert.strictEqual(res.payload, '')
const actualHeaders = {
'access-control-allow-origin': res.headers['access-control-allow-origin'],
'access-control-allow-methods': res.headers['access-control-allow-methods'],
vary: res.headers.vary,
'content-length': res.headers['content-length']
}
t.assert.deepStrictEqual(actualHeaders, {
'access-control-allow-origin': '*',
'access-control-allow-methods': 'GET,HEAD,POST',
vary: 'Access-Control-Request-Headers',
'content-length': '0'
})
})
test('Default empty 200 response with preflightContinue on OPTIONS routes', async t => {
t.plan(4)
const fastify = Fastify()
await fastify.register(cors, { preflightContinue: true })
const res = await fastify.inject({
method: 'OPTIONS',
url: '/doesnotexist',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
})
t.assert.ok(res)
delete res.headers.date
t.assert.strictEqual(res.statusCode, 200)
t.assert.strictEqual(res.payload, '')
const actualHeaders = {
'access-control-allow-origin': res.headers['access-control-allow-origin'],
'access-control-allow-methods': res.headers['access-control-allow-methods'],
vary: res.headers.vary
}
t.assert.deepStrictEqual(actualHeaders, {
'access-control-allow-origin': '*',
'access-control-allow-methods': 'GET,HEAD,POST',
vary: 'Access-Control-Request-Headers'
})
})
test('Can override preflight response with preflightContinue', async t => {
t.plan(4)
const fastify = Fastify()
await fastify.register(cors, { preflightContinue: true })
fastify.options('/', (_req, reply) => {
reply.send('ok')
})
const res = await fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
})
t.assert.ok(res)
delete res.headers.date
t.assert.strictEqual(res.statusCode, 200)
t.assert.strictEqual(res.payload, 'ok')
const actualHeaders = {
'access-control-allow-origin': res.headers['access-control-allow-origin'],
'access-control-allow-methods': res.headers['access-control-allow-methods'],
vary: res.headers.vary
}
t.assert.deepStrictEqual(actualHeaders, {
'access-control-allow-origin': '*',
'access-control-allow-methods': 'GET,HEAD,POST',
vary: 'Access-Control-Request-Headers'
})
})
test('Should support ongoing prefix ', async t => {
t.plan(12)
const fastify = Fastify()
await fastify.register(async (instance) => {
instance.register(cors)
}, { prefix: '/prefix' })
// support prefixed route
let res = await fastify.inject({
method: 'OPTIONS',
url: '/prefix',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
})
t.assert.ok(res)
delete res.headers.date
t.assert.strictEqual(res.statusCode, 204)
t.assert.strictEqual(res.payload, '')
let actualHeaders = {
'access-control-allow-origin': res.headers['access-control-allow-origin'],
'access-control-allow-methods': res.headers['access-control-allow-methods'],
vary: res.headers.vary,
'content-length': res.headers['content-length']
}
t.assert.deepStrictEqual(actualHeaders, {
'access-control-allow-origin': '*',
'access-control-allow-methods': 'GET,HEAD,POST',
vary: 'Access-Control-Request-Headers',
'content-length': '0'
})
// support prefixed route without / continue
res = await fastify.inject({
method: 'OPTIONS',
url: '/prefixfoo',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
})
t.assert.ok(res)
delete res.headers.date
t.assert.strictEqual(res.statusCode, 204)
t.assert.strictEqual(res.payload, '')
actualHeaders = {
'access-control-allow-origin': res.headers['access-control-allow-origin'],
'access-control-allow-methods': res.headers['access-control-allow-methods'],
vary: res.headers.vary,
'content-length': res.headers['content-length']
}
t.assert.deepStrictEqual(actualHeaders, {
'access-control-allow-origin': '*',
'access-control-allow-methods': 'GET,HEAD,POST',
vary: 'Access-Control-Request-Headers',
'content-length': '0'
})
// support prefixed route with / continue
res = await fastify.inject({
method: 'OPTIONS',
url: '/prefix/foo',
headers: {
'access-control-request-method': 'GET',
origin: 'example.com'
}
})
t.assert.ok(res)
delete res.headers.date
t.assert.strictEqual(res.statusCode, 204)
t.assert.strictEqual(res.payload, '')
actualHeaders = {
'access-control-allow-origin': res.headers['access-control-allow-origin'],
'access-control-allow-methods': res.headers['access-control-allow-methods'],
vary: res.headers.vary,
'content-length': res.headers['content-length']
}
t.assert.deepStrictEqual(actualHeaders, {
'access-control-allow-origin': '*',
'access-control-allow-methods': 'GET,HEAD,POST',
vary: 'Access-Control-Request-Headers',
'content-length': '0'
})
})
test('Silences preflight logs when logLevel is "silent"', async t => {
const logs = []
const fastify = Fastify({
logger: {
level: 'info',
stream: {
write (line) {
try {
logs.push(JSON.parse(line))
} catch {
}
}
}
}
})
await fastify.register(cors, { logLevel: 'silent' })
fastify.get('/', async () => ({ ok: true }))
await fastify.ready()
t.assert.ok(fastify)
await fastify.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'https://example.com'
}
})
await fastify.inject({ method: 'GET', url: '/' })
const hasOptionsLog = logs.some(l => l.req && l.req.method === 'OPTIONS')
const hasGetLog = logs.some(l => l.req && l.req.method === 'GET')
t.assert.strictEqual(hasOptionsLog, false)
t.assert.strictEqual(hasGetLog, true)
await fastify.close()
})
test('delegator + logLevel:"silent" → OPTIONS logs are suppressed', async t => {
t.plan(3)
const logs = []
const app = Fastify({
logger: {
level: 'info',
stream: { write: l => { try { logs.push(JSON.parse(l)) } catch {} } }
}
})
await app.register(cors, {
delegator: () => ({ origin: '*' }),
logLevel: 'silent'
})
app.get('/', () => ({ ok: true }))
await app.ready()
t.assert.ok(app)
await app.inject({
method: 'OPTIONS',
url: '/',
headers: {
'access-control-request-method': 'GET',
origin: 'https://example.com'
}
})
await app.inject({ method: 'GET', url: '/' })
const hasOptionsLog = logs.some(l => l.req?.method === 'OPTIONS')
const hasGetLog = logs.some(l => l.req?.method === 'GET')
t.assert.strictEqual(hasOptionsLog, false)
t.assert.strictEqual(hasGetLog, true)
await app.close()
})
test('delegator + hideOptionsRoute:false → OPTIONS route is visible', async t => {
t.plan(2)
const app = Fastify()
app.addHook('onRoute', route => {
if (route.method === 'OPTIONS' && route.url === '*') {
t.assert.strictEqual(route.schema.hide, false)
}
})
await app.register(cors, {
delegator: () => ({ origin: '*' }),
hideOptionsRoute: false
})
await app.ready()
t.assert.ok(app)
await app.close()
})

237
node_modules/@fastify/cors/test/vary.test.js generated vendored Normal file
View File

@@ -0,0 +1,237 @@
'use strict'
const { test } = require('node:test')
const { createAddFieldnameToVary } = require('../vary')
const { parse } = require('../vary')
test('Should set * even if we set a specific field', async t => {
t.plan(1)
const addOriginToVary = createAddFieldnameToVary('Origin')
const replyMock = {
getHeader () {
return '*'
},
header () {
t.fail('Should not be here')
}
}
addOriginToVary(replyMock)
t.assert.ok(true) // equalivant to tap t.pass()
})
test('Should set * even if we set a specific field', t => {
t.plan(2)
const addWildcardToVary = createAddFieldnameToVary('*')
const replyMock = {
getHeader () {
return 'Origin'
},
header (name, value) {
t.assert.deepStrictEqual(name, 'Vary')
t.assert.deepStrictEqual(value, '*')
}
}
addWildcardToVary(replyMock)
})
test('Should set * when field contains a *', t => {
t.plan(3)
const addOriginToVary = createAddFieldnameToVary('Origin')
const replyMock = {
getHeader () {
return ['Origin', '*', 'Access-Control-Request-Headers']
},
header (name, value) {
t.assert.deepStrictEqual(name, 'Vary')
t.assert.deepStrictEqual(value, '*')
}
}
addOriginToVary(replyMock)
t.assert.ok(true) // equalivant to tap t.pass()
})
test('Should concat vary values', t => {
t.plan(3)
const addOriginToVary = createAddFieldnameToVary('Origin')
const replyMock = {
getHeader () {
return 'Access-Control-Request-Headers'
},
header (name, value) {
t.assert.deepStrictEqual(name, 'Vary')
t.assert.deepStrictEqual(value, 'Access-Control-Request-Headers, Origin')
}
}
addOriginToVary(replyMock)
t.assert.ok(true) // equalivant to tap t.pass()
})
test('Should concat vary values ignoring consecutive commas', t => {
t.plan(3)
const addOriginToVary = createAddFieldnameToVary('Origin')
const replyMock = {
getHeader () {
return ' Access-Control-Request-Headers,Access-Control-Request-Method'
},
header (name, value) {
t.assert.deepStrictEqual(name, 'Vary')
t.assert.deepStrictEqual(value, ' Access-Control-Request-Headers,Access-Control-Request-Method, Origin')
}
}
addOriginToVary(replyMock)
t.assert.ok(true) // equalivant to tap t.pass()
})
test('Should concat vary values ignoring whitespace', t => {
t.plan(3)
const addOriginToVary = createAddFieldnameToVary('Origin')
const replyMock = {
getHeader () {
return ' Access-Control-Request-Headers ,Access-Control-Request-Method'
},
header (name, value) {
t.assert.deepStrictEqual(name, 'Vary')
t.assert.deepStrictEqual(value, ' Access-Control-Request-Headers ,Access-Control-Request-Method, Origin')
}
}
addOriginToVary(replyMock)
t.assert.ok(true) // equalivant to tap t.pass()
})
test('Should set the field as value for vary if no vary is defined', t => {
t.plan(2)
const addOriginToVary = createAddFieldnameToVary('Origin')
const replyMock = {
getHeader () {
return undefined
},
header (name, value) {
t.assert.deepStrictEqual(name, 'Vary')
t.assert.deepStrictEqual(value, 'Origin')
}
}
addOriginToVary(replyMock)
})
test('Should set * as value for vary if vary contains *', t => {
t.plan(2)
const addOriginToVary = createAddFieldnameToVary('Origin')
const replyMock = {
getHeader () {
return 'Accept,*'
},
header (name, value) {
t.assert.deepStrictEqual(name, 'Vary')
t.assert.deepStrictEqual(value, '*')
}
}
addOriginToVary(replyMock)
})
test('Should set Accept-Encoding as value for vary if vary is empty string', t => {
t.plan(2)
const addAcceptEncodingToVary = createAddFieldnameToVary('Accept-Encoding')
const replyMock = {
getHeader () {
return ''
},
header (name, value) {
t.assert.deepStrictEqual(name, 'Vary')
t.assert.deepStrictEqual(value, 'Accept-Encoding')
}
}
addAcceptEncodingToVary(replyMock)
})
test('Should have no issues with values containing dashes', t => {
t.plan(2)
const addXFooToVary = createAddFieldnameToVary('X-Foo')
const replyMock = {
value: 'Accept-Encoding',
getHeader () {
return this.value
},
header (name, value) {
t.assert.deepStrictEqual(name, 'Vary')
t.assert.deepStrictEqual(value, 'Accept-Encoding, X-Foo')
this.value = value
}
}
addXFooToVary(replyMock)
addXFooToVary(replyMock)
})
test('Should ignore the header as value for vary if it is already in vary', t => {
t.plan(1)
const addOriginToVary = createAddFieldnameToVary('Origin')
const replyMock = {
getHeader () {
return 'Origin'
},
header () {
t.fail('Should not be here')
}
}
addOriginToVary(replyMock)
addOriginToVary(replyMock)
t.assert.ok(true) // equalivant to tap t.pass()
})
test('parse', t => {
t.plan(18)
t.assert.deepStrictEqual(parse(''), [])
t.assert.deepStrictEqual(parse('a'), ['a'])
t.assert.deepStrictEqual(parse('a,b'), ['a', 'b'])
t.assert.deepStrictEqual(parse(' a,b'), ['a', 'b'])
t.assert.deepStrictEqual(parse('a,b '), ['a', 'b'])
t.assert.deepStrictEqual(parse('a,b,c'), ['a', 'b', 'c'])
t.assert.deepStrictEqual(parse('A,b,c'), ['a', 'b', 'c'])
t.assert.deepStrictEqual(parse('a,b,c,'), ['a', 'b', 'c'])
t.assert.deepStrictEqual(parse('a,b,c, '), ['a', 'b', 'c'])
t.assert.deepStrictEqual(parse(',a,b,c'), ['a', 'b', 'c'])
t.assert.deepStrictEqual(parse(' ,a,b,c'), ['a', 'b', 'c'])
t.assert.deepStrictEqual(parse('a,,b,c'), ['a', 'b', 'c'])
t.assert.deepStrictEqual(parse('a,,,b,,c'), ['a', 'b', 'c'])
t.assert.deepStrictEqual(parse('a, b,c'), ['a', 'b', 'c'])
t.assert.deepStrictEqual(parse('a, b,c'), ['a', 'b', 'c'])
t.assert.deepStrictEqual(parse('a, , b,c'), ['a', 'b', 'c'])
t.assert.deepStrictEqual(parse('a, , b,c'), ['a', 'b', 'c'])
// one for the cache
t.assert.deepStrictEqual(parse('A,b,c'), ['a', 'b', 'c'])
})
test('createAddFieldnameToVary', async t => {
t.plan(4)
t.assert.strictEqual(typeof createAddFieldnameToVary('valid-header'), 'function')
await t.assert.rejects(
async () => createAddFieldnameToVary('invalid:header'),
(err) => {
t.assert.strictEqual(err.name, 'TypeError')
t.assert.strictEqual(err.message, 'Fieldname contains invalid characters.')
return true
}
)
})

126
node_modules/@fastify/cors/types/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1,126 @@
/// <reference types="node" />
import { FastifyInstance, FastifyPluginCallback, FastifyRequest, LogLevel } from 'fastify'
type OriginCallback = (err: Error | null, origin: ValueOrArray<OriginType>) => void
type OriginType = string | boolean | RegExp
type ValueOrArray<T> = T | ArrayOfValueOrArray<T>
interface ArrayOfValueOrArray<T> extends Array<ValueOrArray<T>> {
}
type FastifyCorsPlugin = FastifyPluginCallback<
NonNullable<fastifyCors.FastifyCorsOptions> | fastifyCors.FastifyCorsOptionsDelegate
>
type FastifyCorsHook =
| 'onRequest'
| 'preParsing'
| 'preValidation'
| 'preHandler'
| 'preSerialization'
| 'onSend'
declare namespace fastifyCors {
export type OriginFunction = (origin: string | undefined, callback: OriginCallback) => void
export type AsyncOriginFunction = (origin: string | undefined) => Promise<ValueOrArray<OriginType>>
export interface FastifyCorsOptions {
/**
* Configures the Lifecycle Hook.
*/
hook?: FastifyCorsHook;
/**
* Configures the delegate function.
*/
delegator?: FastifyCorsOptionsDelegate;
/**
* Configures the Access-Control-Allow-Origin CORS header.
*/
origin?: ValueOrArray<OriginType> | fastifyCors.AsyncOriginFunction | fastifyCors.OriginFunction;
/**
* Configures the Access-Control-Allow-Credentials CORS header.
* Set to true to pass the header, otherwise it is omitted.
*/
credentials?: boolean;
/**
* Configures the Access-Control-Expose-Headers CORS header.
* Expects a comma-delimited string (ex: 'Content-Range,X-Content-Range')
* or an array (ex: ['Content-Range', 'X-Content-Range']).
* If not specified, no custom headers are exposed.
*/
exposedHeaders?: string | string[];
/**
* Configures the Access-Control-Allow-Headers CORS header.
* Expects a comma-delimited string (ex: 'Content-Type,Authorization')
* or an array (ex: ['Content-Type', 'Authorization']). If not
* specified, defaults to reflecting the headers specified in the
* request's Access-Control-Request-Headers header.
*/
allowedHeaders?: string | string[];
/**
* Configures the Access-Control-Allow-Methods CORS header.
* Expects a comma-delimited string (ex: 'GET,PUT,POST') or an array (ex: ['GET', 'PUT', 'POST']).
*/
methods?: string | string[];
/**
* Configures the Access-Control-Max-Age CORS header.
* Set to an integer to pass the header, otherwise it is omitted.
*/
maxAge?: number;
/**
* Configures the Cache-Control header for CORS preflight responses.
* Set to an integer to pass the header as `Cache-Control: max-age=${cacheControl}`,
* or set to a string to pass the header as `Cache-Control: ${cacheControl}` (fully define
* the header value), otherwise the header is omitted.
*/
cacheControl?: number | string;
/**
* Pass the CORS preflight response to the route handler (default: false).
*/
preflightContinue?: boolean;
/**
* Provides a status code to use for successful OPTIONS requests,
* since some legacy browsers (IE11, various SmartTVs) choke on 204.
*/
optionsSuccessStatus?: number;
/**
* Pass the CORS preflight response to the route handler (default: true).
*/
preflight?: boolean;
/**
* Enforces strict requirement of the CORS preflight request headers (Access-Control-Request-Method and Origin).
* Preflight requests without the required headers will result in 400 errors when set to `true` (default: `true`).
*/
strictPreflight?: boolean;
/**
* Hide options route from the documentation built using fastify-swagger (default: true).
*/
hideOptionsRoute?: boolean;
/**
* Sets the Fastify log level specifically for the internal OPTIONS route
* used to handle CORS preflight requests. For example, setting this to `'silent'`
* will prevent these requests from being logged.
* Useful for reducing noise in application logs.
* Default: inherits Fastify's global log level.
*/
logLevel?: LogLevel;
}
export interface FastifyCorsOptionsDelegateCallback { (req: FastifyRequest, cb: (error: Error | null, corsOptions?: FastifyCorsOptions) => void): void }
export interface FastifyCorsOptionsDelegatePromise { (req: FastifyRequest): Promise<FastifyCorsOptions> }
export type FastifyCorsOptionsDelegate = FastifyCorsOptionsDelegateCallback | FastifyCorsOptionsDelegatePromise
export type FastifyPluginOptionsDelegate<T = FastifyCorsOptionsDelegate> = (instance: FastifyInstance) => T
export const fastifyCors: FastifyCorsPlugin
export { fastifyCors as default }
}
declare function fastifyCors (
...params: Parameters<FastifyCorsPlugin>
): ReturnType<FastifyCorsPlugin>
export = fastifyCors

388
node_modules/@fastify/cors/types/index.test-d.ts generated vendored Normal file
View File

@@ -0,0 +1,388 @@
import fastify, { FastifyRequest } from 'fastify'
import { expectType } from 'tsd'
import fastifyCors, {
AsyncOriginFunction,
FastifyCorsOptions,
FastifyCorsOptionsDelegate,
FastifyCorsOptionsDelegatePromise,
FastifyPluginOptionsDelegate,
OriginFunction
} from '..'
const app = fastify()
app.register(fastifyCors)
app.register(fastifyCors, {
origin: true,
allowedHeaders: 'authorization,content-type',
methods: 'GET,POST,PUT,PATCH,DELETE,OPTIONS',
credentials: true,
exposedHeaders: 'authorization',
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
app.register(fastifyCors, {
origin: true,
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 'public, max-age=3500',
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
app.register(fastifyCors, {
origin: '*',
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
app.register(fastifyCors, {
origin: /\*/,
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
app.register(fastifyCors, {
origin: ['*', 'something'],
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
app.register(fastifyCors, {
origin: [/\*/, /something/],
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
const corsDelegate: OriginFunction = (origin, cb) => {
if (origin === undefined || /localhost/.test(origin)) {
cb(null, true)
return
}
cb(new Error(), false)
}
app.register(fastifyCors, {
origin: corsDelegate,
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
const asyncCorsDelegate: OriginFunction = async (origin) => {
if (origin === undefined || /localhost/.test(origin)) {
return true
}
return false
}
app.register(fastifyCors, {
origin: asyncCorsDelegate,
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
app.register(fastifyCors, {
origin: (_origin, cb) => cb(null, true)
})
app.register(fastifyCors, {
origin: (_origin, cb) => cb(null, '*')
})
app.register(fastifyCors, {
origin: (_origin, cb) => cb(null, /\*/)
})
const appHttp2 = fastify({ http2: true })
appHttp2.register(fastifyCors)
appHttp2.register(fastifyCors, {
origin: true,
allowedHeaders: 'authorization,content-type',
methods: 'GET,POST,PUT,PATCH,DELETE,OPTIONS',
credentials: true,
exposedHeaders: 'authorization',
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false,
logLevel: 'silent'
})
appHttp2.register(fastifyCors, {
origin: true,
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
appHttp2.register(fastifyCors, {
origin: '*',
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
appHttp2.register(fastifyCors, {
origin: /\*/,
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
appHttp2.register(fastifyCors, {
origin: ['*', 'something'],
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
appHttp2.register(fastifyCors, {
origin: [/\*/, /something/],
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
appHttp2.register(fastifyCors, {
origin: (origin: string | undefined, cb: (err: Error | null, allow: boolean) => void) => {
if (origin === undefined || /localhost/.test(origin)) {
cb(null, true)
return
}
cb(new Error(), false)
},
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
appHttp2.register(fastifyCors, (): FastifyCorsOptionsDelegate => (_req, cb) => {
cb(null, {
origin: [/\*/, /something/],
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
})
appHttp2.register(fastifyCors, (): FastifyCorsOptionsDelegatePromise => () => {
return Promise.resolve({
origin: [/\*/, /something/],
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
})
const delegate: FastifyPluginOptionsDelegate<FastifyCorsOptionsDelegatePromise> = () => async () => {
return {
origin: [/\*/, /something/],
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 13000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
}
}
appHttp2.register(fastifyCors, {
hook: 'onRequest'
})
appHttp2.register(fastifyCors, {
hook: 'preParsing'
})
appHttp2.register(fastifyCors, {
hook: 'preValidation'
})
appHttp2.register(fastifyCors, {
hook: 'preHandler'
})
appHttp2.register(fastifyCors, {
hook: 'preSerialization'
})
appHttp2.register(fastifyCors, {
hook: 'onSend'
})
appHttp2.register(fastifyCors, {
hook: 'preParsing',
delegator: (req, cb) => {
if (req.url.startsWith('/some-value')) {
cb(new Error())
}
cb(null, {
origin: [/\*/, /something/],
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 12000,
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
})
}
})
appHttp2.register(fastifyCors, {
hook: 'preParsing',
delegator: async (_req: FastifyRequest): Promise<FastifyCorsOptions> => {
return {
origin: [/\*/, /something/],
allowedHeaders: ['authorization', 'content-type'],
methods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
exposedHeaders: ['authorization'],
maxAge: 13000,
cacheControl: 'public, max-age=3500',
preflightContinue: false,
optionsSuccessStatus: 200,
preflight: false,
strictPreflight: false
}
}
})
appHttp2.register(fastifyCors, delegate)
appHttp2.register(fastifyCors, {
hook: 'preParsing',
origin: function (origin, cb) {
expectType<string | undefined>(origin)
cb(null, false)
},
})
const asyncOriginFn: AsyncOriginFunction = async function (origin): Promise<boolean> {
expectType<string | undefined>(origin)
return false
}
appHttp2.register(fastifyCors, {
hook: 'preParsing',
origin: asyncOriginFn,
})

116
node_modules/@fastify/cors/vary.js generated vendored Normal file
View File

@@ -0,0 +1,116 @@
'use strict'
const { FifoMap: FifoCache } = require('toad-cache')
/**
* Field Value Components
* Most HTTP header field values are defined using common syntax
* components (token, quoted-string, and comment) separated by
* whitespace or specific delimiting characters. Delimiters are chosen
* from the set of US-ASCII visual characters not allowed in a token
* (DQUOTE and "(),/:;<=>?@[\]{}").
*
* field-name = token
* token = 1*tchar
* tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*"
* / "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
* / DIGIT / ALPHA
* ; any VCHAR, except delimiters
*
* @see https://datatracker.ietf.org/doc/html/rfc7230#section-3.2.6
*/
const validFieldnameRE = /^[!#$%&'*+\-.^\w`|~]+$/u
function validateFieldname (fieldname) {
if (validFieldnameRE.test(fieldname) === false) {
throw new TypeError('Fieldname contains invalid characters.')
}
}
function parse (header) {
header = header.trim().toLowerCase()
const result = []
if (header.length === 0) {
// pass through
} else if (header.indexOf(',') === -1) {
result.push(header)
} else {
const il = header.length
let i = 0
let pos = 0
let char
// tokenize the header
for (i; i < il; ++i) {
char = header[i]
// when we have whitespace set the pos to the next position
if (char === ' ') {
pos = i + 1
// `,` is the separator of vary-values
} else if (char === ',') {
// if pos and current position are not the same we have a valid token
if (pos !== i) {
result.push(header.slice(pos, i))
}
// reset the positions
pos = i + 1
}
}
if (pos !== i) {
result.push(header.slice(pos, i))
}
}
return result
}
function createAddFieldnameToVary (fieldname) {
const headerCache = new FifoCache(1000)
validateFieldname(fieldname)
return function (reply) {
let header = reply.getHeader('Vary')
if (!header) {
reply.header('Vary', fieldname)
return
}
if (header === '*') {
return
}
if (fieldname === '*') {
reply.header('Vary', '*')
return
}
if (Array.isArray(header)) {
header = header.join(', ')
}
if (headerCache.get(header) === undefined) {
const vals = parse(header)
if (vals.indexOf('*') !== -1) {
headerCache.set(header, '*')
} else if (vals.indexOf(fieldname.toLowerCase()) === -1) {
headerCache.set(header, header + ', ' + fieldname)
} else {
headerCache.set(header, null)
}
}
const cached = headerCache.get(header)
if (cached !== null) {
reply.header('Vary', cached)
}
}
}
module.exports.createAddFieldnameToVary = createAddFieldnameToVary
module.exports.addOriginToVaryHeader = createAddFieldnameToVary('Origin')
module.exports.addAccessControlRequestHeadersToVaryHeader = createAddFieldnameToVary('Access-Control-Request-Headers')
module.exports.parse = parse