Projektstart
This commit is contained in:
22
backend/node_modules/@fastify/accept-negotiator/LICENSE
generated
vendored
Normal file
22
backend/node_modules/@fastify/accept-negotiator/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2022 The 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.
|
||||
52
backend/node_modules/@fastify/accept-negotiator/README.md
generated
vendored
Normal file
52
backend/node_modules/@fastify/accept-negotiator/README.md
generated
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
# @fastify/accept-negotiator
|
||||
|
||||
|
||||
[](https://github.com/fastify/accept-negotiator/actions/workflows/ci.yml)
|
||||
[](https://www.npmjs.com/package/@fastify/accept-negotiator)
|
||||
[](https://standardjs.com/)
|
||||
|
||||
A negotiator for the accept-headers
|
||||
|
||||
### Install
|
||||
```
|
||||
npm i @fastify/accept-negotiator
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
The module exports a function that you can use for negotiating an accept-header, e.g. accept-encoding. It takes 2 parameters:
|
||||
|
||||
```
|
||||
negotiate(header, supportedValues)
|
||||
```
|
||||
|
||||
- `header` (`string`, required) - The accept-header, e.g. accept-encoding
|
||||
- `supportedValues` (`string[]`, required) - The values, which are supported
|
||||
|
||||
```js
|
||||
const negotiate = require('@fastify/accept-negotiator').negotiate
|
||||
const encoding = negotiate('gzip, deflate, br', ['br'])
|
||||
console.log(encoding) // 'br*
|
||||
```
|
||||
|
||||
The module also exports a class that you can use for negotiating an accept-header, e.g. accept-encoding, and use caching for better performance.
|
||||
|
||||
|
||||
```
|
||||
Negotiate(supportedValues)
|
||||
```
|
||||
|
||||
- `supportedValues` (`string[]`, required) - The values, which are supported
|
||||
- `cache` (`{ set: Function; get: Function; has: Function }`, optional) - A Cache-Store, e.g. ES6-Map or mnemonist LRUCache
|
||||
|
||||
```js
|
||||
const Negotiator = require('@fastify/accept-negotiator').Negotiator
|
||||
const encodingNegotiator = new Negotiator({ supportedValues: ['br'], cache: new Map() })
|
||||
|
||||
const encoding = encodingNegotiator.negotiate('gzip, deflate, br')
|
||||
console.log(encoding) // 'br*
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Licensed under [MIT](./LICENSE).
|
||||
170
backend/node_modules/@fastify/accept-negotiator/index.js
generated
vendored
Normal file
170
backend/node_modules/@fastify/accept-negotiator/index.js
generated
vendored
Normal file
@@ -0,0 +1,170 @@
|
||||
'use strict'
|
||||
|
||||
function Negotiator (options) {
|
||||
if (!new.target) {
|
||||
return new Negotiator(options)
|
||||
}
|
||||
|
||||
const {
|
||||
supportedValues = [],
|
||||
cache
|
||||
} = (options && typeof options === 'object' && options) || {}
|
||||
|
||||
this.supportedValues = supportedValues
|
||||
|
||||
this.cache = cache
|
||||
}
|
||||
|
||||
Negotiator.prototype.negotiate = function (header) {
|
||||
if (typeof header !== 'string') {
|
||||
return null
|
||||
}
|
||||
if (!this.cache) {
|
||||
return negotiate(header, this.supportedValues)
|
||||
}
|
||||
if (!this.cache.has(header)) {
|
||||
this.cache.set(header, negotiate(header, this.supportedValues))
|
||||
}
|
||||
return this.cache.get(header)
|
||||
}
|
||||
|
||||
function negotiate (header, supportedValues) {
|
||||
if (
|
||||
!header ||
|
||||
!Array.isArray(supportedValues) ||
|
||||
supportedValues.length === 0
|
||||
) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (header === '*') {
|
||||
return supportedValues[0]
|
||||
}
|
||||
|
||||
let preferredEncoding = null
|
||||
let preferredEncodingPriority = Infinity
|
||||
let preferredEncodingQuality = 0
|
||||
|
||||
function processMatch (enc, quality) {
|
||||
if (quality === 0 || preferredEncodingQuality > quality) {
|
||||
return false
|
||||
}
|
||||
|
||||
const encoding = (enc === '*' && supportedValues[0]) || enc
|
||||
const priority = supportedValues.indexOf(encoding)
|
||||
if (priority === -1) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (priority === 0 && quality === 1) {
|
||||
preferredEncoding = encoding
|
||||
return true
|
||||
} else if (preferredEncodingQuality < quality) {
|
||||
preferredEncoding = encoding
|
||||
preferredEncodingPriority = priority
|
||||
preferredEncodingQuality = quality
|
||||
} else if (preferredEncodingPriority > priority) {
|
||||
preferredEncoding = encoding
|
||||
preferredEncodingPriority = priority
|
||||
preferredEncodingQuality = quality
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
parse(header, processMatch)
|
||||
|
||||
return preferredEncoding
|
||||
}
|
||||
|
||||
const BEGIN = 0
|
||||
const TOKEN = 1
|
||||
const QUALITY = 2
|
||||
const END = 3
|
||||
|
||||
function parse (header, processMatch) {
|
||||
let str = ''
|
||||
let quality
|
||||
let state = BEGIN
|
||||
for (let i = 0, il = header.length; i < il; ++i) {
|
||||
const char = header[i]
|
||||
|
||||
if (char === ' ' || char === '\t') {
|
||||
continue
|
||||
} else if (char === ';') {
|
||||
if (state === TOKEN) {
|
||||
state = QUALITY
|
||||
quality = ''
|
||||
}
|
||||
continue
|
||||
} else if (char === ',') {
|
||||
if (state === TOKEN) {
|
||||
if (processMatch(str, 1)) {
|
||||
state = END
|
||||
break
|
||||
}
|
||||
state = BEGIN
|
||||
str = ''
|
||||
} else if (state === QUALITY) {
|
||||
if (processMatch(str, parseFloat(quality) || 0)) {
|
||||
state = END
|
||||
break
|
||||
}
|
||||
state = BEGIN
|
||||
str = ''
|
||||
quality = ''
|
||||
}
|
||||
continue
|
||||
} else if (
|
||||
state === QUALITY
|
||||
) {
|
||||
if (char === 'q' || char === '=') {
|
||||
continue
|
||||
} else if (
|
||||
char === '.' ||
|
||||
char === '1' ||
|
||||
char === '0' ||
|
||||
char === '2' ||
|
||||
char === '3' ||
|
||||
char === '4' ||
|
||||
char === '5' ||
|
||||
char === '6' ||
|
||||
char === '7' ||
|
||||
char === '8' ||
|
||||
char === '9'
|
||||
) {
|
||||
quality += char
|
||||
continue
|
||||
}
|
||||
} else if (state === BEGIN) {
|
||||
state = TOKEN
|
||||
str += char
|
||||
continue
|
||||
}
|
||||
if (state === TOKEN) {
|
||||
const prevChar = header[i - 1]
|
||||
if (prevChar === ' ' || prevChar === '\t') {
|
||||
str = ''
|
||||
}
|
||||
str += char
|
||||
continue
|
||||
}
|
||||
if (processMatch(str, parseFloat(quality) || 0)) {
|
||||
state = END
|
||||
break
|
||||
}
|
||||
state = BEGIN
|
||||
str = char
|
||||
quality = ''
|
||||
}
|
||||
|
||||
if (state === TOKEN) {
|
||||
processMatch(str, 1)
|
||||
} else if (state === QUALITY) {
|
||||
processMatch(str, parseFloat(quality) || 0)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = negotiate
|
||||
module.exports.default = negotiate
|
||||
module.exports.negotiate = negotiate
|
||||
module.exports.Negotiator = Negotiator
|
||||
51
backend/node_modules/@fastify/accept-negotiator/package.json
generated
vendored
Normal file
51
backend/node_modules/@fastify/accept-negotiator/package.json
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"name": "@fastify/accept-negotiator",
|
||||
"version": "1.1.0",
|
||||
"description": "a negotiator for the accept-headers",
|
||||
"type": "commonjs",
|
||||
"main": "index.js",
|
||||
"types": "types/index.d.ts",
|
||||
"scripts": {
|
||||
"lint": "standard index.js test/* benchmarks/*",
|
||||
"test": "npm run test:unit && npm run test:typescript",
|
||||
"test:unit": "tap",
|
||||
"test:typescript": "tsd"
|
||||
},
|
||||
"standard": {
|
||||
"ignore": [
|
||||
"index.d.ts"
|
||||
]
|
||||
},
|
||||
"keywords": [
|
||||
"encoding",
|
||||
"negotiator",
|
||||
"accept-encoding",
|
||||
"accept",
|
||||
"http",
|
||||
"header"
|
||||
],
|
||||
"files": [
|
||||
"README.md",
|
||||
"LICENSE",
|
||||
"index.js",
|
||||
"types/index.d.ts"
|
||||
],
|
||||
"author": "Aras Abbasi",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"benchmark": "2.1.4",
|
||||
"standard": "17.0.0",
|
||||
"tap": "^16.3.0",
|
||||
"tsd": "^0.24.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/fastify/accept-negotiator.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/fastify/accept-negotiator/issues"
|
||||
}
|
||||
}
|
||||
17
backend/node_modules/@fastify/accept-negotiator/types/index.d.ts
generated
vendored
Normal file
17
backend/node_modules/@fastify/accept-negotiator/types/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
type CacheStore = { set: (key: string, value: string) => CacheStore, get: (key: string) => string | undefined, has: (key: string) => boolean }
|
||||
|
||||
type NegotiateFn = typeof negotiate
|
||||
|
||||
declare namespace negotiate {
|
||||
export class Negotiator<K extends string = string> {
|
||||
constructor (options: { supportedValues: K[]; cache?: CacheStore })
|
||||
|
||||
negotiate(header: string): K | null
|
||||
}
|
||||
|
||||
export const negotiate: NegotiateFn
|
||||
export { negotiate as default }
|
||||
}
|
||||
|
||||
declare function negotiate<K extends string = string>(header: string, supportedValues: K[]): K | null;
|
||||
export = negotiate;
|
||||
2
backend/node_modules/@fastify/ajv-compiler/.gitattributes
generated
vendored
Normal file
2
backend/node_modules/@fastify/ajv-compiler/.gitattributes
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Set default behavior to automatically convert line endings
|
||||
* text=auto eol=lf
|
||||
21
backend/node_modules/@fastify/ajv-compiler/.github/.stale.yml
generated
vendored
Normal file
21
backend/node_modules/@fastify/ajv-compiler/.github/.stale.yml
generated
vendored
Normal 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
|
||||
13
backend/node_modules/@fastify/ajv-compiler/.github/dependabot.yml
generated
vendored
Normal file
13
backend/node_modules/@fastify/ajv-compiler/.github/dependabot.yml
generated
vendored
Normal 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: "weekly"
|
||||
open-pull-requests-limit: 10
|
||||
8
backend/node_modules/@fastify/ajv-compiler/.github/tests_checker.yml
generated
vendored
Normal file
8
backend/node_modules/@fastify/ajv-compiler/.github/tests_checker.yml
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
comment: |
|
||||
Hello! Thank you for contributing!
|
||||
It appears that you have changed the code, but the tests that verify your change are missing. Could you please add them?
|
||||
fileExtensions:
|
||||
- '.ts'
|
||||
- '.js'
|
||||
|
||||
testDir: 'test'
|
||||
26
backend/node_modules/@fastify/ajv-compiler/.github/workflows/ci.yml
generated
vendored
Normal file
26
backend/node_modules/@fastify/ajv-compiler/.github/workflows/ci.yml
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
name: Continuous Integration
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
- next
|
||||
- 'v*'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
|
||||
env:
|
||||
TZ: 'UTC'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
uses: fastify/workflows/.github/workflows/plugins-ci.yml@v3
|
||||
with:
|
||||
lint: true
|
||||
license-check: true
|
||||
2
backend/node_modules/@fastify/ajv-compiler/.taprc
generated
vendored
Normal file
2
backend/node_modules/@fastify/ajv-compiler/.taprc
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
files:
|
||||
- test/**/*.test.js
|
||||
24
backend/node_modules/@fastify/ajv-compiler/LICENSE
generated
vendored
Normal file
24
backend/node_modules/@fastify/ajv-compiler/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) The Fastify Team
|
||||
|
||||
The Fastify team members are listed at https://github.com/fastify/fastify#team
|
||||
and in the README file.
|
||||
|
||||
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.
|
||||
236
backend/node_modules/@fastify/ajv-compiler/README.md
generated
vendored
Normal file
236
backend/node_modules/@fastify/ajv-compiler/README.md
generated
vendored
Normal file
@@ -0,0 +1,236 @@
|
||||
# @fastify/ajv-compiler
|
||||
|
||||
[](http://standardjs.com/)
|
||||
[](https://github.com/fastify/ajv-compiler/actions/workflows/ci.yml)
|
||||
|
||||
This module manages the [`ajv`](https://www.npmjs.com/package/ajv) instances for the Fastify framework.
|
||||
It isolates the `ajv` dependency so that the AJV version is not tightly coupled to the Fastify version.
|
||||
This allows the user to decide which version of AJV to use in their Fastify based application.
|
||||
|
||||
|
||||
## Versions
|
||||
|
||||
| `@fastify/ajv-compiler` | `ajv` | Default in `fastify` |
|
||||
|------------------------:|------:|---------------------:|
|
||||
| v1.x | v6.x | ^3.14 |
|
||||
| v2.x | v8.x | - |
|
||||
| v3.x | v8.x | ^4.x |
|
||||
|
||||
|
||||
### AJV Configuration
|
||||
|
||||
The Fastify's default [`ajv` options](https://github.com/ajv-validator/ajv/tree/v6#options) are:
|
||||
|
||||
```js
|
||||
{
|
||||
coerceTypes: 'array',
|
||||
useDefaults: true,
|
||||
removeAdditional: true,
|
||||
uriResolver: require('fast-uri'),
|
||||
addUsedSchema: false,
|
||||
// Explicitly set allErrors to `false`.
|
||||
// When set to `true`, a DoS attack is possible.
|
||||
allErrors: false
|
||||
}
|
||||
```
|
||||
|
||||
Moreover, the [`ajv-formats`](https://www.npmjs.com/package/ajv-formats) module is included by default.
|
||||
If you need to customize it, check the _usage_ section below.
|
||||
|
||||
To customize the `ajv`'s options, see how in the [Fastify official docs](https://fastify.dev/docs/latest/Reference/Server/#ajv).
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
This module is already used as default by Fastify.
|
||||
If you need to provide to your server instance a different version, refer to [the official doc](https://fastify.dev/docs/latest/Reference/Server/#schemacontroller).
|
||||
|
||||
### Customize the `ajv-formats` plugin
|
||||
|
||||
The `format` keyword is not part of the official `ajv` module since v7. To use it, you need to install the `ajv-formats` module and this module
|
||||
does it for you with the default configuration.
|
||||
|
||||
If you need to configure the `ajv-formats` plugin you can do it using the standard Fastify configuration:
|
||||
|
||||
```js
|
||||
const app = fastify({
|
||||
ajv: {
|
||||
plugins: [[require('ajv-formats'), { mode: 'fast' }]]
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
In this way, your setup will have precendence over the `@fastify/ajv-compiler` default configuration.
|
||||
|
||||
### Customize the `ajv` instance
|
||||
|
||||
If you need to customize the `ajv` instance and take full control of its configuration, you can do it by
|
||||
using the `onCreate` option in the Fastify configuration that accepts a syncronous function that receives the `ajv` instance:
|
||||
|
||||
```js
|
||||
const app = fastify({
|
||||
ajv: {
|
||||
onCreate: (ajv) => {
|
||||
// Modify the ajv instance as you need.
|
||||
ajv.addFormat('myFormat', (data) => typeof data === 'string')
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### Fastify with JTD
|
||||
|
||||
The [JSON Type Definition](https://jsontypedef.com/) feature is supported by AJV v8.x and you can benefit from it in your Fastify application.
|
||||
|
||||
With Fastify v3.20.x and higher, you can use the `@fastify/ajv-compiler` module to load JSON Type Definitions like so:
|
||||
|
||||
```js
|
||||
const factory = require('@fastify/ajv-compiler')()
|
||||
|
||||
const app = fastify({
|
||||
jsonShorthand: false,
|
||||
ajv: {
|
||||
customOptions: { }, // additional JTD options
|
||||
mode: 'JTD'
|
||||
},
|
||||
schemaController: {
|
||||
compilersFactory: {
|
||||
buildValidator: factory
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
The defaults AJV JTD options are the same as the [Fastify's default options](#AJV-Configuration).
|
||||
|
||||
#### Fastify with JTD and serialization
|
||||
|
||||
You can use JTD Schemas to serialize your response object too:
|
||||
|
||||
```js
|
||||
const factoryValidator = require('@fastify/ajv-compiler')()
|
||||
const factorySerializer = require('@fastify/ajv-compiler')({ jtdSerializer: true })
|
||||
|
||||
const app = fastify({
|
||||
jsonShorthand: false,
|
||||
ajv: {
|
||||
customOptions: { }, // additional JTD options
|
||||
mode: 'JTD'
|
||||
},
|
||||
schemaController: {
|
||||
compilersFactory: {
|
||||
buildValidator: factoryValidator,
|
||||
buildSerializer: factorySerializer
|
||||
}
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
### AJV Standalone
|
||||
|
||||
AJV v8 introduces the [standalone feature](https://ajv.js.org/standalone.html) that let you to pre-compile your schemas and use them in your application for a faster startup.
|
||||
|
||||
To use this feature, you must be aware of the following:
|
||||
|
||||
1. You must generate and save the application's compiled schemas.
|
||||
2. Read the compiled schemas from the file and provide them back to your Fastify application.
|
||||
|
||||
|
||||
#### Generate and save the compiled schemas
|
||||
|
||||
Fastify helps you to generate the validation schemas functions and it is your choice to save them where you want.
|
||||
To accomplish this, you must use a new compiler: `StandaloneValidator`.
|
||||
|
||||
You must provide 2 parameters to this compiler:
|
||||
|
||||
- `readMode: false`: a boolean to indicate that you want generate the schemas functions string.
|
||||
- `storeFunction`" a sync function that must store the source code of the schemas functions. You may provide an async function too, but you must manage errors.
|
||||
|
||||
When `readMode: false`, **the compiler is meant to be used in development ONLY**.
|
||||
|
||||
|
||||
```js
|
||||
const { StandaloneValidator } = require('@fastify/ajv-compiler')
|
||||
const factory = StandaloneValidator({
|
||||
readMode: false,
|
||||
storeFunction (routeOpts, schemaValidationCode) {
|
||||
// routeOpts is like: { schema, method, url, httpPart }
|
||||
// schemaValidationCode is a string source code that is the compiled schema function
|
||||
const fileName = generateFileName(routeOpts)
|
||||
fs.writeFileSync(path.join(__dirname, fileName), schemaValidationCode)
|
||||
}
|
||||
})
|
||||
|
||||
const app = fastify({
|
||||
jsonShorthand: false,
|
||||
schemaController: {
|
||||
compilersFactory: {
|
||||
buildValidator: factory
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// ... add all your routes with schemas ...
|
||||
|
||||
app.ready().then(() => {
|
||||
// at this stage all your schemas are compiled and stored in the file system
|
||||
// now it is important to turn off the readMode
|
||||
})
|
||||
```
|
||||
|
||||
#### Read the compiled schemas functions
|
||||
|
||||
At this stage, you should have a file for every route's schema.
|
||||
To use them, you must use the `StandaloneValidator` with the parameters:
|
||||
|
||||
- `readMode: true`: a boolean to indicate that you want read and use the schemas functions string.
|
||||
- `restoreFunction`" a sync function that must return a function to validate the route.
|
||||
|
||||
Important keep away before you continue reading the documentation:
|
||||
|
||||
- when you use the `readMode: true`, the application schemas are not compiled (they are ignored). So, if you change your schemas, you must recompile them!
|
||||
- as you can see, you must relate the route's schema to the file name using the `routeOpts` object. You may use the `routeOpts.schema.$id` field to do so, it is up to you to define a unique schema identifier.
|
||||
|
||||
```js
|
||||
const { StandaloneValidator } = require('@fastify/ajv-compiler')
|
||||
const factory = StandaloneValidator({
|
||||
readMode: true,
|
||||
restoreFunction (routeOpts) {
|
||||
// routeOpts is like: { schema, method, url, httpPart }
|
||||
const fileName = generateFileName(routeOpts)
|
||||
return require(path.join(__dirname, fileName))
|
||||
}
|
||||
})
|
||||
|
||||
const app = fastify({
|
||||
jsonShorthand: false,
|
||||
schemaController: {
|
||||
compilersFactory: {
|
||||
buildValidator: factory
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// ... add all your routes with schemas as before...
|
||||
|
||||
app.listen({ port: 3000 })
|
||||
```
|
||||
|
||||
### How it works
|
||||
|
||||
This module provide a factory function to produce [Validator Compilers](https://fastify.dev/docs/latest/Reference/Server/#validatorcompiler) functions.
|
||||
|
||||
The Fastify factory function is just one per server instance and it is called for every encapsulated context created by the application through the `fastify.register()` call.
|
||||
|
||||
Every Validator Compiler produced, has a dedicated AJV instance, so, this factory will try to produce as less as possible AJV instances to reduce the memory footprint and the startup time.
|
||||
|
||||
The variables involved to choose if a Validator Compiler can be reused are:
|
||||
|
||||
- the AJV configuration: it is [one per server](https://fastify.dev/docs/latest/Reference/Server/#ajv)
|
||||
- the external JSON schemas: once a new schema is added to a fastify's context, calling `fastify.addSchema()`, it will cause a new AJV inizialization
|
||||
|
||||
|
||||
## License
|
||||
|
||||
Licensed under [MIT](./LICENSE).
|
||||
37
backend/node_modules/@fastify/ajv-compiler/benchmark/small-object.mjs
generated
vendored
Normal file
37
backend/node_modules/@fastify/ajv-compiler/benchmark/small-object.mjs
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
import cronometro from 'cronometro'
|
||||
|
||||
import fjs from 'fast-json-stringify'
|
||||
import AjvCompiler from '../index.js'
|
||||
|
||||
const fjsSerialize = buildFJSSerializerFunction({
|
||||
type: 'object',
|
||||
properties: {
|
||||
hello: { type: 'string' },
|
||||
name: { type: 'string' }
|
||||
}
|
||||
})
|
||||
const ajvSerialize = buildAJVSerializerFunction({
|
||||
properties: {
|
||||
hello: { type: 'string' },
|
||||
name: { type: 'string' }
|
||||
}
|
||||
})
|
||||
|
||||
await cronometro({
|
||||
'fast-json-stringify': function () {
|
||||
fjsSerialize({ hello: 'Ciao', name: 'Manuel' })
|
||||
},
|
||||
'ajv serializer': function () {
|
||||
ajvSerialize({ hello: 'Ciao', name: 'Manuel' })
|
||||
}
|
||||
})
|
||||
|
||||
function buildFJSSerializerFunction (schema) {
|
||||
return fjs(schema)
|
||||
}
|
||||
|
||||
function buildAJVSerializerFunction (schema) {
|
||||
const factory = AjvCompiler({ jtdSerializer: true })
|
||||
const compiler = factory({}, { customOptions: {} })
|
||||
return compiler({ schema })
|
||||
}
|
||||
53
backend/node_modules/@fastify/ajv-compiler/index.js
generated
vendored
Normal file
53
backend/node_modules/@fastify/ajv-compiler/index.js
generated
vendored
Normal file
@@ -0,0 +1,53 @@
|
||||
'use strict'
|
||||
|
||||
const AjvReference = Symbol.for('fastify.ajv-compiler.reference')
|
||||
const ValidatorCompiler = require('./lib/validator-compiler')
|
||||
const SerializerCompiler = require('./lib/serializer-compiler')
|
||||
|
||||
function AjvCompiler (opts) {
|
||||
const validatorPool = new Map()
|
||||
const serializerPool = new Map()
|
||||
|
||||
if (opts && opts.jtdSerializer === true) {
|
||||
return function buildSerializerFromPool (externalSchemas, serializerOpts) {
|
||||
const uniqueAjvKey = getPoolKey({}, serializerOpts)
|
||||
if (serializerPool.has(uniqueAjvKey)) {
|
||||
return serializerPool.get(uniqueAjvKey)
|
||||
}
|
||||
|
||||
const compiler = new SerializerCompiler(externalSchemas, serializerOpts)
|
||||
const ret = compiler.buildSerializerFunction.bind(compiler)
|
||||
serializerPool.set(uniqueAjvKey, ret)
|
||||
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
return function buildCompilerFromPool (externalSchemas, options) {
|
||||
const uniqueAjvKey = getPoolKey(externalSchemas, options.customOptions)
|
||||
if (validatorPool.has(uniqueAjvKey)) {
|
||||
return validatorPool.get(uniqueAjvKey)
|
||||
}
|
||||
|
||||
const compiler = new ValidatorCompiler(externalSchemas, options)
|
||||
const ret = compiler.buildValidatorFunction.bind(compiler)
|
||||
validatorPool.set(uniqueAjvKey, ret)
|
||||
|
||||
if (options.customOptions.code !== undefined) {
|
||||
ret[AjvReference] = compiler
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
function getPoolKey (externalSchemas, options) {
|
||||
const externals = JSON.stringify(externalSchemas)
|
||||
const ajvConfig = JSON.stringify(options)
|
||||
return `${externals}${ajvConfig}`
|
||||
}
|
||||
module.exports = AjvCompiler
|
||||
module.exports.default = AjvCompiler
|
||||
module.exports.AjvCompiler = AjvCompiler
|
||||
module.exports.AjvReference = AjvReference
|
||||
module.exports.StandaloneValidator = require('./standalone')
|
||||
14
backend/node_modules/@fastify/ajv-compiler/lib/default-ajv-options.js
generated
vendored
Normal file
14
backend/node_modules/@fastify/ajv-compiler/lib/default-ajv-options.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
'use strict'
|
||||
|
||||
const fastUri = require('fast-uri')
|
||||
|
||||
module.exports = Object.freeze({
|
||||
coerceTypes: 'array',
|
||||
useDefaults: true,
|
||||
removeAdditional: true,
|
||||
uriResolver: fastUri,
|
||||
addUsedSchema: false,
|
||||
// Explicitly set allErrors to `false`.
|
||||
// When set to `true`, a DoS attack is possible.
|
||||
allErrors: false
|
||||
})
|
||||
27
backend/node_modules/@fastify/ajv-compiler/lib/serializer-compiler.js
generated
vendored
Normal file
27
backend/node_modules/@fastify/ajv-compiler/lib/serializer-compiler.js
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
'use strict'
|
||||
|
||||
const AjvJTD = require('ajv/dist/jtd')
|
||||
|
||||
const defaultAjvOptions = require('./default-ajv-options')
|
||||
|
||||
class SerializerCompiler {
|
||||
constructor (externalSchemas, options) {
|
||||
this.ajv = new AjvJTD(Object.assign({}, defaultAjvOptions, options))
|
||||
|
||||
/**
|
||||
* https://ajv.js.org/json-type-definition.html#ref-form
|
||||
* Unlike JSON Schema, JTD does not allow to reference:
|
||||
* - any schema fragment other than root level definitions member
|
||||
* - root of the schema - there is another way to define a self-recursive schema (see Example 2)
|
||||
* - another schema file (but you can still combine schemas from multiple files using JavaScript).
|
||||
*
|
||||
* So we ignore the externalSchemas parameter.
|
||||
*/
|
||||
}
|
||||
|
||||
buildSerializerFunction ({ schema/*, method, url, httpStatus */ }) {
|
||||
return this.ajv.compileSerializer(schema)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = SerializerCompiler
|
||||
58
backend/node_modules/@fastify/ajv-compiler/lib/validator-compiler.js
generated
vendored
Normal file
58
backend/node_modules/@fastify/ajv-compiler/lib/validator-compiler.js
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
'use strict'
|
||||
|
||||
const Ajv = require('ajv').default
|
||||
const AjvJTD = require('ajv/dist/jtd')
|
||||
|
||||
const defaultAjvOptions = require('./default-ajv-options')
|
||||
|
||||
class ValidatorCompiler {
|
||||
constructor (externalSchemas, options) {
|
||||
// This instance of Ajv is private
|
||||
// it should not be customized or used
|
||||
if (options.mode === 'JTD') {
|
||||
this.ajv = new AjvJTD(Object.assign({}, defaultAjvOptions, options.customOptions))
|
||||
} else {
|
||||
this.ajv = new Ajv(Object.assign({}, defaultAjvOptions, options.customOptions))
|
||||
}
|
||||
|
||||
let addFormatPlugin = true
|
||||
if (options.plugins && options.plugins.length > 0) {
|
||||
for (const plugin of options.plugins) {
|
||||
if (Array.isArray(plugin)) {
|
||||
addFormatPlugin = addFormatPlugin && plugin[0].name !== 'formatsPlugin'
|
||||
plugin[0](this.ajv, plugin[1])
|
||||
} else {
|
||||
addFormatPlugin = addFormatPlugin && plugin.name !== 'formatsPlugin'
|
||||
plugin(this.ajv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (addFormatPlugin) {
|
||||
require('ajv-formats')(this.ajv)
|
||||
}
|
||||
|
||||
options.onCreate?.(this.ajv)
|
||||
|
||||
const sourceSchemas = Object.values(externalSchemas)
|
||||
for (const extSchema of sourceSchemas) {
|
||||
this.ajv.addSchema(extSchema)
|
||||
}
|
||||
}
|
||||
|
||||
buildValidatorFunction ({ schema/*, method, url, httpPart */ }) {
|
||||
// Ajv does not support compiling two schemas with the same
|
||||
// id inside the same instance. Therefore if we have already
|
||||
// compiled the schema with the given id, we just return it.
|
||||
if (schema.$id) {
|
||||
const stored = this.ajv.getSchema(schema.$id)
|
||||
if (stored) {
|
||||
return stored
|
||||
}
|
||||
}
|
||||
|
||||
return this.ajv.compile(schema)
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ValidatorCompiler
|
||||
54
backend/node_modules/@fastify/ajv-compiler/package.json
generated
vendored
Normal file
54
backend/node_modules/@fastify/ajv-compiler/package.json
generated
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"name": "@fastify/ajv-compiler",
|
||||
"version": "3.6.0",
|
||||
"description": "Build and manage the AJV instances for the fastify framework",
|
||||
"main": "index.js",
|
||||
"type": "commonjs",
|
||||
"types": "types/index.d.ts",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "standard",
|
||||
"lint:fix": "standard --fix",
|
||||
"unit": "tap",
|
||||
"test": "npm run unit && npm run test:typescript",
|
||||
"test:typescript": "tsd",
|
||||
"ajv:compile": "ajv compile -s test/source.json -o test/validate_schema.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/fastify/ajv-compiler.git"
|
||||
},
|
||||
"keywords": [
|
||||
"ajv",
|
||||
"validator",
|
||||
"schema",
|
||||
"compiler",
|
||||
"fastify"
|
||||
],
|
||||
"author": "Manuel Spigolon <behemoth89@gmail.com> (https://github.com/Eomm)",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/fastify/ajv-compiler/issues"
|
||||
},
|
||||
"homepage": "https://github.com/fastify/ajv-compiler#readme",
|
||||
"devDependencies": {
|
||||
"ajv-cli": "^5.0.0",
|
||||
"ajv-errors": "^3.0.0",
|
||||
"ajv-i18n": "^4.0.1",
|
||||
"ajv-merge-patch": "^5.0.1",
|
||||
"cronometro": "^3.0.1",
|
||||
"fastify": "^4.0.0",
|
||||
"require-from-string": "^2.0.2",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"standard": "^17.0.0",
|
||||
"tap": "^16.2.0",
|
||||
"tsd": "^0.31.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ajv": "^8.11.0",
|
||||
"ajv-formats": "^2.1.1",
|
||||
"fast-uri": "^2.0.0"
|
||||
}
|
||||
}
|
||||
44
backend/node_modules/@fastify/ajv-compiler/standalone.js
generated
vendored
Normal file
44
backend/node_modules/@fastify/ajv-compiler/standalone.js
generated
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
'use strict'
|
||||
|
||||
const ValidatorSelector = require('./index')
|
||||
const standaloneCode = require('ajv/dist/standalone').default
|
||||
|
||||
function StandaloneValidator (options = { readMode: true }) {
|
||||
if (options.readMode === true && !options.restoreFunction) {
|
||||
throw new Error('You must provide a restoreFunction options when readMode ON')
|
||||
}
|
||||
|
||||
if (options.readMode !== true && !options.storeFunction) {
|
||||
throw new Error('You must provide a storeFunction options when readMode OFF')
|
||||
}
|
||||
|
||||
if (options.readMode === true) {
|
||||
// READ MODE: it behalf only in the restore function provided by the user
|
||||
return function wrapper () {
|
||||
return function (opts) {
|
||||
return options.restoreFunction(opts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WRITE MODE: it behalf on the default ValidatorSelector, wrapping the API to run the Ajv Standalone code generation
|
||||
const factory = ValidatorSelector()
|
||||
return function wrapper (externalSchemas, ajvOptions = {}) {
|
||||
if (!ajvOptions.customOptions || !ajvOptions.customOptions.code) {
|
||||
// to generate the validation source code, these options are mandatory
|
||||
ajvOptions.customOptions = Object.assign({}, ajvOptions.customOptions, { code: { source: true } })
|
||||
}
|
||||
|
||||
const compiler = factory(externalSchemas, ajvOptions)
|
||||
return function (opts) { // { schema/*, method, url, httpPart */ }
|
||||
const validationFunc = compiler(opts)
|
||||
|
||||
const schemaValidationCode = standaloneCode(compiler[ValidatorSelector.AjvReference].ajv, validationFunc)
|
||||
options.storeFunction(opts, schemaValidationCode)
|
||||
|
||||
return validationFunc
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = StandaloneValidator
|
||||
0
backend/node_modules/@fastify/ajv-compiler/test/.gitkeep
generated
vendored
Normal file
0
backend/node_modules/@fastify/ajv-compiler/test/.gitkeep
generated
vendored
Normal file
59
backend/node_modules/@fastify/ajv-compiler/test/duplicated-id-compile.test.js
generated
vendored
Normal file
59
backend/node_modules/@fastify/ajv-compiler/test/duplicated-id-compile.test.js
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
'use strict'
|
||||
|
||||
const t = require('tap')
|
||||
const AjvCompiler = require('../index')
|
||||
|
||||
const postSchema = Object.freeze({
|
||||
$schema: 'http://json-schema.org/draft-07/schema#',
|
||||
type: 'object',
|
||||
$id: 'http://mydomain.com/user',
|
||||
title: 'User schema',
|
||||
description: 'Contains all user fields',
|
||||
properties: {
|
||||
username: { type: 'string', minLength: 4 },
|
||||
firstName: { type: 'string', minLength: 1 },
|
||||
lastName: { type: 'string', minLength: 1 },
|
||||
email: { type: 'string' },
|
||||
password: { type: 'string', minLength: 6 },
|
||||
bio: { type: 'string' }
|
||||
},
|
||||
required: ['username', 'firstName', 'lastName', 'email', 'bio', 'password']
|
||||
})
|
||||
|
||||
const patchSchema = Object.freeze({
|
||||
$schema: 'http://json-schema.org/draft-07/schema#',
|
||||
type: 'object',
|
||||
$id: 'http://mydomain.com/user',
|
||||
title: 'User schema',
|
||||
description: 'Contains all user fields',
|
||||
properties: {
|
||||
firstName: { type: 'string', minLength: 1 },
|
||||
lastName: { type: 'string', minLength: 1 },
|
||||
bio: { type: 'string' }
|
||||
}
|
||||
})
|
||||
|
||||
const fastifyAjvOptionsDefault = Object.freeze({
|
||||
customOptions: {}
|
||||
})
|
||||
|
||||
t.test('must not store schema on compile', t => {
|
||||
t.plan(4)
|
||||
const factory = AjvCompiler()
|
||||
const compiler = factory({}, fastifyAjvOptionsDefault)
|
||||
const postFn = compiler({ schema: postSchema })
|
||||
const patchFn = compiler({ schema: patchSchema })
|
||||
|
||||
const resultForPost = postFn({})
|
||||
t.equal(resultForPost, false)
|
||||
t.has(postFn.errors, [
|
||||
{
|
||||
keyword: 'required',
|
||||
message: "must have required property 'username'"
|
||||
}
|
||||
])
|
||||
|
||||
const resultForPatch = patchFn({})
|
||||
t.ok(resultForPatch)
|
||||
t.notOk(patchFn.errors)
|
||||
})
|
||||
307
backend/node_modules/@fastify/ajv-compiler/test/index.test.js
generated
vendored
Normal file
307
backend/node_modules/@fastify/ajv-compiler/test/index.test.js
generated
vendored
Normal file
@@ -0,0 +1,307 @@
|
||||
'use strict'
|
||||
|
||||
const t = require('tap')
|
||||
const fastify = require('fastify')
|
||||
const AjvCompiler = require('../index')
|
||||
|
||||
const sym = Symbol.for('fastify.ajv-compiler.reference')
|
||||
|
||||
const sampleSchema = Object.freeze({
|
||||
$id: 'example1',
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' }
|
||||
}
|
||||
})
|
||||
|
||||
const externalSchemas1 = Object.freeze({})
|
||||
const externalSchemas2 = Object.freeze({
|
||||
foo: {
|
||||
$id: 'foo',
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const fastifyAjvOptionsDefault = Object.freeze({
|
||||
customOptions: {}
|
||||
})
|
||||
|
||||
const fastifyJtdDefault = Object.freeze({
|
||||
customOptions: { },
|
||||
mode: 'JTD'
|
||||
})
|
||||
|
||||
const fastifyAjvOptionsCustom = Object.freeze({
|
||||
customOptions: {
|
||||
allErrors: true,
|
||||
removeAdditional: false
|
||||
},
|
||||
plugins: [
|
||||
require('ajv-formats'),
|
||||
[require('ajv-errors'), { singleError: false }]
|
||||
]
|
||||
})
|
||||
|
||||
t.test('basic usage', t => {
|
||||
t.plan(1)
|
||||
const factory = AjvCompiler()
|
||||
const compiler = factory(externalSchemas1, fastifyAjvOptionsDefault)
|
||||
const validatorFunc = compiler({ schema: sampleSchema })
|
||||
const result = validatorFunc({ name: 'hello' })
|
||||
t.equal(result, true)
|
||||
})
|
||||
|
||||
t.test('array coercion', t => {
|
||||
t.plan(2)
|
||||
const factory = AjvCompiler()
|
||||
const compiler = factory(externalSchemas1, fastifyAjvOptionsDefault)
|
||||
|
||||
const arraySchema = {
|
||||
$id: 'example1',
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'array', items: { type: 'string' } }
|
||||
}
|
||||
}
|
||||
|
||||
const validatorFunc = compiler({ schema: arraySchema })
|
||||
|
||||
const inputObj = { name: 'hello' }
|
||||
t.equal(validatorFunc(inputObj), true)
|
||||
t.same(inputObj, { name: ['hello'] }, 'the name property should be coerced to an array')
|
||||
})
|
||||
|
||||
t.test('nullable default', t => {
|
||||
t.plan(2)
|
||||
const factory = AjvCompiler()
|
||||
const compiler = factory({}, fastifyAjvOptionsDefault)
|
||||
const validatorFunc = compiler({
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
nullable: { type: 'string', nullable: true },
|
||||
notNullable: { type: 'string' }
|
||||
}
|
||||
}
|
||||
})
|
||||
const input = { nullable: null, notNullable: null }
|
||||
const result = validatorFunc(input)
|
||||
t.equal(result, true)
|
||||
t.same(input, { nullable: null, notNullable: '' }, 'the notNullable field has been coerced')
|
||||
})
|
||||
|
||||
t.test('plugin loading', t => {
|
||||
t.plan(3)
|
||||
const factory = AjvCompiler()
|
||||
const compiler = factory(externalSchemas1, fastifyAjvOptionsCustom)
|
||||
const validatorFunc = compiler({
|
||||
schema: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
q: {
|
||||
type: 'string',
|
||||
format: 'date',
|
||||
formatMinimum: '2016-02-06',
|
||||
formatExclusiveMaximum: '2016-12-27'
|
||||
}
|
||||
},
|
||||
required: ['q'],
|
||||
errorMessage: 'hello world'
|
||||
}
|
||||
})
|
||||
const result = validatorFunc({ q: '2016-10-02' })
|
||||
t.equal(result, true)
|
||||
|
||||
const resultFail = validatorFunc({})
|
||||
t.equal(resultFail, false)
|
||||
t.equal(validatorFunc.errors[0].message, 'hello world')
|
||||
})
|
||||
|
||||
t.test('optimization - cache ajv instance', t => {
|
||||
t.plan(5)
|
||||
const factory = AjvCompiler()
|
||||
const compiler1 = factory(externalSchemas1, fastifyAjvOptionsDefault)
|
||||
const compiler2 = factory(externalSchemas1, fastifyAjvOptionsDefault)
|
||||
t.equal(compiler1, compiler2, 'same instance')
|
||||
t.same(compiler1, compiler2, 'same instance')
|
||||
|
||||
const compiler3 = factory(externalSchemas2, fastifyAjvOptionsDefault)
|
||||
t.not(compiler3, compiler1, 'new ajv instance when externa schema change')
|
||||
|
||||
const compiler4 = factory(externalSchemas1, fastifyAjvOptionsCustom)
|
||||
t.not(compiler4, compiler1, 'new ajv instance when externa schema change')
|
||||
t.not(compiler4, compiler3, 'new ajv instance when externa schema change')
|
||||
})
|
||||
|
||||
t.test('the onCreate callback can enhance the ajv instance', t => {
|
||||
t.plan(2)
|
||||
const factory = AjvCompiler()
|
||||
|
||||
const fastifyAjvCustomOptionsFormats = Object.freeze({
|
||||
onCreate (ajv) {
|
||||
for (const [formatName, format] of Object.entries(this.customOptions.formats)) {
|
||||
ajv.addFormat(formatName, format)
|
||||
}
|
||||
},
|
||||
customOptions: {
|
||||
formats: {
|
||||
date: /foo/
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const compiler1 = factory(externalSchemas1, fastifyAjvCustomOptionsFormats)
|
||||
const validatorFunc = compiler1({
|
||||
schema: {
|
||||
type: 'string',
|
||||
format: 'date'
|
||||
}
|
||||
})
|
||||
const result = validatorFunc('foo')
|
||||
t.equal(result, true)
|
||||
|
||||
const resultFail = validatorFunc('2016-10-02')
|
||||
t.equal(resultFail, false)
|
||||
})
|
||||
|
||||
// https://github.com/fastify/fastify/pull/2969
|
||||
t.test('compile same $id when in external schema', t => {
|
||||
t.plan(3)
|
||||
const factory = AjvCompiler()
|
||||
|
||||
const base = {
|
||||
$id: 'urn:schema:base',
|
||||
definitions: {
|
||||
hello: { type: 'string' }
|
||||
},
|
||||
type: 'object',
|
||||
properties: {
|
||||
hello: { $ref: '#/definitions/hello' }
|
||||
}
|
||||
}
|
||||
|
||||
const refSchema = {
|
||||
$id: 'urn:schema:ref',
|
||||
type: 'object',
|
||||
properties: {
|
||||
hello: { $ref: 'urn:schema:base#/definitions/hello' }
|
||||
}
|
||||
}
|
||||
|
||||
const compiler = factory({
|
||||
[base.$id]: base,
|
||||
[refSchema.$id]: refSchema
|
||||
|
||||
}, fastifyAjvOptionsDefault)
|
||||
|
||||
t.notOk(compiler[sym], 'the ajv reference do not exists if code is not activated')
|
||||
|
||||
const validatorFunc1 = compiler({
|
||||
schema: {
|
||||
$id: 'urn:schema:ref'
|
||||
}
|
||||
})
|
||||
|
||||
const validatorFunc2 = compiler({
|
||||
schema: {
|
||||
$id: 'urn:schema:ref'
|
||||
}
|
||||
})
|
||||
|
||||
t.pass('the compile does not fail if the schema compiled is already in the external schemas')
|
||||
t.equal(validatorFunc1, validatorFunc2, 'the returned function is the same')
|
||||
})
|
||||
|
||||
t.test('JTD MODE', t => {
|
||||
t.plan(2)
|
||||
|
||||
t.test('compile jtd schema', t => {
|
||||
t.plan(4)
|
||||
const factory = AjvCompiler()
|
||||
|
||||
const jtdSchema = {
|
||||
discriminator: 'version',
|
||||
mapping: {
|
||||
1: {
|
||||
properties: {
|
||||
foo: { type: 'uint8' }
|
||||
}
|
||||
},
|
||||
2: {
|
||||
properties: {
|
||||
foo: { type: 'string' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const compiler = factory({}, fastifyJtdDefault)
|
||||
const validatorFunc = compiler({ schema: jtdSchema })
|
||||
t.pass('generated validation function for JTD SCHEMA')
|
||||
|
||||
const result = validatorFunc({
|
||||
version: '2',
|
||||
foo: []
|
||||
})
|
||||
t.notOk(result, 'failed validation')
|
||||
t.type(validatorFunc.errors, 'Array')
|
||||
|
||||
const success = validatorFunc({
|
||||
version: '1',
|
||||
foo: 42
|
||||
})
|
||||
t.ok(success)
|
||||
})
|
||||
|
||||
t.test('fastify integration', async t => {
|
||||
const factory = AjvCompiler()
|
||||
|
||||
const app = fastify({
|
||||
jsonShorthand: false,
|
||||
ajv: {
|
||||
customOptions: { },
|
||||
mode: 'JTD'
|
||||
},
|
||||
schemaController: {
|
||||
compilersFactory: {
|
||||
buildValidator: factory
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
app.post('/', {
|
||||
schema: {
|
||||
body: {
|
||||
discriminator: 'version',
|
||||
mapping: {
|
||||
1: {
|
||||
properties: {
|
||||
foo: { type: 'uint8' }
|
||||
}
|
||||
},
|
||||
2: {
|
||||
properties: {
|
||||
foo: { type: 'string' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, () => {})
|
||||
|
||||
const res = await app.inject({
|
||||
url: '/',
|
||||
method: 'POST',
|
||||
payload: {
|
||||
version: '1',
|
||||
foo: 'this is not a number'
|
||||
}
|
||||
})
|
||||
|
||||
t.equal(res.statusCode, 400)
|
||||
t.equal(res.json().message, 'body/foo must be uint8')
|
||||
})
|
||||
})
|
||||
264
backend/node_modules/@fastify/ajv-compiler/test/plugins.test.js
generated
vendored
Normal file
264
backend/node_modules/@fastify/ajv-compiler/test/plugins.test.js
generated
vendored
Normal file
@@ -0,0 +1,264 @@
|
||||
'use strict'
|
||||
|
||||
const t = require('tap')
|
||||
const fastify = require('fastify')
|
||||
const AjvCompiler = require('../index')
|
||||
|
||||
const ajvFormats = require('ajv-formats')
|
||||
const ajvErrors = require('ajv-errors')
|
||||
const localize = require('ajv-i18n')
|
||||
|
||||
t.test('Format Baseline test', async (t) => {
|
||||
const app = buildApplication({
|
||||
customOptions: {
|
||||
validateFormats: false
|
||||
}
|
||||
})
|
||||
|
||||
const res = await app.inject({
|
||||
url: '/hello',
|
||||
headers: {
|
||||
'x-foo': 'hello',
|
||||
'x-date': 'not a date',
|
||||
'x-email': 'not an email'
|
||||
},
|
||||
query: {
|
||||
foo: 'hello',
|
||||
date: 'not a date',
|
||||
email: 'not an email'
|
||||
}
|
||||
})
|
||||
t.equal(res.statusCode, 200, 'format validation does not apply as configured')
|
||||
t.equal(res.payload, 'hello')
|
||||
})
|
||||
|
||||
t.test('Custom Format plugin loading test', (t) => {
|
||||
t.plan(6)
|
||||
const app = buildApplication({
|
||||
customOptions: {
|
||||
validateFormats: true
|
||||
},
|
||||
plugins: [[ajvFormats, { mode: 'fast' }]]
|
||||
})
|
||||
|
||||
app.inject('/hello', (err, res) => {
|
||||
t.error(err)
|
||||
t.equal(res.statusCode, 400, 'format validation applies')
|
||||
})
|
||||
|
||||
app.inject('/2ad0612c-7578-4b18-9a6f-579863f40e0b', (err, res) => {
|
||||
t.error(err)
|
||||
t.equal(res.statusCode, 400, 'format validation applies')
|
||||
})
|
||||
|
||||
app.inject({
|
||||
url: '/2ad0612c-7578-4b18-9a6f-579863f40e0b',
|
||||
headers: {
|
||||
'x-foo': 'hello',
|
||||
'x-date': new Date().toISOString(),
|
||||
'x-email': 'foo@bar.baz'
|
||||
},
|
||||
query: {
|
||||
foo: 'hello',
|
||||
date: new Date().toISOString(),
|
||||
email: 'foo@bar.baz'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
t.equal(res.statusCode, 200)
|
||||
})
|
||||
})
|
||||
|
||||
t.test('Format plugin set by default test', (t) => {
|
||||
t.plan(6)
|
||||
const app = buildApplication({})
|
||||
|
||||
app.inject('/hello', (err, res) => {
|
||||
t.error(err)
|
||||
t.equal(res.statusCode, 400, 'format validation applies')
|
||||
})
|
||||
|
||||
app.inject('/2ad0612c-7578-4b18-9a6f-579863f40e0b', (err, res) => {
|
||||
t.error(err)
|
||||
t.equal(res.statusCode, 400, 'format validation applies')
|
||||
})
|
||||
|
||||
app.inject({
|
||||
url: '/2ad0612c-7578-4b18-9a6f-579863f40e0b',
|
||||
headers: {
|
||||
'x-foo': 'hello',
|
||||
'x-date': new Date().toISOString(),
|
||||
'x-email': 'foo@bar.baz'
|
||||
},
|
||||
query: {
|
||||
foo: 'hello',
|
||||
date: new Date().toISOString(),
|
||||
email: 'foo@bar.baz'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
t.equal(res.statusCode, 200)
|
||||
})
|
||||
})
|
||||
|
||||
t.test('Custom error messages', (t) => {
|
||||
t.plan(9)
|
||||
|
||||
const app = buildApplication({
|
||||
customOptions: {
|
||||
removeAdditional: false,
|
||||
allErrors: true
|
||||
},
|
||||
plugins: [ajvFormats, ajvErrors]
|
||||
})
|
||||
|
||||
const errorMessage = {
|
||||
required: 'custom miss',
|
||||
type: 'custom type', // will not replace internal "type" error for the property "foo"
|
||||
_: 'custom type', // this prop will do it
|
||||
additionalProperties: 'custom too many params'
|
||||
}
|
||||
|
||||
app.post('/', {
|
||||
handler: () => { t.fail('dont call me') },
|
||||
schema: {
|
||||
body: {
|
||||
type: 'object',
|
||||
required: ['foo'],
|
||||
properties: {
|
||||
foo: { type: 'integer' }
|
||||
},
|
||||
additionalProperties: false,
|
||||
errorMessage
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
app.inject({
|
||||
url: '/',
|
||||
method: 'post',
|
||||
payload: {}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
t.equal(res.statusCode, 400)
|
||||
t.match(res.json().message, errorMessage.required)
|
||||
})
|
||||
|
||||
app.inject({
|
||||
url: '/',
|
||||
method: 'post',
|
||||
payload: { foo: 'not a number' }
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
t.equal(res.statusCode, 400)
|
||||
t.match(res.json().message, errorMessage.type)
|
||||
})
|
||||
|
||||
app.inject({
|
||||
url: '/',
|
||||
method: 'post',
|
||||
payload: { foo: 3, bar: 'ops' }
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
t.equal(res.statusCode, 400)
|
||||
t.match(res.json().message, errorMessage.additionalProperties)
|
||||
})
|
||||
})
|
||||
|
||||
t.test('Custom i18n error messages', (t) => {
|
||||
t.plan(3)
|
||||
|
||||
const app = buildApplication({
|
||||
customOptions: {
|
||||
allErrors: true,
|
||||
messages: false
|
||||
},
|
||||
plugins: [ajvFormats]
|
||||
})
|
||||
|
||||
app.post('/', {
|
||||
handler: () => { t.fail('dont call me') },
|
||||
schema: {
|
||||
body: {
|
||||
type: 'object',
|
||||
required: ['foo'],
|
||||
properties: {
|
||||
foo: { type: 'integer' }
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
app.setErrorHandler((error, request, reply) => {
|
||||
t.pass('Error handler executed')
|
||||
if (error.validation) {
|
||||
localize.ru(error.validation)
|
||||
reply.status(400).send(error.validation)
|
||||
return
|
||||
}
|
||||
t.fail('not other errors')
|
||||
})
|
||||
|
||||
app.inject({
|
||||
method: 'POST',
|
||||
url: '/',
|
||||
payload: {
|
||||
foo: 'string'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
t.equal(res.json()[0].message, 'должно быть integer')
|
||||
})
|
||||
})
|
||||
|
||||
function buildApplication (ajvOptions) {
|
||||
const factory = AjvCompiler()
|
||||
|
||||
const app = fastify({
|
||||
ajv: ajvOptions,
|
||||
schemaController: {
|
||||
compilersFactory: {
|
||||
buildValidator: factory
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
app.get('/:id', {
|
||||
schema: {
|
||||
headers: {
|
||||
type: 'object',
|
||||
required: [
|
||||
'x-foo',
|
||||
'x-date',
|
||||
'x-email'
|
||||
],
|
||||
properties: {
|
||||
'x-foo': { type: 'string' },
|
||||
'x-date': { type: 'string', format: 'date-time' },
|
||||
'x-email': { type: 'string', format: 'email' }
|
||||
}
|
||||
},
|
||||
query: {
|
||||
type: 'object',
|
||||
required: [
|
||||
'foo',
|
||||
'date',
|
||||
'email'
|
||||
],
|
||||
properties: {
|
||||
foo: { type: 'string' },
|
||||
date: { type: 'string', format: 'date-time' },
|
||||
email: { type: 'string', format: 'email' }
|
||||
}
|
||||
},
|
||||
params: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
id: { type: 'string', format: 'uuid' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}, async () => 'hello')
|
||||
|
||||
return app
|
||||
}
|
||||
279
backend/node_modules/@fastify/ajv-compiler/test/serialization.test.js
generated
vendored
Normal file
279
backend/node_modules/@fastify/ajv-compiler/test/serialization.test.js
generated
vendored
Normal file
@@ -0,0 +1,279 @@
|
||||
'use strict'
|
||||
|
||||
const t = require('tap')
|
||||
const fastify = require('fastify')
|
||||
const AjvCompiler = require('../index')
|
||||
|
||||
const jtdSchema = {
|
||||
discriminator: 'version',
|
||||
mapping: {
|
||||
1: {
|
||||
properties: {
|
||||
foo: { type: 'uint8' }
|
||||
}
|
||||
},
|
||||
2: {
|
||||
properties: {
|
||||
foo: { type: 'string' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const externalSchemas1 = Object.freeze({})
|
||||
const externalSchemas2 = Object.freeze({
|
||||
foo: {
|
||||
definitions: {
|
||||
coordinates: {
|
||||
properties: {
|
||||
lat: { type: 'float32' },
|
||||
lng: { type: 'float32' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const fastifyAjvOptionsDefault = Object.freeze({
|
||||
customOptions: {}
|
||||
})
|
||||
|
||||
t.test('basic serializer usage', t => {
|
||||
t.plan(4)
|
||||
const factory = AjvCompiler({ jtdSerializer: true })
|
||||
const compiler = factory(externalSchemas1, fastifyAjvOptionsDefault)
|
||||
const serializeFunc = compiler({ schema: jtdSchema })
|
||||
t.equal(serializeFunc({ version: '1', foo: 42 }), '{"version":"1","foo":42}')
|
||||
t.equal(serializeFunc({ version: '2', foo: 'hello' }), '{"version":"2","foo":"hello"}')
|
||||
t.equal(serializeFunc({ version: '3', foo: 'hello' }), '{"version":"3"}')
|
||||
t.equal(serializeFunc({ version: '2', foo: ['not', 1, { string: 'string' }] }), '{"version":"2","foo":"not,1,[object Object]"}')
|
||||
})
|
||||
|
||||
t.test('external schemas are ignored', t => {
|
||||
t.plan(1)
|
||||
const factory = AjvCompiler({ jtdSerializer: true })
|
||||
const compiler = factory(externalSchemas2, fastifyAjvOptionsDefault)
|
||||
const serializeFunc = compiler({
|
||||
schema: {
|
||||
definitions: {
|
||||
coordinates: {
|
||||
properties: {
|
||||
lat: { type: 'float32' },
|
||||
lng: { type: 'float32' }
|
||||
}
|
||||
}
|
||||
},
|
||||
properties: {
|
||||
userLoc: { ref: 'coordinates' },
|
||||
serverLoc: { ref: 'coordinates' }
|
||||
}
|
||||
}
|
||||
})
|
||||
t.equal(serializeFunc(
|
||||
{ userLoc: { lat: 50, lng: -90 }, serverLoc: { lat: -15, lng: 50 } }),
|
||||
'{"userLoc":{"lat":50,"lng":-90},"serverLoc":{"lat":-15,"lng":50}}'
|
||||
)
|
||||
})
|
||||
|
||||
t.test('fastify integration within JTD serializer', async t => {
|
||||
const factoryValidator = AjvCompiler()
|
||||
const factorySerializer = AjvCompiler({ jtdSerializer: true })
|
||||
|
||||
const app = fastify({
|
||||
jsonShorthand: false,
|
||||
ajv: {
|
||||
customOptions: { },
|
||||
mode: 'JTD'
|
||||
},
|
||||
schemaController: {
|
||||
compilersFactory: {
|
||||
buildValidator: factoryValidator,
|
||||
buildSerializer: factorySerializer
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
app.post('/', {
|
||||
schema: {
|
||||
body: jtdSchema,
|
||||
response: {
|
||||
200: {
|
||||
properties: {
|
||||
id: { type: 'string' },
|
||||
createdAt: { type: 'timestamp' },
|
||||
karma: { type: 'int32' },
|
||||
isAdmin: { type: 'boolean' }
|
||||
}
|
||||
},
|
||||
400: jtdSchema
|
||||
}
|
||||
}
|
||||
}, async () => {
|
||||
return {
|
||||
id: '123',
|
||||
createdAt: new Date('1999-01-31T23:00:00.000Z'),
|
||||
karma: 42,
|
||||
isAdmin: true,
|
||||
remove: 'me'
|
||||
}
|
||||
})
|
||||
|
||||
{
|
||||
const res = await app.inject({
|
||||
url: '/',
|
||||
method: 'POST',
|
||||
payload: {
|
||||
version: '1',
|
||||
foo: 'not a number'
|
||||
}
|
||||
})
|
||||
|
||||
t.equal(res.statusCode, 400)
|
||||
t.same(res.json(), { version: 'undefined' })
|
||||
}
|
||||
|
||||
{
|
||||
const res = await app.inject({
|
||||
url: '/',
|
||||
method: 'POST',
|
||||
payload: {
|
||||
version: '1',
|
||||
foo: 32
|
||||
}
|
||||
})
|
||||
|
||||
t.equal(res.statusCode, 200)
|
||||
t.same(res.json(), {
|
||||
id: '123',
|
||||
createdAt: '1999-01-31T23:00:00.000Z',
|
||||
karma: 42,
|
||||
isAdmin: true
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.test('fastify integration and cached serializer', async t => {
|
||||
const factoryValidator = AjvCompiler()
|
||||
const factorySerializer = AjvCompiler({ jtdSerializer: true })
|
||||
|
||||
const app = fastify({
|
||||
jsonShorthand: false,
|
||||
ajv: {
|
||||
customOptions: { },
|
||||
mode: 'JTD'
|
||||
},
|
||||
schemaController: {
|
||||
compilersFactory: {
|
||||
buildValidator: factoryValidator,
|
||||
buildSerializer: factorySerializer
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
app.register(async function plugin (app, opts) {
|
||||
app.post('/', {
|
||||
schema: {
|
||||
body: jtdSchema,
|
||||
response: {
|
||||
200: {
|
||||
properties: {
|
||||
id: { type: 'string' },
|
||||
createdAt: { type: 'timestamp' },
|
||||
karma: { type: 'int32' },
|
||||
isAdmin: { type: 'boolean' }
|
||||
}
|
||||
},
|
||||
400: jtdSchema
|
||||
}
|
||||
}
|
||||
}, async () => {
|
||||
return {
|
||||
id: '123',
|
||||
createdAt: new Date('1999-01-31T23:00:00.000Z'),
|
||||
karma: 42,
|
||||
isAdmin: true,
|
||||
remove: 'me'
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
app.register(async function plugin (app, opts) {
|
||||
app.post('/two', {
|
||||
schema: {
|
||||
body: jtdSchema,
|
||||
response: {
|
||||
400: jtdSchema
|
||||
}
|
||||
}
|
||||
}, () => {})
|
||||
})
|
||||
|
||||
{
|
||||
const res = await app.inject({
|
||||
url: '/',
|
||||
method: 'POST',
|
||||
payload: {
|
||||
version: '1',
|
||||
foo: 'not a number'
|
||||
}
|
||||
})
|
||||
|
||||
t.equal(res.statusCode, 400)
|
||||
t.same(res.json(), { version: 'undefined' })
|
||||
}
|
||||
|
||||
{
|
||||
const res = await app.inject({
|
||||
url: '/',
|
||||
method: 'POST',
|
||||
payload: {
|
||||
version: '1',
|
||||
foo: 32
|
||||
}
|
||||
})
|
||||
|
||||
t.equal(res.statusCode, 200)
|
||||
t.same(res.json(), {
|
||||
id: '123',
|
||||
createdAt: '1999-01-31T23:00:00.000Z',
|
||||
karma: 42,
|
||||
isAdmin: true
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
t.test('fastify integration within JTD serializer and custom options', async t => {
|
||||
const factorySerializer = AjvCompiler({ jtdSerializer: true })
|
||||
|
||||
const app = fastify({
|
||||
jsonShorthand: false,
|
||||
serializerOpts: {
|
||||
allErrors: true,
|
||||
logger: 'wrong-value'
|
||||
},
|
||||
schemaController: {
|
||||
compilersFactory: {
|
||||
buildSerializer: factorySerializer
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
app.post('/', {
|
||||
schema: {
|
||||
response: {
|
||||
200: {
|
||||
properties: {
|
||||
test: { type: 'boolean' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, async () => { })
|
||||
|
||||
try {
|
||||
await app.ready()
|
||||
t.fail('should throw')
|
||||
} catch (error) {
|
||||
t.equal(error.message, 'logger must implement log, warn and error methods', 'the wrong setting is forwarded to ajv/jtd')
|
||||
}
|
||||
})
|
||||
203
backend/node_modules/@fastify/ajv-compiler/test/standalone.test.js
generated
vendored
Normal file
203
backend/node_modules/@fastify/ajv-compiler/test/standalone.test.js
generated
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
'use strict'
|
||||
|
||||
const fs = require('node:fs')
|
||||
const path = require('node:path')
|
||||
const t = require('tap')
|
||||
const fastify = require('fastify')
|
||||
const sanitize = require('sanitize-filename')
|
||||
|
||||
const { StandaloneValidator: AjvStandaloneValidator } = require('../')
|
||||
|
||||
function generateFileName (routeOpts) {
|
||||
return `/ajv-generated-${sanitize(routeOpts.schema.$id)}-${routeOpts.method}-${routeOpts.httpPart}-${sanitize(routeOpts.url)}.js`
|
||||
}
|
||||
|
||||
const generatedFileNames = []
|
||||
|
||||
t.test('standalone', t => {
|
||||
t.plan(4)
|
||||
|
||||
t.teardown(async () => {
|
||||
for (const fileName of generatedFileNames) {
|
||||
await fs.promises.unlink(path.join(__dirname, fileName))
|
||||
}
|
||||
})
|
||||
|
||||
t.test('errors', t => {
|
||||
t.plan(2)
|
||||
t.throws(() => {
|
||||
AjvStandaloneValidator()
|
||||
}, 'missing restoreFunction')
|
||||
t.throws(() => {
|
||||
AjvStandaloneValidator({ readMode: false })
|
||||
}, 'missing storeFunction')
|
||||
})
|
||||
|
||||
t.test('generate standalone code', t => {
|
||||
t.plan(5)
|
||||
|
||||
const base = {
|
||||
$id: 'urn:schema:base',
|
||||
definitions: {
|
||||
hello: { type: 'string' }
|
||||
},
|
||||
type: 'object',
|
||||
properties: {
|
||||
hello: { $ref: '#/definitions/hello' }
|
||||
}
|
||||
}
|
||||
|
||||
const refSchema = {
|
||||
$id: 'urn:schema:ref',
|
||||
type: 'object',
|
||||
properties: {
|
||||
hello: { $ref: 'urn:schema:base#/definitions/hello' }
|
||||
}
|
||||
}
|
||||
|
||||
const endpointSchema = {
|
||||
schema: {
|
||||
$id: 'urn:schema:endpoint',
|
||||
$ref: 'urn:schema:ref'
|
||||
}
|
||||
}
|
||||
|
||||
const schemaMap = {
|
||||
[base.$id]: base,
|
||||
[refSchema.$id]: refSchema
|
||||
}
|
||||
|
||||
const factory = AjvStandaloneValidator({
|
||||
readMode: false,
|
||||
storeFunction (routeOpts, schemaValidationCode) {
|
||||
t.same(routeOpts, endpointSchema)
|
||||
t.type(schemaValidationCode, 'string')
|
||||
fs.writeFileSync(path.join(__dirname, '/ajv-generated.js'), schemaValidationCode)
|
||||
generatedFileNames.push('/ajv-generated.js')
|
||||
t.pass('stored the validation function')
|
||||
}
|
||||
})
|
||||
|
||||
const compiler = factory(schemaMap)
|
||||
compiler(endpointSchema)
|
||||
t.pass('compiled the endpoint schema')
|
||||
|
||||
t.test('usage standalone code', t => {
|
||||
t.plan(3)
|
||||
const standaloneValidate = require('./ajv-generated')
|
||||
|
||||
const valid = standaloneValidate({ hello: 'world' })
|
||||
t.ok(valid)
|
||||
|
||||
const invalid = standaloneValidate({ hello: [] })
|
||||
t.notOk(invalid)
|
||||
|
||||
t.ok(standaloneValidate)
|
||||
})
|
||||
})
|
||||
|
||||
t.test('fastify integration - writeMode', async t => {
|
||||
t.plan(6)
|
||||
|
||||
const factory = AjvStandaloneValidator({
|
||||
readMode: false,
|
||||
storeFunction (routeOpts, schemaValidationCode) {
|
||||
const fileName = generateFileName(routeOpts)
|
||||
t.ok(routeOpts)
|
||||
fs.writeFileSync(path.join(__dirname, fileName), schemaValidationCode)
|
||||
t.pass('stored the validation function')
|
||||
generatedFileNames.push(fileName)
|
||||
},
|
||||
restoreFunction () {
|
||||
t.fail('write mode ON')
|
||||
}
|
||||
})
|
||||
|
||||
const app = buildApp(factory)
|
||||
await app.ready()
|
||||
})
|
||||
|
||||
t.test('fastify integration - readMode', async t => {
|
||||
t.plan(6)
|
||||
|
||||
const factory = AjvStandaloneValidator({
|
||||
readMode: true,
|
||||
storeFunction () {
|
||||
t.fail('read mode ON')
|
||||
},
|
||||
restoreFunction (routeOpts) {
|
||||
t.pass('restore the validation function')
|
||||
const fileName = generateFileName(routeOpts)
|
||||
return require(path.join(__dirname, fileName))
|
||||
}
|
||||
})
|
||||
|
||||
const app = buildApp(factory)
|
||||
await app.ready()
|
||||
|
||||
let res = await app.inject({
|
||||
url: '/foo',
|
||||
method: 'POST',
|
||||
payload: { hello: [] }
|
||||
})
|
||||
t.equal(res.statusCode, 400)
|
||||
|
||||
res = await app.inject({
|
||||
url: '/bar?lang=invalid',
|
||||
method: 'GET'
|
||||
})
|
||||
t.equal(res.statusCode, 400)
|
||||
|
||||
res = await app.inject({
|
||||
url: '/bar?lang=it',
|
||||
method: 'GET'
|
||||
})
|
||||
t.equal(res.statusCode, 200)
|
||||
})
|
||||
|
||||
function buildApp (factory) {
|
||||
const app = fastify({
|
||||
jsonShorthand: false,
|
||||
schemaController: {
|
||||
compilersFactory: {
|
||||
buildValidator: factory
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
app.addSchema({
|
||||
$id: 'urn:schema:foo',
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
id: { type: 'integer' }
|
||||
}
|
||||
})
|
||||
|
||||
app.post('/foo', {
|
||||
schema: {
|
||||
body: {
|
||||
$id: 'urn:schema:body',
|
||||
type: 'object',
|
||||
properties: {
|
||||
hello: { $ref: 'urn:schema:foo#/properties/name' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}, () => { return 'ok' })
|
||||
|
||||
app.get('/bar', {
|
||||
schema: {
|
||||
query: {
|
||||
$id: 'urn:schema:query',
|
||||
type: 'object',
|
||||
properties: {
|
||||
lang: { type: 'string', enum: ['it', 'en'] }
|
||||
}
|
||||
}
|
||||
}
|
||||
}, () => { return 'ok' })
|
||||
|
||||
return app
|
||||
}
|
||||
})
|
||||
72
backend/node_modules/@fastify/ajv-compiler/types/index.d.ts
generated
vendored
Normal file
72
backend/node_modules/@fastify/ajv-compiler/types/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
import { AnySchema, default as _ajv, Options as AjvOptions, ValidateFunction } from "ajv";
|
||||
import { default as AjvJTD, JTDOptions } from "ajv/dist/jtd";
|
||||
import type { Options, ErrorObject } from "ajv";
|
||||
import { AnyValidateFunction } from "ajv/dist/core";
|
||||
|
||||
type Ajv = _ajv;
|
||||
type AjvSerializerGenerator = typeof AjvCompiler
|
||||
|
||||
type AjvJTDCompile = AjvJTD['compileSerializer']
|
||||
type AjvCompile = (schema: AnySchema, _meta?: boolean) => AnyValidateFunction
|
||||
|
||||
declare namespace AjvCompiler {
|
||||
export type { Options, ErrorObject }
|
||||
export { Ajv };
|
||||
|
||||
export type BuildSerializerFromPool = typeof buildSerializerFromPool
|
||||
|
||||
export type BuildCompilerFromPool = typeof buildCompilerFromPool
|
||||
|
||||
export const AjvReference: Symbol
|
||||
|
||||
export enum HttpParts {
|
||||
Body = "body",
|
||||
Headers = "headers",
|
||||
Params = "params",
|
||||
Query = "querystring",
|
||||
}
|
||||
|
||||
export type RouteDefinition = {
|
||||
method: string,
|
||||
url: string,
|
||||
httpPart: HttpParts,
|
||||
schema?: unknown,
|
||||
}
|
||||
|
||||
export type StandaloneRestoreFunction = (opts: RouteDefinition) => ValidateFunction
|
||||
|
||||
export type StandaloneStoreFunction = (opts: RouteDefinition, schemaValidationCode: string) => void
|
||||
|
||||
export type StandaloneOptionsReadModeOn = {
|
||||
readMode: true;
|
||||
restoreFunction?: StandaloneRestoreFunction
|
||||
}
|
||||
|
||||
export type StandaloneOptionsReadModeOff = {
|
||||
readMode?: false | undefined;
|
||||
storeFunction?: StandaloneStoreFunction;
|
||||
}
|
||||
|
||||
export type StandaloneOptions = StandaloneOptionsReadModeOn | StandaloneOptionsReadModeOff
|
||||
|
||||
export type ValidatorFactory = BuildCompilerFromPool | BuildSerializerFromPool
|
||||
|
||||
export type ValidatorCompiler = ReturnType<ValidatorFactory>
|
||||
|
||||
export { StandaloneValidator }
|
||||
|
||||
export const AjvCompiler: AjvSerializerGenerator
|
||||
export { AjvCompiler as default }
|
||||
}
|
||||
|
||||
declare function buildCompilerFromPool(externalSchemas: { [key: string]: AnySchema | AnySchema[] }, options?: { mode: 'JTD'; customOptions?: JTDOptions; onCreate?: (ajvInstance: Ajv) => void }): AjvCompile
|
||||
declare function buildCompilerFromPool(externalSchemas: { [key: string]: AnySchema | AnySchema[] }, options?: { mode?: never; customOptions?: AjvOptions; onCreate?: (ajvInstance: Ajv) => void }): AjvCompile
|
||||
|
||||
declare function buildSerializerFromPool(externalSchemas: any, serializerOpts?: { mode?: never; } & JTDOptions): AjvJTDCompile
|
||||
|
||||
declare function AjvCompiler(opts: { jtdSerializer: true }): AjvCompiler.BuildSerializerFromPool
|
||||
declare function AjvCompiler(opts?: { jtdSerializer?: false }): AjvCompiler.BuildCompilerFromPool
|
||||
|
||||
declare function StandaloneValidator(options: AjvCompiler.StandaloneOptions): AjvCompiler.BuildCompilerFromPool;
|
||||
|
||||
export = AjvCompiler
|
||||
227
backend/node_modules/@fastify/ajv-compiler/types/index.test-d.ts
generated
vendored
Normal file
227
backend/node_modules/@fastify/ajv-compiler/types/index.test-d.ts
generated
vendored
Normal file
@@ -0,0 +1,227 @@
|
||||
import { AnySchemaObject, ValidateFunction } from "ajv";
|
||||
import { AnyValidateFunction } from "ajv/dist/core";
|
||||
import { expectAssignable, expectType } from "tsd";
|
||||
import AjvCompiler, { AjvReference, ValidatorFactory, StandaloneValidator, RouteDefinition, ErrorObject, BuildCompilerFromPool, BuildSerializerFromPool, ValidatorCompiler } from "..";
|
||||
|
||||
{
|
||||
const compiler = AjvCompiler({});
|
||||
expectType<BuildCompilerFromPool>(compiler);
|
||||
}
|
||||
{
|
||||
const compiler = AjvCompiler();
|
||||
expectType<BuildCompilerFromPool>(compiler);
|
||||
}
|
||||
{
|
||||
const compiler = AjvCompiler({ jtdSerializer: false});
|
||||
expectType<BuildCompilerFromPool>(compiler);
|
||||
}
|
||||
|
||||
{
|
||||
const factory = AjvCompiler({ jtdSerializer: false });
|
||||
expectType<BuildCompilerFromPool>(factory);
|
||||
factory({}, {
|
||||
onCreate(ajv) {
|
||||
expectType<import("ajv").default>(ajv)
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
const compiler = AjvCompiler({ jtdSerializer: true});
|
||||
expectType<BuildSerializerFromPool>(compiler);
|
||||
}
|
||||
const reader = StandaloneValidator({
|
||||
readMode: true,
|
||||
restoreFunction: (route: RouteDefinition) => {
|
||||
expectAssignable<RouteDefinition>(route)
|
||||
return {} as ValidateFunction
|
||||
},
|
||||
});
|
||||
expectAssignable<ValidatorFactory>(reader);
|
||||
|
||||
const writer = StandaloneValidator({
|
||||
readMode: false,
|
||||
storeFunction: (route: RouteDefinition, code: string) => {
|
||||
expectAssignable<RouteDefinition>(route)
|
||||
expectAssignable<string>(code)
|
||||
},
|
||||
});
|
||||
expectAssignable<ValidatorFactory>(writer);
|
||||
|
||||
expectType<unknown>(({} as ErrorObject).data)
|
||||
expectType<string>(({} as ErrorObject).instancePath)
|
||||
expectType<string>(({} as ErrorObject).keyword)
|
||||
expectType<string | undefined>(({} as ErrorObject).message)
|
||||
expectType<Record<string, any>>(({} as ErrorObject).params)
|
||||
expectType<AnySchemaObject | undefined>(({} as ErrorObject).parentSchema)
|
||||
expectType<string | undefined>(({} as ErrorObject).propertyName)
|
||||
expectType<unknown>(({} as ErrorObject).schema)
|
||||
expectType<string>(({} as ErrorObject).schemaPath)
|
||||
|
||||
expectType<Symbol>(AjvReference)
|
||||
|
||||
{
|
||||
const jtdSchema = {
|
||||
discriminator: 'version',
|
||||
mapping: {
|
||||
1: {
|
||||
properties: {
|
||||
foo: { type: 'uint8' }
|
||||
}
|
||||
},
|
||||
2: {
|
||||
properties: {
|
||||
foo: { type: 'string' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const externalSchemas1 = {
|
||||
foo: {
|
||||
definitions: {
|
||||
coordinates: {
|
||||
properties: {
|
||||
lat: { type: 'float32' },
|
||||
lng: { type: 'float32' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const factory = AjvCompiler({ jtdSerializer: true })
|
||||
expectType<BuildSerializerFromPool>(factory)
|
||||
const compiler = factory(externalSchemas1, {})
|
||||
expectAssignable<Function>(compiler)
|
||||
const serializeFunc = compiler({ schema: jtdSchema })
|
||||
expectType<(data: unknown) => string>(serializeFunc)
|
||||
expectType<string>(serializeFunc({ version: '1', foo: 42 }))
|
||||
}
|
||||
// JTD
|
||||
{
|
||||
|
||||
const factory = AjvCompiler()
|
||||
expectType<BuildCompilerFromPool>(factory)
|
||||
|
||||
const jtdSchema = {
|
||||
discriminator: 'version',
|
||||
mapping: {
|
||||
1: {
|
||||
properties: {
|
||||
foo: { type: 'uint8' }
|
||||
}
|
||||
},
|
||||
2: {
|
||||
properties: {
|
||||
foo: { type: 'string' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const compiler = factory({}, {
|
||||
customOptions: {},
|
||||
mode: 'JTD'
|
||||
})
|
||||
expectAssignable<ValidatorCompiler>(compiler)
|
||||
const validatorFunc = compiler({ schema: jtdSchema })
|
||||
expectAssignable<ValidateFunction>(validatorFunc)
|
||||
|
||||
expectType<boolean | Promise<any>>(validatorFunc({
|
||||
version: '2',
|
||||
foo: []
|
||||
}))
|
||||
}
|
||||
|
||||
// generate standalone code
|
||||
{
|
||||
const base = {
|
||||
$id: 'urn:schema:base',
|
||||
definitions: {
|
||||
hello: { type: 'string' }
|
||||
},
|
||||
type: 'object',
|
||||
properties: {
|
||||
hello: { $ref: '#/definitions/hello' }
|
||||
}
|
||||
}
|
||||
|
||||
const refSchema = {
|
||||
$id: 'urn:schema:ref',
|
||||
type: 'object',
|
||||
properties: {
|
||||
hello: { $ref: 'urn:schema:base#/definitions/hello' }
|
||||
}
|
||||
}
|
||||
|
||||
const endpointSchema = {
|
||||
schema: {
|
||||
$id: 'urn:schema:endpoint',
|
||||
$ref: 'urn:schema:ref'
|
||||
}
|
||||
}
|
||||
|
||||
const schemaMap = {
|
||||
[base.$id]: base,
|
||||
[refSchema.$id]: refSchema
|
||||
}
|
||||
|
||||
const factory = StandaloneValidator({
|
||||
readMode: false,
|
||||
storeFunction(routeOpts, schemaValidationCode) {
|
||||
expectType<RouteDefinition>(routeOpts)
|
||||
expectType<string>(schemaValidationCode)
|
||||
}
|
||||
})
|
||||
expectAssignable<ValidatorFactory>(factory)
|
||||
|
||||
const compiler = factory(schemaMap)
|
||||
expectAssignable<ValidatorCompiler>(compiler)
|
||||
expectAssignable<Function>(compiler(endpointSchema))
|
||||
}
|
||||
|
||||
{
|
||||
const base = {
|
||||
$id: 'urn:schema:base',
|
||||
definitions: {
|
||||
hello: { type: 'string' }
|
||||
},
|
||||
type: 'object',
|
||||
properties: {
|
||||
hello: { $ref: '#/definitions/hello' }
|
||||
}
|
||||
}
|
||||
|
||||
const refSchema = {
|
||||
$id: 'urn:schema:ref',
|
||||
type: 'object',
|
||||
properties: {
|
||||
hello: { $ref: 'urn:schema:base#/definitions/hello' }
|
||||
}
|
||||
}
|
||||
|
||||
const endpointSchema = {
|
||||
schema: {
|
||||
$id: 'urn:schema:endpoint',
|
||||
$ref: 'urn:schema:ref'
|
||||
}
|
||||
}
|
||||
|
||||
const schemaMap = {
|
||||
[base.$id]: base,
|
||||
[refSchema.$id]: refSchema
|
||||
}
|
||||
const factory = StandaloneValidator({
|
||||
readMode: true,
|
||||
restoreFunction(routeOpts) {
|
||||
expectType<RouteDefinition>(routeOpts)
|
||||
return {} as ValidateFunction
|
||||
}
|
||||
})
|
||||
expectAssignable<ValidatorFactory>(factory)
|
||||
|
||||
const compiler = factory(schemaMap)
|
||||
expectAssignable<ValidatorCompiler>(compiler)
|
||||
expectType<AnyValidateFunction<any>>(compiler(endpointSchema))
|
||||
}
|
||||
8
backend/node_modules/@fastify/cors/.editorconfig
generated
vendored
Normal file
8
backend/node_modules/@fastify/cors/.editorconfig
generated
vendored
Normal 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
backend/node_modules/@fastify/cors/.gitattributes
generated
vendored
Normal file
5
backend/node_modules/@fastify/cors/.gitattributes
generated
vendored
Normal 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
|
||||
16
backend/node_modules/@fastify/cors/.github/dependabot.yml
generated
vendored
Normal file
16
backend/node_modules/@fastify/cors/.github/dependabot.yml
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "monthly"
|
||||
open-pull-requests-limit: 10
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
open-pull-requests-limit: 10
|
||||
ignore:
|
||||
- dependency-name: tap
|
||||
update-types: ["version-update:semver-major"]
|
||||
21
backend/node_modules/@fastify/cors/.github/stale.yml
generated
vendored
Normal file
21
backend/node_modules/@fastify/cors/.github/stale.yml
generated
vendored
Normal 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
|
||||
23
backend/node_modules/@fastify/cors/.github/workflows/ci.yml
generated
vendored
Normal file
23
backend/node_modules/@fastify/cors/.github/workflows/ci.yml
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- master
|
||||
- next
|
||||
- 'v*'
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
uses: fastify/workflows/.github/workflows/plugins-ci.yml@v3
|
||||
with:
|
||||
lint: true
|
||||
license-check: true
|
||||
4
backend/node_modules/@fastify/cors/.taprc
generated
vendored
Normal file
4
backend/node_modules/@fastify/cors/.taprc
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
ts: false
|
||||
jsx: false
|
||||
coverage: true
|
||||
flow: true
|
||||
21
backend/node_modules/@fastify/cors/LICENSE
generated
vendored
Normal file
21
backend/node_modules/@fastify/cors/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2018 Fastify
|
||||
|
||||
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.
|
||||
169
backend/node_modules/@fastify/cors/README.md
generated
vendored
Normal file
169
backend/node_modules/@fastify/cors/README.md
generated
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
# @fastify/cors
|
||||
|
||||

|
||||
[](https://www.npmjs.com/package/@fastify/cors)
|
||||
[](https://standardjs.com/)
|
||||
|
||||
|
||||
`@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 |
|
||||
| -------------- |---------------- |
|
||||
| `^8.0.0` | `^4.0.0` |
|
||||
| `^7.0.0` | `^3.0.0` |
|
||||
| `^3.0.0` | `^2.0.0` |
|
||||
| `^1.0.0` | `^1.0.0` |
|
||||
|
||||
|
||||
Please note that if a Fastify version is out of support, then so are the corresponding version(s) 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 will add 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 could be of different types:
|
||||
- `Boolean` - set `origin` to `true` to reflect the [request origin](http://tools.ietf.org/html/draft-abarth-origin-09), or set it to `false` to disable CORS.
|
||||
- `String` - set `origin` to a specific origin. For example if you set it to `"http://example.com"` only requests from "http://example.com" will be allowed. The special `*` value (default) allows any origin.
|
||||
- `RegExp` - set `origin` to a regular expression pattern that will be used to test the request origin. If it is a match, the request origin will be reflected. For example, the pattern `/example\.com$/` will reflect any request that is coming from an origin ending with "example.com".
|
||||
- `Array` - set `origin` to an array of valid origins. Each origin can be a `String` or a `RegExp`. For example `["http://example1.com", /\.example2\.com$/]` will accept any request from "http://example1.com" or from a subdomain of "example2.com".
|
||||
- `Function` - set `origin` to a function implementing some custom logic. The function takes the request origin as the first parameter and a callback as a second (which expects the signature `err [Error | null], origin`), where `origin` is a non-function value of the origin option. *Async-await* and promises are supported as well. The Fastify instance is bound to function call and you may access 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 (ex: 'GET,PUT,POST') or an array (ex: `['GET', 'PUT', 'POST']`).
|
||||
* `hook`: See the section `Custom Fastify hook name` (default: `onRequest`)
|
||||
* `allowedHeaders`: 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.
|
||||
* `exposedHeaders`: 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.
|
||||
* `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}` (fully define the header value), otherwise the header is omitted.
|
||||
* `preflightContinue`: Pass the CORS preflight response to the route handler (default: `false`).
|
||||
* `optionsSuccessStatus`: Provides a status code to use for successful `OPTIONS` requests, since some legacy browsers (IE11, various SmartTVs) choke on `204`.
|
||||
* `preflight`: if needed you can entirely disable preflight by passing `false` here (default: `true`).
|
||||
* `strictPreflight`: Enforces strict requirement of 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) (the current [fetch living specification](https://fetch.spec.whatwg.org/) does not define server behavior for missing headers). Preflight requests without the required headers will result in 400 errors when set to `true` (default: `true`).
|
||||
* `hideOptionsRoute`: hide options route from the documentation built using [@fastify/swagger](https://github.com/fastify/fastify-swagger) (default: `true`).
|
||||
|
||||
### 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 })
|
||||
```
|
||||
|
||||
### Custom Fastify hook name
|
||||
|
||||
By default, `@fastify/cors` adds a `onRequest` hook where the validation and header injection are executed. 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 })
|
||||
```
|
||||
|
||||
When configuring CORS asynchronously, an object with `delegator` key is expected:
|
||||
|
||||
```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 })
|
||||
```
|
||||
|
||||
## Acknowledgements
|
||||
|
||||
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
backend/node_modules/@fastify/cors/bench.js
generated
vendored
Normal file
17
backend/node_modules/@fastify/cors/bench.js
generated
vendored
Normal 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
backend/node_modules/@fastify/cors/benchmark/package.json
generated
vendored
Normal file
15
backend/node_modules/@fastify/cors/benchmark/package.json
generated
vendored
Normal 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
backend/node_modules/@fastify/cors/benchmark/vary.js
generated
vendored
Normal file
34
backend/node_modules/@fastify/cors/benchmark/vary.js
generated
vendored
Normal 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 })
|
||||
307
backend/node_modules/@fastify/cors/index.js
generated
vendored
Normal file
307
backend/node_modules/@fastify/cors/index.js
generated
vendored
Normal file
@@ -0,0 +1,307 @@
|
||||
'use strict'
|
||||
|
||||
const fp = require('fastify-plugin')
|
||||
const {
|
||||
addAccessControlRequestHeadersToVaryHeader,
|
||||
addOriginToVaryHeader
|
||||
} = require('./vary')
|
||||
|
||||
const defaultOptions = {
|
||||
origin: '*',
|
||||
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
||||
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
|
||||
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 {
|
||||
if (opts.hideOptionsRoute !== undefined) hideOptionsRoute = opts.hideOptionsRoute
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 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 } }, (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 && 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, options, req, reply, next) {
|
||||
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()
|
||||
}
|
||||
|
||||
// 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: '4.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
|
||||
59
backend/node_modules/@fastify/cors/package.json
generated
vendored
Normal file
59
backend/node_modules/@fastify/cors/package.json
generated
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"name": "@fastify/cors",
|
||||
"version": "9.0.1",
|
||||
"description": "Fastify CORS",
|
||||
"main": "index.js",
|
||||
"type": "commonjs",
|
||||
"types": "types/index.d.ts",
|
||||
"scripts": {
|
||||
"coverage": "tap --cov --coverage-report=html test",
|
||||
"lint": "standard",
|
||||
"lint:fix": "standard --fix",
|
||||
"test": "npm run test:unit && npm run test:typescript",
|
||||
"test:typescript": "tsd",
|
||||
"test:unit": "tap test/*.test.js"
|
||||
},
|
||||
"keywords": [
|
||||
"fastify",
|
||||
"cors",
|
||||
"headers",
|
||||
"access",
|
||||
"control"
|
||||
],
|
||||
"author": "Tomas Della Vedova - @delvedor (http://delved.org)",
|
||||
"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",
|
||||
"devDependencies": {
|
||||
"@fastify/pre-commit": "^2.0.2",
|
||||
"@types/node": "^20.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.4.1",
|
||||
"@typescript-eslint/parser": "^6.4.0",
|
||||
"cors": "^2.8.5",
|
||||
"fastify": "^4.0.0-rc.2",
|
||||
"standard": "^17.0.0",
|
||||
"tap": "16.3.9",
|
||||
"tsd": "^0.30.0",
|
||||
"typescript": "^5.0.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"fastify-plugin": "^4.0.0",
|
||||
"mnemonist": "0.39.6"
|
||||
},
|
||||
"tsd": {
|
||||
"directory": "test"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"pre-commit": [
|
||||
"lint",
|
||||
"test"
|
||||
]
|
||||
}
|
||||
941
backend/node_modules/@fastify/cors/test/cors.test.js
generated
vendored
Normal file
941
backend/node_modules/@fastify/cors/test/cors.test.js
generated
vendored
Normal file
@@ -0,0 +1,941 @@
|
||||
'use strict'
|
||||
|
||||
const { test } = require('tap')
|
||||
const { createReadStream, statSync, readFileSync } = require('node:fs')
|
||||
const Fastify = require('fastify')
|
||||
const cors = require('../')
|
||||
const { resolve } = require('node:path')
|
||||
|
||||
test('Should add cors headers', t => {
|
||||
t.plan(4)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors)
|
||||
|
||||
fastify.get('/', (req, reply) => {
|
||||
reply.send('ok')
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/'
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': '*'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('Should add cors headers when payload is a stream', t => {
|
||||
t.plan(4)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors)
|
||||
const filePath = resolve(__dirname, __filename)
|
||||
|
||||
fastify.get('/', (req, reply) => {
|
||||
const stream = createReadStream(filePath)
|
||||
reply
|
||||
.type('application/json')
|
||||
.header('Content-Length', statSync(filePath).size)
|
||||
.send(stream)
|
||||
})
|
||||
|
||||
const fileContent = readFileSync(filePath, 'utf-8')
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/'
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, fileContent)
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': '*',
|
||||
'content-length': statSync(filePath).size
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('Should add cors headers (custom values)', t => {
|
||||
t.plan(10)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors, {
|
||||
origin: 'example.com',
|
||||
methods: 'GET',
|
||||
credentials: true,
|
||||
exposedHeaders: ['foo', 'bar'],
|
||||
allowedHeaders: ['baz', 'woo'],
|
||||
maxAge: 123,
|
||||
cacheControl: 321
|
||||
})
|
||||
|
||||
fastify.get('/', (req, reply) => {
|
||||
reply.send('ok')
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/',
|
||||
headers: {
|
||||
'access-control-request-method': 'GET',
|
||||
origin: 'example.com'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 204)
|
||||
t.equal(res.payload, '')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': 'example.com',
|
||||
'access-control-allow-credentials': 'true',
|
||||
'access-control-expose-headers': 'foo, bar',
|
||||
'access-control-allow-methods': 'GET',
|
||||
'access-control-allow-headers': 'baz, woo',
|
||||
'access-control-max-age': '123',
|
||||
'cache-control': 'max-age=321',
|
||||
'content-length': '0'
|
||||
})
|
||||
t.notMatch(res.headers, { vary: 'Origin' })
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/'
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': 'example.com',
|
||||
'access-control-allow-credentials': 'true',
|
||||
'access-control-expose-headers': 'foo, bar',
|
||||
'content-length': '2'
|
||||
})
|
||||
t.notMatch(res.headers, { vary: 'Origin' })
|
||||
})
|
||||
})
|
||||
|
||||
test('Should support dynamic config (callback)', t => {
|
||||
t.plan(16)
|
||||
|
||||
const configs = [{
|
||||
origin: 'example.com',
|
||||
methods: 'GET',
|
||||
credentials: true,
|
||||
exposedHeaders: ['foo', 'bar'],
|
||||
allowedHeaders: ['baz', 'woo'],
|
||||
maxAge: 123,
|
||||
cacheControl: 456
|
||||
}, {
|
||||
origin: 'sample.com',
|
||||
methods: 'GET',
|
||||
credentials: true,
|
||||
exposedHeaders: ['zoo', 'bar'],
|
||||
allowedHeaders: ['baz', 'foo'],
|
||||
maxAge: 321,
|
||||
cacheControl: '456'
|
||||
}]
|
||||
|
||||
const fastify = Fastify()
|
||||
let requestId = 0
|
||||
const configDelegation = function (req, cb) {
|
||||
// request should have id
|
||||
t.ok(req.id)
|
||||
// request should not have send
|
||||
t.notOk(req.send)
|
||||
const config = configs[requestId]
|
||||
requestId++
|
||||
if (config) {
|
||||
cb(null, config)
|
||||
} else {
|
||||
cb(new Error('ouch'))
|
||||
}
|
||||
}
|
||||
fastify.register(cors, () => configDelegation)
|
||||
|
||||
fastify.get('/', (req, reply) => {
|
||||
reply.send('ok')
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/'
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
'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.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 204)
|
||||
t.equal(res.payload, '')
|
||||
t.match(res.headers, {
|
||||
'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',
|
||||
'cache-control': '456',
|
||||
'content-length': '0',
|
||||
vary: 'Origin'
|
||||
})
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/',
|
||||
headers: {
|
||||
'access-control-request-method': 'GET',
|
||||
origin: 'example.com'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
t.equal(res.statusCode, 500)
|
||||
})
|
||||
})
|
||||
|
||||
test('Should support dynamic config (Promise)', t => {
|
||||
t.plan(23)
|
||||
|
||||
const configs = [{
|
||||
origin: 'example.com',
|
||||
methods: 'GET',
|
||||
credentials: true,
|
||||
exposedHeaders: ['foo', 'bar'],
|
||||
allowedHeaders: ['baz', 'woo'],
|
||||
maxAge: 123,
|
||||
cacheControl: 456
|
||||
}, {
|
||||
origin: 'sample.com',
|
||||
methods: 'GET',
|
||||
credentials: true,
|
||||
exposedHeaders: ['zoo', 'bar'],
|
||||
allowedHeaders: ['baz', 'foo'],
|
||||
maxAge: 321,
|
||||
cacheControl: true // Invalid value should be ignored
|
||||
}, {
|
||||
origin: 'sample.com',
|
||||
methods: 'GET',
|
||||
credentials: true,
|
||||
exposedHeaders: ['zoo', 'bar'],
|
||||
allowedHeaders: ['baz', 'foo'],
|
||||
maxAge: 321,
|
||||
cacheControl: 'public, max-age=456'
|
||||
}]
|
||||
|
||||
const fastify = Fastify()
|
||||
let requestId = 0
|
||||
const configDelegation = function (req) {
|
||||
// request should have id
|
||||
t.ok(req.id)
|
||||
// request should not have send
|
||||
t.notOk(req.send)
|
||||
const config = configs[requestId]
|
||||
requestId++
|
||||
if (config) {
|
||||
return Promise.resolve(config)
|
||||
} else {
|
||||
return Promise.reject(new Error('ouch'))
|
||||
}
|
||||
}
|
||||
fastify.register(cors, () => configDelegation)
|
||||
|
||||
fastify.get('/', (req, reply) => {
|
||||
reply.send('ok')
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/'
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
'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: 'sample.com'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 204)
|
||||
t.equal(res.payload, '')
|
||||
t.match(res.headers, {
|
||||
'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'
|
||||
})
|
||||
t.equal(res.headers['cache-control'], undefined, 'cache-control omitted (invalid value)')
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/',
|
||||
headers: {
|
||||
'access-control-request-method': 'GET',
|
||||
origin: 'example.com'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 204)
|
||||
t.equal(res.payload, '')
|
||||
t.match(res.headers, {
|
||||
'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',
|
||||
'cache-control': 'public, max-age=456', // cache-control included (custom string)
|
||||
'content-length': '0',
|
||||
vary: 'Origin'
|
||||
})
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/',
|
||||
headers: {
|
||||
'access-control-request-method': 'GET',
|
||||
origin: 'example.com'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
t.equal(res.statusCode, 500)
|
||||
})
|
||||
})
|
||||
|
||||
test('Should support dynamic config. (Invalid function)', t => {
|
||||
t.plan(2)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors, () => (a, b, c) => {})
|
||||
|
||||
fastify.get('/', (req, reply) => {
|
||||
reply.send('ok')
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/',
|
||||
headers: {
|
||||
'access-control-request-method': 'GET',
|
||||
origin: 'example.com'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
t.equal(res.statusCode, 500)
|
||||
})
|
||||
})
|
||||
|
||||
test('Dynamic origin resolution (valid origin)', t => {
|
||||
t.plan(6)
|
||||
|
||||
const fastify = Fastify()
|
||||
const origin = function (header, cb) {
|
||||
t.equal(header, 'example.com')
|
||||
t.same(this, fastify)
|
||||
cb(null, true)
|
||||
}
|
||||
fastify.register(cors, { origin })
|
||||
|
||||
fastify.get('/', (req, reply) => {
|
||||
reply.send('ok')
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/',
|
||||
headers: { origin: 'example.com' }
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': 'example.com',
|
||||
vary: 'Origin'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('Dynamic origin resolution (not valid origin)', t => {
|
||||
t.plan(5)
|
||||
|
||||
const fastify = Fastify()
|
||||
const origin = (header, cb) => {
|
||||
t.equal(header, 'example.com')
|
||||
cb(null, false)
|
||||
}
|
||||
fastify.register(cors, { origin })
|
||||
|
||||
fastify.get('/', (req, reply) => {
|
||||
reply.send('ok')
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/',
|
||||
headers: { origin: 'example.com' }
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.same(res.headers, {
|
||||
'content-length': '2',
|
||||
'content-type': 'text/plain; charset=utf-8',
|
||||
connection: 'keep-alive',
|
||||
vary: 'Origin'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('Dynamic origin resolution (errored)', t => {
|
||||
t.plan(3)
|
||||
|
||||
const fastify = Fastify()
|
||||
const origin = (header, cb) => {
|
||||
t.equal(header, 'example.com')
|
||||
cb(new Error('ouch'))
|
||||
}
|
||||
fastify.register(cors, { origin })
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/',
|
||||
headers: { origin: 'example.com' }
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
t.equal(res.statusCode, 500)
|
||||
})
|
||||
})
|
||||
|
||||
test('Dynamic origin resolution (invalid result)', t => {
|
||||
t.plan(3)
|
||||
|
||||
const fastify = Fastify()
|
||||
const origin = (header, cb) => {
|
||||
t.equal(header, 'example.com')
|
||||
cb(null, undefined)
|
||||
}
|
||||
fastify.register(cors, { origin })
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/',
|
||||
headers: { origin: 'example.com' }
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
t.equal(res.statusCode, 500)
|
||||
})
|
||||
})
|
||||
|
||||
test('Dynamic origin resolution (valid origin - promises)', t => {
|
||||
t.plan(5)
|
||||
|
||||
const fastify = Fastify()
|
||||
const origin = (header, cb) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
t.equal(header, 'example.com')
|
||||
resolve(true)
|
||||
})
|
||||
}
|
||||
fastify.register(cors, { origin })
|
||||
|
||||
fastify.get('/', (req, reply) => {
|
||||
reply.send('ok')
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/',
|
||||
headers: { origin: 'example.com' }
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': 'example.com',
|
||||
vary: 'Origin'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('Dynamic origin resolution (not valid origin - promises)', t => {
|
||||
t.plan(5)
|
||||
|
||||
const fastify = Fastify()
|
||||
const origin = (header, cb) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
t.equal(header, 'example.com')
|
||||
resolve(false)
|
||||
})
|
||||
}
|
||||
fastify.register(cors, { origin })
|
||||
|
||||
fastify.get('/', (req, reply) => {
|
||||
reply.send('ok')
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/',
|
||||
headers: { origin: 'example.com' }
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.same(res.headers, {
|
||||
'content-length': '2',
|
||||
'content-type': 'text/plain; charset=utf-8',
|
||||
connection: 'keep-alive',
|
||||
vary: 'Origin'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('Dynamic origin resolution (errored - promises)', t => {
|
||||
t.plan(3)
|
||||
|
||||
const fastify = Fastify()
|
||||
const origin = (header, cb) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
t.equal(header, 'example.com')
|
||||
reject(new Error('ouch'))
|
||||
})
|
||||
}
|
||||
fastify.register(cors, { origin })
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/',
|
||||
headers: { origin: 'example.com' }
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
t.equal(res.statusCode, 500)
|
||||
})
|
||||
})
|
||||
|
||||
test('Should reply 404 without cors headers when origin is false', t => {
|
||||
t.plan(8)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors, {
|
||||
origin: false,
|
||||
methods: 'GET',
|
||||
credentials: true,
|
||||
exposedHeaders: ['foo', 'bar'],
|
||||
allowedHeaders: ['baz', 'woo'],
|
||||
maxAge: 123
|
||||
})
|
||||
|
||||
fastify.get('/', (req, reply) => {
|
||||
reply.send('ok')
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/'
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 404)
|
||||
t.equal(res.payload, '{"message":"Route OPTIONS:/ not found","error":"Not Found","statusCode":404}')
|
||||
t.same(res.headers, {
|
||||
'content-length': '76',
|
||||
'content-type': 'application/json; charset=utf-8',
|
||||
connection: 'keep-alive'
|
||||
})
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/'
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.same(res.headers, {
|
||||
'content-length': '2',
|
||||
'content-type': 'text/plain; charset=utf-8',
|
||||
connection: 'keep-alive'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('Server error if origin option is falsy but not false', t => {
|
||||
t.plan(4)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors, { origin: '' })
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/',
|
||||
headers: { origin: 'example.com' }
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 500)
|
||||
t.same(res.json(), { statusCode: 500, error: 'Internal Server Error', message: 'Invalid CORS origin option' })
|
||||
t.same(res.headers, {
|
||||
'content-length': '89',
|
||||
'content-type': 'application/json; charset=utf-8',
|
||||
connection: 'keep-alive'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('Allow only request from a specific origin', t => {
|
||||
t.plan(5)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors, { origin: 'other.io' })
|
||||
|
||||
fastify.get('/', (req, reply) => {
|
||||
reply.send('ok')
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/',
|
||||
headers: { origin: 'example.com' }
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': 'other.io'
|
||||
})
|
||||
t.notMatch(res.headers, { vary: 'Origin' })
|
||||
})
|
||||
})
|
||||
|
||||
test('Allow only request from multiple specific origin', t => {
|
||||
t.plan(9)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors, { origin: ['other.io', 'example.com'] })
|
||||
|
||||
fastify.get('/', (req, reply) => {
|
||||
reply.send('ok')
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/',
|
||||
headers: { origin: 'other.io' }
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': 'other.io',
|
||||
vary: 'Origin'
|
||||
})
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/',
|
||||
headers: { origin: 'foo.com' }
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
vary: 'Origin'
|
||||
})
|
||||
t.equal(res.headers['access-control-allow-origin'], undefined)
|
||||
})
|
||||
})
|
||||
|
||||
test('Allow only request from a specific origin using regex', t => {
|
||||
t.plan(8)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors, { origin: /(?:example|other)\.com/giu })
|
||||
|
||||
fastify.get('/', (req, reply) => {
|
||||
reply.send('ok')
|
||||
})
|
||||
|
||||
// .test was previously used, which caused 2 consecutive requests to return
|
||||
// different results with global (e.g. /g) regexes. Therefore, check this
|
||||
// twice to check consistency
|
||||
for (let i = 0; i < 2; i++) {
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/',
|
||||
headers: { origin: 'https://www.example.com/' }
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': 'https://www.example.com/',
|
||||
vary: 'Origin'
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
test('Disable preflight', t => {
|
||||
t.plan(7)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors, { preflight: false })
|
||||
|
||||
fastify.get('/', (req, reply) => {
|
||||
reply.send('ok')
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/hello'
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 404)
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': '*'
|
||||
})
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/'
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': '*'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('Should always add vary header to `Origin` for reflected origin', t => {
|
||||
t.plan(12)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors, { origin: true })
|
||||
|
||||
fastify.get('/', (req, reply) => {
|
||||
reply.send('ok')
|
||||
})
|
||||
|
||||
// Invalid Preflight
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/'
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 400)
|
||||
t.equal(res.payload, 'Invalid Preflight Request')
|
||||
t.match(res.headers, {
|
||||
vary: 'Origin'
|
||||
})
|
||||
})
|
||||
|
||||
// Valid Preflight
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/',
|
||||
headers: {
|
||||
'access-control-request-method': 'GET',
|
||||
origin: 'example.com'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 204)
|
||||
t.equal(res.payload, '')
|
||||
t.match(res.headers, {
|
||||
vary: 'Origin'
|
||||
})
|
||||
})
|
||||
|
||||
// Other Route
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/'
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
vary: 'Origin'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('Should always add vary header to `Origin` for reflected origin (vary is array)', t => {
|
||||
t.plan(4)
|
||||
|
||||
const fastify = Fastify()
|
||||
|
||||
// Mock getHeader function
|
||||
fastify.decorateReply('getHeader', (name) => ['foo', 'bar'])
|
||||
|
||||
fastify.register(cors, { origin: true })
|
||||
|
||||
fastify.get('/', (req, reply) => {
|
||||
reply.send('ok')
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/'
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
vary: 'foo, bar, Origin'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('Allow only request from with specific headers', t => {
|
||||
t.plan(8)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors, {
|
||||
allowedHeaders: 'foo',
|
||||
exposedHeaders: 'bar'
|
||||
})
|
||||
|
||||
fastify.get('/', (req, reply) => {
|
||||
reply.send('ok')
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/',
|
||||
headers: {
|
||||
'access-control-request-method': 'GET',
|
||||
origin: 'example.com'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 204)
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-headers': 'foo'
|
||||
})
|
||||
t.notMatch(res.headers, { vary: 'Origin' })
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/'
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
'access-control-expose-headers': 'bar'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('Should support wildcard config /1', t => {
|
||||
t.plan(4)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors, { origin: '*' })
|
||||
|
||||
fastify.get('/', (req, reply) => {
|
||||
reply.send('ok')
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/'
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.equal(res.headers['access-control-allow-origin'], '*')
|
||||
})
|
||||
})
|
||||
|
||||
test('Should support wildcard config /2', t => {
|
||||
t.plan(4)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors, { origin: ['*'] })
|
||||
|
||||
fastify.get('/', (req, reply) => {
|
||||
reply.send('ok')
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/'
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.equal(res.headers['access-control-allow-origin'], '*')
|
||||
})
|
||||
})
|
||||
706
backend/node_modules/@fastify/cors/test/hooks.test.js
generated
vendored
Normal file
706
backend/node_modules/@fastify/cors/test/hooks.test.js
generated
vendored
Normal file
@@ -0,0 +1,706 @@
|
||||
'use strict'
|
||||
|
||||
const { test } = require('tap')
|
||||
const Fastify = require('fastify')
|
||||
const kFastifyContext = require('fastify/lib/symbols').kRouteContext
|
||||
const cors = require('..')
|
||||
|
||||
test('Should error on invalid hook option', async (t) => {
|
||||
t.plan(1)
|
||||
|
||||
const fastify = Fastify()
|
||||
t.rejects(fastify.register(cors, { hook: 'invalid' }), new TypeError('@fastify/cors: Invalid hook option provided.'))
|
||||
})
|
||||
|
||||
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.equal(request[kFastifyContext].onError, null)
|
||||
t.equal(request[kFastifyContext].onRequest.length, 1)
|
||||
t.equal(request[kFastifyContext].onSend, null)
|
||||
t.equal(request[kFastifyContext].preHandler, null)
|
||||
t.equal(request[kFastifyContext].preParsing, null)
|
||||
t.equal(request[kFastifyContext].preSerialization, null)
|
||||
t.equal(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.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
'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.equal(request[kFastifyContext].onError, null)
|
||||
t.equal(request[kFastifyContext].onRequest.length, 1)
|
||||
t.equal(request[kFastifyContext].onSend, null)
|
||||
t.equal(request[kFastifyContext].preHandler, null)
|
||||
t.equal(request[kFastifyContext].preParsing, null)
|
||||
t.equal(request[kFastifyContext].preSerialization, null)
|
||||
t.equal(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.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
'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.equal(request[kFastifyContext].onError, null)
|
||||
t.equal(request[kFastifyContext].onRequest, null)
|
||||
t.equal(request[kFastifyContext].onSend, null)
|
||||
t.equal(request[kFastifyContext].preHandler, null)
|
||||
t.equal(request[kFastifyContext].preParsing.length, 1)
|
||||
t.equal(request[kFastifyContext].preSerialization, null)
|
||||
t.equal(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.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': '*'
|
||||
})
|
||||
t.notMatch(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.equal(request[kFastifyContext].onError, null)
|
||||
t.equal(request[kFastifyContext].onRequest, null)
|
||||
t.equal(request[kFastifyContext].onSend, null)
|
||||
t.equal(request[kFastifyContext].preHandler, null)
|
||||
t.equal(request[kFastifyContext].preParsing, null)
|
||||
t.equal(request[kFastifyContext].preSerialization, null)
|
||||
t.equal(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.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': '*'
|
||||
})
|
||||
t.notMatch(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.equal(request[kFastifyContext].onError, null)
|
||||
t.equal(request[kFastifyContext].onRequest, null)
|
||||
t.equal(request[kFastifyContext].onSend, null)
|
||||
t.equal(request[kFastifyContext].preHandler, null)
|
||||
t.equal(request[kFastifyContext].preParsing.length, 1)
|
||||
t.equal(request[kFastifyContext].preSerialization, null)
|
||||
t.equal(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.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': '*'
|
||||
})
|
||||
t.notMatch(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.equal(request[kFastifyContext].onError, null)
|
||||
t.equal(request[kFastifyContext].onRequest, null)
|
||||
t.equal(request[kFastifyContext].onSend, null)
|
||||
t.equal(request[kFastifyContext].preHandler.length, 1)
|
||||
t.equal(request[kFastifyContext].preParsing, null)
|
||||
t.equal(request[kFastifyContext].preSerialization, null)
|
||||
t.equal(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.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': '*'
|
||||
})
|
||||
t.notMatch(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.equal(request[kFastifyContext].onError, null)
|
||||
t.equal(request[kFastifyContext].onRequest, null)
|
||||
t.equal(request[kFastifyContext].onSend.length, 1)
|
||||
t.equal(request[kFastifyContext].preHandler, null)
|
||||
t.equal(request[kFastifyContext].preParsing, null)
|
||||
t.equal(request[kFastifyContext].preSerialization, null)
|
||||
t.equal(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.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': '*'
|
||||
})
|
||||
t.notMatch(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.equal(request[kFastifyContext].onError, null)
|
||||
t.equal(request[kFastifyContext].onRequest, null)
|
||||
t.equal(request[kFastifyContext].onSend, null)
|
||||
t.equal(request[kFastifyContext].preHandler, null)
|
||||
t.equal(request[kFastifyContext].preParsing, null)
|
||||
t.equal(request[kFastifyContext].preSerialization.length, 1)
|
||||
t.equal(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.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, '{"nonString":true}')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': '*'
|
||||
})
|
||||
t.notMatch(res.headers, { vary: 'Origin' })
|
||||
})
|
||||
|
||||
test('Should support custom hook with dynamic config', 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.ok(req.id)
|
||||
// request should not have send
|
||||
t.notOk(req.send)
|
||||
const config = configs[requestId]
|
||||
requestId++
|
||||
if (config) {
|
||||
cb(null, config)
|
||||
} else {
|
||||
cb(new Error('ouch'))
|
||||
}
|
||||
}
|
||||
fastify.register(cors, {
|
||||
hook: 'preHandler',
|
||||
delegator: configDelegation
|
||||
})
|
||||
|
||||
fastify.get('/', (req, reply) => {
|
||||
reply.send('ok')
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/'
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
'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.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 204)
|
||||
t.equal(res.payload, '')
|
||||
t.match(res.headers, {
|
||||
'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.error(err)
|
||||
t.equal(res.statusCode, 500)
|
||||
})
|
||||
})
|
||||
|
||||
test('Should support custom hook with dynamic config (callback)', 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.ok(req.id)
|
||||
// request should not have send
|
||||
t.notOk(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.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
'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.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 204)
|
||||
t.equal(res.payload, '')
|
||||
t.match(res.headers, {
|
||||
'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.error(err)
|
||||
t.equal(res.statusCode, 500)
|
||||
})
|
||||
})
|
||||
|
||||
test('Should support custom hook with dynamic config (Promise)', 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) {
|
||||
// request should have id
|
||||
t.ok(req.id)
|
||||
// request should not have send
|
||||
t.notOk(req.send)
|
||||
const config = configs[requestId]
|
||||
requestId++
|
||||
if (config) {
|
||||
return Promise.resolve(config)
|
||||
} else {
|
||||
return Promise.reject(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.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
'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.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 204)
|
||||
t.equal(res.payload, '')
|
||||
t.match(res.headers, {
|
||||
'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.error(err)
|
||||
t.equal(res.statusCode, 500)
|
||||
})
|
||||
})
|
||||
|
||||
test('Should support custom hook with dynamic config (Promise), but should error /1', t => {
|
||||
t.plan(6)
|
||||
|
||||
const fastify = Fastify()
|
||||
const configDelegation = function () {
|
||||
return false
|
||||
}
|
||||
|
||||
fastify.register(cors, {
|
||||
hook: 'preParsing',
|
||||
delegator: configDelegation
|
||||
})
|
||||
|
||||
fastify.get('/', (req, reply) => {
|
||||
reply.send('ok')
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/',
|
||||
headers: {
|
||||
'access-control-request-method': 'GET',
|
||||
origin: 'example.com'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 500)
|
||||
t.equal(res.payload, '{"statusCode":500,"error":"Internal Server Error","message":"Invalid CORS origin option"}')
|
||||
t.match(res.headers, {
|
||||
'content-length': '89'
|
||||
})
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/',
|
||||
headers: {
|
||||
'access-control-request-method': 'GET',
|
||||
origin: 'example.com'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
t.equal(res.statusCode, 500)
|
||||
})
|
||||
})
|
||||
|
||||
test('Should support custom hook with dynamic config (Promise), but should error /2', t => {
|
||||
t.plan(6)
|
||||
|
||||
const fastify = Fastify()
|
||||
const configDelegation = function () {
|
||||
return false
|
||||
}
|
||||
|
||||
fastify.register(cors, {
|
||||
delegator: configDelegation
|
||||
})
|
||||
|
||||
fastify.get('/', (req, reply) => {
|
||||
reply.send('ok')
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/',
|
||||
headers: {
|
||||
'access-control-request-method': 'GET',
|
||||
origin: 'example.com'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 500)
|
||||
t.equal(res.payload, '{"statusCode":500,"error":"Internal Server Error","message":"Invalid CORS origin option"}')
|
||||
t.match(res.headers, {
|
||||
'content-length': '89'
|
||||
})
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'GET',
|
||||
url: '/',
|
||||
headers: {
|
||||
'access-control-request-method': 'GET',
|
||||
origin: 'example.com'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
t.equal(res.statusCode, 500)
|
||||
})
|
||||
})
|
||||
435
backend/node_modules/@fastify/cors/test/preflight.test.js
generated
vendored
Normal file
435
backend/node_modules/@fastify/cors/test/preflight.test.js
generated
vendored
Normal file
@@ -0,0 +1,435 @@
|
||||
'use strict'
|
||||
|
||||
const { test } = require('tap')
|
||||
const Fastify = require('fastify')
|
||||
const cors = require('../')
|
||||
|
||||
test('Should reply to preflight requests', t => {
|
||||
t.plan(4)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors)
|
||||
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/',
|
||||
headers: {
|
||||
'access-control-request-method': 'GET',
|
||||
origin: 'example.com'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 204)
|
||||
t.equal(res.payload, '')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': '*',
|
||||
'access-control-allow-methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
||||
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', t => {
|
||||
t.plan(4)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors)
|
||||
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/',
|
||||
headers: {
|
||||
'access-control-request-headers': 'x-requested-with',
|
||||
'access-control-request-method': 'GET',
|
||||
origin: 'example.com'
|
||||
}
|
||||
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 204)
|
||||
t.equal(res.payload, '')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': '*',
|
||||
'access-control-allow-methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
||||
'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', t => {
|
||||
t.plan(4)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors, { optionsSuccessStatus: 200 })
|
||||
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/',
|
||||
headers: {
|
||||
'access-control-request-method': 'GET',
|
||||
origin: 'example.com'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, '')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': '*',
|
||||
'access-control-allow-methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
||||
vary: 'Access-Control-Request-Headers',
|
||||
'content-length': '0'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('Should be able to override preflight response with a route', t => {
|
||||
t.plan(5)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors, { preflightContinue: true })
|
||||
|
||||
fastify.options('/', (req, reply) => {
|
||||
reply.send('ok')
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/',
|
||||
headers: {
|
||||
'access-control-request-method': 'GET',
|
||||
origin: 'example.com'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
// Only the base cors headers and no preflight headers
|
||||
'access-control-allow-origin': '*'
|
||||
})
|
||||
t.notMatch(res.headers, { vary: 'Origin' })
|
||||
})
|
||||
})
|
||||
|
||||
test('Should reply to all options requests', t => {
|
||||
t.plan(4)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors)
|
||||
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/hello',
|
||||
headers: {
|
||||
'access-control-request-method': 'GET',
|
||||
origin: 'example.com'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 204)
|
||||
t.equal(res.payload, '')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': '*',
|
||||
'access-control-allow-methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
||||
vary: 'Access-Control-Request-Headers',
|
||||
'content-length': '0'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('Should support a prefix for preflight requests', t => {
|
||||
t.plan(6)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register((instance, opts, next) => {
|
||||
instance.register(cors)
|
||||
next()
|
||||
}, { prefix: '/subsystem' })
|
||||
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/hello'
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
t.equal(res.statusCode, 404)
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/subsystem/hello',
|
||||
headers: {
|
||||
'access-control-request-method': 'GET',
|
||||
origin: 'example.com'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 204)
|
||||
t.equal(res.payload, '')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': '*',
|
||||
'access-control-allow-methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
||||
vary: 'Access-Control-Request-Headers',
|
||||
'content-length': '0'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('hide options route by default', t => {
|
||||
t.plan(2)
|
||||
|
||||
const fastify = Fastify()
|
||||
|
||||
fastify.addHook('onRoute', (route) => {
|
||||
if (route.method === 'OPTIONS' && route.url === '*') {
|
||||
t.equal(route.schema.hide, true)
|
||||
}
|
||||
})
|
||||
fastify.register(cors)
|
||||
|
||||
fastify.ready(err => {
|
||||
t.error(err)
|
||||
})
|
||||
})
|
||||
|
||||
test('show options route', t => {
|
||||
t.plan(2)
|
||||
|
||||
const fastify = Fastify()
|
||||
|
||||
fastify.addHook('onRoute', (route) => {
|
||||
if (route.method === 'OPTIONS' && route.url === '*') {
|
||||
t.equal(route.schema.hide, false)
|
||||
}
|
||||
})
|
||||
fastify.register(cors, { hideOptionsRoute: false })
|
||||
|
||||
fastify.ready(err => {
|
||||
t.error(err)
|
||||
})
|
||||
})
|
||||
|
||||
test('Allow only request from with specific methods', t => {
|
||||
t.plan(4)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors, { methods: ['GET', 'POST'] })
|
||||
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/',
|
||||
headers: {
|
||||
'access-control-request-method': 'GET',
|
||||
origin: 'example.com'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 204)
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-methods': 'GET, POST'
|
||||
})
|
||||
t.notMatch(res.headers, { vary: 'Origin' })
|
||||
})
|
||||
})
|
||||
|
||||
test('Should reply with 400 error to OPTIONS requests missing origin header when default strictPreflight', t => {
|
||||
t.plan(3)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors)
|
||||
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/',
|
||||
headers: {
|
||||
'access-control-request-method': 'GET'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
t.equal(res.statusCode, 400)
|
||||
t.equal(res.payload, 'Invalid Preflight Request')
|
||||
})
|
||||
})
|
||||
|
||||
test('Should reply with 400 to OPTIONS requests when missing Access-Control-Request-Method header when default strictPreflight', t => {
|
||||
t.plan(3)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors, {
|
||||
strictPreflight: true
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/',
|
||||
headers: {
|
||||
origin: 'example.com'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
t.equal(res.statusCode, 400)
|
||||
t.equal(res.payload, 'Invalid Preflight Request')
|
||||
})
|
||||
})
|
||||
|
||||
test('Should reply to all preflight requests when strictPreflight is disabled', t => {
|
||||
t.plan(4)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors, { strictPreflight: false })
|
||||
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/'
|
||||
// No access-control-request-method or origin headers
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 204)
|
||||
t.equal(res.payload, '')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': '*',
|
||||
'access-control-allow-methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
||||
vary: 'Access-Control-Request-Headers',
|
||||
'content-length': '0'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('Default empty 200 response with preflightContinue on OPTIONS routes', t => {
|
||||
t.plan(4)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors, { preflightContinue: true })
|
||||
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/doesnotexist',
|
||||
headers: {
|
||||
'access-control-request-method': 'GET',
|
||||
origin: 'example.com'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, '')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': '*',
|
||||
'access-control-allow-methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
||||
vary: 'Access-Control-Request-Headers'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('Can override preflight response with preflightContinue', t => {
|
||||
t.plan(4)
|
||||
|
||||
const fastify = Fastify()
|
||||
fastify.register(cors, { preflightContinue: true })
|
||||
|
||||
fastify.options('/', (req, reply) => {
|
||||
reply.send('ok')
|
||||
})
|
||||
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/',
|
||||
headers: {
|
||||
'access-control-request-method': 'GET',
|
||||
origin: 'example.com'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, 'ok')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': '*',
|
||||
'access-control-allow-methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
||||
vary: 'Access-Control-Request-Headers'
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
test('Should support ongoing prefix ', t => {
|
||||
t.plan(12)
|
||||
|
||||
const fastify = Fastify()
|
||||
|
||||
fastify.register(async (instance) => {
|
||||
instance.register(cors)
|
||||
}, { prefix: '/prefix' })
|
||||
|
||||
// support prefixed route
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/prefix',
|
||||
headers: {
|
||||
'access-control-request-method': 'GET',
|
||||
origin: 'example.com'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 204)
|
||||
t.equal(res.payload, '')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': '*',
|
||||
'access-control-allow-methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
||||
vary: 'Access-Control-Request-Headers',
|
||||
'content-length': '0'
|
||||
})
|
||||
})
|
||||
|
||||
// support prefixed route without / continue
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/prefixfoo',
|
||||
headers: {
|
||||
'access-control-request-method': 'GET',
|
||||
origin: 'example.com'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 204)
|
||||
t.equal(res.payload, '')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': '*',
|
||||
'access-control-allow-methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
||||
vary: 'Access-Control-Request-Headers',
|
||||
'content-length': '0'
|
||||
})
|
||||
})
|
||||
|
||||
// support prefixed route with / continue
|
||||
fastify.inject({
|
||||
method: 'OPTIONS',
|
||||
url: '/prefix/foo',
|
||||
headers: {
|
||||
'access-control-request-method': 'GET',
|
||||
origin: 'example.com'
|
||||
}
|
||||
}, (err, res) => {
|
||||
t.error(err)
|
||||
delete res.headers.date
|
||||
t.equal(res.statusCode, 204)
|
||||
t.equal(res.payload, '')
|
||||
t.match(res.headers, {
|
||||
'access-control-allow-origin': '*',
|
||||
'access-control-allow-methods': 'GET,HEAD,PUT,PATCH,POST,DELETE',
|
||||
vary: 'Access-Control-Request-Headers',
|
||||
'content-length': '0'
|
||||
})
|
||||
})
|
||||
})
|
||||
229
backend/node_modules/@fastify/cors/test/vary.test.js
generated
vendored
Normal file
229
backend/node_modules/@fastify/cors/test/vary.test.js
generated
vendored
Normal file
@@ -0,0 +1,229 @@
|
||||
'use strict'
|
||||
|
||||
const test = require('tap').test
|
||||
const createAddFieldnameToVary = require('../vary').createAddFieldnameToVary
|
||||
const parse = require('../vary').parse
|
||||
|
||||
test('Should set * even if we set a specific field', t => {
|
||||
t.plan(1)
|
||||
|
||||
const addOriginToVary = createAddFieldnameToVary('Origin')
|
||||
const replyMock = {
|
||||
getHeader (name) {
|
||||
return '*'
|
||||
},
|
||||
header (name, value) {
|
||||
t.fail('Should not be here')
|
||||
}
|
||||
}
|
||||
|
||||
addOriginToVary(replyMock)
|
||||
t.pass()
|
||||
})
|
||||
|
||||
test('Should set * even if we set a specific field', t => {
|
||||
t.plan(2)
|
||||
|
||||
const addWildcardToVary = createAddFieldnameToVary('*')
|
||||
const replyMock = {
|
||||
getHeader (name) {
|
||||
return 'Origin'
|
||||
},
|
||||
header (name, value) {
|
||||
t.same(name, 'Vary')
|
||||
t.same(value, '*')
|
||||
}
|
||||
}
|
||||
|
||||
addWildcardToVary(replyMock)
|
||||
})
|
||||
|
||||
test('Should set * when field contains a *', t => {
|
||||
t.plan(3)
|
||||
|
||||
const addOriginToVary = createAddFieldnameToVary('Origin')
|
||||
const replyMock = {
|
||||
getHeader (name) {
|
||||
return ['Origin', '*', 'Access-Control-Request-Headers']
|
||||
},
|
||||
header (name, value) {
|
||||
t.same(name, 'Vary')
|
||||
t.same(value, '*')
|
||||
}
|
||||
}
|
||||
|
||||
addOriginToVary(replyMock)
|
||||
t.pass()
|
||||
})
|
||||
|
||||
test('Should concat vary values', t => {
|
||||
t.plan(3)
|
||||
|
||||
const addOriginToVary = createAddFieldnameToVary('Origin')
|
||||
const replyMock = {
|
||||
getHeader (name) {
|
||||
return 'Access-Control-Request-Headers'
|
||||
},
|
||||
header (name, value) {
|
||||
t.same(name, 'Vary')
|
||||
t.same(value, 'Access-Control-Request-Headers, Origin')
|
||||
}
|
||||
}
|
||||
|
||||
addOriginToVary(replyMock)
|
||||
t.pass()
|
||||
})
|
||||
|
||||
test('Should concat vary values ignoring consecutive commas', t => {
|
||||
t.plan(3)
|
||||
|
||||
const addOriginToVary = createAddFieldnameToVary('Origin')
|
||||
const replyMock = {
|
||||
getHeader (name) {
|
||||
return ' Access-Control-Request-Headers,Access-Control-Request-Method'
|
||||
},
|
||||
header (name, value) {
|
||||
t.same(name, 'Vary')
|
||||
t.same(value, ' Access-Control-Request-Headers,Access-Control-Request-Method, Origin')
|
||||
}
|
||||
}
|
||||
|
||||
addOriginToVary(replyMock)
|
||||
t.pass()
|
||||
})
|
||||
|
||||
test('Should concat vary values ignoring whitespace', t => {
|
||||
t.plan(3)
|
||||
|
||||
const addOriginToVary = createAddFieldnameToVary('Origin')
|
||||
const replyMock = {
|
||||
getHeader (name) {
|
||||
return ' Access-Control-Request-Headers ,Access-Control-Request-Method'
|
||||
},
|
||||
header (name, value) {
|
||||
t.same(name, 'Vary')
|
||||
t.same(value, ' Access-Control-Request-Headers ,Access-Control-Request-Method, Origin')
|
||||
}
|
||||
}
|
||||
|
||||
addOriginToVary(replyMock)
|
||||
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 (name) {
|
||||
return undefined
|
||||
},
|
||||
header (name, value) {
|
||||
t.same(name, 'Vary')
|
||||
t.same(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 (name) {
|
||||
return 'Accept,*'
|
||||
},
|
||||
header (name, value) {
|
||||
t.same(name, 'Vary')
|
||||
t.same(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 (name) {
|
||||
return ''
|
||||
},
|
||||
header (name, value) {
|
||||
t.same(name, 'Vary')
|
||||
t.same(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 (name) {
|
||||
return this.value
|
||||
},
|
||||
header (name, value) {
|
||||
t.same(name, 'Vary')
|
||||
t.same(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 (name) {
|
||||
return 'Origin'
|
||||
},
|
||||
header (name, value) {
|
||||
t.fail('Should not be here')
|
||||
}
|
||||
}
|
||||
addOriginToVary(replyMock)
|
||||
addOriginToVary(replyMock)
|
||||
t.pass()
|
||||
})
|
||||
|
||||
test('parse', t => {
|
||||
t.plan(18)
|
||||
t.same(parse(''), [])
|
||||
t.same(parse('a'), ['a'])
|
||||
t.same(parse('a,b'), ['a', 'b'])
|
||||
t.same(parse(' a,b'), ['a', 'b'])
|
||||
t.same(parse('a,b '), ['a', 'b'])
|
||||
t.same(parse('a,b,c'), ['a', 'b', 'c'])
|
||||
t.same(parse('A,b,c'), ['a', 'b', 'c'])
|
||||
t.same(parse('a,b,c,'), ['a', 'b', 'c'])
|
||||
t.same(parse('a,b,c, '), ['a', 'b', 'c'])
|
||||
t.same(parse(',a,b,c'), ['a', 'b', 'c'])
|
||||
t.same(parse(' ,a,b,c'), ['a', 'b', 'c'])
|
||||
t.same(parse('a,,b,c'), ['a', 'b', 'c'])
|
||||
t.same(parse('a,,,b,,c'), ['a', 'b', 'c'])
|
||||
t.same(parse('a, b,c'), ['a', 'b', 'c'])
|
||||
t.same(parse('a, b,c'), ['a', 'b', 'c'])
|
||||
t.same(parse('a, , b,c'), ['a', 'b', 'c'])
|
||||
t.same(parse('a, , b,c'), ['a', 'b', 'c'])
|
||||
|
||||
// one for the cache
|
||||
t.same(parse('A,b,c'), ['a', 'b', 'c'])
|
||||
})
|
||||
|
||||
test('createAddFieldnameToVary', t => {
|
||||
t.plan(2)
|
||||
t.same(typeof createAddFieldnameToVary('valid-header'), 'function')
|
||||
t.throws(() => createAddFieldnameToVary('invalid:header'), TypeError, 'Field contains invalid characters.')
|
||||
})
|
||||
116
backend/node_modules/@fastify/cors/types/index.d.ts
generated
vendored
Normal file
116
backend/node_modules/@fastify/cors/types/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
/// <reference types="node" />
|
||||
|
||||
import { FastifyInstance, FastifyPluginCallback, FastifyRequest } 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 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.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: false).
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
355
backend/node_modules/@fastify/cors/types/index.test-d.ts
generated
vendored
Normal file
355
backend/node_modules/@fastify/cors/types/index.test-d.ts
generated
vendored
Normal file
@@ -0,0 +1,355 @@
|
||||
import fastify, { FastifyRequest } from 'fastify'
|
||||
import { expectType } from 'tsd'
|
||||
import fastifyCors, {
|
||||
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
|
||||
})
|
||||
|
||||
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
|
||||
})
|
||||
|
||||
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 => (req) => {
|
||||
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 (req) => {
|
||||
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) {
|
||||
expectType<string|undefined>(origin)
|
||||
},
|
||||
})
|
||||
116
backend/node_modules/@fastify/cors/vary.js
generated
vendored
Normal file
116
backend/node_modules/@fastify/cors/vary.js
generated
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
'use strict'
|
||||
|
||||
const LRUCache = require('mnemonist/lru-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 = 0; 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 LRUCache(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.has(header)) {
|
||||
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
|
||||
2
backend/node_modules/@fastify/error/.gitattributes
generated
vendored
Normal file
2
backend/node_modules/@fastify/error/.gitattributes
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Set default behavior to automatically convert line endings
|
||||
* text=auto eol=lf
|
||||
13
backend/node_modules/@fastify/error/.github/dependabot.yml
generated
vendored
Normal file
13
backend/node_modules/@fastify/error/.github/dependabot.yml
generated
vendored
Normal 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
|
||||
28
backend/node_modules/@fastify/error/.github/workflows/ci.yml
generated
vendored
Normal file
28
backend/node_modules/@fastify/error/.github/workflows/ci.yml
generated
vendored
Normal 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
|
||||
21
backend/node_modules/@fastify/error/LICENSE
generated
vendored
Normal file
21
backend/node_modules/@fastify/error/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Fastify
|
||||
|
||||
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.
|
||||
140
backend/node_modules/@fastify/error/README.md
generated
vendored
Normal file
140
backend/node_modules/@fastify/error/README.md
generated
vendored
Normal file
@@ -0,0 +1,140 @@
|
||||
# @fastify/error
|
||||
|
||||
[](https://github.com/fastify/fastify-error/actions/workflows/ci.yml)
|
||||
[](https://www.npmjs.com/package/@fastify/error)
|
||||
[](https://github.com/neostandard/neostandard)
|
||||
|
||||
A small utility, used by Fastify itself, for generating consistent error objects across your codebase and plugins.
|
||||
|
||||
### Install
|
||||
```
|
||||
npm i @fastify/error
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
The module exports a function that you can use for consistent error objects, it takes 4 parameters:
|
||||
|
||||
```js
|
||||
createError(code, message [, statusCode [, Base [, captureStackTrace]]])
|
||||
```
|
||||
|
||||
- `code` (`string`, required) - The error code, you can access it later with `error.code`. For consistency, we recommend prefixing plugin error codes with `FST_`
|
||||
- `message` (`string`, required) - The error message. You can also use interpolated strings for formatting the message.
|
||||
- `statusCode` (`number`, optional) - The status code that Fastify will use if the error is sent via HTTP.
|
||||
- `Base` (`ErrorConstructor`, optional) - The base error object that will be used. (eg `TypeError`, `RangeError`)
|
||||
- `captureStackTrace` (`boolean`, optional) - Whether to capture the stack trace or not.
|
||||
|
||||
```js
|
||||
const createError = require('@fastify/error')
|
||||
const CustomError = createError('ERROR_CODE', 'Hello')
|
||||
console.log(new CustomError()) // error.message => 'Hello'
|
||||
```
|
||||
|
||||
How to use an interpolated string:
|
||||
```js
|
||||
const createError = require('@fastify/error')
|
||||
const CustomError = createError('ERROR_CODE', 'Hello %s')
|
||||
console.log(new CustomError('world')) // error.message => 'Hello world'
|
||||
```
|
||||
|
||||
How to add cause:
|
||||
```js
|
||||
const createError = require('@fastify/error')
|
||||
const CustomError = createError('ERROR_CODE', 'Hello %s')
|
||||
console.log(new CustomError('world', {cause: new Error('cause')}))
|
||||
// error.message => 'Hello world'
|
||||
// error.cause => Error('cause')
|
||||
```
|
||||
|
||||
### TypeScript
|
||||
|
||||
It is possible to limit your error constructor with a generic type using TypeScript:
|
||||
|
||||
```ts
|
||||
const CustomError = createError<[string]>('ERROR_CODE', 'Hello %s')
|
||||
new CustomError('world')
|
||||
//@ts-expect-error
|
||||
new CustomError(1)
|
||||
```
|
||||
|
||||
### instanceof
|
||||
|
||||
All errors created with `createError` will be instances of the base error constructor you provided, or `Error` if none was provided.
|
||||
|
||||
```js
|
||||
const createError = require('@fastify/error')
|
||||
const CustomError = createError('ERROR_CODE', 'Hello %s', 500, TypeError)
|
||||
const customError = new CustomError('world')
|
||||
|
||||
console.log(customError instanceof CustomError) // true
|
||||
console.log(customError instanceof TypeError) // true
|
||||
console.log(customError instanceof Error) // true
|
||||
```
|
||||
|
||||
All instantiated errors are instances of the `FastifyError` class, which can be required directly from the module.
|
||||
|
||||
```js
|
||||
const { createError, FastifyError } = require('@fastify/error')
|
||||
const CustomError = createError('ERROR_CODE', 'Hello %s', 500, TypeError)
|
||||
const customError = new CustomError('world')
|
||||
|
||||
console.log(customError instanceof FastifyError) // true
|
||||
```
|
||||
|
||||
A `FastifyError` created by `createError` can extend another `FastifyError` while maintaining correct `instanceof` behavior.
|
||||
|
||||
```js
|
||||
const { createError, FastifyError } = require('@fastify/error')
|
||||
|
||||
const CustomError = createError('ERROR_CODE', 'Hello %s', 500, TypeError)
|
||||
const ChildCustomError = createError('CHILD_ERROR_CODE', 'Hello %s', 500, CustomError)
|
||||
|
||||
const customError = new ChildCustomError('world')
|
||||
|
||||
console.log(customError instanceof ChildCustomError) // true
|
||||
console.log(customError instanceof CustomError) // true
|
||||
console.log(customError instanceof FastifyError) // true
|
||||
console.log(customError instanceof TypeError) // true
|
||||
console.log(customError instanceof Error) // true
|
||||
```
|
||||
|
||||
If `fastify-error` is installed multiple times directly or as a transitive dependency, `instanceof` checks for errors created by `createError` will still work correctly across these installations, as long as their error codes (e.g., `FST_ERR_CUSTOM_ERROR`) are identical.
|
||||
|
||||
```js
|
||||
const { createError, FastifyError } = require('@fastify/error')
|
||||
|
||||
// CustomError from `@fastify/some-plugin` is created with `createError` and
|
||||
// has its own `@fastify/error` installation as dependency. CustomError has
|
||||
// FST_ERR_CUSTOM_ERROR as code.
|
||||
const { CustomError: CustomErrorFromPlugin } = require('@fastify/some-plugin')
|
||||
|
||||
const CustomError = createError('FST_ERR_CUSTOM_ERROR', 'Hello %s', 500)
|
||||
|
||||
const customError = new CustomError('world')
|
||||
const customErrorFromPlugin = new CustomErrorFromPlugin('world')
|
||||
|
||||
console.log(customError instanceof CustomError) // true
|
||||
console.log(customError instanceof CustomErrorFromPlugin) // true
|
||||
console.log(customErrorFromPlugin instanceof CustomError) // true
|
||||
console.log(customErrorFromPlugin instanceof CustomErrorFromPlugin) // true
|
||||
```
|
||||
|
||||
Changing the code of an instantiated Error will not change the result of the `instanceof` operator.
|
||||
|
||||
```js
|
||||
const { createError, FastifyError } = require('@fastify/error')
|
||||
|
||||
const CustomError = createError('ERROR_CODE', 'Hello %s', 500, TypeError)
|
||||
const AnotherCustomError = createError('ANOTHER_ERROR_CODE', 'Hello %s', 500, CustomError)
|
||||
|
||||
const customError = new CustomError('world')
|
||||
customError.code = 'ANOTHER_ERROR_CODE'
|
||||
|
||||
console.log(customError instanceof CustomError) // true
|
||||
console.log(customError instanceof AnotherCustomError) // false
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Licensed under [MIT](./LICENSE).
|
||||
9
backend/node_modules/@fastify/error/benchmarks/create.js
generated
vendored
Normal file
9
backend/node_modules/@fastify/error/benchmarks/create.js
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
'use strict'
|
||||
|
||||
const benchmark = require('benchmark')
|
||||
const createError = require('..')
|
||||
|
||||
new benchmark.Suite()
|
||||
.add('create FastifyError', function () { createError('CODE', 'Not available') }, { minSamples: 100 })
|
||||
.on('cycle', function onCycle (event) { console.log(String(event.target)) })
|
||||
.run({ async: false })
|
||||
18
backend/node_modules/@fastify/error/benchmarks/instantiate.js
generated
vendored
Normal file
18
backend/node_modules/@fastify/error/benchmarks/instantiate.js
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
'use strict'
|
||||
|
||||
const benchmark = require('benchmark')
|
||||
const createError = require('..')
|
||||
|
||||
const FastifyError = createError('CODE', 'Not available')
|
||||
const FastifyError1 = createError('CODE', 'Not %s available')
|
||||
const FastifyError2 = createError('CODE', 'Not %s available %s')
|
||||
|
||||
const cause = new Error('cause')
|
||||
new benchmark.Suite()
|
||||
.add('instantiate Error', function () { new Error() }, { minSamples: 100 }) // eslint-disable-line no-new
|
||||
.add('instantiate FastifyError', function () { new FastifyError() }, { minSamples: 100 }) // eslint-disable-line no-new
|
||||
.add('instantiate FastifyError arg 1', function () { new FastifyError1('q') }, { minSamples: 100 }) // eslint-disable-line no-new
|
||||
.add('instantiate FastifyError arg 2', function () { new FastifyError2('qq', 'ss') }, { minSamples: 100 }) // eslint-disable-line no-new
|
||||
.add('instantiate FastifyError cause', function () { new FastifyError2({ cause }) }, { minSamples: 100 }) // eslint-disable-line no-new
|
||||
.on('cycle', function onCycle (event) { console.log(String(event.target)) })
|
||||
.run({ async: false })
|
||||
13
backend/node_modules/@fastify/error/benchmarks/no-stack.js
generated
vendored
Normal file
13
backend/node_modules/@fastify/error/benchmarks/no-stack.js
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
'use strict'
|
||||
|
||||
const benchmark = require('benchmark')
|
||||
const createError = require('..')
|
||||
|
||||
const FastifyError = createError('CODE', 'Not available')
|
||||
Error.stackTraceLimit = 0
|
||||
|
||||
new benchmark.Suite()
|
||||
.add('no-stack instantiate Error', function () { new Error() }, { minSamples: 100 }) // eslint-disable-line no-new
|
||||
.add('no-stack instantiate FastifyError', function () { new FastifyError() }, { minSamples: 100 }) // eslint-disable-line no-new
|
||||
.on('cycle', function onCycle (event) { console.log(String(event.target)) })
|
||||
.run({ async: false })
|
||||
11
backend/node_modules/@fastify/error/benchmarks/toString.js
generated
vendored
Normal file
11
backend/node_modules/@fastify/error/benchmarks/toString.js
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
'use strict'
|
||||
|
||||
const benchmark = require('benchmark')
|
||||
const createError = require('..')
|
||||
|
||||
const FastifyError = createError('CODE', 'Not available')
|
||||
|
||||
new benchmark.Suite()
|
||||
.add('FastifyError toString', function () { new FastifyError().toString() }, { minSamples: 100 })
|
||||
.on('cycle', function onCycle (event) { console.log(String(event.target)) })
|
||||
.run({ async: false })
|
||||
6
backend/node_modules/@fastify/error/eslint.config.js
generated
vendored
Normal file
6
backend/node_modules/@fastify/error/eslint.config.js
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = require('neostandard')({
|
||||
ignores: require('neostandard').resolveIgnoresFromGitignore(),
|
||||
ts: true
|
||||
})
|
||||
100
backend/node_modules/@fastify/error/index.js
generated
vendored
Normal file
100
backend/node_modules/@fastify/error/index.js
generated
vendored
Normal file
@@ -0,0 +1,100 @@
|
||||
'use strict'
|
||||
|
||||
const { format } = require('node:util')
|
||||
|
||||
function toString () {
|
||||
return `${this.name} [${this.code}]: ${this.message}`
|
||||
}
|
||||
|
||||
const FastifyGenericErrorSymbol = Symbol.for('fastify-error-generic')
|
||||
|
||||
function createError (code, message, statusCode = 500, Base = Error, captureStackTrace = createError.captureStackTrace) {
|
||||
const shouldCreateFastifyGenericError = code === FastifyGenericErrorSymbol
|
||||
|
||||
if (shouldCreateFastifyGenericError) {
|
||||
code = 'FST_ERR'
|
||||
}
|
||||
|
||||
if (!code) throw new Error('Fastify error code must not be empty')
|
||||
if (!message) throw new Error('Fastify error message must not be empty')
|
||||
|
||||
code = code.toUpperCase()
|
||||
!statusCode && (statusCode = undefined)
|
||||
|
||||
const FastifySpecificErrorSymbol = Symbol.for(`fastify-error ${code}`)
|
||||
|
||||
function FastifyError (...args) {
|
||||
if (!new.target) {
|
||||
return new FastifyError(...args)
|
||||
}
|
||||
|
||||
this.code = code
|
||||
this.name = 'FastifyError'
|
||||
this.statusCode = statusCode
|
||||
|
||||
const lastElement = args.length - 1
|
||||
if (lastElement !== -1 && args[lastElement] && typeof args[lastElement] === 'object' && 'cause' in args[lastElement]) {
|
||||
this.cause = args.pop().cause
|
||||
}
|
||||
|
||||
this.message = format(message, ...args)
|
||||
|
||||
Error.stackTraceLimit && captureStackTrace && Error.captureStackTrace(this, FastifyError)
|
||||
}
|
||||
|
||||
FastifyError.prototype = Object.create(Base.prototype, {
|
||||
constructor: {
|
||||
value: FastifyError,
|
||||
enumerable: false,
|
||||
writable: true,
|
||||
configurable: true
|
||||
},
|
||||
[FastifyGenericErrorSymbol]: {
|
||||
value: true,
|
||||
enumerable: false,
|
||||
writable: false,
|
||||
configurable: false
|
||||
},
|
||||
[FastifySpecificErrorSymbol]: {
|
||||
value: true,
|
||||
enumerable: false,
|
||||
writable: false,
|
||||
configurable: false
|
||||
}
|
||||
})
|
||||
|
||||
if (shouldCreateFastifyGenericError) {
|
||||
Object.defineProperty(FastifyError, Symbol.hasInstance, {
|
||||
value (instance) {
|
||||
return instance && instance[FastifyGenericErrorSymbol]
|
||||
},
|
||||
configurable: false,
|
||||
writable: false,
|
||||
enumerable: false
|
||||
})
|
||||
} else {
|
||||
Object.defineProperty(FastifyError, Symbol.hasInstance, {
|
||||
value (instance) {
|
||||
return instance && instance[FastifySpecificErrorSymbol]
|
||||
},
|
||||
configurable: false,
|
||||
writable: false,
|
||||
enumerable: false
|
||||
})
|
||||
}
|
||||
|
||||
FastifyError.prototype[Symbol.toStringTag] = 'Error'
|
||||
|
||||
FastifyError.prototype.toString = toString
|
||||
|
||||
return FastifyError
|
||||
}
|
||||
|
||||
createError.captureStackTrace = true
|
||||
|
||||
const FastifyErrorConstructor = createError(FastifyGenericErrorSymbol, 'Fastify Error', 500, Error)
|
||||
|
||||
module.exports = createError
|
||||
module.exports.FastifyError = FastifyErrorConstructor
|
||||
module.exports.default = createError
|
||||
module.exports.createError = createError
|
||||
75
backend/node_modules/@fastify/error/package.json
generated
vendored
Normal file
75
backend/node_modules/@fastify/error/package.json
generated
vendored
Normal file
@@ -0,0 +1,75 @@
|
||||
{
|
||||
"name": "@fastify/error",
|
||||
"version": "4.2.0",
|
||||
"description": "A small utility, used by Fastify itself, for generating consistent error objects across your codebase and plugins.",
|
||||
"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:unit": "c8 --100 node --test",
|
||||
"test:typescript": "tsd"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/fastify/fastify-error.git"
|
||||
},
|
||||
"keywords": [
|
||||
"fastify",
|
||||
"error",
|
||||
"utility",
|
||||
"plugin"
|
||||
],
|
||||
"author": "Tomas Della Vedova",
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Matteo Collina",
|
||||
"email": "hello@matteocollina.com"
|
||||
},
|
||||
{
|
||||
"name": "James Sumners",
|
||||
"url": "https://james.sumners.info"
|
||||
},
|
||||
{
|
||||
"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-error/issues"
|
||||
},
|
||||
"homepage": "https://github.com/fastify/fastify-error#readme",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"devDependencies": {
|
||||
"benchmark": "^2.1.4",
|
||||
"c8": "^10.1.2",
|
||||
"eslint": "^9.17.0",
|
||||
"neostandard": "^0.12.0",
|
||||
"tsd": "^0.32.0"
|
||||
},
|
||||
"tsd": {
|
||||
"compilerOptions": {
|
||||
"esModuleInterop": true
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
}
|
||||
}
|
||||
232
backend/node_modules/@fastify/error/test/index.test.js
generated
vendored
Normal file
232
backend/node_modules/@fastify/error/test/index.test.js
generated
vendored
Normal file
@@ -0,0 +1,232 @@
|
||||
'use strict'
|
||||
|
||||
const test = require('node:test')
|
||||
const { createError, FastifyError } = require('..')
|
||||
|
||||
test('Create error with zero parameter', (t) => {
|
||||
t.plan(6)
|
||||
|
||||
const NewError = createError('CODE', 'Not available')
|
||||
const err = new NewError()
|
||||
t.assert.ok(err instanceof Error)
|
||||
t.assert.equal(err.name, 'FastifyError')
|
||||
t.assert.equal(err.message, 'Not available')
|
||||
t.assert.equal(err.code, 'CODE')
|
||||
t.assert.equal(err.statusCode, 500)
|
||||
t.assert.ok(err.stack)
|
||||
})
|
||||
|
||||
test('Create error with 1 parameter', (t) => {
|
||||
t.plan(6)
|
||||
|
||||
const NewError = createError('CODE', 'hey %s')
|
||||
const err = new NewError('alice')
|
||||
t.assert.equal(err.name, 'FastifyError')
|
||||
t.assert.ok(err instanceof Error)
|
||||
t.assert.equal(err.message, 'hey alice')
|
||||
t.assert.equal(err.code, 'CODE')
|
||||
t.assert.equal(err.statusCode, 500)
|
||||
t.assert.ok(err.stack)
|
||||
})
|
||||
|
||||
test('Create error with 1 parameter set to undefined', (t) => {
|
||||
t.plan(1)
|
||||
|
||||
const NewError = createError('CODE', 'hey %s')
|
||||
const err = new NewError(undefined)
|
||||
t.assert.equal(err.message, 'hey undefined')
|
||||
})
|
||||
|
||||
test('Create error with 2 parameters', (t) => {
|
||||
t.plan(6)
|
||||
|
||||
const NewError = createError('CODE', 'hey %s, I like your %s')
|
||||
const err = new NewError('alice', 'attitude')
|
||||
t.assert.ok(err instanceof Error)
|
||||
t.assert.equal(err.name, 'FastifyError')
|
||||
t.assert.equal(err.message, 'hey alice, I like your attitude')
|
||||
t.assert.equal(err.code, 'CODE')
|
||||
t.assert.equal(err.statusCode, 500)
|
||||
t.assert.ok(err.stack)
|
||||
})
|
||||
|
||||
test('Create error with 2 parameters set to undefined', (t) => {
|
||||
t.plan(1)
|
||||
|
||||
const NewError = createError('CODE', 'hey %s, I like your %s')
|
||||
const err = new NewError(undefined, undefined)
|
||||
t.assert.equal(err.message, 'hey undefined, I like your undefined')
|
||||
})
|
||||
|
||||
test('Create error with 3 parameters', (t) => {
|
||||
t.plan(6)
|
||||
|
||||
const NewError = createError('CODE', 'hey %s, I like your %s %s')
|
||||
const err = new NewError('alice', 'attitude', 'see you')
|
||||
t.assert.ok(err instanceof Error)
|
||||
t.assert.equal(err.name, 'FastifyError')
|
||||
t.assert.equal(err.message, 'hey alice, I like your attitude see you')
|
||||
t.assert.equal(err.code, 'CODE')
|
||||
t.assert.equal(err.statusCode, 500)
|
||||
t.assert.ok(err.stack)
|
||||
})
|
||||
|
||||
test('Create error with 3 parameters set to undefined', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const NewError = createError('CODE', 'hey %s, I like your %s %s')
|
||||
const err = new NewError(undefined, undefined, undefined)
|
||||
t.assert.equal(err.message, 'hey undefined, I like your undefined undefined')
|
||||
t.assert.equal(err.code, 'CODE')
|
||||
t.assert.equal(err.statusCode, 500)
|
||||
t.assert.ok(err.stack)
|
||||
})
|
||||
|
||||
test('Create error with 4 parameters set to undefined', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const NewError = createError('CODE', 'hey %s, I like your %s %s and %s')
|
||||
const err = new NewError(undefined, undefined, undefined, undefined)
|
||||
t.assert.equal(
|
||||
err.message,
|
||||
'hey undefined, I like your undefined undefined and undefined'
|
||||
)
|
||||
t.assert.equal(err.code, 'CODE')
|
||||
t.assert.equal(err.statusCode, 500)
|
||||
t.assert.ok(err.stack)
|
||||
})
|
||||
|
||||
test('Create error with no statusCode property', (t) => {
|
||||
t.plan(6)
|
||||
|
||||
const NewError = createError('CODE', 'hey %s', 0)
|
||||
const err = new NewError('dude')
|
||||
t.assert.ok(err instanceof Error)
|
||||
t.assert.equal(err.name, 'FastifyError')
|
||||
t.assert.equal(err.message, 'hey dude')
|
||||
t.assert.equal(err.code, 'CODE')
|
||||
t.assert.equal(err.statusCode, undefined)
|
||||
t.assert.ok(err.stack)
|
||||
})
|
||||
|
||||
test('Should throw when error code has no fastify code', (t) => {
|
||||
t.plan(1)
|
||||
t.assert.throws(
|
||||
() => createError(),
|
||||
new Error('Fastify error code must not be empty')
|
||||
)
|
||||
})
|
||||
|
||||
test('Should throw when error code has no message', (t) => {
|
||||
t.assert.throws(
|
||||
() => createError('code'),
|
||||
new Error('Fastify error message must not be empty')
|
||||
)
|
||||
})
|
||||
|
||||
test('Create error with different base', (t) => {
|
||||
t.plan(7)
|
||||
|
||||
const NewError = createError('CODE', 'hey %s', 500, TypeError)
|
||||
const err = new NewError('dude')
|
||||
t.assert.ok(err instanceof Error)
|
||||
t.assert.ok(err instanceof TypeError)
|
||||
t.assert.equal(err.name, 'FastifyError')
|
||||
t.assert.equal(err.message, 'hey dude')
|
||||
t.assert.equal(err.code, 'CODE')
|
||||
t.assert.equal(err.statusCode, 500)
|
||||
t.assert.ok(err.stack)
|
||||
})
|
||||
|
||||
test('Create error with different base (no stack) (global)', (t) => {
|
||||
t.plan(7)
|
||||
|
||||
createError.captureStackTrace = false
|
||||
const NewError = createError('CODE', 'hey %s', 500, TypeError)
|
||||
const err = new NewError('dude')
|
||||
t.assert.ok(err instanceof Error)
|
||||
t.assert.ok(err instanceof TypeError)
|
||||
t.assert.equal(err.name, 'FastifyError')
|
||||
t.assert.equal(err.message, 'hey dude')
|
||||
t.assert.equal(err.code, 'CODE')
|
||||
t.assert.equal(err.statusCode, 500)
|
||||
t.assert.equal(err.stack, undefined)
|
||||
createError.captureStackTrace = true
|
||||
})
|
||||
|
||||
test('Create error with different base (no stack) (parameter)', (t) => {
|
||||
t.plan(7)
|
||||
|
||||
const NewError = createError('CODE', 'hey %s', 500, TypeError, false)
|
||||
const err = new NewError('dude')
|
||||
t.assert.ok(err instanceof Error)
|
||||
t.assert.ok(err instanceof TypeError)
|
||||
t.assert.equal(err.name, 'FastifyError')
|
||||
t.assert.equal(err.message, 'hey dude')
|
||||
t.assert.equal(err.code, 'CODE')
|
||||
t.assert.equal(err.statusCode, 500)
|
||||
t.assert.equal(err.stack, undefined)
|
||||
})
|
||||
|
||||
test('FastifyError.toString returns code', (t) => {
|
||||
t.plan(1)
|
||||
|
||||
const NewError = createError('CODE', 'foo')
|
||||
const err = new NewError()
|
||||
t.assert.equal(err.toString(), 'FastifyError [CODE]: foo')
|
||||
})
|
||||
|
||||
test('Create the error without the new keyword', (t) => {
|
||||
t.plan(6)
|
||||
|
||||
const NewError = createError('CODE', 'Not available')
|
||||
const err = NewError()
|
||||
t.assert.ok(err instanceof Error)
|
||||
t.assert.equal(err.name, 'FastifyError')
|
||||
t.assert.equal(err.message, 'Not available')
|
||||
t.assert.equal(err.code, 'CODE')
|
||||
t.assert.equal(err.statusCode, 500)
|
||||
t.assert.ok(err.stack)
|
||||
})
|
||||
|
||||
test('Create an error with cause', (t) => {
|
||||
t.plan(2)
|
||||
|
||||
const cause = new Error('HEY')
|
||||
const NewError = createError('CODE', 'Not available')
|
||||
const err = NewError({ cause })
|
||||
|
||||
t.assert.ok(err instanceof Error)
|
||||
t.assert.equal(err.cause, cause)
|
||||
})
|
||||
|
||||
test('Create an error with cause and message', (t) => {
|
||||
t.plan(2)
|
||||
|
||||
const cause = new Error('HEY')
|
||||
const NewError = createError('CODE', 'Not available: %s')
|
||||
const err = NewError('foo', { cause })
|
||||
|
||||
t.assert.ok(err instanceof Error)
|
||||
t.assert.equal(err.cause, cause)
|
||||
})
|
||||
|
||||
test('Create an error with last argument null', (t) => {
|
||||
t.plan(2)
|
||||
|
||||
const cause = new Error('HEY')
|
||||
const NewError = createError('CODE', 'Not available')
|
||||
const err = NewError({ cause }, null)
|
||||
|
||||
t.assert.ok(err instanceof Error)
|
||||
t.assert.ifError(err.cause)
|
||||
})
|
||||
|
||||
test('check if FastifyError is instantiable', (t) => {
|
||||
t.plan(2)
|
||||
|
||||
const err = new FastifyError()
|
||||
|
||||
t.assert.ok(err instanceof FastifyError)
|
||||
t.assert.ok(err instanceof Error)
|
||||
})
|
||||
263
backend/node_modules/@fastify/error/test/instanceof.test.js
generated
vendored
Normal file
263
backend/node_modules/@fastify/error/test/instanceof.test.js
generated
vendored
Normal file
@@ -0,0 +1,263 @@
|
||||
'use strict'
|
||||
|
||||
const cp = require('node:child_process')
|
||||
const fs = require('node:fs')
|
||||
const path = require('node:path')
|
||||
const os = require('node:os')
|
||||
const test = require('node:test')
|
||||
const { createError, FastifyError } = require('..')
|
||||
|
||||
test('Readme: All errors created with `createError` will be instances of the base error constructor you provided, or `Error` if none was provided.', (t) => {
|
||||
t.plan(3)
|
||||
|
||||
const CustomError = createError('ERROR_CODE', 'Hello %s', 500, TypeError)
|
||||
const customError = new CustomError('world')
|
||||
|
||||
t.assert.ok(customError instanceof CustomError)
|
||||
t.assert.ok(customError instanceof TypeError)
|
||||
t.assert.ok(customError instanceof Error)
|
||||
})
|
||||
|
||||
test('Readme: All instantiated errors will be instances of the `FastifyError` class. The `FastifyError` class can be required from the module directly.', (t) => {
|
||||
t.plan(1)
|
||||
|
||||
const CustomError = createError('ERROR_CODE', 'Hello %s', 500, TypeError)
|
||||
const customError = new CustomError('world')
|
||||
|
||||
t.assert.ok(customError instanceof FastifyError)
|
||||
})
|
||||
|
||||
test('Readme: It is possible to create a `FastifyError` that extends another `FastifyError`, created by `createError`, while instanceof working correctly.', (t) => {
|
||||
t.plan(5)
|
||||
|
||||
const CustomError = createError('ERROR_CODE', 'Hello %s', 500, TypeError)
|
||||
const ChildCustomError = createError('CHILD_ERROR_CODE', 'Hello %s', 500, CustomError)
|
||||
|
||||
const customError = new ChildCustomError('world')
|
||||
|
||||
t.assert.ok(customError instanceof ChildCustomError)
|
||||
t.assert.ok(customError instanceof CustomError)
|
||||
t.assert.ok(customError instanceof FastifyError)
|
||||
t.assert.ok(customError instanceof TypeError)
|
||||
t.assert.ok(customError instanceof Error)
|
||||
})
|
||||
|
||||
test('Readme: Changing the code of an instantiated Error will not change the result of the `instanceof` operator.', (t) => {
|
||||
t.plan(3)
|
||||
|
||||
const CustomError = createError('ERROR_CODE', 'Hello %s', 500, TypeError)
|
||||
const AnotherCustomError = createError('ANOTHER_ERROR_CODE', 'Hello %s', 500, CustomError)
|
||||
|
||||
const customError = new CustomError('world')
|
||||
customError.code = 'ANOTHER_ERROR_CODE'
|
||||
|
||||
t.assert.ok(customError instanceof CustomError)
|
||||
t.assert.ok(customError instanceof AnotherCustomError === false)
|
||||
t.assert.ok(customError instanceof FastifyError)
|
||||
})
|
||||
|
||||
test('check if createError creates an Error which is instanceof Error', (t) => {
|
||||
t.plan(3)
|
||||
|
||||
const CustomFastifyError = createError('CODE', 'Not available')
|
||||
const err = CustomFastifyError()
|
||||
|
||||
t.assert.ok(err instanceof Error)
|
||||
t.assert.ok(err instanceof SyntaxError === false)
|
||||
t.assert.ok(err instanceof TypeError === false)
|
||||
})
|
||||
|
||||
test('check if createError creates an Error which is instanceof FastifyError', (t) => {
|
||||
t.plan(4)
|
||||
|
||||
const CustomFastifyError = createError('CODE', 'Not available')
|
||||
const err = CustomFastifyError()
|
||||
|
||||
t.assert.ok(err instanceof Error)
|
||||
t.assert.ok(err instanceof FastifyError)
|
||||
t.assert.ok(err instanceof SyntaxError === false)
|
||||
t.assert.ok(err instanceof TypeError === false)
|
||||
})
|
||||
|
||||
test('check if createError creates an Error with the right BaseConstructor', (t) => {
|
||||
t.plan(2)
|
||||
|
||||
const CustomFastifyError = createError('CODE', 'Not available', 500, TypeError)
|
||||
const err = CustomFastifyError()
|
||||
|
||||
t.assert.ok(err instanceof Error)
|
||||
t.assert.ok(err instanceof TypeError)
|
||||
})
|
||||
|
||||
test('check if createError creates an Error with the right BaseConstructor, which is a FastifyError', (t) => {
|
||||
t.plan(6)
|
||||
|
||||
const BaseFastifyError = createError('CODE', 'Not available', 500, TypeError)
|
||||
const CustomFastifyError = createError('CODE', 'Not available', 500, BaseFastifyError)
|
||||
const err = CustomFastifyError()
|
||||
|
||||
t.assert.ok(err instanceof Error)
|
||||
t.assert.ok(err instanceof TypeError)
|
||||
t.assert.ok(err instanceof FastifyError)
|
||||
t.assert.ok(err instanceof BaseFastifyError)
|
||||
t.assert.ok(err instanceof CustomFastifyError)
|
||||
t.assert.ok(err instanceof SyntaxError === false)
|
||||
})
|
||||
|
||||
// for more information see https://github.com/fastify/fastify-error/pull/86#issuecomment-1301466407
|
||||
test('ensure that instanceof works accross different installations of the fastify-error module', async (t) => {
|
||||
const assertsPlanned = 5
|
||||
t.plan(assertsPlanned)
|
||||
|
||||
// We need to create a test environment where fastify-error is installed in two different locations
|
||||
// and then we will check if the error created in one location is instanceof the error created in the other location
|
||||
// This is done by creating a test directory with the following structure:
|
||||
|
||||
// /
|
||||
// ├── index.js
|
||||
// └── node_modules/
|
||||
// ├── fastify-error/
|
||||
// │ └── index.js
|
||||
// └── dep/
|
||||
// ├── index.js
|
||||
// └── node_modules/
|
||||
// └── fastify-error/
|
||||
// └── index.js
|
||||
|
||||
const testDirectoryPrefix = 'fastify-error-instanceof-test-'
|
||||
|
||||
const testCwd = path.resolve(os.tmpdir(), `${testDirectoryPrefix}${Math.random().toString(36).substring(2, 15)}`)
|
||||
fs.mkdirSync(testCwd, { recursive: true })
|
||||
|
||||
// Create the index.js. It will be executed as a forked process, so we need to
|
||||
// use process.send to send messages back to the parent process.
|
||||
fs.writeFileSync(path.resolve(testCwd, 'index.js'), `
|
||||
'use strict'
|
||||
|
||||
const path = require('node:path')
|
||||
const { createError, FastifyError } = require('fastify-error')
|
||||
const { foo } = require('dep')
|
||||
|
||||
const actualPathOfFastifyError = require.resolve('fastify-error')
|
||||
const expectedPathOfFastifyError = path.resolve('node_modules', 'fastify-error', 'index.js')
|
||||
|
||||
// Ensure that fastify-error is required from the node_modules directory of the test-project
|
||||
if (actualPathOfFastifyError !== expectedPathOfFastifyError) {
|
||||
console.error('actualPathOfFastifyError', actualPathOfFastifyError)
|
||||
console.error('expectedPathOfFastifyError', expectedPathOfFastifyError)
|
||||
throw new Error('fastify-error should be required from the node_modules directory of the test-project')
|
||||
}
|
||||
|
||||
const Boom = createError('Boom', 'Boom', 500)
|
||||
const ChildBoom = createError('ChildBoom', 'Boom', 500, Boom)
|
||||
const NotChildBoom = createError('NotChildBoom', 'NotChildBoom', 500, Boom)
|
||||
|
||||
try {
|
||||
foo()
|
||||
} catch (err) {
|
||||
process.send(err instanceof Error)
|
||||
process.send(err instanceof FastifyError)
|
||||
process.send(err instanceof NotChildBoom)
|
||||
process.send(err instanceof Boom)
|
||||
process.send(err instanceof ChildBoom)
|
||||
}
|
||||
`)
|
||||
|
||||
// Create /node_modules/fastify-error directory
|
||||
// Copy the index.js file to the fastify-error directory
|
||||
fs.mkdirSync(path.resolve(testCwd, 'node_modules', 'fastify-error'), { recursive: true })
|
||||
fs.copyFileSync(path.resolve(process.cwd(), 'index.js'), path.resolve(testCwd, 'node_modules', 'fastify-error', 'index.js'))
|
||||
|
||||
// Create /node_modules/dep/node_modules/fastify-error directory
|
||||
// Copy the index.js to the fastify-error directory
|
||||
fs.mkdirSync(path.resolve(testCwd, 'node_modules', 'dep', 'node_modules', 'fastify-error'), { recursive: true })
|
||||
fs.copyFileSync(path.resolve(process.cwd(), 'index.js'), path.resolve(testCwd, 'node_modules', 'dep', 'node_modules', 'fastify-error', 'index.js'))
|
||||
|
||||
// Create /node_modules/dep/index.js. It will export a function foo which will
|
||||
// throw an error when called. The error will be an instance of ChildBoom, created
|
||||
// by the fastify-error module in the node_modules directory of dep.
|
||||
fs.writeFileSync(path.resolve(testCwd, 'node_modules', 'dep', 'index.js'), `
|
||||
'use strict'
|
||||
|
||||
const path = require('node:path')
|
||||
const { createError } = require('fastify-error')
|
||||
|
||||
const actualPathOfFastifyError = require.resolve('fastify-error')
|
||||
const expectedPathOfFastifyError = path.resolve('node_modules', 'dep', 'node_modules', 'fastify-error', 'index.js')
|
||||
|
||||
// Ensure that fastify-error is required from the node_modules directory of the test-project
|
||||
if (actualPathOfFastifyError !== expectedPathOfFastifyError) {
|
||||
console.error('actualPathOfFastifyError', actualPathOfFastifyError)
|
||||
console.error('expectedPathOfFastifyError', expectedPathOfFastifyError)
|
||||
throw new Error('fastify-error should be required from the node_modules directory of dep')
|
||||
}
|
||||
|
||||
const Boom = createError('Boom', 'Boom', 500)
|
||||
const ChildBoom = createError('ChildBoom', 'Boom', 500, Boom)
|
||||
|
||||
module.exports.foo = function foo () {
|
||||
throw new ChildBoom('foo go Boom')
|
||||
}
|
||||
`)
|
||||
|
||||
const finishedPromise = {
|
||||
promise: undefined,
|
||||
reject: undefined,
|
||||
resolve: undefined,
|
||||
}
|
||||
|
||||
finishedPromise.promise = new Promise((resolve, reject) => {
|
||||
finishedPromise.resolve = resolve
|
||||
finishedPromise.reject = reject
|
||||
})
|
||||
|
||||
const child = cp.fork(path.resolve(testCwd, 'index.js'), {
|
||||
cwd: testCwd,
|
||||
stdio: 'inherit',
|
||||
env: {
|
||||
...process.env,
|
||||
NODE_OPTIONS: '--no-warnings'
|
||||
},
|
||||
})
|
||||
|
||||
let messageCount = 0
|
||||
child.on('message', message => {
|
||||
try {
|
||||
switch (messageCount) {
|
||||
case 0:
|
||||
t.assert.strictEqual(message, true, 'instanceof Error')
|
||||
break
|
||||
case 1:
|
||||
t.assert.strictEqual(message, true, 'instanceof FastifyError')
|
||||
break
|
||||
case 2:
|
||||
t.assert.strictEqual(message, false, 'instanceof NotChildBoom')
|
||||
break
|
||||
case 3:
|
||||
t.assert.strictEqual(message, true, 'instanceof Boom')
|
||||
break
|
||||
case 4:
|
||||
t.assert.strictEqual(message, true, 'instanceof ChildBoom')
|
||||
break
|
||||
}
|
||||
if (++messageCount === assertsPlanned) {
|
||||
finishedPromise.resolve()
|
||||
}
|
||||
} catch (err) {
|
||||
finishedPromise.reject(err)
|
||||
}
|
||||
})
|
||||
|
||||
child.on('error', err => {
|
||||
finishedPromise.reject(err)
|
||||
})
|
||||
|
||||
await finishedPromise.promise
|
||||
|
||||
// Cleanup
|
||||
// As we are creating the test-setup on the fly in the /tmp directory, we can remove it
|
||||
// safely when we are done. It is not relevant for the test if the deletion fails.
|
||||
try {
|
||||
fs.rmSync(testCwd, { recursive: true, force: true })
|
||||
} catch {}
|
||||
})
|
||||
49
backend/node_modules/@fastify/error/types/index.d.ts
generated
vendored
Normal file
49
backend/node_modules/@fastify/error/types/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
declare function createError<C extends string, SC extends number, Arg extends unknown[] = [any?, any?, any?]> (
|
||||
code: C,
|
||||
message: string,
|
||||
statusCode: SC,
|
||||
Base?: ErrorConstructor,
|
||||
captureStackTrace?: boolean
|
||||
): createError.FastifyErrorConstructor<{ code: C, statusCode: SC }, Arg>
|
||||
|
||||
declare function createError<C extends string, Arg extends unknown[] = [any?, any?, any?]> (
|
||||
code: C,
|
||||
message: string,
|
||||
statusCode?: number,
|
||||
Base?: ErrorConstructor,
|
||||
captureStackTrace?: boolean
|
||||
): createError.FastifyErrorConstructor<{ code: C }, Arg>
|
||||
|
||||
declare function createError<Arg extends unknown[] = [any?, any?, any?]> (
|
||||
code: string,
|
||||
message: string,
|
||||
statusCode?: number,
|
||||
Base?: ErrorConstructor,
|
||||
captureStackTrace?: boolean
|
||||
): createError.FastifyErrorConstructor<{ code: string }, Arg>
|
||||
|
||||
type CreateError = typeof createError
|
||||
|
||||
declare namespace createError {
|
||||
export interface FastifyError extends Error {
|
||||
code: string
|
||||
name: string
|
||||
statusCode?: number
|
||||
}
|
||||
|
||||
export interface FastifyErrorConstructor<
|
||||
E extends { code: string, statusCode?: number } = { code: string, statusCode?: number },
|
||||
T extends unknown[] = [any?, any?, any?]
|
||||
> {
|
||||
new(...arg: T): FastifyError & E
|
||||
(...arg: T): FastifyError & E
|
||||
readonly prototype: FastifyError & E
|
||||
}
|
||||
|
||||
export const FastifyError: FastifyErrorConstructor
|
||||
|
||||
export const createError: CreateError
|
||||
export { createError as default }
|
||||
}
|
||||
|
||||
export = createError
|
||||
92
backend/node_modules/@fastify/error/types/index.test-d.ts
generated
vendored
Normal file
92
backend/node_modules/@fastify/error/types/index.test-d.ts
generated
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
import createError, { FastifyError, FastifyErrorConstructor } from '..'
|
||||
import { expectType, expectError } from 'tsd'
|
||||
|
||||
const CustomError = createError('ERROR_CODE', 'message')
|
||||
expectType<FastifyErrorConstructor<{ code: 'ERROR_CODE' }>>(CustomError)
|
||||
const err = new CustomError()
|
||||
expectType<FastifyError & { code: 'ERROR_CODE' }>(err)
|
||||
expectType<'ERROR_CODE'>(err.code)
|
||||
expectType<string>(err.message)
|
||||
expectType<number | undefined>(err.statusCode)
|
||||
|
||||
const CustomErrorNoStackTrace = createError('ERROR_CODE', 'message', undefined, undefined, false)
|
||||
expectType<FastifyErrorConstructor<{ code: 'ERROR_CODE' }>>(CustomErrorNoStackTrace)
|
||||
const errNoStackTrace = new CustomErrorNoStackTrace()
|
||||
expectType<FastifyError & { code: 'ERROR_CODE' }>(errNoStackTrace)
|
||||
expectType<'ERROR_CODE'>(errNoStackTrace.code)
|
||||
expectType<string>(errNoStackTrace.message)
|
||||
expectType<number | undefined>(errNoStackTrace.statusCode)
|
||||
|
||||
const CustomTypedError = createError('OTHER_CODE', 'message', 400)
|
||||
expectType<FastifyErrorConstructor<{ code: 'OTHER_CODE', statusCode: 400 }>>(CustomTypedError)
|
||||
const typed = new CustomTypedError()
|
||||
expectType<FastifyError & { code: 'OTHER_CODE', statusCode: 400 }>(typed)
|
||||
expectType<'OTHER_CODE'>(typed.code)
|
||||
expectType<string>(typed.message)
|
||||
expectType<400>(typed.statusCode)
|
||||
|
||||
/* eslint-disable no-new */
|
||||
const CustomTypedArgError = createError<[string]>('OTHER_CODE', 'expect %s message', 400)
|
||||
CustomTypedArgError('a')
|
||||
expectError(CustomTypedArgError('a', 'b'))
|
||||
expectError(new CustomTypedArgError('a', 'b'))
|
||||
expectError(CustomTypedArgError(1))
|
||||
expectError(new CustomTypedArgError(1))
|
||||
|
||||
const CustomTypedArgError2 = createError<string, number, [string]>('OTHER_CODE', 'expect %s message', 400)
|
||||
CustomTypedArgError2('a')
|
||||
expectError(CustomTypedArgError2('a', 'b'))
|
||||
expectError(new CustomTypedArgError2('a', 'b'))
|
||||
expectError(CustomTypedArgError2(1))
|
||||
expectError(new CustomTypedArgError2(1))
|
||||
|
||||
const CustomTypedArgError3 = createError<string, number, [string, string]>('OTHER_CODE', 'expect %s message but got %s', 400)
|
||||
expectError(CustomTypedArgError3('a'))
|
||||
CustomTypedArgError3('a', 'b')
|
||||
new CustomTypedArgError3('a', 'b')
|
||||
expectError(CustomTypedArgError3(1))
|
||||
expectError(new CustomTypedArgError3(1))
|
||||
expectError(new CustomTypedArgError3(1, 2))
|
||||
expectError(new CustomTypedArgError3('1', 2))
|
||||
expectError(new CustomTypedArgError3(1, '2'))
|
||||
|
||||
const CustomTypedArgError4 = createError<string, number, [string, string]>('OTHER_CODE', 'expect %s message but got %s', 400)
|
||||
expectError(CustomTypedArgError4('a'))
|
||||
CustomTypedArgError4('a', 'b')
|
||||
new CustomTypedArgError4('a', 'b')
|
||||
expectError(CustomTypedArgError4(1))
|
||||
expectError(new CustomTypedArgError4(1))
|
||||
expectError(new CustomTypedArgError4(1, 2))
|
||||
expectError(new CustomTypedArgError4('1', 2))
|
||||
expectError(new CustomTypedArgError4(1, '2'))
|
||||
|
||||
const CustomTypedArgError5 = createError<[string, string, string, string]>('OTHER_CODE', 'expect %s message but got %s. Please contact %s by emailing to %s', 400)
|
||||
expectError(CustomTypedArgError5('a'))
|
||||
expectError(new CustomTypedArgError5('a', 'b'))
|
||||
expectError(new CustomTypedArgError5('a', 'b', 'c'))
|
||||
CustomTypedArgError5('a', 'b', 'c', 'd')
|
||||
expectError(new CustomTypedArgError5('a', 'b', 'c', 'd', 'e'))
|
||||
|
||||
const CustomTypedArgError6 = createError<string, number, [string, string, string, string]>('OTHER_CODE', 'expect %s message but got %s. Please contact %s by emailing to %s', 400)
|
||||
expectError(CustomTypedArgError6('a'))
|
||||
expectError(new CustomTypedArgError6('a', 'b'))
|
||||
expectError(new CustomTypedArgError6('a', 'b', 'c'))
|
||||
CustomTypedArgError6('a', 'b', 'c', 'd')
|
||||
expectError(new CustomTypedArgError6('a', 'b', 'c', 'd', 'e'))
|
||||
|
||||
const CustomErrorWithErrorConstructor = createError('ERROR_CODE', 'message', 500, TypeError)
|
||||
expectType<FastifyErrorConstructor<{ code: 'ERROR_CODE', statusCode: 500 }>>(CustomErrorWithErrorConstructor)
|
||||
CustomErrorWithErrorConstructor({ cause: new Error('Error') })
|
||||
const customErrorWithErrorConstructor = CustomErrorWithErrorConstructor()
|
||||
if (customErrorWithErrorConstructor instanceof FastifyError) {
|
||||
expectType<'ERROR_CODE'>(customErrorWithErrorConstructor.code)
|
||||
expectType<string>(customErrorWithErrorConstructor.message)
|
||||
expectType<500>(customErrorWithErrorConstructor.statusCode)
|
||||
}
|
||||
|
||||
const error = new FastifyError('ERROR_CODE', 'message', 500)
|
||||
if (error instanceof FastifyError) {
|
||||
expectType<string>(error.code)
|
||||
expectType<string>(error.message)
|
||||
expectType<number | undefined>(error.statusCode)
|
||||
}
|
||||
1
backend/node_modules/@fastify/fast-json-stringify-compiler/.eslintrc
generated
vendored
Normal file
1
backend/node_modules/@fastify/fast-json-stringify-compiler/.eslintrc
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{"extends": "standard"}
|
||||
13
backend/node_modules/@fastify/fast-json-stringify-compiler/.github/dependabot.yml
generated
vendored
Normal file
13
backend/node_modules/@fastify/fast-json-stringify-compiler/.github/dependabot.yml
generated
vendored
Normal 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: "weekly"
|
||||
open-pull-requests-limit: 10
|
||||
18
backend/node_modules/@fastify/fast-json-stringify-compiler/.github/workflows/ci.yml
generated
vendored
Normal file
18
backend/node_modules/@fastify/fast-json-stringify-compiler/.github/workflows/ci.yml
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'docs/**'
|
||||
- '*.md'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
uses: fastify/workflows/.github/workflows/plugins-ci.yml@v3
|
||||
with:
|
||||
license-check: true
|
||||
lint: true
|
||||
21
backend/node_modules/@fastify/fast-json-stringify-compiler/LICENSE
generated
vendored
Normal file
21
backend/node_modules/@fastify/fast-json-stringify-compiler/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Fastify
|
||||
|
||||
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.
|
||||
127
backend/node_modules/@fastify/fast-json-stringify-compiler/README.md
generated
vendored
Normal file
127
backend/node_modules/@fastify/fast-json-stringify-compiler/README.md
generated
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
# @fastify/fast-json-stringify-compiler
|
||||
Build and manage the [`fast-json-stringify`](https://www.npmjs.com/package/fast-json-stringify) instances for the fastify framework.
|
||||
This package is responsible for compiling the application's `response` JSON schemas into optimized functions to speed up the response time.
|
||||
|
||||
[](http://standardjs.com/)
|
||||
[](https://github.com/fastify/fast-json-stringify-compiler/actions/workflows/ci.yml)
|
||||
|
||||
|
||||
## Versions
|
||||
|
||||
| `@fastify/fast-json-stringify-compiler` | `fast-json-stringify` | Supported `fastify` |
|
||||
|----------------------------------------:|----------------------:|--------------------:|
|
||||
| v1.x | v3.x | ^3.x |
|
||||
| v2.x | v3.x | ^4.x |
|
||||
| v3.x | v4.x | ^4.x |
|
||||
| v4.x | v5.x | ^4.x |
|
||||
|
||||
### fast-json-stringify Configuration
|
||||
|
||||
The `fast-json-stringify` configuration is the default one. You can check it the default settings in the [`fast-json-stringify` option](https://github.com/fastify/fast-json-stringify/#options) documentation.
|
||||
|
||||
You can also override the default configuration by passing the [`serializerOpts`](https://www.fastify.io/docs/latest/Reference/Server/#serializeropts) configuration to the Fastify instance.
|
||||
|
||||
## Usage
|
||||
|
||||
This module is already used as default by Fastify.
|
||||
If you need to provide to your server instance a different version, refer to [the official doc](https://www.fastify.io/docs/latest/Reference/Server/#schemacontroller).
|
||||
|
||||
### fast-json-stringify Standalone
|
||||
|
||||
`fast-json-stringify@v4.1.0` introduces the [standalone feature](https://github.com/fastify/fast-json-stringify#standalone) that let you to pre-compile your schemas and use them in your application for a faster startup.
|
||||
|
||||
To use this feature, you must be aware of the following:
|
||||
|
||||
1. You must generate and save the application's compiled schemas.
|
||||
2. Read the compiled schemas from the file and provide them back to your Fastify application.
|
||||
|
||||
|
||||
#### Generate and save the compiled schemas
|
||||
|
||||
Fastify helps you to generate the serialization schemas functions and it is your choice to save them where you want.
|
||||
To accomplish this, you must use a new compiler: `@fastify/fast-json-stringify-compiler/standalone`.
|
||||
|
||||
You must provide 2 parameters to this compiler:
|
||||
|
||||
- `readMode: false`: a boolean to indicate that you want generate the schemas functions string.
|
||||
- `storeFunction`" a sync function that must store the source code of the schemas functions. You may provide an async function too, but you must manage errors.
|
||||
|
||||
When `readMode: false`, **the compiler is meant to be used in development ONLY**.
|
||||
|
||||
|
||||
```js
|
||||
const { StandaloneSerializer } = require('@fastify/fast-json-stringify-compiler')
|
||||
|
||||
const factory = StandaloneSerializer({
|
||||
readMode: false,
|
||||
storeFunction (routeOpts, schemaSerializationCode) {
|
||||
// routeOpts is like: { schema, method, url, httpStatus }
|
||||
// schemaSerializationCode is a string source code that is the compiled schema function
|
||||
const fileName = generateFileName(routeOpts)
|
||||
fs.writeFileSync(path.join(__dirname, fileName), schemaSerializationCode)
|
||||
}
|
||||
})
|
||||
|
||||
const app = fastify({
|
||||
jsonShorthand: false,
|
||||
schemaController: {
|
||||
compilersFactory: {
|
||||
buildSerializer: factory
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// ... add all your routes with schemas ...
|
||||
|
||||
app.ready().then(() => {
|
||||
// at this stage all your schemas are compiled and stored in the file system
|
||||
// now it is important to turn off the readMode
|
||||
})
|
||||
```
|
||||
|
||||
#### Read the compiled schemas functions
|
||||
|
||||
At this stage, you should have a file for every route's schema.
|
||||
To use them, you must use the `@fastify/fast-json-stringify-compiler/standalone` with the parameters:
|
||||
|
||||
- `readMode: true`: a boolean to indicate that you want read and use the schemas functions string.
|
||||
- `restoreFunction`" a sync function that must return a function to serialize the route's payload.
|
||||
|
||||
Important keep away before you continue reading the documentation:
|
||||
|
||||
- when you use the `readMode: true`, the application schemas are not compiled (they are ignored). So, if you change your schemas, you must recompile them!
|
||||
- as you can see, you must relate the route's schema to the file name using the `routeOpts` object. You may use the `routeOpts.schema.$id` field to do so, it is up to you to define a unique schema identifier.
|
||||
|
||||
```js
|
||||
const { StandaloneSerializer } = require('@fastify/fast-json-stringify-compiler')
|
||||
|
||||
const factory = StandaloneSerializer({
|
||||
readMode: true,
|
||||
restoreFunction (routeOpts) {
|
||||
// routeOpts is like: { schema, method, url, httpStatus }
|
||||
const fileName = generateFileName(routeOpts)
|
||||
return require(path.join(__dirname, fileName))
|
||||
}
|
||||
})
|
||||
|
||||
const app = fastify({
|
||||
jsonShorthand: false,
|
||||
schemaController: {
|
||||
compilersFactory: {
|
||||
buildSerializer: factory
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// ... add all your routes with schemas as before...
|
||||
|
||||
app.listen({ port: 3000 })
|
||||
```
|
||||
|
||||
### How it works
|
||||
|
||||
This module provide a factory function to produce [Serializer Compilers](https://www.fastify.io/docs/latest/Reference/Server/#serializercompiler) functions.
|
||||
|
||||
## License
|
||||
|
||||
Licensed under [MIT](./LICENSE).
|
||||
23
backend/node_modules/@fastify/fast-json-stringify-compiler/index.js
generated
vendored
Normal file
23
backend/node_modules/@fastify/fast-json-stringify-compiler/index.js
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
'use strict'
|
||||
|
||||
const fastJsonStringify = require('fast-json-stringify')
|
||||
|
||||
function SerializerSelector () {
|
||||
return function buildSerializerFactory (externalSchemas, serializerOpts) {
|
||||
const fjsOpts = Object.assign({}, serializerOpts, { schema: externalSchemas })
|
||||
return responseSchemaCompiler.bind(null, fjsOpts)
|
||||
}
|
||||
}
|
||||
|
||||
function responseSchemaCompiler (fjsOpts, { schema /* method, url, httpStatus */ }) {
|
||||
if (fjsOpts.schema && schema.$id && fjsOpts.schema[schema.$id]) {
|
||||
fjsOpts.schema = { ...fjsOpts.schema }
|
||||
delete fjsOpts.schema[schema.$id]
|
||||
}
|
||||
return fastJsonStringify(schema, fjsOpts)
|
||||
}
|
||||
|
||||
module.exports = SerializerSelector
|
||||
module.exports.default = SerializerSelector
|
||||
module.exports.SerializerSelector = SerializerSelector
|
||||
module.exports.StandaloneSerializer = require('./standalone')
|
||||
40
backend/node_modules/@fastify/fast-json-stringify-compiler/package.json
generated
vendored
Normal file
40
backend/node_modules/@fastify/fast-json-stringify-compiler/package.json
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "@fastify/fast-json-stringify-compiler",
|
||||
"description": "Build and manage the fast-json-stringify instances for the fastify framework",
|
||||
"version": "4.3.0",
|
||||
"main": "index.js",
|
||||
"types": "types/index.d.ts",
|
||||
"scripts": {
|
||||
"lint": "standard",
|
||||
"lint:fix": "standard --fix",
|
||||
"unit": "tap test/**/*.test.js",
|
||||
"test": "npm run unit && npm run test:typescript",
|
||||
"test:typescript": "tsd"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/fastify/fast-json-stringify-compiler.git"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Manuel Spigolon <manuel.spigolon@nearform.com> (https://github.com/Eomm)",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/fastify/fast-json-stringify-compiler/issues"
|
||||
},
|
||||
"homepage": "https://github.com/fastify/fast-json-stringify-compiler#readme",
|
||||
"devDependencies": {
|
||||
"@fastify/pre-commit": "^2.0.2",
|
||||
"fastify": "^4.0.0",
|
||||
"sanitize-filename": "^1.6.3",
|
||||
"standard": "^17.0.0",
|
||||
"tap": "^16.0.0",
|
||||
"tsd": "^0.28.0"
|
||||
},
|
||||
"pre-commit": [
|
||||
"lint",
|
||||
"test"
|
||||
],
|
||||
"dependencies": {
|
||||
"fast-json-stringify": "^5.7.0"
|
||||
}
|
||||
}
|
||||
42
backend/node_modules/@fastify/fast-json-stringify-compiler/standalone.js
generated
vendored
Normal file
42
backend/node_modules/@fastify/fast-json-stringify-compiler/standalone.js
generated
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
'use strict'
|
||||
|
||||
const SerializerSelector = require('./index')
|
||||
|
||||
function StandaloneSerializer (options = { readMode: true }) {
|
||||
if (options.readMode === true && typeof options.restoreFunction !== 'function') {
|
||||
throw new Error('You must provide a function for the restoreFunction-option when readMode ON')
|
||||
}
|
||||
|
||||
if (options.readMode !== true && typeof options.storeFunction !== 'function') {
|
||||
throw new Error('You must provide a function for the storeFunction-option when readMode OFF')
|
||||
}
|
||||
|
||||
if (options.readMode === true) {
|
||||
// READ MODE: it behalf only in the restore function provided by the user
|
||||
return function wrapper () {
|
||||
return function (opts) {
|
||||
return options.restoreFunction(opts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WRITE MODE: it behalf on the default SerializerSelector, wrapping the API to run the Ajv Standalone code generation
|
||||
const factory = SerializerSelector()
|
||||
return function wrapper (externalSchemas, serializerOpts = {}) {
|
||||
// to generate the serialization source code, this option is mandatory
|
||||
serializerOpts.mode = 'standalone'
|
||||
|
||||
const compiler = factory(externalSchemas, serializerOpts)
|
||||
return function (opts) { // { schema/*, method, url, httpPart */ }
|
||||
const serializeFuncCode = compiler(opts)
|
||||
|
||||
options.storeFunction(opts, serializeFuncCode)
|
||||
|
||||
// eslint-disable-next-line no-new-func
|
||||
return new Function(serializeFuncCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = StandaloneSerializer
|
||||
module.exports.default = StandaloneSerializer
|
||||
26
backend/node_modules/@fastify/fast-json-stringify-compiler/test/duplicate-schema.test.js
generated
vendored
Normal file
26
backend/node_modules/@fastify/fast-json-stringify-compiler/test/duplicate-schema.test.js
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
'use strict'
|
||||
|
||||
const t = require('tap')
|
||||
const FjsCompiler = require('../index')
|
||||
|
||||
t.test('Use input schema duplicate in the externalSchemas', async t => {
|
||||
t.plan(1)
|
||||
const externalSchemas = {
|
||||
schema1: {
|
||||
$id: 'schema1',
|
||||
type: 'number'
|
||||
},
|
||||
schema2: {
|
||||
$id: 'schema2',
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
|
||||
const factory = FjsCompiler()
|
||||
const compiler = factory(externalSchemas)
|
||||
|
||||
compiler({ schema: externalSchemas.schema1 })
|
||||
compiler({ schema: externalSchemas.schema2 })
|
||||
|
||||
t.pass()
|
||||
})
|
||||
78
backend/node_modules/@fastify/fast-json-stringify-compiler/test/plugin.test.js
generated
vendored
Normal file
78
backend/node_modules/@fastify/fast-json-stringify-compiler/test/plugin.test.js
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
'use strict'
|
||||
|
||||
const t = require('tap')
|
||||
const fastify = require('fastify')
|
||||
const FjsCompiler = require('../index')
|
||||
|
||||
const echo = async (req, reply) => { return req.body }
|
||||
|
||||
const sampleSchema = Object.freeze({
|
||||
$id: 'example1',
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' }
|
||||
}
|
||||
})
|
||||
|
||||
const externalSchemas1 = Object.freeze({})
|
||||
const externalSchemas2 = Object.freeze({
|
||||
foo: {
|
||||
$id: 'foo',
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' }
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const fastifyFjsOptionsDefault = Object.freeze({})
|
||||
|
||||
t.test('basic usage', t => {
|
||||
t.plan(1)
|
||||
const factory = FjsCompiler()
|
||||
const compiler = factory(externalSchemas1, fastifyFjsOptionsDefault)
|
||||
const serializeFunc = compiler({ schema: sampleSchema })
|
||||
const result = serializeFunc({ name: 'hello' })
|
||||
t.equal(result, '{"name":"hello"}')
|
||||
})
|
||||
|
||||
t.test('fastify integration', async t => {
|
||||
const factory = FjsCompiler()
|
||||
|
||||
const app = fastify({
|
||||
serializerOpts: {
|
||||
rounding: 'ceil'
|
||||
},
|
||||
schemaController: {
|
||||
compilersFactory: {
|
||||
buildSerializer: factory
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
app.addSchema(externalSchemas2.foo)
|
||||
|
||||
app.post('/', {
|
||||
handler: echo,
|
||||
schema: {
|
||||
response: {
|
||||
200: {
|
||||
$ref: 'foo#'
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const res = await app.inject({
|
||||
url: '/',
|
||||
method: 'POST',
|
||||
payload: {
|
||||
version: '1',
|
||||
foo: 'this is not a number',
|
||||
name: 'serialize me'
|
||||
}
|
||||
})
|
||||
|
||||
t.equal(res.statusCode, 200)
|
||||
t.same(res.json(), { name: 'serialize me' })
|
||||
})
|
||||
230
backend/node_modules/@fastify/fast-json-stringify-compiler/test/standalone.test.js
generated
vendored
Normal file
230
backend/node_modules/@fastify/fast-json-stringify-compiler/test/standalone.test.js
generated
vendored
Normal file
@@ -0,0 +1,230 @@
|
||||
'use strict'
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const t = require('tap')
|
||||
const fastify = require('fastify')
|
||||
const sanitize = require('sanitize-filename')
|
||||
|
||||
const { StandaloneSerializer: FjsStandaloneCompiler } = require('../')
|
||||
|
||||
const generatedFileNames = []
|
||||
|
||||
function generateFileName (routeOpts) {
|
||||
const fileName = `/fjs-generated-${sanitize(routeOpts.schema.$id)}-${routeOpts.method}-${routeOpts.httpPart}-${sanitize(routeOpts.url)}.js`
|
||||
generatedFileNames.push(fileName)
|
||||
return fileName
|
||||
}
|
||||
|
||||
t.test('standalone', t => {
|
||||
t.plan(5)
|
||||
|
||||
t.teardown(async () => {
|
||||
for (const fileName of generatedFileNames) {
|
||||
try {
|
||||
await fs.promises.unlink(path.join(__dirname, fileName))
|
||||
} catch (e) {}
|
||||
}
|
||||
})
|
||||
|
||||
t.test('errors', t => {
|
||||
t.plan(2)
|
||||
t.throws(() => {
|
||||
FjsStandaloneCompiler()
|
||||
}, 'missing restoreFunction')
|
||||
t.throws(() => {
|
||||
FjsStandaloneCompiler({ readMode: false })
|
||||
}, 'missing storeFunction')
|
||||
})
|
||||
|
||||
t.test('generate standalone code', t => {
|
||||
t.plan(5)
|
||||
|
||||
const base = {
|
||||
$id: 'urn:schema:base',
|
||||
definitions: {
|
||||
hello: { type: 'string' }
|
||||
},
|
||||
type: 'object',
|
||||
properties: {
|
||||
hello: { $ref: '#/definitions/hello' }
|
||||
}
|
||||
}
|
||||
|
||||
const refSchema = {
|
||||
$id: 'urn:schema:ref',
|
||||
type: 'object',
|
||||
properties: {
|
||||
hello: { $ref: 'urn:schema:base#/definitions/hello' }
|
||||
}
|
||||
}
|
||||
|
||||
const endpointSchema = {
|
||||
schema: {
|
||||
$id: 'urn:schema:endpoint',
|
||||
$ref: 'urn:schema:ref'
|
||||
}
|
||||
}
|
||||
|
||||
const schemaMap = {
|
||||
[base.$id]: base,
|
||||
[refSchema.$id]: refSchema
|
||||
}
|
||||
|
||||
const factory = FjsStandaloneCompiler({
|
||||
readMode: false,
|
||||
storeFunction (routeOpts, schemaSerializerCode) {
|
||||
t.same(routeOpts, endpointSchema)
|
||||
t.type(schemaSerializerCode, 'string')
|
||||
fs.writeFileSync(path.join(__dirname, '/fjs-generated.js'), schemaSerializerCode)
|
||||
generatedFileNames.push('/fjs-generated.js')
|
||||
t.pass('stored the serializer function')
|
||||
}
|
||||
})
|
||||
|
||||
const compiler = factory(schemaMap)
|
||||
compiler(endpointSchema)
|
||||
t.pass('compiled the endpoint schema')
|
||||
|
||||
t.test('usage standalone code', t => {
|
||||
t.plan(3)
|
||||
const standaloneSerializer = require('./fjs-generated')
|
||||
t.ok(standaloneSerializer)
|
||||
|
||||
const valid = standaloneSerializer({ hello: 'world' })
|
||||
t.same(valid, JSON.stringify({ hello: 'world' }))
|
||||
|
||||
const invalid = standaloneSerializer({ hello: [] })
|
||||
t.same(invalid, '{"hello":""}')
|
||||
})
|
||||
})
|
||||
|
||||
t.test('fastify integration - writeMode', async t => {
|
||||
t.plan(4)
|
||||
|
||||
const factory = FjsStandaloneCompiler({
|
||||
readMode: false,
|
||||
storeFunction (routeOpts, schemaSerializationCode) {
|
||||
const fileName = generateFileName(routeOpts)
|
||||
t.ok(routeOpts)
|
||||
fs.writeFileSync(path.join(__dirname, fileName), schemaSerializationCode)
|
||||
t.pass(`stored the serializer function ${fileName}`)
|
||||
},
|
||||
restoreFunction () {
|
||||
t.fail('write mode ON')
|
||||
}
|
||||
})
|
||||
|
||||
const app = buildApp(factory)
|
||||
await app.ready()
|
||||
})
|
||||
|
||||
t.test('fastify integration - writeMode forces standalone', async t => {
|
||||
t.plan(4)
|
||||
|
||||
const factory = FjsStandaloneCompiler({
|
||||
readMode: false,
|
||||
storeFunction (routeOpts, schemaSerializationCode) {
|
||||
const fileName = generateFileName(routeOpts)
|
||||
t.ok(routeOpts)
|
||||
fs.writeFileSync(path.join(__dirname, fileName), schemaSerializationCode)
|
||||
t.pass(`stored the serializer function ${fileName}`)
|
||||
},
|
||||
restoreFunction () {
|
||||
t.fail('write mode ON')
|
||||
}
|
||||
})
|
||||
|
||||
const app = buildApp(factory, {
|
||||
mode: 'not-standalone',
|
||||
rounding: 'ceil'
|
||||
})
|
||||
|
||||
await app.ready()
|
||||
})
|
||||
|
||||
t.test('fastify integration - readMode', async t => {
|
||||
t.plan(6)
|
||||
|
||||
const factory = FjsStandaloneCompiler({
|
||||
readMode: true,
|
||||
storeFunction () {
|
||||
t.fail('read mode ON')
|
||||
},
|
||||
restoreFunction (routeOpts) {
|
||||
const fileName = generateFileName(routeOpts)
|
||||
t.pass(`restore the serializer function ${fileName}}`)
|
||||
return require(path.join(__dirname, fileName))
|
||||
}
|
||||
})
|
||||
|
||||
const app = buildApp(factory)
|
||||
await app.ready()
|
||||
|
||||
let res = await app.inject({
|
||||
url: '/foo',
|
||||
method: 'POST'
|
||||
})
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, JSON.stringify({ hello: 'world' }))
|
||||
|
||||
res = await app.inject({
|
||||
url: '/bar?lang=it',
|
||||
method: 'GET'
|
||||
})
|
||||
t.equal(res.statusCode, 200)
|
||||
t.equal(res.payload, JSON.stringify({ lang: 'en' }))
|
||||
})
|
||||
|
||||
function buildApp (factory, serializerOpts) {
|
||||
const app = fastify({
|
||||
exposeHeadRoutes: false,
|
||||
jsonShorthand: false,
|
||||
schemaController: {
|
||||
compilersFactory: {
|
||||
buildSerializer: factory
|
||||
}
|
||||
},
|
||||
serializerOpts
|
||||
})
|
||||
|
||||
app.addSchema({
|
||||
$id: 'urn:schema:foo',
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' },
|
||||
id: { type: 'integer' }
|
||||
}
|
||||
})
|
||||
|
||||
app.post('/foo', {
|
||||
schema: {
|
||||
response: {
|
||||
200: {
|
||||
$id: 'urn:schema:response',
|
||||
type: 'object',
|
||||
properties: {
|
||||
hello: { $ref: 'urn:schema:foo#/properties/name' }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, () => { return { hello: 'world' } })
|
||||
|
||||
app.get('/bar', {
|
||||
schema: {
|
||||
response: {
|
||||
200: {
|
||||
$id: 'urn:schema:response:bar',
|
||||
type: 'object',
|
||||
properties: {
|
||||
lang: { type: 'string', enum: ['it', 'en'] }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, () => { return { lang: 'en' } })
|
||||
|
||||
return app
|
||||
}
|
||||
})
|
||||
45
backend/node_modules/@fastify/fast-json-stringify-compiler/types/index.d.ts
generated
vendored
Normal file
45
backend/node_modules/@fastify/fast-json-stringify-compiler/types/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
import { Options } from 'fast-json-stringify'
|
||||
|
||||
type FastJsonStringifyFactory = () => SerializerSelector.SerializerFactory
|
||||
|
||||
declare namespace SerializerSelector {
|
||||
export type SerializerFactory = (
|
||||
externalSchemas?: unknown,
|
||||
options?: Options
|
||||
) => SerializerCompiler;
|
||||
|
||||
export type SerializerCompiler = (
|
||||
externalSchemas?: unknown,
|
||||
options?: Options
|
||||
) => Serializer;
|
||||
|
||||
export type Serializer = (doc: any) => string
|
||||
|
||||
export type RouteDefinition = {
|
||||
method: string;
|
||||
url: string;
|
||||
httpStatus: string;
|
||||
schema?: unknown;
|
||||
}
|
||||
|
||||
export type StandaloneOptions = StandaloneOptionsReadModeOn | StandaloneOptionsReadModeOff
|
||||
|
||||
export type StandaloneOptionsReadModeOn = {
|
||||
readMode: true;
|
||||
restoreFunction?(opts: RouteDefinition): Serializer;
|
||||
}
|
||||
|
||||
export type StandaloneOptionsReadModeOff = {
|
||||
readMode?: false | undefined;
|
||||
storeFunction?(opts: RouteDefinition, schemaSerializationCode: string): void;
|
||||
}
|
||||
|
||||
export type { Options }
|
||||
export const SerializerSelector: FastJsonStringifyFactory;
|
||||
export function StandaloneSerializer(options: StandaloneOptions): SerializerFactory;
|
||||
|
||||
export { SerializerSelector as default }
|
||||
}
|
||||
|
||||
declare function SerializerSelector(...params: Parameters<FastJsonStringifyFactory>): ReturnType<FastJsonStringifyFactory>
|
||||
export = SerializerSelector
|
||||
141
backend/node_modules/@fastify/fast-json-stringify-compiler/types/index.test-d.ts
generated
vendored
Normal file
141
backend/node_modules/@fastify/fast-json-stringify-compiler/types/index.test-d.ts
generated
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
import { expectAssignable, expectError, expectType } from "tsd";
|
||||
import SerializerSelector, {
|
||||
RouteDefinition,
|
||||
Serializer,
|
||||
SerializerCompiler,
|
||||
SerializerFactory,
|
||||
SerializerSelector as SerializerSelectorNamed,
|
||||
StandaloneSerializer,
|
||||
} from "..";
|
||||
|
||||
/**
|
||||
* SerializerSelector
|
||||
*/
|
||||
|
||||
{
|
||||
const compiler = SerializerSelector();
|
||||
expectType<SerializerFactory>(compiler);
|
||||
}
|
||||
|
||||
{
|
||||
const compiler = SerializerSelectorNamed();
|
||||
expectType<SerializerFactory>(compiler);
|
||||
}
|
||||
|
||||
{
|
||||
{
|
||||
const sampleSchema = {
|
||||
$id: 'example1',
|
||||
type: 'object',
|
||||
properties: {
|
||||
name: { type: 'string' }
|
||||
}
|
||||
}
|
||||
|
||||
const externalSchemas1 = {}
|
||||
|
||||
const factory = SerializerSelector()
|
||||
expectType<SerializerFactory>(factory);
|
||||
const compiler = factory(externalSchemas1, {})
|
||||
expectType<SerializerCompiler>(compiler);
|
||||
const serializeFunc = compiler({ schema: sampleSchema })
|
||||
expectType<Serializer>(serializeFunc);
|
||||
|
||||
expectType<string>(serializeFunc({ name: 'hello' }))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* StandaloneSerializer
|
||||
*/
|
||||
|
||||
const reader = StandaloneSerializer({
|
||||
readMode: true,
|
||||
restoreFunction: (route: RouteDefinition) => {
|
||||
expectAssignable<RouteDefinition>(route)
|
||||
return {} as Serializer
|
||||
},
|
||||
});
|
||||
expectType<SerializerFactory>(reader);
|
||||
|
||||
const writer = StandaloneSerializer({
|
||||
readMode: false,
|
||||
storeFunction: (route: RouteDefinition, code: string) => {
|
||||
expectAssignable<RouteDefinition>(route)
|
||||
expectAssignable<string>(code)
|
||||
},
|
||||
});
|
||||
expectType<SerializerFactory>(writer);
|
||||
|
||||
{
|
||||
const base = {
|
||||
$id: 'urn:schema:base',
|
||||
definitions: {
|
||||
hello: { type: 'string' }
|
||||
},
|
||||
type: 'object',
|
||||
properties: {
|
||||
hello: { $ref: '#/definitions/hello' }
|
||||
}
|
||||
}
|
||||
|
||||
const refSchema = {
|
||||
$id: 'urn:schema:ref',
|
||||
type: 'object',
|
||||
properties: {
|
||||
hello: { $ref: 'urn:schema:base#/definitions/hello' }
|
||||
}
|
||||
}
|
||||
|
||||
const endpointSchema = {
|
||||
schema: {
|
||||
$id: 'urn:schema:endpoint',
|
||||
$ref: 'urn:schema:ref'
|
||||
}
|
||||
}
|
||||
|
||||
const schemaMap = {
|
||||
[base.$id]: base,
|
||||
[refSchema.$id]: refSchema
|
||||
}
|
||||
|
||||
expectError(StandaloneSerializer({
|
||||
readMode: true,
|
||||
storeFunction () { }
|
||||
}))
|
||||
expectError(StandaloneSerializer({
|
||||
readMode: false,
|
||||
restoreFunction () {}
|
||||
}))
|
||||
expectError(StandaloneSerializer({
|
||||
restoreFunction () {}
|
||||
}))
|
||||
|
||||
expectType<SerializerFactory>(StandaloneSerializer({
|
||||
storeFunction (routeOpts, schemaSerializerCode) {
|
||||
expectType<RouteDefinition>(routeOpts)
|
||||
expectType<string>(schemaSerializerCode)
|
||||
}
|
||||
}))
|
||||
|
||||
expectType<SerializerFactory>(StandaloneSerializer({
|
||||
readMode: true,
|
||||
restoreFunction (routeOpts) {
|
||||
expectType<RouteDefinition>(routeOpts)
|
||||
return {} as Serializer
|
||||
}
|
||||
}))
|
||||
|
||||
const factory = StandaloneSerializer({
|
||||
readMode: false,
|
||||
storeFunction (routeOpts, schemaSerializerCode) {
|
||||
expectType<RouteDefinition>(routeOpts)
|
||||
expectType<string>(schemaSerializerCode)
|
||||
}
|
||||
})
|
||||
expectType<SerializerFactory>(factory)
|
||||
|
||||
const compiler = factory(schemaMap)
|
||||
expectType<SerializerCompiler>(compiler)
|
||||
expectType<Serializer>(compiler(endpointSchema))
|
||||
}
|
||||
2
backend/node_modules/@fastify/helmet/.gitattributes
generated
vendored
Normal file
2
backend/node_modules/@fastify/helmet/.gitattributes
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Set default behavior to automatically convert line endings
|
||||
* text=auto eol=lf
|
||||
13
backend/node_modules/@fastify/helmet/.github/dependabot.yml
generated
vendored
Normal file
13
backend/node_modules/@fastify/helmet/.github/dependabot.yml
generated
vendored
Normal 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
backend/node_modules/@fastify/helmet/.github/stale.yml
generated
vendored
Normal file
21
backend/node_modules/@fastify/helmet/.github/stale.yml
generated
vendored
Normal 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
|
||||
28
backend/node_modules/@fastify/helmet/.github/workflows/ci.yml
generated
vendored
Normal file
28
backend/node_modules/@fastify/helmet/.github/workflows/ci.yml
generated
vendored
Normal 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
|
||||
49
backend/node_modules/@fastify/helmet/LICENSE
generated
vendored
Normal file
49
backend/node_modules/@fastify/helmet/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,49 @@
|
||||
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.
|
||||
|
||||
--> Original helmet license
|
||||
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2012-2017 Evan Hahn, Adam Baldwin
|
||||
|
||||
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.
|
||||
|
||||
262
backend/node_modules/@fastify/helmet/README.md
generated
vendored
Normal file
262
backend/node_modules/@fastify/helmet/README.md
generated
vendored
Normal file
@@ -0,0 +1,262 @@
|
||||
# @fastify/helmet
|
||||
|
||||
[](https://github.com/fastify/fastify-helmet/actions/workflows/ci.yml)
|
||||
[](https://www.npmjs.com/package/@fastify/helmet)
|
||||
[](https://github.com/neostandard/neostandard)
|
||||
|
||||
Important security headers for Fastify, using [helmet](https://npm.im/helmet).
|
||||
|
||||
## Install
|
||||
```
|
||||
npm i @fastify/helmet
|
||||
```
|
||||
|
||||
### Compatibility
|
||||
|
||||
| Plugin version | Fastify version |
|
||||
| ---------------|-----------------|
|
||||
| `>=12.x` | `^5.x` |
|
||||
| `>=9.x <12.x` | `^4.x` |
|
||||
| `>=7.x <9.x` | `^3.x` |
|
||||
| `>=1.x <7.x` | `^2.x` |
|
||||
| `>=1.x <7.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
|
||||
|
||||
Simply require this plugin to set basic security headers.
|
||||
|
||||
```js
|
||||
const fastify = require('fastify')()
|
||||
const helmet = require('@fastify/helmet')
|
||||
|
||||
fastify.register(
|
||||
helmet,
|
||||
// Example disables the `contentSecurityPolicy` middleware but keeps the rest.
|
||||
{ contentSecurityPolicy: false }
|
||||
)
|
||||
|
||||
fastify.listen({ port: 3000 }, err => {
|
||||
if (err) throw err
|
||||
})
|
||||
```
|
||||
|
||||
## How it works
|
||||
|
||||
`@fastify/helmet` is a wrapper around `helmet` that adds an `'onRequest'` hook
|
||||
and a `reply.helmet` decorator.
|
||||
|
||||
It accepts the same options as `helmet`. See [helmet documentation](https://helmetjs.github.io/).
|
||||
|
||||
### Apply Helmet to all routes
|
||||
|
||||
Pass `{ global: true }` to register Helmet for all routes.
|
||||
For granular control, pass `{ global: false }` to disable it at a global scope.
|
||||
Default is `true`.
|
||||
|
||||
#### Example - enable `@fastify/helmet` globally
|
||||
|
||||
```js
|
||||
fastify.register(helmet)
|
||||
// or
|
||||
fastify.register(helmet, { global: true })
|
||||
```
|
||||
|
||||
#### Example - disable `@fastify/helmet` globally
|
||||
|
||||
```js
|
||||
// register the package with the `{ global: false }` option
|
||||
fastify.register(helmet, { global: false })
|
||||
|
||||
fastify.get('/route-with-disabled-helmet', async (request, reply) => {
|
||||
return { message: 'helmet is not enabled here' }
|
||||
})
|
||||
|
||||
fastify.get('/route-with-enabled-helmet', {
|
||||
// We enable and configure helmet for this route only
|
||||
helmet: {
|
||||
dnsPrefetchControl: {
|
||||
allow: true
|
||||
},
|
||||
frameguard: {
|
||||
action: 'foo'
|
||||
},
|
||||
referrerPolicy: false
|
||||
}
|
||||
}, async (request, reply) => {
|
||||
return { message: 'helmet is enabled here' }
|
||||
})
|
||||
|
||||
// helmet is disabled on this route but we have access to `reply.helmet` decorator
|
||||
// that allows us to apply helmet conditionally
|
||||
fastify.get('/here-we-use-helmet-reply-decorator', async (request, reply) => {
|
||||
if (condition) {
|
||||
// we apply the default options
|
||||
await reply.helmet()
|
||||
} else {
|
||||
// we apply customized options
|
||||
await reply.helmet({ frameguard: false })
|
||||
}
|
||||
|
||||
return {
|
||||
message: 'we use the helmet reply decorator to conditionally apply helmet middlewares'
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
### `helmet` route option
|
||||
|
||||
`@fastify/helmet` allows enabling, disabling, and customizing `helmet` for each route using the `helmet` shorthand option
|
||||
when registering routes.
|
||||
|
||||
To disable `helmet` for a specific endpoint, pass `{ helmet: false }` to the route options.
|
||||
|
||||
To enable or customize `helmet` for a specific endpoint, pass a configuration object to route options, e.g., `{ helmet: { frameguard: false } }`.
|
||||
|
||||
#### Example - `@fastify/helmet` configuration using the `helmet` shorthand route option
|
||||
|
||||
```js
|
||||
// register the package with the `{ global: true }` option
|
||||
fastify.register(helmet, { global: true })
|
||||
|
||||
fastify.get('/route-with-disabled-helmet', { helmet: false }, async (request, reply) => {
|
||||
return { message: 'helmet is not enabled here' }
|
||||
})
|
||||
|
||||
fastify.get('/route-with-enabled-helmet', async (request, reply) => {
|
||||
return { message: 'helmet is enabled by default here' }
|
||||
})
|
||||
|
||||
fastify.get('/route-with-custom-helmet-configuration', {
|
||||
// We change the helmet configuration for this route only
|
||||
helmet: {
|
||||
enableCSPNonces: true,
|
||||
contentSecurityPolicy: {
|
||||
directives: {
|
||||
'directive-1': ['foo', 'bar']
|
||||
},
|
||||
reportOnly: true
|
||||
},
|
||||
dnsPrefetchControl: {
|
||||
allow: true
|
||||
},
|
||||
frameguard: {
|
||||
action: 'foo'
|
||||
},
|
||||
hsts: {
|
||||
maxAge: 1,
|
||||
includeSubDomains: true,
|
||||
preload: true
|
||||
},
|
||||
permittedCrossDomainPolicies: {
|
||||
permittedPolicies: 'foo'
|
||||
},
|
||||
referrerPolicy: false
|
||||
}
|
||||
}, async (request, reply) => {
|
||||
return { message: 'helmet is enabled with a custom configuration on this route' }
|
||||
})
|
||||
```
|
||||
|
||||
### Content-Security-Policy Nonce
|
||||
|
||||
`@fastify/helmet` also allows CSP nonce generation, which can be enabled by passing `{ enableCSPNonces: true }` into the options.
|
||||
Retrieve the `nonces` through `reply.cspNonce`.
|
||||
|
||||
> ℹ️ Note: This feature is implemented by this module and is not supported by `helmet`.
|
||||
> For using `helmet` only for csp nonces, see [example](#example---generate-by-helmet).
|
||||
|
||||
#### Example - Generate by options
|
||||
|
||||
```js
|
||||
fastify.register(
|
||||
helmet,
|
||||
// enable csp nonces generation with default content-security-policy option
|
||||
{ enableCSPNonces: true }
|
||||
)
|
||||
|
||||
fastify.register(
|
||||
helmet,
|
||||
// customize content security policy with nonce generation
|
||||
{
|
||||
enableCSPNonces: true,
|
||||
contentSecurityPolicy: {
|
||||
directives: {
|
||||
...
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
fastify.get('/', function(request, reply) {
|
||||
// retrieve script nonce
|
||||
reply.cspNonce.script
|
||||
// retrieve style nonce
|
||||
reply.cspNonce.style
|
||||
})
|
||||
```
|
||||
|
||||
#### Example - Generate by helmet
|
||||
|
||||
```js
|
||||
fastify.register(
|
||||
helmet,
|
||||
{
|
||||
contentSecurityPolicy: {
|
||||
directives: {
|
||||
defaultSrc: ["'self'"],
|
||||
scriptSrc: [
|
||||
function (req, res) {
|
||||
// "res" here is actually "reply.raw" in fastify
|
||||
res.scriptNonce = crypto.randomBytes(16).toString('hex')
|
||||
// make sure to return nonce-... directive to helmet, so it can be sent in the headers
|
||||
return `'nonce-${res.scriptNonce}'`
|
||||
}
|
||||
],
|
||||
styleSrc: [
|
||||
function (req, res) {
|
||||
// "res" here is actually "reply.raw" in fastify
|
||||
res.styleNonce = crypto.randomBytes(16).toString('hex')
|
||||
// make sure to return nonce-... directive to helmet, so it can be sent in the headers
|
||||
return `'nonce-${res.styleNonce}'`
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
fastify.get('/', function(request, reply) {
|
||||
// access the generated nonce by "reply.raw"
|
||||
reply.raw.scriptNonce
|
||||
reply.raw.styleNonce
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
### Disable Default `helmet` Directives
|
||||
|
||||
By default, `helmet` adds [a default set of CSP directives](https://github.com/helmetjs/helmet/tree/main/middlewares/content-security-policy#content-security-policy-middleware) to the response.
|
||||
Disable this by setting `useDefaults: false` in the `contentSecurityPolicy` configuration.
|
||||
|
||||
```js
|
||||
fastify.register(
|
||||
helmet,
|
||||
{
|
||||
contentSecurityPolicy: {
|
||||
useDefaults: false,
|
||||
directives: {
|
||||
'default-src': ["'self'"]
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
Licensed under [MIT](./LICENSE).
|
||||
6
backend/node_modules/@fastify/helmet/eslint.config.js
generated
vendored
Normal file
6
backend/node_modules/@fastify/helmet/eslint.config.js
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = require('neostandard')({
|
||||
ignores: require('neostandard').resolveIgnoresFromGitignore(),
|
||||
ts: true
|
||||
})
|
||||
46
backend/node_modules/@fastify/helmet/examples/example.js
generated
vendored
Normal file
46
backend/node_modules/@fastify/helmet/examples/example.js
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
'use strict'
|
||||
|
||||
const Fastify = require('fastify')
|
||||
const helmet = require('..')
|
||||
|
||||
const fastify = Fastify({
|
||||
logger: {
|
||||
level: 'info'
|
||||
}
|
||||
})
|
||||
|
||||
fastify.register(helmet)
|
||||
|
||||
const opts = {
|
||||
schema: {
|
||||
response: {
|
||||
200: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
hello: {
|
||||
type: 'string'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fastify.get('/', opts, function (_request, reply) {
|
||||
reply
|
||||
.header('Content-Type', 'application/json')
|
||||
.code(200)
|
||||
.send({ hello: 'world' })
|
||||
})
|
||||
|
||||
fastify.get('/route-with-disabled-helmet', { ...opts, helmet: false }, function (_request, reply) {
|
||||
reply
|
||||
.header('Content-Type', 'application/json')
|
||||
.code(200)
|
||||
.send({ hello: 'world' })
|
||||
})
|
||||
|
||||
fastify.listen({ port: 3000 }, err => {
|
||||
if (err) throw err
|
||||
fastify.log.info(`Server listening on ${fastify.server.address().address}:${fastify.server.address().port}`)
|
||||
})
|
||||
151
backend/node_modules/@fastify/helmet/index.js
generated
vendored
Normal file
151
backend/node_modules/@fastify/helmet/index.js
generated
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
'use strict'
|
||||
|
||||
const { randomBytes } = require('node:crypto')
|
||||
const fp = require('fastify-plugin')
|
||||
const helmet = require('helmet')
|
||||
|
||||
async function fastifyHelmet (fastify, options) {
|
||||
// helmet will throw when any option is explicitly set to "true"
|
||||
// using ECMAScript destructuring is a clean workaround as we do not need to alter options
|
||||
const { enableCSPNonces, global, ...globalConfiguration } = options
|
||||
|
||||
const isGlobal = typeof global === 'boolean' ? global : true
|
||||
|
||||
// We initialize the `helmet` reply decorator only if it does not already exists
|
||||
if (!fastify.hasReplyDecorator('helmet')) {
|
||||
fastify.decorateReply('helmet', null)
|
||||
}
|
||||
|
||||
// We initialize the `cspNonce` reply decorator only if it does not already exists
|
||||
if (!fastify.hasReplyDecorator('cspNonce')) {
|
||||
fastify.decorateReply('cspNonce', null)
|
||||
}
|
||||
|
||||
fastify.addHook('onRoute', (routeOptions) => {
|
||||
if (routeOptions.helmet !== undefined) {
|
||||
if (typeof routeOptions.helmet === 'object') {
|
||||
routeOptions.config = Object.assign(routeOptions.config || Object.create(null), { helmet: routeOptions.helmet })
|
||||
} else if (routeOptions.helmet === false) {
|
||||
routeOptions.config = Object.assign(routeOptions.config || Object.create(null), { helmet: { skipRoute: true } })
|
||||
} else {
|
||||
throw new Error('Unknown value for route helmet configuration')
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
fastify.addHook('onRequest', async function helmetConfigureReply (request, reply) {
|
||||
const { helmet: routeOptions } = request.routeOptions?.config
|
||||
|
||||
if (routeOptions !== undefined) {
|
||||
const { enableCSPNonces: enableRouteCSPNonces, skipRoute, ...helmetRouteConfiguration } = routeOptions
|
||||
// If route helmet options are set they overwrite the global helmet configuration
|
||||
const mergedHelmetConfiguration = Object.assign(Object.create(null), globalConfiguration, helmetRouteConfiguration)
|
||||
|
||||
// We decorate the reply with a fallback to the route scoped helmet options
|
||||
return replyDecorators(request, reply, mergedHelmetConfiguration, enableRouteCSPNonces)
|
||||
}
|
||||
|
||||
// We decorate the reply with a fallback to the global helmet options
|
||||
return replyDecorators(request, reply, globalConfiguration, enableCSPNonces)
|
||||
})
|
||||
|
||||
fastify.addHook('onRequest', function helmetApplyHeaders (request, reply, next) {
|
||||
const { helmet: routeOptions } = request.routeOptions?.config
|
||||
|
||||
if (routeOptions !== undefined) {
|
||||
const { enableCSPNonces: enableRouteCSPNonces, skipRoute, ...helmetRouteConfiguration } = routeOptions
|
||||
|
||||
if (skipRoute === true) {
|
||||
// If helmet route option is set to `false` we skip the route
|
||||
} else {
|
||||
// If route helmet options are set they overwrite the global helmet configuration
|
||||
const mergedHelmetConfiguration = Object.assign(Object.create(null), globalConfiguration, helmetRouteConfiguration)
|
||||
|
||||
return buildHelmetOnRoutes(request, reply, mergedHelmetConfiguration, enableRouteCSPNonces)
|
||||
}
|
||||
|
||||
return next()
|
||||
}
|
||||
if (isGlobal) {
|
||||
// if the plugin is set globally (meaning that all the routes will be decorated)
|
||||
// As the endpoint, does not have a custom helmet configuration, use the global one.
|
||||
return buildHelmetOnRoutes(request, reply, globalConfiguration, enableCSPNonces)
|
||||
}
|
||||
|
||||
// if the plugin is not global we can skip the route
|
||||
return next()
|
||||
})
|
||||
}
|
||||
|
||||
async function replyDecorators (request, reply, configuration, enableCSP) {
|
||||
if (enableCSP) {
|
||||
reply.cspNonce = {
|
||||
script: randomBytes(16).toString('hex'),
|
||||
style: randomBytes(16).toString('hex')
|
||||
}
|
||||
}
|
||||
|
||||
reply.helmet = function (opts) {
|
||||
const helmetConfiguration = opts
|
||||
? Object.assign(Object.create(null), configuration, opts)
|
||||
: configuration
|
||||
|
||||
return helmet(helmetConfiguration)(request.raw, reply.raw, done)
|
||||
}
|
||||
}
|
||||
|
||||
async function buildHelmetOnRoutes (request, reply, configuration, enableCSP) {
|
||||
if (enableCSP === true && configuration.contentSecurityPolicy !== false) {
|
||||
const cspDirectives = configuration.contentSecurityPolicy
|
||||
? configuration.contentSecurityPolicy.directives
|
||||
: helmet.contentSecurityPolicy.getDefaultDirectives()
|
||||
const cspReportOnly = configuration.contentSecurityPolicy
|
||||
? configuration.contentSecurityPolicy.reportOnly
|
||||
: undefined
|
||||
const cspUseDefaults = configuration.contentSecurityPolicy
|
||||
? configuration.contentSecurityPolicy.useDefaults
|
||||
: undefined
|
||||
|
||||
// We get the csp nonce from the reply
|
||||
const { script: scriptCSPNonce, style: styleCSPNonce } = reply.cspNonce
|
||||
|
||||
// We prevent object reference: https://github.com/fastify/fastify-helmet/issues/118
|
||||
const directives = { ...cspDirectives }
|
||||
|
||||
// We push nonce to csp
|
||||
// We allow both 'script-src' or 'scriptSrc' syntax
|
||||
const scriptKey = Array.isArray(directives['script-src']) ? 'script-src' : 'scriptSrc'
|
||||
directives[scriptKey] = Array.isArray(directives[scriptKey]) ? [...directives[scriptKey]] : []
|
||||
directives[scriptKey].push(`'nonce-${scriptCSPNonce}'`)
|
||||
// allow both style-src or styleSrc syntax
|
||||
const styleKey = Array.isArray(directives['style-src']) ? 'style-src' : 'styleSrc'
|
||||
directives[styleKey] = Array.isArray(directives[styleKey]) ? [...directives[styleKey]] : []
|
||||
directives[styleKey].push(`'nonce-${styleCSPNonce}'`)
|
||||
|
||||
const contentSecurityPolicy = { directives, reportOnly: cspReportOnly, useDefaults: cspUseDefaults }
|
||||
const mergedHelmetConfiguration = Object.assign(Object.create(null), configuration, { contentSecurityPolicy })
|
||||
|
||||
helmet(mergedHelmetConfiguration)(request.raw, reply.raw, done)
|
||||
} else {
|
||||
helmet(configuration)(request.raw, reply.raw, done)
|
||||
}
|
||||
}
|
||||
|
||||
function done (error) {
|
||||
// Helmet used to forward an Error object, so we could just rethrow it.
|
||||
// Since Helmet v8.1.0 (see PR https://github.com/helmetjs/helmet/pull/485 ),
|
||||
// errors are thrown directly instead of being passed to a callback.
|
||||
// We keep the argument for compatibility, as v8.1.0 still accepts it
|
||||
// (see https://github.com/helmetjs/helmet/blob/v8.1.0/index.ts#L109 ).
|
||||
/* c8 ignore next */
|
||||
if (error) throw error
|
||||
}
|
||||
|
||||
module.exports = fp(fastifyHelmet, {
|
||||
fastify: '5.x',
|
||||
name: '@fastify/helmet'
|
||||
})
|
||||
module.exports.default = fastifyHelmet
|
||||
module.exports.fastifyHelmet = fastifyHelmet
|
||||
|
||||
module.exports.contentSecurityPolicy = helmet.contentSecurityPolicy
|
||||
2
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/.gitattributes
generated
vendored
Normal file
2
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/.gitattributes
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Set default behavior to automatically convert line endings
|
||||
* text=auto eol=lf
|
||||
13
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/.github/dependabot.yml
generated
vendored
Normal file
13
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/.github/dependabot.yml
generated
vendored
Normal 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
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/.github/stale.yml
generated
vendored
Normal file
21
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/.github/stale.yml
generated
vendored
Normal 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
|
||||
28
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/.github/workflows/ci.yml
generated
vendored
Normal file
28
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/.github/workflows/ci.yml
generated
vendored
Normal 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
|
||||
23
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/LICENSE
generated
vendored
Normal file
23
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/LICENSE
generated
vendored
Normal 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.
|
||||
188
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/README.md
generated
vendored
Normal file
188
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/README.md
generated
vendored
Normal file
@@ -0,0 +1,188 @@
|
||||
# fastify-plugin
|
||||
|
||||
[](https://github.com/fastify/fastify-plugin/actions/workflows/ci.yml)
|
||||
[](https://www.npmjs.com/package/fastify-plugin)
|
||||
[](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).
|
||||
6
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/eslint.config.js
generated
vendored
Normal file
6
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/eslint.config.js
generated
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
'use strict'
|
||||
|
||||
module.exports = require('neostandard')({
|
||||
ignores: require('neostandard').resolveIgnoresFromGitignore(),
|
||||
ts: true
|
||||
})
|
||||
25
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/lib/getPluginName.js
generated
vendored
Normal file
25
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/lib/getPluginName.js
generated
vendored
Normal 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
|
||||
10
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/lib/toCamelCase.js
generated
vendored
Normal file
10
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/lib/toCamelCase.js
generated
vendored
Normal 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()
|
||||
})
|
||||
}
|
||||
70
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/package.json
generated
vendored
Normal file
70
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/package.json
generated
vendored
Normal 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"
|
||||
}
|
||||
}
|
||||
67
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/plugin.js
generated
vendored
Normal file
67
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/plugin.js
generated
vendored
Normal 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
|
||||
110
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/test/bundlers.test.js
generated
vendored
Normal file
110
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/test/bundlers.test.js
generated
vendored
Normal 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)
|
||||
})
|
||||
67
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/test/checkVersion.test.js
generated
vendored
Normal file
67
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/test/checkVersion.test.js
generated
vendored
Normal 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'
|
||||
})
|
||||
})
|
||||
14
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/test/composite.test.js
generated
vendored
Normal file
14
backend/node_modules/@fastify/helmet/node_modules/fastify-plugin/test/composite.test.js
generated
vendored
Normal 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')
|
||||
})
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user