1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.FastifyAdapter = void 0;
|
4 | const common_1 = require("@nestjs/common");
|
5 | const load_package_util_1 = require("@nestjs/common/utils/load-package.util");
|
6 | const shared_utils_1 = require("@nestjs/common/utils/shared.utils");
|
7 | const http_adapter_1 = require("@nestjs/core/adapters/http-adapter");
|
8 | const fastify_1 = require("fastify");
|
9 | const Reply = require("fastify/lib/reply");
|
10 |
|
11 | const querystring_1 = require("querystring");
|
12 | class 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 |
|
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
|
62 | : acceptHeaderVersionParameter.split(this.versioningOptions.key)[1];
|
63 | }
|
64 |
|
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
|
70 | : customHeaderVersionParameter;
|
71 | }
|
72 |
|
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 |
|
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 |
|
264 | normalizedPath = normalizedPath === '/(.*)' ? '(.*)' : normalizedPath;
|
265 |
|
266 |
|
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 | }
|
325 | exports.FastifyAdapter = FastifyAdapter;
|