UNPKG

13.4 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.FastifyAdapter = void 0;
4const common_1 = require("@nestjs/common");
5const load_package_util_1 = require("@nestjs/common/utils/load-package.util");
6const shared_utils_1 = require("@nestjs/common/utils/shared.utils");
7const http_adapter_1 = require("@nestjs/core/adapters/http-adapter");
8const fastify_1 = require("fastify");
9const Reply = require("fastify/lib/reply");
10// `querystring` is used internally in fastify for registering urlencoded body parser.
11const querystring_1 = require("querystring");
12class FastifyAdapter extends http_adapter_1.AbstractHttpAdapter {
13 constructor(instanceOrOptions) {
14 super();
15 this.versionConstraint = {
16 name: 'version',
17 validate(value) {
18 if (!(0, shared_utils_1.isString)(value) && !Array.isArray(value)) {
19 throw new Error('Version constraint should be a string or an array of strings.');
20 }
21 },
22 storage() {
23 const versions = new Map();
24 return {
25 get(version) {
26 if (Array.isArray(version)) {
27 return versions.get(version.find(v => versions.has(v))) || null;
28 }
29 return versions.get(version) || null;
30 },
31 set(versionOrVersions, store) {
32 const storeVersionConstraint = (version) => versions.set(version, store);
33 if (Array.isArray(versionOrVersions))
34 versionOrVersions.forEach(storeVersionConstraint);
35 else
36 storeVersionConstraint(versionOrVersions);
37 },
38 del(version) {
39 if (Array.isArray(version)) {
40 version.forEach(v => versions.delete(v));
41 }
42 else {
43 versions.delete(version);
44 }
45 },
46 empty() {
47 versions.clear();
48 },
49 };
50 },
51 deriveConstraint: (req) => {
52 var _a, _b, _c, _d;
53 // Media Type (Accept Header) Versioning Handler
54 if (this.versioningOptions.type === common_1.VersioningType.MEDIA_TYPE) {
55 const MEDIA_TYPE_HEADER = 'Accept';
56 const acceptHeaderValue = (((_a = req.headers) === null || _a === void 0 ? void 0 : _a[MEDIA_TYPE_HEADER]) || ((_b = req.headers) === null || _b === void 0 ? void 0 : _b[MEDIA_TYPE_HEADER.toLowerCase()]));
57 const acceptHeaderVersionParameter = acceptHeaderValue
58 ? acceptHeaderValue.split(';')[1]
59 : '';
60 return (0, shared_utils_1.isUndefined)(acceptHeaderVersionParameter)
61 ? common_1.VERSION_NEUTRAL // No version was supplied
62 : acceptHeaderVersionParameter.split(this.versioningOptions.key)[1];
63 }
64 // Header Versioning Handler
65 else if (this.versioningOptions.type === common_1.VersioningType.HEADER) {
66 const customHeaderVersionParameter = ((_c = req.headers) === null || _c === void 0 ? void 0 : _c[this.versioningOptions.header]) ||
67 ((_d = req.headers) === null || _d === void 0 ? void 0 : _d[this.versioningOptions.header.toLowerCase()]);
68 return (0, shared_utils_1.isUndefined)(customHeaderVersionParameter)
69 ? common_1.VERSION_NEUTRAL // No version was supplied
70 : customHeaderVersionParameter;
71 }
72 // Custom Versioning Handler
73 else if (this.versioningOptions.type === common_1.VersioningType.CUSTOM) {
74 return this.versioningOptions.extractor(req);
75 }
76 return undefined;
77 },
78 mustMatchWhenDerived: false,
79 };
80 const instance = instanceOrOptions && instanceOrOptions.server
81 ? instanceOrOptions
82 : (0, fastify_1.fastify)(Object.assign({ constraints: {
83 version: this.versionConstraint,
84 } }, instanceOrOptions));
85 this.setInstance(instance);
86 }
87 get isParserRegistered() {
88 return !!this._isParserRegistered;
89 }
90 async init() {
91 if (this.isMiddieRegistered) {
92 return;
93 }
94 await this.registerMiddie();
95 }
96 listen(port, ...args) {
97 const isFirstArgTypeofFunction = typeof args[0] === 'function';
98 const callback = isFirstArgTypeofFunction ? args[0] : args[1];
99 const options = {
100 port: +port,
101 };
102 if (!isFirstArgTypeofFunction) {
103 options.host = args[0];
104 }
105 return this.instance.listen(options, callback);
106 }
107 get(...args) {
108 return this.injectConstraintsIfVersioned('get', ...args);
109 }
110 post(...args) {
111 return this.injectConstraintsIfVersioned('post', ...args);
112 }
113 head(...args) {
114 return this.injectConstraintsIfVersioned('head', ...args);
115 }
116 delete(...args) {
117 return this.injectConstraintsIfVersioned('delete', ...args);
118 }
119 put(...args) {
120 return this.injectConstraintsIfVersioned('put', ...args);
121 }
122 patch(...args) {
123 return this.injectConstraintsIfVersioned('patch', ...args);
124 }
125 options(...args) {
126 return this.injectConstraintsIfVersioned('options', ...args);
127 }
128 applyVersionFilter(handler, version, versioningOptions) {
129 if (!this.versioningOptions) {
130 this.versioningOptions = versioningOptions;
131 }
132 const versionedRoute = handler;
133 versionedRoute.version = version;
134 return versionedRoute;
135 }
136 reply(response, body, statusCode) {
137 const fastifyReply = this.isNativeResponse(response)
138 ? new Reply(response, {
139 context: {
140 preSerialization: null,
141 preValidation: [],
142 preHandler: [],
143 onSend: [],
144 onError: [],
145 },
146 }, {})
147 : response;
148 if (statusCode) {
149 fastifyReply.status(statusCode);
150 }
151 if (body instanceof common_1.StreamableFile) {
152 const streamHeaders = body.getHeaders();
153 if (fastifyReply.getHeader('Content-Type') === undefined &&
154 streamHeaders.type !== undefined) {
155 fastifyReply.header('Content-Type', streamHeaders.type);
156 }
157 if (fastifyReply.getHeader('Content-Disposition') === undefined &&
158 streamHeaders.disposition !== undefined) {
159 fastifyReply.header('Content-Disposition', streamHeaders.disposition);
160 }
161 if (fastifyReply.getHeader('Content-Length') === undefined &&
162 streamHeaders.length !== undefined) {
163 fastifyReply.header('Content-Length', streamHeaders.length);
164 }
165 body = body.getStream();
166 }
167 return fastifyReply.send(body);
168 }
169 status(response, statusCode) {
170 if (this.isNativeResponse(response)) {
171 response.statusCode = statusCode;
172 return response;
173 }
174 return response.code(statusCode);
175 }
176 end(response, message) {
177 response.raw.end(message);
178 }
179 render(response, view, options) {
180 return response && response.view(view, options);
181 }
182 redirect(response, statusCode, url) {
183 const code = statusCode !== null && statusCode !== void 0 ? statusCode : common_1.HttpStatus.FOUND;
184 return response.status(code).redirect(url);
185 }
186 setErrorHandler(handler) {
187 return this.instance.setErrorHandler(handler);
188 }
189 setNotFoundHandler(handler) {
190 return this.instance.setNotFoundHandler(handler);
191 }
192 getHttpServer() {
193 return this.instance.server;
194 }
195 getInstance() {
196 return this.instance;
197 }
198 register(plugin, opts) {
199 return this.instance.register(plugin, opts);
200 }
201 inject(opts) {
202 return this.instance.inject(opts);
203 }
204 async close() {
205 try {
206 return await this.instance.close();
207 }
208 catch (err) {
209 // Check if server is still running
210 if (err.code !== 'ERR_SERVER_NOT_RUNNING') {
211 throw err;
212 }
213 return;
214 }
215 }
216 initHttpServer() {
217 this.httpServer = this.instance.server;
218 }
219 useStaticAssets(options) {
220 return this.register((0, load_package_util_1.loadPackage)('@fastify/static', 'FastifyAdapter.useStaticAssets()', () => require('@fastify/static')), options);
221 }
222 setViewEngine(options) {
223 if ((0, shared_utils_1.isString)(options)) {
224 new common_1.Logger('FastifyAdapter').error("setViewEngine() doesn't support a string argument.");
225 process.exit(1);
226 }
227 return this.register((0, load_package_util_1.loadPackage)('@fastify/view', 'FastifyAdapter.setViewEngine()', () => require('@fastify/view')), options);
228 }
229 isHeadersSent(response) {
230 return response.sent;
231 }
232 setHeader(response, name, value) {
233 return response.header(name, value);
234 }
235 getRequestHostname(request) {
236 return request.hostname;
237 }
238 getRequestMethod(request) {
239 return request.raw ? request.raw.method : request.method;
240 }
241 getRequestUrl(request) {
242 return this.getRequestOriginalUrl(request.raw || request);
243 }
244 enableCors(options) {
245 this.register(Promise.resolve().then(() => require('@fastify/cors')), options);
246 }
247 registerParserMiddleware(prefix, rawBody) {
248 if (this._isParserRegistered) {
249 return;
250 }
251 this.registerUrlencodedContentParser(rawBody);
252 this.registerJsonContentParser(rawBody);
253 this._isParserRegistered = true;
254 }
255 async createMiddlewareFactory(requestMethod) {
256 if (!this.isMiddieRegistered) {
257 await this.registerMiddie();
258 }
259 return (path, callback) => {
260 let normalizedPath = path.endsWith('/*')
261 ? `${path.slice(0, -1)}(.*)`
262 : path;
263 // Fallback to "(.*)" to support plugins like GraphQL
264 normalizedPath = normalizedPath === '/(.*)' ? '(.*)' : normalizedPath;
265 // The following type assertion is valid as we use import('@fastify/middie') rather than require('@fastify/middie')
266 // ref https://github.com/fastify/middie/pull/55
267 this.instance.use(normalizedPath, callback);
268 };
269 }
270 getType() {
271 return 'fastify';
272 }
273 registerWithPrefix(factory, prefix = '/') {
274 return this.instance.register(factory, { prefix });
275 }
276 isNativeResponse(response) {
277 return !('status' in response);
278 }
279 registerJsonContentParser(rawBody) {
280 const { bodyLimit } = this.getInstance().initialConfig;
281 this.getInstance().addContentTypeParser('application/json', { parseAs: 'buffer', bodyLimit }, (req, body, done) => {
282 if (rawBody === true && Buffer.isBuffer(body)) {
283 req.rawBody = body;
284 }
285 const { onProtoPoisoning, onConstructorPoisoning } = this.instance.initialConfig;
286 const defaultJsonParser = this.instance.getDefaultJsonParser(onProtoPoisoning || 'error', onConstructorPoisoning || 'error');
287 defaultJsonParser(req, body, done);
288 });
289 }
290 registerUrlencodedContentParser(rawBody) {
291 const { bodyLimit } = this.getInstance().initialConfig;
292 this.getInstance().addContentTypeParser('application/x-www-form-urlencoded', { parseAs: 'buffer', bodyLimit }, (req, body, done) => {
293 if (rawBody === true && Buffer.isBuffer(body)) {
294 req.rawBody = body;
295 }
296 done(null, (0, querystring_1.parse)(body.toString()));
297 });
298 }
299 async registerMiddie() {
300 this.isMiddieRegistered = true;
301 await this.register(Promise.resolve().then(() => require('@fastify/middie')));
302 }
303 getRequestOriginalUrl(rawRequest) {
304 return rawRequest.originalUrl || rawRequest.url;
305 }
306 injectConstraintsIfVersioned(routerMethodKey, ...args) {
307 const handlerRef = args[args.length - 1];
308 const isVersioned = !(0, shared_utils_1.isUndefined)(handlerRef.version) &&
309 handlerRef.version !== common_1.VERSION_NEUTRAL;
310 if (isVersioned) {
311 const isPathAndRouteTuple = args.length === 2;
312 if (isPathAndRouteTuple) {
313 const options = {
314 constraints: {
315 version: handlerRef.version,
316 },
317 };
318 const path = args[0];
319 return this.instance[routerMethodKey](path, options, handlerRef);
320 }
321 }
322 return this.instance[routerMethodKey](...args);
323 }
324}
325exports.FastifyAdapter = FastifyAdapter;