| 'use strict' | ||
| const { test } = require('node:test') | ||
| const Fastify = require('..') | ||
| test('reply.mediaType should match the content-type header', async (t) => { | ||
| t.plan(2) | ||
| const fastify = Fastify() | ||
| fastify.get('/', (request, reply) => { | ||
| reply.type('application/json') | ||
| t.assert.strictEqual(reply.mediaType, 'application/json') | ||
| reply.send({ mediaType: reply.mediaType }) | ||
| }) | ||
| const response = await fastify.inject({ | ||
| method: 'GET', | ||
| url: '/' | ||
| }) | ||
| const body = await response.json() | ||
| t.assert.strictEqual(body.mediaType, 'application/json') | ||
| }) | ||
| test('reply.mediaType should strip the charset parameter', async (t) => { | ||
| t.plan(2) | ||
| const fastify = Fastify() | ||
| fastify.get('/', (request, reply) => { | ||
| reply.header('content-type', 'application/json; charset=utf-8') | ||
| t.assert.strictEqual(reply.mediaType, 'application/json') | ||
| reply.send({ mediaType: reply.mediaType }) | ||
| }) | ||
| const response = await fastify.inject({ | ||
| method: 'GET', | ||
| url: '/' | ||
| }) | ||
| const body = await response.json() | ||
| t.assert.strictEqual(body.mediaType, 'application/json') | ||
| }) | ||
| test('reply.mediaType should strip the space', async (t) => { | ||
| t.plan(2) | ||
| const fastify = Fastify() | ||
| fastify.get('/', (request, reply) => { | ||
| reply.header('content-type', ' application/json ; charset=utf-8') | ||
| t.assert.strictEqual(reply.mediaType, 'application/json') | ||
| reply.send({ mediaType: reply.mediaType }) | ||
| }) | ||
| const response = await fastify.inject({ | ||
| method: 'GET', | ||
| url: '/' | ||
| }) | ||
| const body = await response.json() | ||
| t.assert.strictEqual(body.mediaType, 'application/json') | ||
| }) | ||
| test('reply.mediaType is undefined when content-type is not set', async (t) => { | ||
| t.plan(2) | ||
| const fastify = Fastify() | ||
| fastify.get('/', (request, reply) => { | ||
| t.assert.strictEqual(reply.mediaType, undefined) | ||
| reply.send({ mediaType: reply.mediaType }) | ||
| }) | ||
| const response = await fastify.inject({ | ||
| method: 'GET', | ||
| url: '/' | ||
| }) | ||
| const body = await response.json() | ||
| t.assert.strictEqual(body.mediaType, undefined) | ||
| }) | ||
| test('reply.mediaType supported in hooks', async (t) => { | ||
| t.plan(5) | ||
| const fastify = Fastify() | ||
| fastify.get('/', { | ||
| preHandler: (request, reply, done) => { | ||
| reply.type('application/json') | ||
| t.assert.strictEqual(reply.mediaType, 'application/json') | ||
| done() | ||
| }, | ||
| preSerialization: (request, reply, payload, done) => { | ||
| t.assert.strictEqual(reply.mediaType, 'application/json') | ||
| done(null, payload) | ||
| }, | ||
| onSend: (request, reply, payload, done) => { | ||
| t.assert.strictEqual(reply.mediaType, 'application/json') | ||
| done(null, payload) | ||
| } | ||
| }, (request, reply) => { | ||
| t.assert.strictEqual(reply.mediaType, 'application/json') | ||
| reply.send({ mediaType: reply.mediaType }) | ||
| }) | ||
| const response = await fastify.inject({ | ||
| method: 'GET', | ||
| url: '/' | ||
| }) | ||
| const body = await response.json() | ||
| t.assert.strictEqual(body.mediaType, 'application/json') | ||
| }) | ||
| test('reply.mediaType should reflect the last type set', async (t) => { | ||
| t.plan(2) | ||
| const fastify = Fastify() | ||
| fastify.get('/', (request, reply) => { | ||
| reply.type('application/json') | ||
| reply.type('text/plain') | ||
| t.assert.strictEqual(reply.mediaType, 'text/plain') | ||
| reply.send(`mediaType = ${reply.mediaType}`) | ||
| }) | ||
| const response = await fastify.inject({ | ||
| method: 'GET', | ||
| url: '/' | ||
| }) | ||
| const body = response.body | ||
| t.assert.strictEqual(body, 'mediaType = text/plain') | ||
| }) |
@@ -259,5 +259,37 @@ <h1 align="center">Serverless</h1> | ||
| #### Per-route logging | ||
| Google Cloud Functions exposes one entry point for your Fastify instance, so | ||
| the dashboard may show all HTTP traffic under that function. Instead of | ||
| deploying one function per endpoint, keep the single Fastify instance and emit | ||
| a structured log line after each request with the matched route: | ||
| ```js | ||
| fastify.addHook('onResponse', async (request, reply) => { | ||
| request.log.info({ | ||
| route: request.routeOptions.url, | ||
| method: request.method, | ||
| statusCode: reply.statusCode, | ||
| responseTime: reply.elapsedTime | ||
| }, 'request completed') | ||
| }) | ||
| ``` | ||
| `request.routeOptions.url` is the route pattern, so `/users/123` and | ||
| `/users/456` are grouped under `/users/:id`. With Fastify's logger enabled, the | ||
| fields are emitted as structured log fields, as described in the [Cloud | ||
| Logging structured logging documentation](https://cloud.google.com/logging/docs/structured-logging) | ||
| and the [Cloud Functions logging guide](https://cloud.google.com/functions/docs/monitoring/logging). | ||
| In Cloud Logging, create a [log-based | ||
| metric](https://cloud.google.com/logging/docs/logs-based-metrics) or filter on | ||
| `jsonPayload.route` to get per-route request counts, latency, and error rates. | ||
| The same hook works when deploying through Firebase Functions with `onRequest`. | ||
| ### References | ||
| - [Google Cloud Functions - Node.js Quickstart | ||
| ](https://docs.cloud.google.com/run/docs/quickstarts/functions/deploy-functions-gcloud) | ||
| ](https://cloud.google.com/run/docs/quickstarts/functions/deploy-functions-gcloud) | ||
| - [Cloud Logging - Structured Logging](https://cloud.google.com/logging/docs/structured-logging) | ||
| - [Cloud Logging - Log-based Metrics](https://cloud.google.com/logging/docs/logs-based-metrics) | ||
| - [Cloud Functions - Logging](https://cloud.google.com/functions/docs/monitoring/logging) | ||
@@ -598,2 +630,2 @@ ## Google Firebase Functions | ||
| [here]( | ||
| https://vercel.com/docs/fluid-compute#enabling-fluid-compute). | ||
| https://vercel.com/docs/fluid-compute#enabling-fluid-compute). |
@@ -9,2 +9,3 @@ <h1 align="center">Fastify</h1> | ||
| - [.statusCode](#statuscode) | ||
| - [.mediaType](#mediatype) | ||
| - [.server](#server) | ||
@@ -55,2 +56,3 @@ - [.header(key, value)](#headerkey-value) | ||
| since the request was received by Fastify. | ||
| - `.mediaType` - The media type extracted from `Content-Type` header. | ||
| - `.server` - A reference to the fastify instance object. | ||
@@ -134,3 +136,15 @@ - `.header(name, value)` - Sets a response header. | ||
| ``` | ||
| ### .mediaType | ||
| <a id="mediatype"></a> | ||
| Returns the media type extracted from `Content-Type` header. When `Content-Type` | ||
| header is missing, it will return `undefined`. | ||
| ```js | ||
| if (reply.mediaType === 'image/gif') { | ||
| const versionBuffer = new Uint8Array(reply.payload, 3, 3) | ||
| reply.header('x-gif-version', new TextDecoder().decode(versionBuffer)) | ||
| } | ||
| ``` | ||
| ### .server | ||
@@ -137,0 +151,0 @@ <a id="server"></a> |
+1
-1
| 'use strict' | ||
| const VERSION = '5.11.3' | ||
| const VERSION = '5.12.0' | ||
@@ -5,0 +5,0 @@ const Avvio = require('avvio') |
@@ -186,3 +186,6 @@ 'use strict' | ||
| get mediaType () { return `${this.#type}/${this.#subtype}` } | ||
| get mediaType () { | ||
| if (this.#valid === false) return undefined | ||
| return `${this.#type}/${this.#subtype}` | ||
| } | ||
@@ -189,0 +192,0 @@ get type () { return this.#type } |
+11
-1
@@ -96,2 +96,8 @@ 'use strict' | ||
| }, | ||
| mediaType: { | ||
| get () { | ||
| const contentTypeHeader = this[kReplyHeaders]['content-type'] | ||
| return ContentType.from(contentTypeHeader).mediaType | ||
| } | ||
| }, | ||
| server: { | ||
@@ -258,5 +264,9 @@ get () { | ||
| Reply.prototype.removeHeader = function (key) { | ||
| key = key.toLowerCase() | ||
| // Node.js does not like headers with keys set to undefined, | ||
| // so we have to delete the key. | ||
| delete this[kReplyHeaders][key.toLowerCase()] | ||
| delete this[kReplyHeaders][key] | ||
| if (!this.raw.headersSent) { | ||
| this.raw.removeHeader(key) | ||
| } | ||
| return this | ||
@@ -263,0 +273,0 @@ } |
+2
-3
| { | ||
| "name": "fastify", | ||
| "version": "5.11.3", | ||
| "version": "5.12.0", | ||
| "description": "Fast and low overhead web framework, for Node.js", | ||
@@ -170,3 +170,2 @@ "main": "fastify.js", | ||
| "@jsumners/line-reporter": "^1.0.1", | ||
| "@sinonjs/fake-timers": "^11.2.2", | ||
| "@stylistic/eslint-plugin": "^5.1.0", | ||
@@ -218,3 +217,3 @@ "@stylistic/eslint-plugin-js": "^4.1.0", | ||
| "pino": "^9.14.0 || ^10.1.0", | ||
| "process-warning": "^5.0.0", | ||
| "process-warning": "^5.1.0", | ||
| "rfdc": "^1.3.1", | ||
@@ -221,0 +220,0 @@ "secure-json-parse": "^4.0.0", |
+1
-0
@@ -22,2 +22,3 @@ # Sponsors | ||
| - [N-iX](https://www.n-ix.com/) | ||
| - [TestMu AI](https://www.testmuai.com/?utm_medium=sponsor&utm_source=fastify) | ||
@@ -24,0 +25,0 @@ ## Tier 2 |
| 'use strict' | ||
| const { test } = require('node:test') | ||
| const { spyWarning } = require('process-warning') | ||
| const Fastify = require('..') | ||
| const keys = require('../lib/symbols') | ||
| const { FST_ERR_CTP_ALREADY_PRESENT, FST_ERR_CTP_INVALID_TYPE, FST_ERR_CTP_INVALID_MEDIA_TYPE } = require('../lib/errors') | ||
| const { FSTSEC001 } = require('../lib/warnings') | ||
@@ -491,14 +493,9 @@ const first = function (req, payload, done) {} | ||
| test('Warning against improper content-type - regexp', async t => { | ||
| await t.test('improper regex - text plain', (t, done) => { | ||
| await t.test('improper regex - text plain', async (t) => { | ||
| t.plan(2) | ||
| const spyData = spyWarning(FSTSEC001) | ||
| t.after(spyData.restore) | ||
| const fastify = Fastify() | ||
| process.on('warning', onWarning) | ||
| function onWarning (warning) { | ||
| t.assert.strictEqual(warning.name, 'FastifySecurity') | ||
| t.assert.strictEqual(warning.code, 'FSTSEC001') | ||
| done() | ||
| } | ||
| t.after(() => process.removeListener('warning', onWarning)) | ||
| fastify.removeAllContentTypeParsers() | ||
@@ -508,16 +505,14 @@ fastify.addContentTypeParser(/text\/plain/, function (request, body, done) { | ||
| }) | ||
| await fastify.ready() | ||
| t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['text\\/plain'], result: true }]) | ||
| t.assert.strictEqual(spyData.callCount(), 1) | ||
| }) | ||
| await t.test('improper regex - application json', (t, done) => { | ||
| await t.test('improper regex - application json', async (t) => { | ||
| t.plan(2) | ||
| const spyData = spyWarning(FSTSEC001) | ||
| t.after(spyData.restore) | ||
| const fastify = Fastify() | ||
| process.on('warning', onWarning) | ||
| function onWarning (warning) { | ||
| t.assert.strictEqual(warning.name, 'FastifySecurity') | ||
| t.assert.strictEqual(warning.code, 'FSTSEC001') | ||
| done() | ||
| } | ||
| t.after(() => process.removeListener('warning', onWarning)) | ||
| fastify.removeAllContentTypeParsers() | ||
@@ -528,2 +523,5 @@ | ||
| }) | ||
| t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['application\\/json'], result: true }]) | ||
| t.assert.deepEqual(spyData.callCount(), 1) | ||
| }) | ||
@@ -530,0 +528,0 @@ }) |
@@ -49,8 +49,11 @@ 'use strict' | ||
| t.assert.equal(found.isEmpty, true) | ||
| t.assert.equal(found.mediaType, undefined) | ||
| found = new ContentType('undefined') | ||
| t.assert.equal(found.isEmpty, true) | ||
| t.assert.equal(found.mediaType, undefined) | ||
| found = new ContentType() | ||
| t.assert.equal(found.isEmpty, true) | ||
| t.assert.equal(found.mediaType, undefined) | ||
| }) | ||
@@ -62,2 +65,3 @@ | ||
| t.assert.equal(found.isValid, false) | ||
| t.assert.equal(found.mediaType, undefined) | ||
@@ -64,0 +68,0 @@ found = new ContentType('foo /bar') |
+73
-1
@@ -7,3 +7,2 @@ 'use strict' | ||
| const symbols = require('../lib/symbols') | ||
| const { waitForCb } = require('./toolkit') | ||
| const assert = require('node:assert') | ||
@@ -13,2 +12,34 @@ | ||
| function waitForCb (options) { | ||
| let count = null | ||
| let done = false | ||
| let iResolve | ||
| let iReject | ||
| function stepIn () { | ||
| if (done) { | ||
| iReject(new Error('Unexpected done call')) | ||
| return | ||
| } | ||
| if (--count) { | ||
| return | ||
| } | ||
| done = true | ||
| iResolve() | ||
| } | ||
| const patience = new Promise((resolve, reject) => { | ||
| iResolve = resolve | ||
| iReject = reject | ||
| }) | ||
| count = options.steps || 1 | ||
| done = false | ||
| return { stepIn, patience } | ||
| } | ||
| module.exports.waitForCb = waitForCb | ||
| /** | ||
@@ -499,1 +530,42 @@ * @param method HTTP request method | ||
| } | ||
| module.exports.partialDeepStrictEqual = function partialDeepStrictEqual (actual, expected) { | ||
| if (typeof expected !== 'object' || expected === null) { | ||
| return actual === expected | ||
| } | ||
| if (typeof actual !== 'object' || actual === null) { | ||
| return false | ||
| } | ||
| if (Array.isArray(expected)) { | ||
| if (!Array.isArray(actual)) return false | ||
| if (expected.length > actual.length) return false | ||
| for (let i = 0; i < expected.length; i++) { | ||
| if (!partialDeepStrictEqual(actual[i], expected[i])) { | ||
| return false | ||
| } | ||
| } | ||
| return true | ||
| } | ||
| for (const key of Object.keys(expected)) { | ||
| if (!(key in actual)) return false | ||
| if (!partialDeepStrictEqual(actual[key], expected[key])) { | ||
| return false | ||
| } | ||
| } | ||
| return true | ||
| } | ||
| module.exports.assertNoWarning = function (t) { | ||
| function doNotWarn () { | ||
| t.assert.fail('no warning') | ||
| } | ||
| process.on('warning', doNotWarn) | ||
| t.after(() => { | ||
| process.off('warning', doNotWarn) | ||
| }) | ||
| } |
@@ -7,4 +7,3 @@ 'use strict' | ||
| const fs = require('node:fs') | ||
| const { sleep } = require('./helper') | ||
| const { waitForCb } = require('./toolkit') | ||
| const { sleep, waitForCb } = require('./helper') | ||
@@ -11,0 +10,0 @@ process.removeAllListeners('warning') |
@@ -37,3 +37,3 @@ 'use strict' | ||
| test('Once called, Reply should return an object with methods', t => { | ||
| t.plan(15) | ||
| t.plan(16) | ||
| const response = { res: 'res' } | ||
@@ -53,2 +53,3 @@ const context = { | ||
| t.assert.strictEqual(typeof reply.code, 'function') | ||
| t.assert.strictEqual(typeof reply.mediaType, 'string') | ||
| t.assert.strictEqual(typeof reply.status, 'function') | ||
@@ -1119,2 +1120,76 @@ t.assert.strictEqual(typeof reply.header, 'function') | ||
| test('reply.removeHeader removes raw headers', async t => { | ||
| t.plan(9) | ||
| const fastify = require('../../')() | ||
| t.after(() => fastify.close()) | ||
| fastify.get('/headers', function (req, reply) { | ||
| reply.raw.setHeader('X-Foo', 'raw') | ||
| t.assert.strictEqual(reply.getHeader('x-foo'), 'raw') | ||
| t.assert.strictEqual(reply.getHeaders()['x-foo'], 'raw') | ||
| t.assert.strictEqual(reply.hasHeader('x-foo'), true) | ||
| t.assert.strictEqual(reply.removeHeader('x-FoO'), reply) | ||
| t.assert.strictEqual(reply.getHeader('x-foo'), undefined) | ||
| t.assert.strictEqual(Object.hasOwn(reply.getHeaders(), 'x-foo'), false) | ||
| t.assert.strictEqual(reply.hasHeader('x-foo'), false) | ||
| t.assert.strictEqual(reply.removeHeader('X-FOO'), reply) | ||
| reply.send() | ||
| }) | ||
| const fastifyServer = await fastify.listen({ port: 0 }) | ||
| const response = await fetch(`${fastifyServer}/headers`) | ||
| t.assert.strictEqual(response.headers.get('x-foo'), null) | ||
| }) | ||
| test('reply.removeHeader removes layered headers', async t => { | ||
| t.plan(7) | ||
| const fastify = require('../../')() | ||
| t.after(() => fastify.close()) | ||
| fastify.get('/headers', function (req, reply) { | ||
| reply.raw.setHeader('x-foo', 'raw') | ||
| reply.header('x-foo', 'fastify') | ||
| t.assert.strictEqual(reply.getHeader('x-foo'), 'fastify') | ||
| t.assert.strictEqual(reply.getHeaders()['x-foo'], 'fastify') | ||
| t.assert.strictEqual(reply.removeHeader('x-foo'), reply) | ||
| t.assert.strictEqual(reply.getHeader('x-foo'), undefined) | ||
| t.assert.strictEqual(Object.hasOwn(reply.getHeaders(), 'x-foo'), false) | ||
| t.assert.strictEqual(reply.hasHeader('x-foo'), false) | ||
| reply.send() | ||
| }) | ||
| const fastifyServer = await fastify.listen({ port: 0 }) | ||
| const response = await fetch(`${fastifyServer}/headers`) | ||
| t.assert.strictEqual(response.headers.get('x-foo'), null) | ||
| }) | ||
| test('reply.removeHeader does not throw after headers are sent', async t => { | ||
| t.plan(3) | ||
| const fastify = require('../../')() | ||
| t.after(() => fastify.close()) | ||
| fastify.get('/headers', function (req, reply) { | ||
| reply.hijack() | ||
| reply.raw.setHeader('x-foo', 'raw') | ||
| reply.raw.flushHeaders() | ||
| t.assert.strictEqual(reply.raw.headersSent, true) | ||
| t.assert.doesNotThrow(() => reply.removeHeader('x-foo')) | ||
| reply.raw.end() | ||
| }) | ||
| const fastifyServer = await fastify.listen({ port: 0 }) | ||
| const response = await fetch(`${fastifyServer}/headers`) | ||
| t.assert.strictEqual(response.headers.get('x-foo'), 'raw') | ||
| }) | ||
| test('reply.header can reset the value', async t => { | ||
@@ -1121,0 +1196,0 @@ t.plan(1) |
+17
-52
@@ -7,2 +7,3 @@ 'use strict' | ||
| const helper = require('./helper') | ||
| const { assertNoWarning } = require('./helper') | ||
@@ -17,12 +18,6 @@ let localhost | ||
| test('listen works without arguments', async t => { | ||
| const doNotWarn = () => { | ||
| t.assert.fail('should not be deprecated') | ||
| } | ||
| process.on('warning', doNotWarn) | ||
| assertNoWarning(t) | ||
| const fastify = Fastify() | ||
| t.after(() => { | ||
| fastify.close() | ||
| process.removeListener('warning', doNotWarn) | ||
| }) | ||
| await fastify.listen() | ||
@@ -32,15 +27,10 @@ const address = fastify.server.address() | ||
| t.assert.ok(address.port > 0) | ||
| await fastify.close() | ||
| }) | ||
| test('Async/await listen with arguments', async t => { | ||
| const doNotWarn = () => { | ||
| t.assert.fail('should not be deprecated') | ||
| } | ||
| process.on('warning', doNotWarn) | ||
| assertNoWarning(t) | ||
| const fastify = Fastify() | ||
| t.after(() => { | ||
| fastify.close() | ||
| process.removeListener('warning', doNotWarn) | ||
| }) | ||
| const addr = await fastify.listen({ port: 0, host: '0.0.0.0' }) | ||
@@ -61,2 +51,3 @@ const address = fastify.server.address() | ||
| }) | ||
| await fastify.close() | ||
| }) | ||
@@ -66,16 +57,10 @@ | ||
| t.plan(2) | ||
| const doNotWarn = () => { | ||
| t.assert.fail('should not be deprecated') | ||
| } | ||
| process.on('warning', doNotWarn) | ||
| assertNoWarning(t) | ||
| const fastify = Fastify() | ||
| t.after(() => { | ||
| fastify.close() | ||
| process.removeListener('warning', doNotWarn) | ||
| }) | ||
| fastify.listen({ port: 0 }, (err) => { | ||
| t.assert.ifError(err) | ||
| t.assert.strictEqual(fastify.server.address().address, localhost) | ||
| done() | ||
| fastify.close(done) | ||
| }) | ||
@@ -86,12 +71,5 @@ }) | ||
| t.plan(1) | ||
| const doNotWarn = () => { | ||
| t.assert.fail('should not be deprecated') | ||
| } | ||
| process.on('warning', doNotWarn) | ||
| assertNoWarning(t) | ||
| const fastify = Fastify() | ||
| t.after(() => { | ||
| fastify.close() | ||
| process.removeListener('warning', doNotWarn) | ||
| }) | ||
| fastify.listen({ | ||
@@ -107,3 +85,3 @@ port: 0, | ||
| t.assert.ifError(err) | ||
| done() | ||
| fastify.close(done) | ||
| }) | ||
@@ -128,13 +106,5 @@ }) | ||
| test('listen works with undefined host', async t => { | ||
| const doNotWarn = () => { | ||
| t.assert.fail('should not be deprecated') | ||
| } | ||
| process.on('warning', doNotWarn) | ||
| assertNoWarning(t) | ||
| const fastify = Fastify() | ||
| t.after(() => fastify.close()) | ||
| t.after(() => { | ||
| fastify.close() | ||
| process.removeListener('warning', doNotWarn) | ||
| }) | ||
| await fastify.listen({ host: undefined, port: 0 }) | ||
@@ -144,16 +114,10 @@ const address = fastify.server.address() | ||
| t.assert.ok(address.port > 0) | ||
| await fastify.close() | ||
| }) | ||
| test('listen works with null host', async t => { | ||
| const doNotWarn = () => { | ||
| t.fail('should not be deprecated') | ||
| } | ||
| process.on('warning', doNotWarn) | ||
| assertNoWarning(t) | ||
| const fastify = Fastify() | ||
| t.after(() => fastify.close()) | ||
| t.after(() => { | ||
| fastify.close() | ||
| process.removeListener('warning', doNotWarn) | ||
| }) | ||
| await fastify.listen({ host: null, port: 0 }) | ||
@@ -163,2 +127,3 @@ const address = fastify.server.address() | ||
| t.assert.ok(address.port > 0) | ||
| await fastify.close() | ||
| }) |
@@ -5,4 +5,5 @@ 'use strict' | ||
| const net = require('node:net') | ||
| const { once } = require('node:events') | ||
| const { spyWarning } = require('process-warning') | ||
| const Fastify = require('../fastify') | ||
| const { once } = require('node:events') | ||
| const { FSTWRN003 } = require('../lib/warnings.js') | ||
@@ -106,8 +107,3 @@ | ||
| process.on('warning', onWarning) | ||
| function onWarning (warning) { | ||
| t.assert.strictEqual(warning.name, 'FastifyWarning') | ||
| t.assert.strictEqual(warning.code, FSTWRN003.code) | ||
| } | ||
| const spyData = spyWarning(FSTWRN003) | ||
| const fastify = Fastify() | ||
@@ -117,9 +113,10 @@ | ||
| await fastify.close() | ||
| process.removeListener('warning', onWarning) | ||
| FSTWRN003.emitted = false | ||
| spyData.restore() | ||
| }) | ||
| fastify.listen({ port: 0 }, async function doNotUseAsyncCallback () { | ||
| t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['listen method'], result: true }]) | ||
| t.assert.strictEqual(spyData.callCount(), 1) | ||
| done() | ||
| }) | ||
| }) |
@@ -17,3 +17,3 @@ 'use strict' | ||
| const { createTempFile, request } = require('./logger-test-utils') | ||
| const { partialDeepStrictEqual } = require('../toolkit') | ||
| const { partialDeepStrictEqual } = require('../helper') | ||
@@ -20,0 +20,0 @@ t.test('logger instantiation', { timeout: 60000 }, async (t) => { |
@@ -14,3 +14,3 @@ 'use strict' | ||
| const { request } = require('./logger-test-utils') | ||
| const { partialDeepStrictEqual } = require('../toolkit') | ||
| const { partialDeepStrictEqual } = require('../helper') | ||
@@ -17,0 +17,0 @@ t.test('logging', { timeout: 60000 }, async (t) => { |
@@ -12,3 +12,3 @@ 'use strict' | ||
| const { request } = require('./logger-test-utils') | ||
| const { partialDeepStrictEqual } = require('../toolkit') | ||
| const { partialDeepStrictEqual } = require('../helper') | ||
@@ -15,0 +15,0 @@ t.test('request', { timeout: 60000 }, async (t) => { |
@@ -10,3 +10,3 @@ 'use strict' | ||
| const Fastify = require('../../fastify') | ||
| const { partialDeepStrictEqual } = require('../toolkit') | ||
| const { partialDeepStrictEqual } = require('../helper') | ||
| const { on } = stream | ||
@@ -13,0 +13,0 @@ |
@@ -6,3 +6,2 @@ 'use strict' | ||
| const fp = require('fastify-plugin') | ||
| const fakeTimer = require('@sinonjs/fake-timers') | ||
| const { FST_ERR_PLUGIN_INVALID_ASYNC_HANDLER } = require('../lib/errors') | ||
@@ -50,3 +49,5 @@ | ||
| t.plan(5) | ||
| const clock = fakeTimer.install({ shouldClearNativeTimers: true }) | ||
| t.mock.timers.enable({ | ||
| apis: ['setTimeout', 'Date'] | ||
| }) | ||
@@ -56,3 +57,3 @@ const fastify = Fastify() | ||
| // default time elapsed without calling done | ||
| clock.tick(10000) | ||
| t.mock.timers.tick(10000) | ||
| }) | ||
@@ -70,3 +71,3 @@ | ||
| t.after(clock.uninstall) | ||
| t.after(() => t.mock.timers.reset()) | ||
| }) | ||
@@ -73,0 +74,0 @@ |
@@ -5,3 +5,3 @@ 'use strict' | ||
| const Fastify = require('..') | ||
| const { waitForCb } = require('./toolkit') | ||
| const { waitForCb } = require('./helper') | ||
@@ -8,0 +8,0 @@ test('Prefix options should add a prefix for all the routes inside a register / 1', (t, testDone) => { |
+4
-11
@@ -7,2 +7,3 @@ 'use strict' | ||
| const Fastify = require('..') | ||
| const { assertNoWarning } = require('./helper') | ||
@@ -285,2 +286,4 @@ test('Creates a HEAD route for a GET one with prefixTrailingSlash', async (t) => { | ||
| test('no warning for exposeHeadRoute', async t => { | ||
| assertNoWarning(t) | ||
| const fastify = Fastify() | ||
@@ -297,13 +300,3 @@ | ||
| const listener = (w) => { | ||
| t.assert.fail('no warning') | ||
| } | ||
| process.on('warning', listener) | ||
| await fastify.listen({ port: 0 }) | ||
| process.removeListener('warning', listener) | ||
| await fastify.close() | ||
| await fastify.ready() | ||
| }) |
| 'use strict' | ||
| const { test } = require('node:test') | ||
| const fp = require('fastify-plugin') | ||
| const { spyWarning } = require('process-warning') | ||
| const Fastify = require('..') | ||
| const fp = require('fastify-plugin') | ||
| const deepClone = require('rfdc')({ circles: true, proto: false }) | ||
@@ -10,3 +11,3 @@ const Ajv = require('ajv') | ||
| const { FSTWRN001 } = require('../lib/warnings') | ||
| const { waitForCb } = require('./toolkit') | ||
| const { waitForCb } = require('./helper') | ||
@@ -324,15 +325,7 @@ const echoParams = (req, reply) => { reply.send(req.params) } | ||
| t.plan(4) | ||
| const spyData = spyWarning(FSTWRN001) | ||
| t.after(spyData.restore) | ||
| const fastify = Fastify() | ||
| process.on('warning', onWarning) | ||
| function onWarning (warning) { | ||
| t.assert.strictEqual(warning.name, 'FastifyWarning') | ||
| t.assert.strictEqual(warning.code, FSTWRN001.code) | ||
| } | ||
| t.after(() => { | ||
| process.removeListener('warning', onWarning) | ||
| FSTWRN001.emitted = false | ||
| }) | ||
| fastify.post('/:id', { | ||
@@ -351,2 +344,4 @@ handler: echoParams, | ||
| t.assert.strictEqual(res.statusCode, 200) | ||
| t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['headers', 'POST', '/:id'], result: true }]) | ||
| t.assert.strictEqual(spyData.callCount(), 1) | ||
| testDone() | ||
@@ -358,15 +353,7 @@ }) | ||
| t.plan(4) | ||
| const spyData = spyWarning(FSTWRN001) | ||
| t.after(spyData.restore) | ||
| const fastify = Fastify() | ||
| process.on('warning', onWarning) | ||
| function onWarning (warning) { | ||
| t.assert.strictEqual(warning.name, 'FastifyWarning') | ||
| t.assert.strictEqual(warning.code, FSTWRN001.code) | ||
| } | ||
| t.after(() => { | ||
| process.removeListener('warning', onWarning) | ||
| FSTWRN001.emitted = false | ||
| }) | ||
| fastify.post('/:id', { | ||
@@ -385,2 +372,4 @@ handler: echoParams, | ||
| t.assert.strictEqual(res.statusCode, 200) | ||
| t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['body', 'POST', '/:id'], result: true }]) | ||
| t.assert.strictEqual(spyData.callCount(), 1) | ||
| testDone() | ||
@@ -392,15 +381,7 @@ }) | ||
| t.plan(4) | ||
| const spyData = spyWarning(FSTWRN001) | ||
| t.after(spyData.restore) | ||
| const fastify = Fastify() | ||
| process.on('warning', onWarning) | ||
| function onWarning (warning) { | ||
| t.assert.strictEqual(warning.name, 'FastifyWarning') | ||
| t.assert.strictEqual(warning.code, FSTWRN001.code) | ||
| } | ||
| t.after(() => { | ||
| process.removeListener('warning', onWarning) | ||
| FSTWRN001.emitted = false | ||
| }) | ||
| fastify.post('/:id', { | ||
@@ -419,2 +400,4 @@ handler: echoParams, | ||
| t.assert.strictEqual(res.statusCode, 200) | ||
| t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['querystring', 'POST', '/:id'], result: true }]) | ||
| t.assert.strictEqual(spyData.callCount(), 1) | ||
| testDone() | ||
@@ -426,15 +409,7 @@ }) | ||
| t.plan(4) | ||
| const spyData = spyWarning(FSTWRN001) | ||
| t.after(spyData.restore) | ||
| const fastify = Fastify() | ||
| process.on('warning', onWarning) | ||
| function onWarning (warning) { | ||
| t.assert.strictEqual(warning.name, 'FastifyWarning') | ||
| t.assert.strictEqual(warning.code, FSTWRN001.code) | ||
| } | ||
| t.after(() => { | ||
| process.removeListener('warning', onWarning) | ||
| FSTWRN001.emitted = false | ||
| }) | ||
| fastify.post('/:id', { | ||
@@ -453,2 +428,4 @@ handler: echoParams, | ||
| t.assert.strictEqual(res.statusCode, 200) | ||
| t.assert.deepStrictEqual(spyData.calls, [{ arguments: ['params', 'POST', '/:id'], result: true }]) | ||
| t.assert.strictEqual(spyData.callCount(), 1) | ||
| testDone() | ||
@@ -459,23 +436,8 @@ }) | ||
| test('Should emit a warning for every route with undefined schema', (t, testDone) => { | ||
| t.plan(16) | ||
| t.plan(9) | ||
| const spyData = spyWarning(FSTWRN001) | ||
| t.after(spyData.restore) | ||
| const fastify = Fastify() | ||
| let runs = 0 | ||
| const expectedWarningEmitted = [0, 1, 2, 3] | ||
| // It emits 4 warnings: | ||
| // - 2 - GET and HEAD for /undefinedParams/:id | ||
| // - 2 - GET and HEAD for /undefinedBody/:id | ||
| // => 3 x 4 assertions = 12 assertions | ||
| function onWarning (warning) { | ||
| t.assert.strictEqual(warning.name, 'FastifyWarning') | ||
| t.assert.strictEqual(warning.code, FSTWRN001.code) | ||
| t.assert.strictEqual(runs++, expectedWarningEmitted.shift()) | ||
| } | ||
| process.on('warning', onWarning) | ||
| t.after(() => { | ||
| process.removeListener('warning', onWarning) | ||
| FSTWRN001.emitted = false | ||
| }) | ||
| fastify.get('/undefinedParams/:id', { | ||
@@ -509,2 +471,11 @@ handler: echoParams, | ||
| t.assert.strictEqual(res.statusCode, 200) | ||
| // fastify.inject run in series | ||
| // last callback recieve all warnings at once | ||
| // GET /undefinedParams/123 | ||
| t.assert.deepStrictEqual(spyData.calls[0], { arguments: ['params', 'GET', '/undefinedParams/:id'], result: true }) | ||
| t.assert.deepStrictEqual(spyData.calls[1], { arguments: ['params', 'HEAD', '/undefinedParams/:id'], result: true }) | ||
| // GET /undefinedBody/123 | ||
| t.assert.deepStrictEqual(spyData.calls[2], { arguments: ['body', 'GET', '/undefinedBody/:id'], result: true }) | ||
| t.assert.deepStrictEqual(spyData.calls[3], { arguments: ['body', 'HEAD', '/undefinedBody/:id'], result: true }) | ||
| t.assert.strictEqual(spyData.callCount(), 4) | ||
| testDone() | ||
@@ -511,0 +482,0 @@ }) |
@@ -6,3 +6,3 @@ 'use strict' | ||
| const ContentType = require('../lib/content-type') | ||
| const { waitForCb } = require('./toolkit') | ||
| const { waitForCb } = require('./helper') | ||
@@ -9,0 +9,0 @@ const echoBody = (req, reply) => { reply.send(req.body) } |
@@ -12,3 +12,3 @@ 'use strict' | ||
| const proxyquire = require('proxyquire') | ||
| const { waitForCb } = require('./toolkit') | ||
| const { waitForCb } = require('./helper') | ||
@@ -15,0 +15,0 @@ test('Ajv plugins array parameter', (t, testDone) => { |
@@ -8,3 +8,3 @@ 'use strict' | ||
| const Schema = require('fluent-json-schema') | ||
| const { waitForCb } = require('./toolkit') | ||
| const { waitForCb } = require('./helper') | ||
| const { kRequestContentType } = require('../lib/symbols') | ||
@@ -11,0 +11,0 @@ |
@@ -10,3 +10,3 @@ 'use strict' | ||
| const Fastify = require('..') | ||
| const { waitForCb } = require('./toolkit') | ||
| const { waitForCb } = require('./helper') | ||
@@ -13,0 +13,0 @@ test('onSend hook stream', t => { |
| 'use strict' | ||
| exports.waitForCb = function (options) { | ||
| let count = null | ||
| let done = false | ||
| let iResolve | ||
| let iReject | ||
| function stepIn () { | ||
| if (done) { | ||
| iReject(new Error('Unexpected done call')) | ||
| return | ||
| } | ||
| if (--count) { | ||
| return | ||
| } | ||
| done = true | ||
| iResolve() | ||
| } | ||
| const patience = new Promise((resolve, reject) => { | ||
| iResolve = resolve | ||
| iReject = reject | ||
| }) | ||
| count = options.steps || 1 | ||
| done = false | ||
| return { stepIn, patience } | ||
| } | ||
| exports.partialDeepStrictEqual = function partialDeepStrictEqual (actual, expected) { | ||
| if (typeof expected !== 'object' || expected === null) { | ||
| return actual === expected | ||
| } | ||
| if (typeof actual !== 'object' || actual === null) { | ||
| return false | ||
| } | ||
| if (Array.isArray(expected)) { | ||
| if (!Array.isArray(actual)) return false | ||
| if (expected.length > actual.length) return false | ||
| for (let i = 0; i < expected.length; i++) { | ||
| if (!partialDeepStrictEqual(actual[i], expected[i])) { | ||
| return false | ||
| } | ||
| } | ||
| return true | ||
| } | ||
| for (const key of Object.keys(expected)) { | ||
| if (!(key in actual)) return false | ||
| if (!partialDeepStrictEqual(actual[key], expected[key])) { | ||
| return false | ||
| } | ||
| } | ||
| return true | ||
| } |
Sorry, the diff of this file is too big to display
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
2911243
0.25%35
-2.78%67227
0.17%489
0.62%Updated