node-fastify 5.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.borp.yaml +3 -0
- package/.markdownlint-cli2.yaml +22 -0
- package/.prettierignore +1 -0
- package/GOVERNANCE.md +4 -0
- package/LICENSE +21 -0
- package/PROJECT_CHARTER.md +126 -0
- package/README.md +423 -0
- package/SECURITY.md +220 -0
- package/SPONSORS.md +24 -0
- package/build/build-error-serializer.js +35 -0
- package/build/build-validation.js +169 -0
- package/build/sync-version.js +11 -0
- package/docs/Guides/Benchmarking.md +60 -0
- package/docs/Guides/Database.md +321 -0
- package/docs/Guides/Delay-Accepting-Requests.md +608 -0
- package/docs/Guides/Detecting-When-Clients-Abort.md +172 -0
- package/docs/Guides/Ecosystem.md +726 -0
- package/docs/Guides/Fluent-Schema.md +127 -0
- package/docs/Guides/Getting-Started.md +620 -0
- package/docs/Guides/Index.md +43 -0
- package/docs/Guides/Migration-Guide-V3.md +287 -0
- package/docs/Guides/Migration-Guide-V4.md +267 -0
- package/docs/Guides/Migration-Guide-V5.md +727 -0
- package/docs/Guides/Plugins-Guide.md +520 -0
- package/docs/Guides/Prototype-Poisoning.md +383 -0
- package/docs/Guides/Recommendations.md +378 -0
- package/docs/Guides/Serverless.md +604 -0
- package/docs/Guides/Style-Guide.md +246 -0
- package/docs/Guides/Testing.md +481 -0
- package/docs/Guides/Write-Plugin.md +103 -0
- package/docs/Guides/Write-Type-Provider.md +34 -0
- package/docs/Reference/ContentTypeParser.md +271 -0
- package/docs/Reference/Decorators.md +436 -0
- package/docs/Reference/Encapsulation.md +194 -0
- package/docs/Reference/Errors.md +377 -0
- package/docs/Reference/HTTP2.md +94 -0
- package/docs/Reference/Hooks.md +958 -0
- package/docs/Reference/Index.md +73 -0
- package/docs/Reference/LTS.md +86 -0
- package/docs/Reference/Lifecycle.md +99 -0
- package/docs/Reference/Logging.md +268 -0
- package/docs/Reference/Middleware.md +79 -0
- package/docs/Reference/Plugins.md +245 -0
- package/docs/Reference/Principles.md +73 -0
- package/docs/Reference/Reply.md +1001 -0
- package/docs/Reference/Request.md +295 -0
- package/docs/Reference/Routes.md +802 -0
- package/docs/Reference/Server.md +2389 -0
- package/docs/Reference/Type-Providers.md +256 -0
- package/docs/Reference/TypeScript.md +1729 -0
- package/docs/Reference/Validation-and-Serialization.md +1130 -0
- package/docs/Reference/Warnings.md +58 -0
- package/docs/index.md +24 -0
- package/docs/resources/encapsulation_context.drawio +1 -0
- package/docs/resources/encapsulation_context.svg +3 -0
- package/eslint.config.js +35 -0
- package/examples/asyncawait.js +38 -0
- package/examples/benchmark/body.json +3 -0
- package/examples/benchmark/hooks-benchmark-async-await.js +44 -0
- package/examples/benchmark/hooks-benchmark.js +52 -0
- package/examples/benchmark/parser.js +47 -0
- package/examples/benchmark/simple.js +30 -0
- package/examples/benchmark/webstream.js +27 -0
- package/examples/hooks.js +91 -0
- package/examples/http2.js +39 -0
- package/examples/https.js +38 -0
- package/examples/parser.js +53 -0
- package/examples/plugin.js +12 -0
- package/examples/route-prefix.js +38 -0
- package/examples/shared-schema.js +38 -0
- package/examples/simple-stream.js +20 -0
- package/examples/simple.js +32 -0
- package/examples/simple.mjs +27 -0
- package/examples/typescript-server.ts +79 -0
- package/examples/use-plugin.js +29 -0
- package/fastify.d.ts +253 -0
- package/fastify.js +985 -0
- package/integration/server.js +29 -0
- package/integration/test.sh +23 -0
- package/lib/config-validator.js +1266 -0
- package/lib/content-type-parser.js +413 -0
- package/lib/content-type.js +160 -0
- package/lib/context.js +98 -0
- package/lib/decorate.js +152 -0
- package/lib/error-handler.js +173 -0
- package/lib/error-serializer.js +134 -0
- package/lib/error-status.js +14 -0
- package/lib/errors.js +516 -0
- package/lib/four-oh-four.js +190 -0
- package/lib/handle-request.js +195 -0
- package/lib/head-route.js +45 -0
- package/lib/hooks.js +429 -0
- package/lib/initial-config-validation.js +37 -0
- package/lib/logger-factory.js +136 -0
- package/lib/logger-pino.js +68 -0
- package/lib/noop-set.js +10 -0
- package/lib/plugin-override.js +90 -0
- package/lib/plugin-utils.js +169 -0
- package/lib/promise.js +23 -0
- package/lib/reply.js +1030 -0
- package/lib/req-id-gen-factory.js +52 -0
- package/lib/request.js +391 -0
- package/lib/route.js +686 -0
- package/lib/schema-controller.js +164 -0
- package/lib/schemas.js +207 -0
- package/lib/server.js +441 -0
- package/lib/symbols.js +71 -0
- package/lib/validation.js +280 -0
- package/lib/warnings.js +57 -0
- package/lib/wrap-thenable.js +84 -0
- package/package.json +225 -0
- package/scripts/validate-ecosystem-links.js +179 -0
- package/test/404s.test.js +2035 -0
- package/test/500s.test.js +422 -0
- package/test/allow-unsafe-regex.test.js +92 -0
- package/test/als.test.js +65 -0
- package/test/async-await.test.js +705 -0
- package/test/async-dispose.test.js +20 -0
- package/test/async_hooks.test.js +52 -0
- package/test/body-limit.test.js +224 -0
- package/test/buffer.test.js +74 -0
- package/test/build/error-serializer.test.js +36 -0
- package/test/build/version.test.js +14 -0
- package/test/build-certificate.js +109 -0
- package/test/bundler/README.md +29 -0
- package/test/bundler/esbuild/bundler-test.js +32 -0
- package/test/bundler/esbuild/package.json +10 -0
- package/test/bundler/esbuild/src/fail-plugin-version.js +14 -0
- package/test/bundler/esbuild/src/index.js +9 -0
- package/test/bundler/webpack/bundler-test.js +32 -0
- package/test/bundler/webpack/package.json +11 -0
- package/test/bundler/webpack/src/fail-plugin-version.js +14 -0
- package/test/bundler/webpack/src/index.js +9 -0
- package/test/bundler/webpack/webpack.config.js +15 -0
- package/test/case-insensitive.test.js +102 -0
- package/test/chainable.test.js +40 -0
- package/test/child-logger-factory.test.js +128 -0
- package/test/client-timeout.test.js +38 -0
- package/test/close-pipelining.test.js +78 -0
- package/test/close.test.js +706 -0
- package/test/conditional-pino.test.js +47 -0
- package/test/connection-timeout.test.js +42 -0
- package/test/constrained-routes.test.js +1138 -0
- package/test/content-length.test.js +174 -0
- package/test/content-parser.test.js +739 -0
- package/test/content-type.test.js +181 -0
- package/test/context-config.test.js +164 -0
- package/test/custom-http-server.test.js +118 -0
- package/test/custom-parser-async.test.js +59 -0
- package/test/custom-parser.0.test.js +701 -0
- package/test/custom-parser.1.test.js +266 -0
- package/test/custom-parser.2.test.js +91 -0
- package/test/custom-parser.3.test.js +208 -0
- package/test/custom-parser.4.test.js +218 -0
- package/test/custom-parser.5.test.js +130 -0
- package/test/custom-querystring-parser.test.js +129 -0
- package/test/decorator.test.js +1330 -0
- package/test/delete.test.js +344 -0
- package/test/diagnostics-channel/404.test.js +49 -0
- package/test/diagnostics-channel/async-delay-request.test.js +65 -0
- package/test/diagnostics-channel/async-request.test.js +64 -0
- package/test/diagnostics-channel/error-before-handler.test.js +35 -0
- package/test/diagnostics-channel/error-request.test.js +53 -0
- package/test/diagnostics-channel/error-status.test.js +123 -0
- package/test/diagnostics-channel/init.test.js +50 -0
- package/test/diagnostics-channel/sync-delay-request.test.js +49 -0
- package/test/diagnostics-channel/sync-request-reply.test.js +51 -0
- package/test/diagnostics-channel/sync-request.test.js +54 -0
- package/test/encapsulated-child-logger-factory.test.js +69 -0
- package/test/encapsulated-error-handler.test.js +237 -0
- package/test/esm/errorCodes.test.mjs +10 -0
- package/test/esm/esm.test.mjs +13 -0
- package/test/esm/index.test.js +8 -0
- package/test/esm/named-exports.mjs +14 -0
- package/test/esm/other.mjs +8 -0
- package/test/esm/plugin.mjs +8 -0
- package/test/fastify-instance.test.js +300 -0
- package/test/find-route.test.js +152 -0
- package/test/fluent-schema.test.js +209 -0
- package/test/genReqId.test.js +426 -0
- package/test/handler-context.test.js +45 -0
- package/test/handler-timeout.test.js +367 -0
- package/test/has-route.test.js +88 -0
- package/test/header-overflow.test.js +55 -0
- package/test/helper.js +496 -0
- package/test/hooks-async.test.js +1099 -0
- package/test/hooks.on-listen.test.js +1162 -0
- package/test/hooks.on-ready.test.js +421 -0
- package/test/hooks.test.js +3578 -0
- package/test/http-methods/copy.test.js +35 -0
- package/test/http-methods/custom-http-methods.test.js +114 -0
- package/test/http-methods/get.test.js +412 -0
- package/test/http-methods/head.test.js +263 -0
- package/test/http-methods/lock.test.js +108 -0
- package/test/http-methods/mkcalendar.test.js +143 -0
- package/test/http-methods/mkcol.test.js +35 -0
- package/test/http-methods/move.test.js +42 -0
- package/test/http-methods/propfind.test.js +136 -0
- package/test/http-methods/proppatch.test.js +105 -0
- package/test/http-methods/report.test.js +142 -0
- package/test/http-methods/search.test.js +233 -0
- package/test/http-methods/trace.test.js +21 -0
- package/test/http-methods/unlock.test.js +38 -0
- package/test/http2/closing.test.js +270 -0
- package/test/http2/constraint.test.js +109 -0
- package/test/http2/head.test.js +34 -0
- package/test/http2/plain.test.js +68 -0
- package/test/http2/secure-with-fallback.test.js +113 -0
- package/test/http2/secure.test.js +67 -0
- package/test/http2/unknown-http-method.test.js +34 -0
- package/test/https/custom-https-server.test.js +58 -0
- package/test/https/https.test.js +136 -0
- package/test/imports.test.js +17 -0
- package/test/inject.test.js +502 -0
- package/test/input-validation.js +335 -0
- package/test/internals/all.test.js +38 -0
- package/test/internals/content-type-parser.test.js +111 -0
- package/test/internals/context.test.js +31 -0
- package/test/internals/decorator.test.js +156 -0
- package/test/internals/errors.test.js +982 -0
- package/test/internals/handle-request.test.js +270 -0
- package/test/internals/hook-runner.test.js +449 -0
- package/test/internals/hooks.test.js +96 -0
- package/test/internals/initial-config.test.js +383 -0
- package/test/internals/logger.test.js +163 -0
- package/test/internals/plugin.test.js +170 -0
- package/test/internals/promise.test.js +63 -0
- package/test/internals/reply-serialize.test.js +714 -0
- package/test/internals/reply.test.js +1920 -0
- package/test/internals/req-id-gen-factory.test.js +133 -0
- package/test/internals/request-validate.test.js +1402 -0
- package/test/internals/request.test.js +506 -0
- package/test/internals/schema-controller-perf.test.js +40 -0
- package/test/internals/server.test.js +91 -0
- package/test/internals/validation.test.js +352 -0
- package/test/issue-4959.test.js +118 -0
- package/test/keep-alive-timeout.test.js +42 -0
- package/test/listen.1.test.js +154 -0
- package/test/listen.2.test.js +113 -0
- package/test/listen.3.test.js +83 -0
- package/test/listen.4.test.js +168 -0
- package/test/listen.5.test.js +122 -0
- package/test/logger/instantiation.test.js +341 -0
- package/test/logger/logger-test-utils.js +47 -0
- package/test/logger/logging.test.js +460 -0
- package/test/logger/options.test.js +579 -0
- package/test/logger/request.test.js +292 -0
- package/test/logger/response.test.js +183 -0
- package/test/logger/tap-parallel-not-ok +0 -0
- package/test/max-requests-per-socket.test.js +113 -0
- package/test/middleware.test.js +37 -0
- package/test/noop-set.test.js +19 -0
- package/test/nullable-validation.test.js +187 -0
- package/test/options.error-handler.test.js +5 -0
- package/test/options.test.js +5 -0
- package/test/output-validation.test.js +140 -0
- package/test/patch.error-handler.test.js +5 -0
- package/test/patch.test.js +5 -0
- package/test/plugin.1.test.js +230 -0
- package/test/plugin.2.test.js +314 -0
- package/test/plugin.3.test.js +287 -0
- package/test/plugin.4.test.js +504 -0
- package/test/plugin.helper.js +8 -0
- package/test/plugin.name.display.js +10 -0
- package/test/post-empty-body.test.js +38 -0
- package/test/pretty-print.test.js +366 -0
- package/test/promises.test.js +125 -0
- package/test/proto-poisoning.test.js +145 -0
- package/test/put.error-handler.test.js +5 -0
- package/test/put.test.js +5 -0
- package/test/register.test.js +184 -0
- package/test/reply-code.test.js +148 -0
- package/test/reply-early-hints.test.js +100 -0
- package/test/reply-error.test.js +815 -0
- package/test/reply-trailers.test.js +445 -0
- package/test/reply-web-stream-locked.test.js +37 -0
- package/test/request-error.test.js +624 -0
- package/test/request-header-host.test.js +339 -0
- package/test/request-id.test.js +118 -0
- package/test/request-timeout.test.js +53 -0
- package/test/route-hooks.test.js +635 -0
- package/test/route-prefix.test.js +904 -0
- package/test/route-shorthand.test.js +48 -0
- package/test/route.1.test.js +259 -0
- package/test/route.2.test.js +100 -0
- package/test/route.3.test.js +213 -0
- package/test/route.4.test.js +127 -0
- package/test/route.5.test.js +211 -0
- package/test/route.6.test.js +306 -0
- package/test/route.7.test.js +406 -0
- package/test/route.8.test.js +225 -0
- package/test/router-options.test.js +1108 -0
- package/test/same-shape.test.js +124 -0
- package/test/schema-examples.test.js +661 -0
- package/test/schema-feature.test.js +2198 -0
- package/test/schema-serialization.test.js +1171 -0
- package/test/schema-special-usage.test.js +1348 -0
- package/test/schema-validation.test.js +1572 -0
- package/test/scripts/validate-ecosystem-links.test.js +339 -0
- package/test/serialize-response.test.js +186 -0
- package/test/server.test.js +347 -0
- package/test/set-error-handler.test.js +69 -0
- package/test/skip-reply-send.test.js +317 -0
- package/test/stream-serializers.test.js +40 -0
- package/test/stream.1.test.js +94 -0
- package/test/stream.2.test.js +129 -0
- package/test/stream.3.test.js +198 -0
- package/test/stream.4.test.js +176 -0
- package/test/stream.5.test.js +188 -0
- package/test/sync-routes.test.js +32 -0
- package/test/throw.test.js +359 -0
- package/test/toolkit.js +63 -0
- package/test/trust-proxy.test.js +162 -0
- package/test/type-provider.test.js +22 -0
- package/test/types/content-type-parser.test-d.ts +72 -0
- package/test/types/decorate-request-reply.test-d.ts +18 -0
- package/test/types/dummy-plugin.ts +9 -0
- package/test/types/errors.test-d.ts +90 -0
- package/test/types/fastify.test-d.ts +352 -0
- package/test/types/hooks.test-d.ts +550 -0
- package/test/types/import.ts +2 -0
- package/test/types/instance.test-d.ts +588 -0
- package/test/types/logger.test-d.ts +277 -0
- package/test/types/plugin.test-d.ts +97 -0
- package/test/types/register.test-d.ts +237 -0
- package/test/types/reply.test-d.ts +254 -0
- package/test/types/request.test-d.ts +188 -0
- package/test/types/route.test-d.ts +553 -0
- package/test/types/schema.test-d.ts +135 -0
- package/test/types/serverFactory.test-d.ts +37 -0
- package/test/types/type-provider.test-d.ts +1213 -0
- package/test/types/using.test-d.ts +17 -0
- package/test/upgrade.test.js +52 -0
- package/test/url-rewriting.test.js +122 -0
- package/test/use-semicolon-delimiter.test.js +168 -0
- package/test/validation-error-handling.test.js +900 -0
- package/test/versioned-routes.test.js +603 -0
- package/test/web-api.test.js +616 -0
- package/test/wrap-thenable.test.js +30 -0
- package/types/content-type-parser.d.ts +75 -0
- package/types/context.d.ts +22 -0
- package/types/errors.d.ts +92 -0
- package/types/hooks.d.ts +875 -0
- package/types/instance.d.ts +609 -0
- package/types/logger.d.ts +107 -0
- package/types/plugin.d.ts +44 -0
- package/types/register.d.ts +42 -0
- package/types/reply.d.ts +81 -0
- package/types/request.d.ts +95 -0
- package/types/route.d.ts +199 -0
- package/types/schema.d.ts +61 -0
- package/types/server-factory.d.ts +19 -0
- package/types/type-provider.d.ts +130 -0
- package/types/utils.d.ts +98 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { buildSchemas } = require('./schemas')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Called at every fastify context that is being created.
|
|
7
|
+
* @param {object} parentSchemaCtrl: the SchemaController instance of the Fastify parent context
|
|
8
|
+
* @param {object} opts: the `schemaController` server option. It can be undefined when a parentSchemaCtrl is set
|
|
9
|
+
* @return {object}:a new SchemaController
|
|
10
|
+
*/
|
|
11
|
+
function buildSchemaController (parentSchemaCtrl, opts) {
|
|
12
|
+
if (parentSchemaCtrl) {
|
|
13
|
+
return new SchemaController(parentSchemaCtrl, opts)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const compilersFactory = Object.assign({
|
|
17
|
+
buildValidator: null,
|
|
18
|
+
buildSerializer: null
|
|
19
|
+
}, opts?.compilersFactory)
|
|
20
|
+
|
|
21
|
+
if (!compilersFactory.buildValidator) {
|
|
22
|
+
const ValidatorSelector = require('@fastify/ajv-compiler')
|
|
23
|
+
compilersFactory.buildValidator = ValidatorSelector()
|
|
24
|
+
}
|
|
25
|
+
if (!compilersFactory.buildSerializer) {
|
|
26
|
+
const SerializerSelector = require('@fastify/fast-json-stringify-compiler')
|
|
27
|
+
compilersFactory.buildSerializer = SerializerSelector()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const option = {
|
|
31
|
+
bucket: (opts && opts.bucket) || buildSchemas,
|
|
32
|
+
compilersFactory,
|
|
33
|
+
isCustomValidatorCompiler: typeof opts?.compilersFactory?.buildValidator === 'function',
|
|
34
|
+
isCustomSerializerCompiler: typeof opts?.compilersFactory?.buildValidator === 'function'
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return new SchemaController(undefined, option)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
class SchemaController {
|
|
41
|
+
constructor (parent, options) {
|
|
42
|
+
this.opts = options || parent?.opts
|
|
43
|
+
this.addedSchemas = false
|
|
44
|
+
|
|
45
|
+
this.compilersFactory = this.opts.compilersFactory
|
|
46
|
+
|
|
47
|
+
if (parent) {
|
|
48
|
+
this.schemaBucket = this.opts.bucket(parent.getSchemas())
|
|
49
|
+
this.validatorCompiler = parent.getValidatorCompiler()
|
|
50
|
+
this.serializerCompiler = parent.getSerializerCompiler()
|
|
51
|
+
this.isCustomValidatorCompiler = parent.isCustomValidatorCompiler
|
|
52
|
+
this.isCustomSerializerCompiler = parent.isCustomSerializerCompiler
|
|
53
|
+
this.parent = parent
|
|
54
|
+
} else {
|
|
55
|
+
this.schemaBucket = this.opts.bucket()
|
|
56
|
+
this.isCustomValidatorCompiler = this.opts.isCustomValidatorCompiler || false
|
|
57
|
+
this.isCustomSerializerCompiler = this.opts.isCustomSerializerCompiler || false
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Bucket interface
|
|
62
|
+
add (schema) {
|
|
63
|
+
this.addedSchemas = true
|
|
64
|
+
return this.schemaBucket.add(schema)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
getSchema (schemaId) {
|
|
68
|
+
return this.schemaBucket.getSchema(schemaId)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
getSchemas () {
|
|
72
|
+
return this.schemaBucket.getSchemas()
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
setValidatorCompiler (validatorCompiler) {
|
|
76
|
+
// Set up as if the fixed validator compiler had been provided
|
|
77
|
+
// by a custom 'options.compilersFactory.buildValidator' that
|
|
78
|
+
// always returns the same compiler object. This is required because:
|
|
79
|
+
//
|
|
80
|
+
// - setValidatorCompiler must immediately install a compiler to preserve
|
|
81
|
+
// legacy behavior
|
|
82
|
+
// - setupValidator will recreate compilers from builders in some
|
|
83
|
+
// circumstances, so we have to install this adapter to make it
|
|
84
|
+
// behave the same if the legacy API is used
|
|
85
|
+
//
|
|
86
|
+
// The cloning of the compilersFactory object is necessary because
|
|
87
|
+
// we are aliasing the parent compilersFactory if none was provided
|
|
88
|
+
// to us (see constructor.)
|
|
89
|
+
this.compilersFactory = Object.assign(
|
|
90
|
+
{},
|
|
91
|
+
this.compilersFactory,
|
|
92
|
+
{ buildValidator: () => validatorCompiler })
|
|
93
|
+
this.validatorCompiler = validatorCompiler
|
|
94
|
+
this.isCustomValidatorCompiler = true
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
setSerializerCompiler (serializerCompiler) {
|
|
98
|
+
// Set up as if the fixed serializer compiler had been provided
|
|
99
|
+
// by a custom 'options.compilersFactory.buildSerializer' that
|
|
100
|
+
// always returns the same compiler object. This is required because:
|
|
101
|
+
//
|
|
102
|
+
// - setSerializerCompiler must immediately install a compiler to preserve
|
|
103
|
+
// legacy behavior
|
|
104
|
+
// - setupSerializer will recreate compilers from builders in some
|
|
105
|
+
// circumstances, so we have to install this adapter to make it
|
|
106
|
+
// behave the same if the legacy API is used
|
|
107
|
+
//
|
|
108
|
+
// The cloning of the compilersFactory object is necessary because
|
|
109
|
+
// we are aliasing the parent compilersFactory if none was provided
|
|
110
|
+
// to us (see constructor.)
|
|
111
|
+
this.compilersFactory = Object.assign(
|
|
112
|
+
{},
|
|
113
|
+
this.compilersFactory,
|
|
114
|
+
{ buildSerializer: () => serializerCompiler })
|
|
115
|
+
this.serializerCompiler = serializerCompiler
|
|
116
|
+
this.isCustomSerializerCompiler = true
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
getValidatorCompiler () {
|
|
120
|
+
return this.validatorCompiler || (this.parent && this.parent.getValidatorCompiler())
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
getSerializerCompiler () {
|
|
124
|
+
return this.serializerCompiler || (this.parent && this.parent.getSerializerCompiler())
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
getSerializerBuilder () {
|
|
128
|
+
return this.compilersFactory.buildSerializer || (this.parent && this.parent.getSerializerBuilder())
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
getValidatorBuilder () {
|
|
132
|
+
return this.compilersFactory.buildValidator || (this.parent && this.parent.getValidatorBuilder())
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* This method will be called when a validator must be setup.
|
|
137
|
+
* Do not setup the compiler more than once
|
|
138
|
+
* @param {object} serverOptions the fastify server options
|
|
139
|
+
*/
|
|
140
|
+
setupValidator (serverOptions) {
|
|
141
|
+
const isReady = this.validatorCompiler !== undefined && !this.addedSchemas
|
|
142
|
+
if (isReady) {
|
|
143
|
+
return
|
|
144
|
+
}
|
|
145
|
+
this.validatorCompiler = this.getValidatorBuilder()(this.schemaBucket.getSchemas(), serverOptions.ajv)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* This method will be called when a serializer must be setup.
|
|
150
|
+
* Do not setup the compiler more than once
|
|
151
|
+
* @param {object} serverOptions the fastify server options
|
|
152
|
+
*/
|
|
153
|
+
setupSerializer (serverOptions) {
|
|
154
|
+
const isReady = this.serializerCompiler !== undefined && !this.addedSchemas
|
|
155
|
+
if (isReady) {
|
|
156
|
+
return
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
this.serializerCompiler = this.getSerializerBuilder()(this.schemaBucket.getSchemas(), serverOptions.serializerOpts)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
SchemaController.buildSchemaController = buildSchemaController
|
|
164
|
+
module.exports = SchemaController
|
package/lib/schemas.js
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const fastClone = require('rfdc')({ circles: false, proto: true })
|
|
4
|
+
const { kSchemaVisited, kSchemaResponse } = require('./symbols')
|
|
5
|
+
const kFluentSchema = Symbol.for('fluent-schema-object')
|
|
6
|
+
|
|
7
|
+
const {
|
|
8
|
+
FST_ERR_SCH_MISSING_ID,
|
|
9
|
+
FST_ERR_SCH_ALREADY_PRESENT,
|
|
10
|
+
FST_ERR_SCH_DUPLICATE,
|
|
11
|
+
FST_ERR_SCH_CONTENT_MISSING_SCHEMA
|
|
12
|
+
} = require('./errors')
|
|
13
|
+
|
|
14
|
+
const SCHEMAS_SOURCE = ['params', 'body', 'querystring', 'query', 'headers']
|
|
15
|
+
|
|
16
|
+
function Schemas (initStore) {
|
|
17
|
+
this.store = initStore || {}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
Schemas.prototype.add = function (inputSchema) {
|
|
21
|
+
const schema = fastClone((inputSchema.isFluentSchema || inputSchema.isFluentJSONSchema || inputSchema[kFluentSchema])
|
|
22
|
+
? inputSchema.valueOf()
|
|
23
|
+
: inputSchema
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
// developers can add schemas without $id, but with $def instead
|
|
27
|
+
const id = schema.$id
|
|
28
|
+
if (!id) {
|
|
29
|
+
throw new FST_ERR_SCH_MISSING_ID()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (this.store[id]) {
|
|
33
|
+
throw new FST_ERR_SCH_ALREADY_PRESENT(id)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
this.store[id] = schema
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
Schemas.prototype.getSchemas = function () {
|
|
40
|
+
return Object.assign({}, this.store)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
Schemas.prototype.getSchema = function (schemaId) {
|
|
44
|
+
return this.store[schemaId]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Checks whether a schema is a non-plain object.
|
|
49
|
+
*
|
|
50
|
+
* @param {*} schema the schema to check
|
|
51
|
+
* @returns {boolean} true if schema has a custom prototype
|
|
52
|
+
*/
|
|
53
|
+
function isCustomSchemaPrototype (schema) {
|
|
54
|
+
return typeof schema === 'object' && Object.getPrototypeOf(schema) !== Object.prototype
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function normalizeSchema (routeSchemas, serverOptions) {
|
|
58
|
+
if (routeSchemas[kSchemaVisited]) {
|
|
59
|
+
return routeSchemas
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// alias query to querystring schema
|
|
63
|
+
if (routeSchemas.query) {
|
|
64
|
+
// check if our schema has both querystring and query
|
|
65
|
+
if (routeSchemas.querystring) {
|
|
66
|
+
throw new FST_ERR_SCH_DUPLICATE('querystring')
|
|
67
|
+
}
|
|
68
|
+
routeSchemas.querystring = routeSchemas.query
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
generateFluentSchema(routeSchemas)
|
|
72
|
+
|
|
73
|
+
for (const key of SCHEMAS_SOURCE) {
|
|
74
|
+
const schema = routeSchemas[key]
|
|
75
|
+
if (schema && !isCustomSchemaPrototype(schema)) {
|
|
76
|
+
if (key === 'body' && schema.content) {
|
|
77
|
+
const contentProperty = schema.content
|
|
78
|
+
const keys = Object.keys(contentProperty)
|
|
79
|
+
for (let i = 0; i < keys.length; i++) {
|
|
80
|
+
const contentType = keys[i]
|
|
81
|
+
const contentSchema = contentProperty[contentType].schema
|
|
82
|
+
if (!contentSchema) {
|
|
83
|
+
throw new FST_ERR_SCH_CONTENT_MISSING_SCHEMA(contentType)
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
continue
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (routeSchemas.response) {
|
|
92
|
+
const httpCodes = Object.keys(routeSchemas.response)
|
|
93
|
+
for (const code of httpCodes) {
|
|
94
|
+
if (isCustomSchemaPrototype(routeSchemas.response[code])) {
|
|
95
|
+
continue
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const contentProperty = routeSchemas.response[code].content
|
|
99
|
+
|
|
100
|
+
if (contentProperty) {
|
|
101
|
+
const keys = Object.keys(contentProperty)
|
|
102
|
+
for (let i = 0; i < keys.length; i++) {
|
|
103
|
+
const mediaName = keys[i]
|
|
104
|
+
if (!contentProperty[mediaName].schema) {
|
|
105
|
+
throw new FST_ERR_SCH_CONTENT_MISSING_SCHEMA(mediaName)
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
routeSchemas[kSchemaVisited] = true
|
|
113
|
+
return routeSchemas
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function generateFluentSchema (schema) {
|
|
117
|
+
for (const key of SCHEMAS_SOURCE) {
|
|
118
|
+
if (schema[key] && (schema[key].isFluentSchema || schema[key][kFluentSchema])) {
|
|
119
|
+
schema[key] = schema[key].valueOf()
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (schema.response) {
|
|
124
|
+
const httpCodes = Object.keys(schema.response)
|
|
125
|
+
for (const code of httpCodes) {
|
|
126
|
+
if (schema.response[code].isFluentSchema || schema.response[code][kFluentSchema]) {
|
|
127
|
+
schema.response[code] = schema.response[code].valueOf()
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Search for the right JSON schema compiled function in the request context
|
|
135
|
+
* setup by the route configuration `schema.response`.
|
|
136
|
+
* It will look for the exact match (eg 200) or generic (eg 2xx)
|
|
137
|
+
*
|
|
138
|
+
* @param {object} context the request context
|
|
139
|
+
* @param {number} statusCode the http status code
|
|
140
|
+
* @param {string} [contentType] the reply content type
|
|
141
|
+
* @returns {function|false} the right JSON Schema function to serialize
|
|
142
|
+
* the reply or false if it is not set
|
|
143
|
+
*/
|
|
144
|
+
function getSchemaSerializer (context, statusCode, contentType) {
|
|
145
|
+
const responseSchemaDef = context[kSchemaResponse]
|
|
146
|
+
if (!responseSchemaDef) {
|
|
147
|
+
return false
|
|
148
|
+
}
|
|
149
|
+
if (responseSchemaDef[statusCode]) {
|
|
150
|
+
if (responseSchemaDef[statusCode].constructor === Object && contentType) {
|
|
151
|
+
const mediaName = contentType.split(';', 1)[0]
|
|
152
|
+
if (responseSchemaDef[statusCode][mediaName]) {
|
|
153
|
+
return responseSchemaDef[statusCode][mediaName]
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// fallback to match all media-type
|
|
157
|
+
if (responseSchemaDef[statusCode]['*/*']) {
|
|
158
|
+
return responseSchemaDef[statusCode]['*/*']
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return false
|
|
162
|
+
}
|
|
163
|
+
return responseSchemaDef[statusCode]
|
|
164
|
+
}
|
|
165
|
+
const fallbackStatusCode = (statusCode + '')[0] + 'xx'
|
|
166
|
+
if (responseSchemaDef[fallbackStatusCode]) {
|
|
167
|
+
if (responseSchemaDef[fallbackStatusCode].constructor === Object && contentType) {
|
|
168
|
+
const mediaName = contentType.split(';', 1)[0]
|
|
169
|
+
if (responseSchemaDef[fallbackStatusCode][mediaName]) {
|
|
170
|
+
return responseSchemaDef[fallbackStatusCode][mediaName]
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// fallback to match all media-type
|
|
174
|
+
if (responseSchemaDef[fallbackStatusCode]['*/*']) {
|
|
175
|
+
return responseSchemaDef[fallbackStatusCode]['*/*']
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return false
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return responseSchemaDef[fallbackStatusCode]
|
|
182
|
+
}
|
|
183
|
+
if (responseSchemaDef.default) {
|
|
184
|
+
if (responseSchemaDef.default.constructor === Object && contentType) {
|
|
185
|
+
const mediaName = contentType.split(';', 1)[0]
|
|
186
|
+
if (responseSchemaDef.default[mediaName]) {
|
|
187
|
+
return responseSchemaDef.default[mediaName]
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// fallback to match all media-type
|
|
191
|
+
if (responseSchemaDef.default['*/*']) {
|
|
192
|
+
return responseSchemaDef.default['*/*']
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return false
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return responseSchemaDef.default
|
|
199
|
+
}
|
|
200
|
+
return false
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
module.exports = {
|
|
204
|
+
buildSchemas (initStore) { return new Schemas(initStore) },
|
|
205
|
+
getSchemaSerializer,
|
|
206
|
+
normalizeSchema
|
|
207
|
+
}
|