@ailura/nestjs-hono-adapter 1.0.0

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.
Files changed (118) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +411 -0
  3. package/dist/body.d.ts +35 -0
  4. package/dist/body.d.ts.map +1 -0
  5. package/dist/body.js +180 -0
  6. package/dist/body.js.map +1 -0
  7. package/dist/bridge.d.ts +64 -0
  8. package/dist/bridge.d.ts.map +1 -0
  9. package/dist/bridge.js +168 -0
  10. package/dist/bridge.js.map +1 -0
  11. package/dist/closing.d.ts +13 -0
  12. package/dist/closing.d.ts.map +1 -0
  13. package/dist/closing.js +30 -0
  14. package/dist/closing.js.map +1 -0
  15. package/dist/context.d.ts +22 -0
  16. package/dist/context.d.ts.map +1 -0
  17. package/dist/context.js +2 -0
  18. package/dist/context.js.map +1 -0
  19. package/dist/cors-middleware.d.ts +64 -0
  20. package/dist/cors-middleware.d.ts.map +1 -0
  21. package/dist/cors-middleware.js +211 -0
  22. package/dist/cors-middleware.js.map +1 -0
  23. package/dist/handler-bridge.d.ts +51 -0
  24. package/dist/handler-bridge.d.ts.map +1 -0
  25. package/dist/handler-bridge.js +122 -0
  26. package/dist/handler-bridge.js.map +1 -0
  27. package/dist/hono-lifecycle.d.ts +90 -0
  28. package/dist/hono-lifecycle.d.ts.map +1 -0
  29. package/dist/hono-lifecycle.js +169 -0
  30. package/dist/hono-lifecycle.js.map +1 -0
  31. package/dist/index.d.ts +16 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +8 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/path.d.ts +3 -0
  36. package/dist/path.d.ts.map +1 -0
  37. package/dist/path.js +144 -0
  38. package/dist/path.js.map +1 -0
  39. package/dist/query.d.ts +19 -0
  40. package/dist/query.d.ts.map +1 -0
  41. package/dist/query.js +238 -0
  42. package/dist/query.js.map +1 -0
  43. package/dist/response-helpers.d.ts +16 -0
  44. package/dist/response-helpers.d.ts.map +1 -0
  45. package/dist/response-helpers.js +45 -0
  46. package/dist/response-helpers.js.map +1 -0
  47. package/dist/response-writer.d.ts +28 -0
  48. package/dist/response-writer.d.ts.map +1 -0
  49. package/dist/response-writer.js +52 -0
  50. package/dist/response-writer.js.map +1 -0
  51. package/dist/route-adapter.d.ts +53 -0
  52. package/dist/route-adapter.d.ts.map +1 -0
  53. package/dist/route-adapter.js +141 -0
  54. package/dist/route-adapter.js.map +1 -0
  55. package/dist/server-adapter.d.ts +106 -0
  56. package/dist/server-adapter.d.ts.map +1 -0
  57. package/dist/server-adapter.js +153 -0
  58. package/dist/server-adapter.js.map +1 -0
  59. package/dist/sse.d.ts +67 -0
  60. package/dist/sse.d.ts.map +1 -0
  61. package/dist/sse.js +211 -0
  62. package/dist/sse.js.map +1 -0
  63. package/dist/static-assets.d.ts +39 -0
  64. package/dist/static-assets.d.ts.map +1 -0
  65. package/dist/static-assets.js +155 -0
  66. package/dist/static-assets.js.map +1 -0
  67. package/dist/version-filter.d.ts +24 -0
  68. package/dist/version-filter.d.ts.map +1 -0
  69. package/dist/version-filter.js +107 -0
  70. package/dist/version-filter.js.map +1 -0
  71. package/dist/versioned-route.d.ts +21 -0
  72. package/dist/versioned-route.d.ts.map +1 -0
  73. package/dist/versioned-route.js +15 -0
  74. package/dist/versioned-route.js.map +1 -0
  75. package/dist/views.d.ts +42 -0
  76. package/dist/views.d.ts.map +1 -0
  77. package/dist/views.js +110 -0
  78. package/dist/views.js.map +1 -0
  79. package/dist/ws-adapter.d.ts +81 -0
  80. package/dist/ws-adapter.d.ts.map +1 -0
  81. package/dist/ws-adapter.js +214 -0
  82. package/dist/ws-adapter.js.map +1 -0
  83. package/dist/ws-client.d.ts +68 -0
  84. package/dist/ws-client.d.ts.map +1 -0
  85. package/dist/ws-client.js +135 -0
  86. package/dist/ws-client.js.map +1 -0
  87. package/dist/ws-server.d.ts +23 -0
  88. package/dist/ws-server.d.ts.map +1 -0
  89. package/dist/ws-server.js +37 -0
  90. package/dist/ws-server.js.map +1 -0
  91. package/dist/ws.d.ts +14 -0
  92. package/dist/ws.d.ts.map +1 -0
  93. package/dist/ws.js +12 -0
  94. package/dist/ws.js.map +1 -0
  95. package/package.json +99 -0
  96. package/src/body.ts +251 -0
  97. package/src/bridge.ts +308 -0
  98. package/src/closing.ts +38 -0
  99. package/src/context.ts +25 -0
  100. package/src/cors-middleware.ts +347 -0
  101. package/src/handler-bridge.ts +226 -0
  102. package/src/hono-lifecycle.ts +259 -0
  103. package/src/index.ts +27 -0
  104. package/src/path.ts +169 -0
  105. package/src/query.ts +304 -0
  106. package/src/response-helpers.ts +60 -0
  107. package/src/response-writer.ts +100 -0
  108. package/src/route-adapter.ts +261 -0
  109. package/src/server-adapter.ts +294 -0
  110. package/src/sse.ts +274 -0
  111. package/src/static-assets.ts +247 -0
  112. package/src/version-filter.ts +170 -0
  113. package/src/versioned-route.ts +30 -0
  114. package/src/views.ts +188 -0
  115. package/src/ws-adapter.ts +329 -0
  116. package/src/ws-client.ts +190 -0
  117. package/src/ws-server.ts +40 -0
  118. package/src/ws.ts +13 -0
package/src/query.ts ADDED
@@ -0,0 +1,304 @@
1
+ /**
2
+ * A query string parsed into the shape a controller receives:
3
+ * every leaf is a string, and a name that appears more than
4
+ * once or uses brackets becomes a list or a nested object.
5
+ */
6
+ type ParsedQuery = Record<string, unknown>;
7
+
8
+ /**
9
+ * Names that reach the prototype chain. A parser that writes
10
+ * them hands a caller control over every object in the process,
11
+ * so they are dropped instead.
12
+ */
13
+ const FORBIDDEN_NAMES = new Set([
14
+ '__proto__',
15
+ 'constructor',
16
+ 'prototype',
17
+ ]);
18
+
19
+ const INDEX = /^\d+$/u;
20
+ const BRACKETS = /\[(?<name>[^\]]*)\]/gu;
21
+ const MISSING = -1;
22
+
23
+ /** One assignment: the names still to walk and the value. */
24
+ interface Target {
25
+ readonly names: readonly string[];
26
+ readonly value: string;
27
+ }
28
+
29
+ function isRecord(
30
+ value: unknown,
31
+ ): value is Record<string, unknown> {
32
+ return (
33
+ typeof value === 'object' &&
34
+ value !== null &&
35
+ !Array.isArray(value)
36
+ );
37
+ }
38
+
39
+ function asRecord(value: unknown): Record<string, unknown> {
40
+ if (isRecord(value)) {
41
+ return value;
42
+ }
43
+ return {};
44
+ }
45
+
46
+ function asList(value: unknown): unknown[] {
47
+ if (Array.isArray(value)) {
48
+ return value;
49
+ }
50
+ return [];
51
+ }
52
+
53
+ /**
54
+ * Says whether the next segment builds a list: `[]` appends,
55
+ * and a number indexes.
56
+ */
57
+ function isListStep(name: string): boolean {
58
+ return name === '' || INDEX.test(name);
59
+ }
60
+
61
+ /**
62
+ * Decodes one side of a pair. A sequence that is not valid
63
+ * percent-encoding is kept as it arrived, which is what the
64
+ * platform parsers do rather than refusing the request.
65
+ */
66
+ function decode(value: string): string {
67
+ const spaced = value.replaceAll('+', ' ');
68
+ try {
69
+ return decodeURIComponent(spaced);
70
+ } catch {
71
+ return spaced;
72
+ }
73
+ }
74
+
75
+ /**
76
+ * Splits `filter[name][]` into `['filter', 'name', '']`. A name
77
+ * without brackets is its own single segment.
78
+ */
79
+ function toNames(name: string): string[] {
80
+ const first = name.indexOf('[');
81
+ if (first === MISSING) {
82
+ return [name];
83
+ }
84
+ const names = [name.slice(0, first)];
85
+ for (const match of name.slice(first).matchAll(BRACKETS)) {
86
+ const groups = match.groups ?? {};
87
+ names.push(groups.name ?? '');
88
+ }
89
+ return names;
90
+ }
91
+
92
+ /**
93
+ * Adds a value to a name that may already hold one. The second
94
+ * value turns the entry into a list, which is how a repeated
95
+ * name is read.
96
+ */
97
+ function repeated(current: unknown, value: string): unknown {
98
+ if (current === undefined) {
99
+ return value;
100
+ }
101
+ if (Array.isArray(current)) {
102
+ current.push(value);
103
+ return current;
104
+ }
105
+ return [current, value];
106
+ }
107
+
108
+ function sidesOf(pair: string): readonly [string, string] {
109
+ const separator = pair.indexOf('=');
110
+ if (separator === MISSING) {
111
+ return [pair, ''];
112
+ }
113
+ return [pair.slice(0, separator), pair.slice(separator + 1)];
114
+ }
115
+
116
+ function withoutQuestionMark(source: string): string {
117
+ if (source.startsWith('?')) {
118
+ return source.slice(1);
119
+ }
120
+ return source;
121
+ }
122
+
123
+ function accepts(names: readonly string[]): boolean {
124
+ if (names.length === 0) {
125
+ return false;
126
+ }
127
+ return !names.some((segment) => FORBIDDEN_NAMES.has(segment));
128
+ }
129
+
130
+ /**
131
+ * Builds the tree one pair at a time.
132
+ *
133
+ * A step whose next name is `[]` or a number builds a list, and
134
+ * every other step builds an object; the methods below are
135
+ * split along that decision so each one stays readable on its
136
+ * own.
137
+ */
138
+ class QueryBuilder {
139
+ private readonly query: ParsedQuery = {};
140
+
141
+ public parse(source: string): ParsedQuery {
142
+ for (const pair of withoutQuestionMark(source).split('&')) {
143
+ this.addPair(pair);
144
+ }
145
+ return this.query;
146
+ }
147
+
148
+ private addPair(pair: string): void {
149
+ if (pair === '') {
150
+ return;
151
+ }
152
+ const [name, value] = sidesOf(pair);
153
+ const names = toNames(decode(name));
154
+ if (!accepts(names)) {
155
+ return;
156
+ }
157
+ this.assign(this.query, { names, value: decode(value) });
158
+ }
159
+
160
+ private assign(
161
+ container: Record<string, unknown>,
162
+ target: Target,
163
+ ): void {
164
+ const [name, ...rest] = target.names;
165
+ if (name === undefined) {
166
+ return;
167
+ }
168
+ if (rest.length === 0) {
169
+ container[name] = repeated(container[name], target.value);
170
+ return;
171
+ }
172
+ this.assignDeeper(container, name, {
173
+ names: rest,
174
+ value: target.value,
175
+ });
176
+ }
177
+
178
+ private assignDeeper(
179
+ container: Record<string, unknown>,
180
+ name: string,
181
+ target: Target,
182
+ ): void {
183
+ const [next] = target.names;
184
+ if (isListStep(next ?? '')) {
185
+ this.assignList(container, name, target);
186
+ return;
187
+ }
188
+ this.assignRecord(container, name, target);
189
+ }
190
+
191
+ private assignList(
192
+ container: Record<string, unknown>,
193
+ name: string,
194
+ target: Target,
195
+ ): void {
196
+ const list = asList(container[name]);
197
+ container[name] = list;
198
+ this.intoList(list, target);
199
+ }
200
+
201
+ private assignRecord(
202
+ container: Record<string, unknown>,
203
+ name: string,
204
+ target: Target,
205
+ ): void {
206
+ const existing = container[name];
207
+ if (Array.isArray(existing)) {
208
+ const nested: Record<string, unknown> = {};
209
+ existing.push(nested);
210
+ this.assign(nested, target);
211
+ return;
212
+ }
213
+ container[name] = this.toRecord(existing, target);
214
+ }
215
+
216
+ private intoList(list: unknown[], target: Target): void {
217
+ const [name, ...rest] = target.names;
218
+ if (name === undefined) {
219
+ return;
220
+ }
221
+ const nested = { names: rest, value: target.value };
222
+ if (name === '') {
223
+ this.addElement(list, nested);
224
+ return;
225
+ }
226
+ this.addIndex(list, name, nested);
227
+ }
228
+
229
+ private addIndex(
230
+ list: unknown[],
231
+ name: string,
232
+ target: Target,
233
+ ): void {
234
+ const index = Number(name);
235
+ if (!Number.isSafeInteger(index) || index < 0) {
236
+ return;
237
+ }
238
+ this.addAt(list, index, target);
239
+ }
240
+
241
+ private addElement(list: unknown[], target: Target): void {
242
+ const [next] = target.names;
243
+ if (next === undefined) {
244
+ list.push(target.value);
245
+ return;
246
+ }
247
+ if (isListStep(next)) {
248
+ const nested: unknown[] = [];
249
+ list.push(nested);
250
+ this.intoList(nested, target);
251
+ return;
252
+ }
253
+ list.push(this.toRecord(undefined, target));
254
+ }
255
+
256
+ private addAt(
257
+ list: unknown[],
258
+ index: number,
259
+ target: Target,
260
+ ): void {
261
+ const existing = list[index];
262
+ if (target.names.length === 0) {
263
+ list[index] = repeated(existing, target.value);
264
+ return;
265
+ }
266
+ const [next] = target.names;
267
+ if (isListStep(next ?? '')) {
268
+ list[index] = this.toList(existing, target);
269
+ return;
270
+ }
271
+ list[index] = this.toRecord(existing, target);
272
+ }
273
+
274
+ private toList(existing: unknown, target: Target): unknown[] {
275
+ const nested = asList(existing);
276
+ this.intoList(nested, target);
277
+ return nested;
278
+ }
279
+
280
+ private toRecord(
281
+ existing: unknown,
282
+ target: Target,
283
+ ): Record<string, unknown> {
284
+ const nested = asRecord(existing);
285
+ this.assign(nested, target);
286
+ return nested;
287
+ }
288
+ }
289
+
290
+ /**
291
+ * Parses a query string, with or without its leading `?`.
292
+ *
293
+ * Repeated names become lists, `name[]` appends, `name[0]`
294
+ * indexes and `name[child]` nests; anything else is a string.
295
+ * The grammar is the one the platform parsers accept, which is
296
+ * what a controller written against Express or Fastify expects
297
+ * to read.
298
+ */
299
+ function parseQuery(source: string): ParsedQuery {
300
+ return new QueryBuilder().parse(source);
301
+ }
302
+
303
+ export { parseQuery };
304
+ export type { ParsedQuery };
@@ -0,0 +1,60 @@
1
+ import type { NestContext } from './context.ts';
2
+
3
+ /** The Hono helpers that write the answer for a request. */
4
+ const RESPONSE_HELPERS = [
5
+ 'body',
6
+ 'html',
7
+ 'json',
8
+ 'newResponse',
9
+ 'notFound',
10
+ 'redirect',
11
+ 'text',
12
+ ] as const;
13
+
14
+ type ResponseHelper = (...args: never[]) => Response;
15
+
16
+ type ResponseHelperCarrier = Record<
17
+ (typeof RESPONSE_HELPERS)[number],
18
+ ResponseHelper
19
+ >;
20
+
21
+ /**
22
+ * Wraps one helper so that the response it built becomes the
23
+ * response of the context.
24
+ */
25
+ function wrapHelper(
26
+ carrier: ResponseHelperCarrier,
27
+ name: (typeof RESPONSE_HELPERS)[number],
28
+ context: NestContext,
29
+ ): void {
30
+ const helper = carrier[name];
31
+ if (typeof helper !== 'function') {
32
+ return;
33
+ }
34
+ carrier[name] = (...args: never[]): Response => {
35
+ const response = helper.apply(context, args);
36
+ context.res = response;
37
+ return response;
38
+ };
39
+ }
40
+
41
+ /**
42
+ * Makes the Hono response helpers finalize the context.
43
+ *
44
+ * A handler that answers through `@Res()` calls one of these
45
+ * helpers instead of returning a value, and Nest then skips its
46
+ * own reply path. Hono only finalizes a context when `res` is
47
+ * assigned, and the helpers do not assign it, so the answer
48
+ * would be dropped and the client would read an empty response.
49
+ * Wrapping them on the instance — Hono's helpers are own
50
+ * properties, not prototype methods — keeps the imperative
51
+ * style working without taking the response away from Hono.
52
+ */
53
+ function finalizeOnResponse(context: NestContext): void {
54
+ const carrier = context as unknown as ResponseHelperCarrier;
55
+ for (const name of RESPONSE_HELPERS) {
56
+ wrapHelper(carrier, name, context);
57
+ }
58
+ }
59
+
60
+ export { finalizeOnResponse };
@@ -0,0 +1,100 @@
1
+ import { HttpStatus } from '@nestjs/common';
2
+
3
+ import { buildResponse } from './bridge.ts';
4
+ import type { NestContext } from './context.ts';
5
+
6
+ /**
7
+ * Writes what Nest answers onto the context of the request.
8
+ *
9
+ * A Hono context doubles as the response object, so a value is
10
+ * turned into a Web response and stored on it. A status set
11
+ * through `@HttpCode()` or `@Res()` is remembered per request,
12
+ * because the adapter is asked for it after the handler ran.
13
+ */
14
+ class ResponseWriter {
15
+ private readonly pendingStatus = new WeakMap<
16
+ NestContext,
17
+ number
18
+ >();
19
+
20
+ public status(
21
+ response: NestContext,
22
+ statusCode: number,
23
+ ): void {
24
+ this.pendingStatus.set(response, statusCode);
25
+ }
26
+
27
+ /**
28
+ * The status Nest asked for, before any body was written. An
29
+ * event stream needs it while the handler still runs.
30
+ */
31
+ public statusOf(response: NestContext): number | undefined {
32
+ return this.pendingStatus.get(response);
33
+ }
34
+
35
+ public reply(
36
+ response: NestContext,
37
+ body: unknown,
38
+ statusCode?: number,
39
+ ): void {
40
+ response.res = buildResponse(
41
+ response,
42
+ body,
43
+ statusCode ?? this.resolve(response),
44
+ );
45
+ }
46
+
47
+ public end(response: NestContext, message?: string): void {
48
+ response.res = buildResponse(
49
+ response,
50
+ message,
51
+ this.resolve(response),
52
+ );
53
+ }
54
+
55
+ public redirect(
56
+ response: NestContext,
57
+ statusCode: number,
58
+ url: string,
59
+ ): void {
60
+ response.header('Location', url);
61
+ response.res = buildResponse(
62
+ response,
63
+ undefined,
64
+ statusCode,
65
+ );
66
+ }
67
+
68
+ public setHeader(
69
+ response: NestContext,
70
+ name: string,
71
+ value: string,
72
+ ): void {
73
+ response.header(name, value);
74
+ }
75
+
76
+ public getHeader(
77
+ response: NestContext,
78
+ name: string,
79
+ ): string | undefined {
80
+ return response.res.headers.get(name) ?? undefined;
81
+ }
82
+
83
+ public appendHeader(
84
+ response: NestContext,
85
+ name: string,
86
+ value: string,
87
+ ): void {
88
+ response.header(name, value, { append: true });
89
+ }
90
+
91
+ public isHeadersSent(response: NestContext): boolean {
92
+ return response.finalized;
93
+ }
94
+
95
+ private resolve(response: NestContext): number {
96
+ return this.pendingStatus.get(response) ?? HttpStatus.OK;
97
+ }
98
+ }
99
+
100
+ export { ResponseWriter };
@@ -0,0 +1,261 @@
1
+ import type { ServerType } from '@hono/node-server';
2
+ import { RequestMethod } from '@nestjs/common';
3
+ import type { VersioningOptions } from '@nestjs/common';
4
+ import { AbstractHttpAdapter } from '@nestjs/core';
5
+
6
+ import type { NestHandler, NestRequest } from './bridge.ts';
7
+ import type { NestContext } from './context.ts';
8
+ import { createVersionFilter } from './version-filter.ts';
9
+ import type { VersionValue } from './version-filter.ts';
10
+ import { asVersionedRoute } from './versioned-route.ts';
11
+ import type { VersionedRoute } from './versioned-route.ts';
12
+
13
+ /**
14
+ * Nest's verb enum mapped onto HTTP method names, for the one
15
+ * place that receives the enum rather than the name.
16
+ */
17
+ const METHOD_BY_REQUEST_METHOD: Record<number, string> = {
18
+ [RequestMethod.GET]: 'GET',
19
+ [RequestMethod.POST]: 'POST',
20
+ [RequestMethod.PUT]: 'PUT',
21
+ [RequestMethod.DELETE]: 'DELETE',
22
+ [RequestMethod.PATCH]: 'PATCH',
23
+ [RequestMethod.ALL]: 'ALL',
24
+ [RequestMethod.OPTIONS]: 'OPTIONS',
25
+ [RequestMethod.HEAD]: 'HEAD',
26
+ [RequestMethod.SEARCH]: 'SEARCH',
27
+ [RequestMethod.PROPFIND]: 'PROPFIND',
28
+ [RequestMethod.PROPPATCH]: 'PROPPATCH',
29
+ [RequestMethod.MKCOL]: 'MKCOL',
30
+ [RequestMethod.COPY]: 'COPY',
31
+ [RequestMethod.MOVE]: 'MOVE',
32
+ [RequestMethod.LOCK]: 'LOCK',
33
+ [RequestMethod.UNLOCK]: 'UNLOCK',
34
+ [RequestMethod.QUERY]: 'QUERY',
35
+ };
36
+
37
+ /**
38
+ * Method registered for a route that answers every verb, and
39
+ * for middleware, which Nest mounts without one.
40
+ */
41
+ const ALL_METHOD = 'ALL';
42
+
43
+ /** Path a handler is mounted at when Nest passes it without one. */
44
+ const ROOT_PATH = '/';
45
+
46
+ /** Either a route path, or the handler to mount at the root. */
47
+ type RouteTarget = string | NestHandler;
48
+
49
+ function isHandler(value: unknown): value is NestHandler {
50
+ return typeof value === 'function';
51
+ }
52
+
53
+ function resolveRoute(
54
+ target: RouteTarget,
55
+ handler: NestHandler | undefined,
56
+ ): [string, NestHandler] {
57
+ if (typeof target === 'function') {
58
+ return [ROOT_PATH, target];
59
+ }
60
+ if (handler === undefined) {
61
+ throw new TypeError(
62
+ 'A route registered with a path also needs a handler.',
63
+ );
64
+ }
65
+ return [target, handler];
66
+ }
67
+
68
+ /**
69
+ * The routing half of the Nest adapter contract: every verb a
70
+ * route can be mapped to, the middleware factory and version
71
+ * filtering. The concrete adapter supplies the registration, so
72
+ * this half never touches Hono itself.
73
+ */
74
+ abstract class RouteAdapter extends AbstractHttpAdapter<
75
+ ServerType,
76
+ NestRequest,
77
+ NestContext
78
+ > {
79
+ /** Registers one route with the underlying framework. */
80
+ protected abstract register(
81
+ method: string,
82
+ path: string,
83
+ handler: NestHandler,
84
+ ): void;
85
+
86
+ public override get(
87
+ target: RouteTarget,
88
+ handler?: NestHandler,
89
+ ): void {
90
+ this.route('GET', target, handler);
91
+ }
92
+
93
+ public override post(
94
+ target: RouteTarget,
95
+ handler?: NestHandler,
96
+ ): void {
97
+ this.route('POST', target, handler);
98
+ }
99
+
100
+ public override put(
101
+ target: RouteTarget,
102
+ handler?: NestHandler,
103
+ ): void {
104
+ this.route('PUT', target, handler);
105
+ }
106
+
107
+ public override patch(
108
+ target: RouteTarget,
109
+ handler?: NestHandler,
110
+ ): void {
111
+ this.route('PATCH', target, handler);
112
+ }
113
+
114
+ public override delete(
115
+ target: RouteTarget,
116
+ handler?: NestHandler,
117
+ ): void {
118
+ this.route('DELETE', target, handler);
119
+ }
120
+
121
+ public override options(
122
+ target: RouteTarget,
123
+ handler?: NestHandler,
124
+ ): void {
125
+ this.route('OPTIONS', target, handler);
126
+ }
127
+
128
+ public override head(
129
+ target: RouteTarget,
130
+ handler?: NestHandler,
131
+ ): void {
132
+ this.route('HEAD', target, handler);
133
+ }
134
+
135
+ public override all(
136
+ target: RouteTarget,
137
+ handler?: NestHandler,
138
+ ): void {
139
+ this.route(ALL_METHOD, target, handler);
140
+ }
141
+
142
+ public override search(
143
+ target: RouteTarget,
144
+ handler?: NestHandler,
145
+ ): void {
146
+ this.route('SEARCH', target, handler);
147
+ }
148
+
149
+ public override query(
150
+ target: RouteTarget,
151
+ handler?: NestHandler,
152
+ ): void {
153
+ this.route('QUERY', target, handler);
154
+ }
155
+
156
+ public override propfind(
157
+ target: RouteTarget,
158
+ handler?: NestHandler,
159
+ ): void {
160
+ this.route('PROPFIND', target, handler);
161
+ }
162
+
163
+ public override proppatch(
164
+ target: RouteTarget,
165
+ handler?: NestHandler,
166
+ ): void {
167
+ this.route('PROPPATCH', target, handler);
168
+ }
169
+
170
+ public override mkcol(
171
+ target: RouteTarget,
172
+ handler?: NestHandler,
173
+ ): void {
174
+ this.route('MKCOL', target, handler);
175
+ }
176
+
177
+ public override copy(
178
+ target: RouteTarget,
179
+ handler?: NestHandler,
180
+ ): void {
181
+ this.route('COPY', target, handler);
182
+ }
183
+
184
+ public override move(
185
+ target: RouteTarget,
186
+ handler?: NestHandler,
187
+ ): void {
188
+ this.route('MOVE', target, handler);
189
+ }
190
+
191
+ public override lock(
192
+ target: RouteTarget,
193
+ handler?: NestHandler,
194
+ ): void {
195
+ this.route('LOCK', target, handler);
196
+ }
197
+
198
+ public override unlock(
199
+ target: RouteTarget,
200
+ handler?: NestHandler,
201
+ ): void {
202
+ this.route('UNLOCK', target, handler);
203
+ }
204
+
205
+ /**
206
+ * Mounts global middleware. Nest calls this with a handler,
207
+ * or with a path and a handler, and both answer every
208
+ * method.
209
+ */
210
+ public override use(...args: unknown[]): void {
211
+ const [first, second] = args;
212
+ if (isHandler(first) && second === undefined) {
213
+ this.route(ALL_METHOD, first);
214
+ return;
215
+ }
216
+ if (typeof first === 'string' && isHandler(second)) {
217
+ this.route(ALL_METHOD, first, second);
218
+ return;
219
+ }
220
+ throw new TypeError(
221
+ 'Middleware is mounted as a handler, or as a path and a ' +
222
+ 'handler.',
223
+ );
224
+ }
225
+
226
+ public override createMiddlewareFactory(
227
+ requestMethod: RequestMethod,
228
+ ): (path: string, callback: unknown) => void {
229
+ const method =
230
+ METHOD_BY_REQUEST_METHOD[requestMethod] ?? ALL_METHOD;
231
+ return (path, callback) => {
232
+ if (!isHandler(callback)) {
233
+ throw new TypeError(
234
+ 'Nest middleware must be registered as a function.',
235
+ );
236
+ }
237
+ this.route(method, path, callback);
238
+ };
239
+ }
240
+
241
+ public override applyVersionFilter(
242
+ handler: NestHandler,
243
+ version: VersionValue,
244
+ versioningOptions: VersioningOptions,
245
+ ): VersionedRoute {
246
+ return asVersionedRoute(
247
+ createVersionFilter(handler, version, versioningOptions),
248
+ );
249
+ }
250
+
251
+ private route(
252
+ method: string,
253
+ target: RouteTarget,
254
+ handler?: NestHandler,
255
+ ): void {
256
+ const [path, routeHandler] = resolveRoute(target, handler);
257
+ this.register(method, path, routeHandler);
258
+ }
259
+ }
260
+
261
+ export { ALL_METHOD, RouteAdapter };