@fluojs/http 1.0.0-beta.1 → 1.0.0-beta.11
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/README.ko.md +39 -5
- package/README.md +39 -5
- package/dist/adapter.d.ts +31 -0
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +37 -0
- package/dist/adapters/binding.d.ts +6 -0
- package/dist/adapters/binding.d.ts.map +1 -1
- package/dist/adapters/binding.js +54 -55
- package/dist/adapters/dto-binding-plan.d.ts +22 -0
- package/dist/adapters/dto-binding-plan.d.ts.map +1 -0
- package/dist/adapters/dto-binding-plan.js +86 -0
- package/dist/adapters/dto-validation-adapter.d.ts +3 -1
- package/dist/adapters/dto-validation-adapter.d.ts.map +1 -1
- package/dist/adapters/dto-validation-adapter.js +10 -16
- package/dist/context/request-context-node-store.d.ts +25 -0
- package/dist/context/request-context-node-store.d.ts.map +1 -0
- package/dist/context/request-context-node-store.js +32 -0
- package/dist/context/request-context-stack-store.d.ts +8 -0
- package/dist/context/request-context-stack-store.d.ts.map +1 -0
- package/dist/context/request-context-stack-store.js +29 -0
- package/dist/context/request-context-store.d.ts +7 -0
- package/dist/context/request-context-store.d.ts.map +1 -0
- package/dist/context/request-context-store.js +1 -0
- package/dist/context/request-context.d.ts +7 -3
- package/dist/context/request-context.d.ts.map +1 -1
- package/dist/context/request-context.js +17 -5
- package/dist/context/sse.d.ts +38 -0
- package/dist/context/sse.d.ts.map +1 -1
- package/dist/context/sse.js +50 -2
- package/dist/decorators.d.ts.map +1 -1
- package/dist/decorators.js +262 -53
- package/dist/dispatch/dispatch-content-negotiation.d.ts +17 -0
- package/dist/dispatch/dispatch-content-negotiation.d.ts.map +1 -1
- package/dist/dispatch/dispatch-content-negotiation.js +21 -0
- package/dist/dispatch/dispatch-error-policy.d.ts +8 -0
- package/dist/dispatch/dispatch-error-policy.d.ts.map +1 -1
- package/dist/dispatch/dispatch-error-policy.js +9 -0
- package/dist/dispatch/dispatch-handler-policy.d.ts +11 -1
- package/dist/dispatch/dispatch-handler-policy.d.ts.map +1 -1
- package/dist/dispatch/dispatch-handler-policy.js +17 -5
- package/dist/dispatch/dispatch-response-policy.d.ts +11 -1
- package/dist/dispatch/dispatch-response-policy.d.ts.map +1 -1
- package/dist/dispatch/dispatch-response-policy.js +44 -2
- package/dist/dispatch/dispatch-routing-policy.d.ts +13 -0
- package/dist/dispatch/dispatch-routing-policy.d.ts.map +1 -1
- package/dist/dispatch/dispatch-routing-policy.js +49 -4
- package/dist/dispatch/dispatcher.d.ts +24 -7
- package/dist/dispatch/dispatcher.d.ts.map +1 -1
- package/dist/dispatch/dispatcher.js +464 -48
- package/dist/dispatch/fast-path/debug-visibility.d.ts +18 -0
- package/dist/dispatch/fast-path/debug-visibility.d.ts.map +1 -0
- package/dist/dispatch/fast-path/debug-visibility.js +39 -0
- package/dist/dispatch/fast-path/eligibility-checker.d.ts +22 -0
- package/dist/dispatch/fast-path/eligibility-checker.d.ts.map +1 -0
- package/dist/dispatch/fast-path/eligibility-checker.js +107 -0
- package/dist/dispatch/fast-path/eligibility.d.ts +61 -0
- package/dist/dispatch/fast-path/eligibility.d.ts.map +1 -0
- package/dist/dispatch/fast-path/eligibility.js +23 -0
- package/dist/dispatch/fast-path/fast-path-executor.d.ts +21 -0
- package/dist/dispatch/fast-path/fast-path-executor.d.ts.map +1 -0
- package/dist/dispatch/fast-path/fast-path-executor.js +80 -0
- package/dist/dispatch/fast-path/index.d.ts +6 -0
- package/dist/dispatch/fast-path/index.d.ts.map +1 -0
- package/dist/dispatch/fast-path/index.js +4 -0
- package/dist/dispatch/native-route-handoff.d.ts +53 -0
- package/dist/dispatch/native-route-handoff.d.ts.map +1 -0
- package/dist/dispatch/native-route-handoff.js +97 -0
- package/dist/errors.d.ts +3 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +4 -0
- package/dist/guards.d.ts +7 -0
- package/dist/guards.d.ts.map +1 -1
- package/dist/guards.js +11 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/input-error-detail.d.ts +9 -0
- package/dist/input-error-detail.d.ts.map +1 -1
- package/dist/input-error-detail.js +10 -0
- package/dist/interceptors.d.ts +8 -0
- package/dist/interceptors.d.ts.map +1 -1
- package/dist/interceptors.js +14 -1
- package/dist/internal.d.ts +3 -0
- package/dist/internal.d.ts.map +1 -1
- package/dist/internal.js +3 -1
- package/dist/mapping.d.ts +7 -0
- package/dist/mapping.d.ts.map +1 -1
- package/dist/mapping.js +93 -11
- package/dist/middleware/correlation.d.ts +5 -0
- package/dist/middleware/correlation.d.ts.map +1 -1
- package/dist/middleware/correlation.js +14 -2
- package/dist/middleware/cors.d.ts +9 -0
- package/dist/middleware/cors.d.ts.map +1 -1
- package/dist/middleware/cors.js +11 -0
- package/dist/middleware/middleware.d.ts +34 -0
- package/dist/middleware/middleware.d.ts.map +1 -1
- package/dist/middleware/middleware.js +47 -0
- package/dist/middleware/security-headers.d.ts +9 -0
- package/dist/middleware/security-headers.d.ts.map +1 -1
- package/dist/middleware/security-headers.js +11 -0
- package/dist/route-path.d.ts +41 -0
- package/dist/route-path.d.ts.map +1 -1
- package/dist/route-path.js +50 -0
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/decorators.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineControllerMetadata, defineDtoFieldBindingMetadata, defineRouteMetadata, ensureMetadataSymbol, getControllerMetadata, getDtoFieldBindingMetadata, getRouteMetadata, getStandardMetadataBag as readStandardMetadataBag } from '@fluojs/core/internal';
|
|
2
2
|
import { validateRoutePath } from './route-path.js';
|
|
3
3
|
const standardControllerMetadataKey = Symbol.for('fluo.standard.controller');
|
|
4
4
|
const standardRouteMetadataKey = Symbol.for('fluo.standard.route');
|
|
5
5
|
const standardDtoBindingMetadataKey = Symbol.for('fluo.standard.dto-binding');
|
|
6
|
+
const legacyRouteMetadataStore = new WeakMap();
|
|
7
|
+
const legacyDtoBindingMetadataStore = new WeakMap();
|
|
8
|
+
ensureMetadataSymbol();
|
|
6
9
|
function normalizeProducesMediaTypes(mediaTypes) {
|
|
7
10
|
const normalized = [];
|
|
8
11
|
for (const mediaType of mediaTypes) {
|
|
@@ -24,8 +27,115 @@ function mergeUnique(existing, values) {
|
|
|
24
27
|
return merged;
|
|
25
28
|
}
|
|
26
29
|
function getStandardMetadataBag(metadata) {
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
return typeof metadata === 'object' && metadata !== null ? metadata : {};
|
|
31
|
+
}
|
|
32
|
+
function isStandardDecoratorContext(value) {
|
|
33
|
+
return typeof value === 'object' && value !== null && 'kind' in value && 'name' in value;
|
|
34
|
+
}
|
|
35
|
+
function isMetadataPropertyKey(value) {
|
|
36
|
+
return typeof value === 'string' || typeof value === 'symbol';
|
|
37
|
+
}
|
|
38
|
+
function getLegacyRouteRecord(target, propertyKey) {
|
|
39
|
+
let routeMap = legacyRouteMetadataStore.get(target);
|
|
40
|
+
if (!routeMap) {
|
|
41
|
+
routeMap = new Map();
|
|
42
|
+
legacyRouteMetadataStore.set(target, routeMap);
|
|
43
|
+
}
|
|
44
|
+
let record = routeMap.get(propertyKey);
|
|
45
|
+
if (!record) {
|
|
46
|
+
record = {};
|
|
47
|
+
routeMap.set(propertyKey, record);
|
|
48
|
+
}
|
|
49
|
+
return record;
|
|
50
|
+
}
|
|
51
|
+
function flushLegacyRouteMetadata(target, propertyKey, record) {
|
|
52
|
+
if (!record.method || record.path === undefined) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const existing = getRouteMetadata(target, propertyKey);
|
|
56
|
+
const metadata = {
|
|
57
|
+
guards: mergeUnique(existing?.guards, record.guards ?? []),
|
|
58
|
+
headers: record.headers ?? existing?.headers,
|
|
59
|
+
interceptors: mergeUnique(existing?.interceptors, record.interceptors ?? []),
|
|
60
|
+
method: record.method,
|
|
61
|
+
path: record.path,
|
|
62
|
+
redirect: record.redirect ?? existing?.redirect,
|
|
63
|
+
request: record.request ?? existing?.request,
|
|
64
|
+
successStatus: record.successStatus ?? existing?.successStatus,
|
|
65
|
+
version: record.version ?? existing?.version
|
|
66
|
+
};
|
|
67
|
+
defineRouteMetadata(target, propertyKey, metadata);
|
|
68
|
+
}
|
|
69
|
+
function mergeLegacyRouteMetadata(target, propertyKey, partial) {
|
|
70
|
+
const record = getLegacyRouteRecord(target, propertyKey);
|
|
71
|
+
Object.assign(record, partial);
|
|
72
|
+
flushLegacyRouteMetadata(target, propertyKey, record);
|
|
73
|
+
}
|
|
74
|
+
function defineLegacyControllerMetadata(target, partial) {
|
|
75
|
+
const existing = getControllerMetadata(target);
|
|
76
|
+
defineControllerMetadata(target, {
|
|
77
|
+
basePath: partial.basePath ?? existing?.basePath ?? '',
|
|
78
|
+
guards: partial.guards ?? existing?.guards,
|
|
79
|
+
interceptors: partial.interceptors ?? existing?.interceptors,
|
|
80
|
+
version: partial.version ?? existing?.version
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
function getLegacyDtoBindingRecord(target, propertyKey) {
|
|
84
|
+
let bindingMap = legacyDtoBindingMetadataStore.get(target);
|
|
85
|
+
if (!bindingMap) {
|
|
86
|
+
bindingMap = new Map();
|
|
87
|
+
legacyDtoBindingMetadataStore.set(target, bindingMap);
|
|
88
|
+
}
|
|
89
|
+
let record = bindingMap.get(propertyKey);
|
|
90
|
+
if (!record) {
|
|
91
|
+
record = {};
|
|
92
|
+
bindingMap.set(propertyKey, record);
|
|
93
|
+
}
|
|
94
|
+
return record;
|
|
95
|
+
}
|
|
96
|
+
function mergeLegacyDtoBinding(target, propertyKey, partial) {
|
|
97
|
+
const record = getLegacyDtoBindingRecord(target, propertyKey);
|
|
98
|
+
Object.assign(record, partial);
|
|
99
|
+
const source = record.source ?? getDtoFieldBindingMetadata(target, propertyKey)?.source;
|
|
100
|
+
if (!source) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
defineDtoFieldBindingMetadata(target, propertyKey, {
|
|
104
|
+
converter: record.converter,
|
|
105
|
+
key: record.key,
|
|
106
|
+
optional: record.optional,
|
|
107
|
+
source
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function appendLegacyRouteHeader(target, propertyKey, name, value) {
|
|
111
|
+
const record = getLegacyRouteRecord(target, propertyKey);
|
|
112
|
+
record.headers = [...(record.headers ?? []), {
|
|
113
|
+
name,
|
|
114
|
+
value
|
|
115
|
+
}];
|
|
116
|
+
flushLegacyRouteMetadata(target, propertyKey, record);
|
|
117
|
+
}
|
|
118
|
+
function appendLegacyControllerGuards(target, guards) {
|
|
119
|
+
const existing = getControllerMetadata(target);
|
|
120
|
+
defineLegacyControllerMetadata(target, {
|
|
121
|
+
guards: mergeUnique(existing?.guards, guards)
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
function appendLegacyControllerInterceptors(target, interceptors) {
|
|
125
|
+
const existing = getControllerMetadata(target);
|
|
126
|
+
defineLegacyControllerMetadata(target, {
|
|
127
|
+
interceptors: mergeUnique(existing?.interceptors, interceptors)
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
function appendLegacyRouteGuards(target, propertyKey, guards) {
|
|
131
|
+
const record = getLegacyRouteRecord(target, propertyKey);
|
|
132
|
+
record.guards = mergeUnique(record.guards, guards);
|
|
133
|
+
flushLegacyRouteMetadata(target, propertyKey, record);
|
|
134
|
+
}
|
|
135
|
+
function appendLegacyRouteInterceptors(target, propertyKey, interceptors) {
|
|
136
|
+
const record = getLegacyRouteRecord(target, propertyKey);
|
|
137
|
+
record.interceptors = mergeUnique(record.interceptors, interceptors);
|
|
138
|
+
flushLegacyRouteMetadata(target, propertyKey, record);
|
|
29
139
|
}
|
|
30
140
|
function getStandardControllerRecord(metadata) {
|
|
31
141
|
const bag = getStandardMetadataBag(metadata);
|
|
@@ -77,29 +187,55 @@ function mergeStandardDtoBinding(metadata, propertyKey, partial) {
|
|
|
77
187
|
function createRouteDecorator(method) {
|
|
78
188
|
return path => {
|
|
79
189
|
validateRoutePath(path, `@${method}() path`);
|
|
80
|
-
const decorator = (
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
190
|
+
const decorator = (valueOrTarget, contextOrPropertyKey) => {
|
|
191
|
+
if (isStandardDecoratorContext(contextOrPropertyKey)) {
|
|
192
|
+
const route = getStandardRouteRecord(contextOrPropertyKey.metadata, contextOrPropertyKey.name);
|
|
193
|
+
route.method = method;
|
|
194
|
+
route.path = path;
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
if (isMetadataPropertyKey(contextOrPropertyKey)) {
|
|
198
|
+
mergeLegacyRouteMetadata(valueOrTarget, contextOrPropertyKey, {
|
|
199
|
+
method,
|
|
200
|
+
path
|
|
201
|
+
});
|
|
202
|
+
}
|
|
84
203
|
};
|
|
85
204
|
return decorator;
|
|
86
205
|
};
|
|
87
206
|
}
|
|
88
207
|
function createRouteValueDecorator(apply) {
|
|
89
208
|
return value => {
|
|
90
|
-
const decorator = (
|
|
91
|
-
|
|
209
|
+
const decorator = (valueOrTarget, contextOrPropertyKey) => {
|
|
210
|
+
if (isStandardDecoratorContext(contextOrPropertyKey)) {
|
|
211
|
+
apply(getStandardRouteRecord(contextOrPropertyKey.metadata, contextOrPropertyKey.name), value);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (isMetadataPropertyKey(contextOrPropertyKey)) {
|
|
215
|
+
const record = getLegacyRouteRecord(valueOrTarget, contextOrPropertyKey);
|
|
216
|
+
apply(record, value);
|
|
217
|
+
flushLegacyRouteMetadata(valueOrTarget, contextOrPropertyKey, record);
|
|
218
|
+
}
|
|
92
219
|
};
|
|
93
220
|
return decorator;
|
|
94
221
|
};
|
|
95
222
|
}
|
|
96
223
|
function createDtoFieldDecorator(source) {
|
|
97
224
|
return key => {
|
|
98
|
-
const decorator = (
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
225
|
+
const decorator = (valueOrTarget, contextOrPropertyKey) => {
|
|
226
|
+
if (isStandardDecoratorContext(contextOrPropertyKey)) {
|
|
227
|
+
mergeStandardDtoBinding(contextOrPropertyKey.metadata, contextOrPropertyKey.name, {
|
|
228
|
+
key,
|
|
229
|
+
source
|
|
230
|
+
});
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (isMetadataPropertyKey(contextOrPropertyKey) && valueOrTarget && typeof valueOrTarget === 'object') {
|
|
234
|
+
mergeLegacyDtoBinding(valueOrTarget, contextOrPropertyKey, {
|
|
235
|
+
key,
|
|
236
|
+
source
|
|
237
|
+
});
|
|
238
|
+
}
|
|
103
239
|
};
|
|
104
240
|
return decorator;
|
|
105
241
|
};
|
|
@@ -113,8 +249,14 @@ function createDtoFieldDecorator(source) {
|
|
|
113
249
|
*/
|
|
114
250
|
export function Controller(basePath = '') {
|
|
115
251
|
validateRoutePath(basePath, '@Controller() base path');
|
|
116
|
-
const decorator = (
|
|
117
|
-
|
|
252
|
+
const decorator = (target, context) => {
|
|
253
|
+
if (isStandardDecoratorContext(context)) {
|
|
254
|
+
getStandardControllerRecord(context.metadata).basePath = basePath;
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
defineLegacyControllerMetadata(target, {
|
|
258
|
+
basePath
|
|
259
|
+
});
|
|
118
260
|
};
|
|
119
261
|
return decorator;
|
|
120
262
|
}
|
|
@@ -126,12 +268,26 @@ export function Controller(basePath = '') {
|
|
|
126
268
|
* @returns A decorator that applies version metadata at class or method scope.
|
|
127
269
|
*/
|
|
128
270
|
export function Version(version) {
|
|
129
|
-
const decorator = (
|
|
130
|
-
if (
|
|
131
|
-
|
|
271
|
+
const decorator = (target, contextOrPropertyKey) => {
|
|
272
|
+
if (isStandardDecoratorContext(contextOrPropertyKey)) {
|
|
273
|
+
if (contextOrPropertyKey.kind === 'class') {
|
|
274
|
+
getStandardControllerRecord(contextOrPropertyKey.metadata).version = version;
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
getStandardRouteRecord(contextOrPropertyKey.metadata, contextOrPropertyKey.name).version = version;
|
|
132
278
|
return;
|
|
133
279
|
}
|
|
134
|
-
|
|
280
|
+
if (isMetadataPropertyKey(contextOrPropertyKey)) {
|
|
281
|
+
mergeLegacyRouteMetadata(target, contextOrPropertyKey, {
|
|
282
|
+
version
|
|
283
|
+
});
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
if (typeof target === 'function') {
|
|
287
|
+
defineLegacyControllerMetadata(target, {
|
|
288
|
+
version
|
|
289
|
+
});
|
|
290
|
+
}
|
|
135
291
|
};
|
|
136
292
|
return decorator;
|
|
137
293
|
}
|
|
@@ -233,9 +389,9 @@ export const HttpCode = createRouteValueDecorator((record, status) => {
|
|
|
233
389
|
* @returns A defensive copy of declared media types, or `undefined` when not configured.
|
|
234
390
|
*/
|
|
235
391
|
export function getRouteProducesMetadata(controllerToken, propertyKey) {
|
|
236
|
-
const bag = controllerToken
|
|
392
|
+
const bag = readStandardMetadataBag(controllerToken);
|
|
237
393
|
const routeMap = bag?.[standardRouteMetadataKey];
|
|
238
|
-
const produces = routeMap?.get(propertyKey)?.produces;
|
|
394
|
+
const produces = routeMap?.get(propertyKey)?.produces ?? legacyRouteMetadataStore.get(controllerToken.prototype)?.get(propertyKey)?.produces;
|
|
239
395
|
return produces ? [...produces] : undefined;
|
|
240
396
|
}
|
|
241
397
|
|
|
@@ -281,10 +437,18 @@ export const FromBody = createDtoFieldDecorator('body');
|
|
|
281
437
|
* @returns A field decorator that marks the DTO binding as optional.
|
|
282
438
|
*/
|
|
283
439
|
export function Optional() {
|
|
284
|
-
const decorator = (
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
440
|
+
const decorator = (valueOrTarget, contextOrPropertyKey) => {
|
|
441
|
+
if (isStandardDecoratorContext(contextOrPropertyKey)) {
|
|
442
|
+
mergeStandardDtoBinding(contextOrPropertyKey.metadata, contextOrPropertyKey.name, {
|
|
443
|
+
optional: true
|
|
444
|
+
});
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
if (isMetadataPropertyKey(contextOrPropertyKey) && valueOrTarget && typeof valueOrTarget === 'object') {
|
|
448
|
+
mergeLegacyDtoBinding(valueOrTarget, contextOrPropertyKey, {
|
|
449
|
+
optional: true
|
|
450
|
+
});
|
|
451
|
+
}
|
|
288
452
|
};
|
|
289
453
|
return decorator;
|
|
290
454
|
}
|
|
@@ -296,10 +460,18 @@ export function Optional() {
|
|
|
296
460
|
* @returns A field decorator that stores converter metadata for the DTO field.
|
|
297
461
|
*/
|
|
298
462
|
export function Convert(converter) {
|
|
299
|
-
const decorator = (
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
463
|
+
const decorator = (valueOrTarget, contextOrPropertyKey) => {
|
|
464
|
+
if (isStandardDecoratorContext(contextOrPropertyKey)) {
|
|
465
|
+
mergeStandardDtoBinding(contextOrPropertyKey.metadata, contextOrPropertyKey.name, {
|
|
466
|
+
converter
|
|
467
|
+
});
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
if (isMetadataPropertyKey(contextOrPropertyKey) && valueOrTarget && typeof valueOrTarget === 'object') {
|
|
471
|
+
mergeLegacyDtoBinding(valueOrTarget, contextOrPropertyKey, {
|
|
472
|
+
converter
|
|
473
|
+
});
|
|
474
|
+
}
|
|
303
475
|
};
|
|
304
476
|
return decorator;
|
|
305
477
|
}
|
|
@@ -312,12 +484,18 @@ export function Convert(converter) {
|
|
|
312
484
|
* @returns A method decorator that appends route-level response-header metadata.
|
|
313
485
|
*/
|
|
314
486
|
export function Header(name, value) {
|
|
315
|
-
const decorator = (
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
487
|
+
const decorator = (valueOrTarget, contextOrPropertyKey) => {
|
|
488
|
+
if (isStandardDecoratorContext(contextOrPropertyKey)) {
|
|
489
|
+
const route = getStandardRouteRecord(contextOrPropertyKey.metadata, contextOrPropertyKey.name);
|
|
490
|
+
route.headers = [...(route.headers ?? []), {
|
|
491
|
+
name,
|
|
492
|
+
value
|
|
493
|
+
}];
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
if (isMetadataPropertyKey(contextOrPropertyKey)) {
|
|
497
|
+
appendLegacyRouteHeader(valueOrTarget, contextOrPropertyKey, name, value);
|
|
498
|
+
}
|
|
321
499
|
};
|
|
322
500
|
return decorator;
|
|
323
501
|
}
|
|
@@ -330,11 +508,22 @@ export function Header(name, value) {
|
|
|
330
508
|
* @returns A method decorator that writes redirect metadata for the route.
|
|
331
509
|
*/
|
|
332
510
|
export function Redirect(url, statusCode) {
|
|
333
|
-
const decorator = (
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
511
|
+
const decorator = (valueOrTarget, contextOrPropertyKey) => {
|
|
512
|
+
if (isStandardDecoratorContext(contextOrPropertyKey)) {
|
|
513
|
+
getStandardRouteRecord(contextOrPropertyKey.metadata, contextOrPropertyKey.name).redirect = {
|
|
514
|
+
url,
|
|
515
|
+
statusCode
|
|
516
|
+
};
|
|
517
|
+
return;
|
|
518
|
+
}
|
|
519
|
+
if (isMetadataPropertyKey(contextOrPropertyKey)) {
|
|
520
|
+
mergeLegacyRouteMetadata(valueOrTarget, contextOrPropertyKey, {
|
|
521
|
+
redirect: {
|
|
522
|
+
url,
|
|
523
|
+
statusCode
|
|
524
|
+
}
|
|
525
|
+
});
|
|
526
|
+
}
|
|
338
527
|
};
|
|
339
528
|
return decorator;
|
|
340
529
|
}
|
|
@@ -346,14 +535,24 @@ export function Redirect(url, statusCode) {
|
|
|
346
535
|
* @returns A decorator applicable to classes and methods.
|
|
347
536
|
*/
|
|
348
537
|
export function UseGuards(...guards) {
|
|
349
|
-
const decorator = (
|
|
350
|
-
if (
|
|
351
|
-
|
|
352
|
-
|
|
538
|
+
const decorator = (target, contextOrPropertyKey) => {
|
|
539
|
+
if (isStandardDecoratorContext(contextOrPropertyKey)) {
|
|
540
|
+
if (contextOrPropertyKey.kind === 'class') {
|
|
541
|
+
const controller = getStandardControllerRecord(contextOrPropertyKey.metadata);
|
|
542
|
+
controller.guards = mergeUnique(controller.guards, guards);
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
const route = getStandardRouteRecord(contextOrPropertyKey.metadata, contextOrPropertyKey.name);
|
|
546
|
+
route.guards = mergeUnique(route.guards, guards);
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
if (isMetadataPropertyKey(contextOrPropertyKey)) {
|
|
550
|
+
appendLegacyRouteGuards(target, contextOrPropertyKey, guards);
|
|
353
551
|
return;
|
|
354
552
|
}
|
|
355
|
-
|
|
356
|
-
|
|
553
|
+
if (typeof target === 'function') {
|
|
554
|
+
appendLegacyControllerGuards(target, guards);
|
|
555
|
+
}
|
|
357
556
|
};
|
|
358
557
|
return decorator;
|
|
359
558
|
}
|
|
@@ -365,14 +564,24 @@ export function UseGuards(...guards) {
|
|
|
365
564
|
* @returns A decorator applicable to classes and methods.
|
|
366
565
|
*/
|
|
367
566
|
export function UseInterceptors(...interceptors) {
|
|
368
|
-
const decorator = (
|
|
369
|
-
if (
|
|
370
|
-
|
|
371
|
-
|
|
567
|
+
const decorator = (target, contextOrPropertyKey) => {
|
|
568
|
+
if (isStandardDecoratorContext(contextOrPropertyKey)) {
|
|
569
|
+
if (contextOrPropertyKey.kind === 'class') {
|
|
570
|
+
const controller = getStandardControllerRecord(contextOrPropertyKey.metadata);
|
|
571
|
+
controller.interceptors = mergeUnique(controller.interceptors, interceptors);
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
const route = getStandardRouteRecord(contextOrPropertyKey.metadata, contextOrPropertyKey.name);
|
|
575
|
+
route.interceptors = mergeUnique(route.interceptors, interceptors);
|
|
372
576
|
return;
|
|
373
577
|
}
|
|
374
|
-
|
|
375
|
-
|
|
578
|
+
if (isMetadataPropertyKey(contextOrPropertyKey)) {
|
|
579
|
+
appendLegacyRouteInterceptors(target, contextOrPropertyKey, interceptors);
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
if (typeof target === 'function') {
|
|
583
|
+
appendLegacyControllerInterceptors(target, interceptors);
|
|
584
|
+
}
|
|
376
585
|
};
|
|
377
586
|
return decorator;
|
|
378
587
|
}
|
|
@@ -1,9 +1,26 @@
|
|
|
1
1
|
import type { ContentNegotiationOptions, FrameworkRequest, HandlerDescriptor, ResponseFormatter } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Describes the resolved content negotiation contract.
|
|
4
|
+
*/
|
|
2
5
|
export interface ResolvedContentNegotiation {
|
|
3
6
|
defaultFormatter: ResponseFormatter;
|
|
4
7
|
formatters: ResponseFormatter[];
|
|
5
8
|
normalizedMediaTypes: string[];
|
|
6
9
|
}
|
|
10
|
+
/**
|
|
11
|
+
* Resolve content negotiation.
|
|
12
|
+
*
|
|
13
|
+
* @param options The options.
|
|
14
|
+
* @returns The resolve content negotiation result.
|
|
15
|
+
*/
|
|
7
16
|
export declare function resolveContentNegotiation(options: ContentNegotiationOptions | undefined): ResolvedContentNegotiation | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* Select response formatter.
|
|
19
|
+
*
|
|
20
|
+
* @param handler The handler.
|
|
21
|
+
* @param request The request.
|
|
22
|
+
* @param contentNegotiation The content negotiation.
|
|
23
|
+
* @returns The select response formatter result.
|
|
24
|
+
*/
|
|
8
25
|
export declare function selectResponseFormatter(handler: HandlerDescriptor, request: FrameworkRequest, contentNegotiation: ResolvedContentNegotiation): ResponseFormatter;
|
|
9
26
|
//# sourceMappingURL=dispatch-content-negotiation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch-content-negotiation.d.ts","sourceRoot":"","sources":["../../src/dispatch/dispatch-content-negotiation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,yBAAyB,EACzB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,aAAa,CAAC;AAQrB,MAAM,WAAW,0BAA0B;IACzC,gBAAgB,EAAE,iBAAiB,CAAC;IACpC,UAAU,EAAE,iBAAiB,EAAE,CAAC;IAChC,oBAAoB,EAAE,MAAM,EAAE,CAAC;CAChC;AA2GD,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,yBAAyB,GAAG,SAAS,GAAG,0BAA0B,GAAG,SAAS,CA+BhI;AAqCD,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,gBAAgB,EACzB,kBAAkB,EAAE,0BAA0B,GAC7C,iBAAiB,CA2CnB"}
|
|
1
|
+
{"version":3,"file":"dispatch-content-negotiation.d.ts","sourceRoot":"","sources":["../../src/dispatch/dispatch-content-negotiation.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,yBAAyB,EACzB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,aAAa,CAAC;AAQrB;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC,gBAAgB,EAAE,iBAAiB,CAAC;IACpC,UAAU,EAAE,iBAAiB,EAAE,CAAC;IAChC,oBAAoB,EAAE,MAAM,EAAE,CAAC;CAChC;AA2GD;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,yBAAyB,GAAG,SAAS,GAAG,0BAA0B,GAAG,SAAS,CA+BhI;AAqCD;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,gBAAgB,EACzB,kBAAkB,EAAE,0BAA0B,GAC7C,iBAAiB,CA2CnB"}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { NotAcceptableException } from '../exceptions.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Describes the resolved content negotiation contract.
|
|
5
|
+
*/
|
|
6
|
+
|
|
2
7
|
const NO_ACCEPTABLE_REPRESENTATION_MESSAGE = 'No acceptable response representation found.';
|
|
3
8
|
function normalizeMediaType(value) {
|
|
4
9
|
return value.split(';')[0]?.trim().toLowerCase() ?? '';
|
|
@@ -77,6 +82,13 @@ function matchesMediaRange(mediaRange, mediaType) {
|
|
|
77
82
|
}
|
|
78
83
|
return rangeSubtype === '*' || rangeSubtype === mediaTypeSubtype;
|
|
79
84
|
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Resolve content negotiation.
|
|
88
|
+
*
|
|
89
|
+
* @param options The options.
|
|
90
|
+
* @returns The resolve content negotiation result.
|
|
91
|
+
*/
|
|
80
92
|
export function resolveContentNegotiation(options) {
|
|
81
93
|
if (!options?.formatters?.length) {
|
|
82
94
|
return undefined;
|
|
@@ -128,6 +140,15 @@ function resolveDefaultFormatter(allowedFormatters, allowedNormalizedMediaTypes,
|
|
|
128
140
|
const idx = allowedNormalizedMediaTypes.indexOf(defaultMediaType);
|
|
129
141
|
return idx >= 0 ? allowedFormatters[idx] : allowedFormatters[0] ?? contentNegotiation.defaultFormatter;
|
|
130
142
|
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Select response formatter.
|
|
146
|
+
*
|
|
147
|
+
* @param handler The handler.
|
|
148
|
+
* @param request The request.
|
|
149
|
+
* @param contentNegotiation The content negotiation.
|
|
150
|
+
* @returns The select response formatter result.
|
|
151
|
+
*/
|
|
131
152
|
export function selectResponseFormatter(handler, request, contentNegotiation) {
|
|
132
153
|
const {
|
|
133
154
|
formatters: allowedFormatters,
|
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
import type { FrameworkResponse } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Write error response.
|
|
4
|
+
*
|
|
5
|
+
* @param error The error.
|
|
6
|
+
* @param response The response.
|
|
7
|
+
* @param requestId The request id.
|
|
8
|
+
* @returns The write error response result.
|
|
9
|
+
*/
|
|
2
10
|
export declare function writeErrorResponse(error: unknown, response: FrameworkResponse, requestId?: string): Promise<void>;
|
|
3
11
|
//# sourceMappingURL=dispatch-error-policy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch-error-policy.d.ts","sourceRoot":"","sources":["../../src/dispatch/dispatch-error-policy.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAiBrD,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQvH"}
|
|
1
|
+
{"version":3,"file":"dispatch-error-policy.d.ts","sourceRoot":"","sources":["../../src/dispatch/dispatch-error-policy.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAiBrD;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,iBAAiB,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQvH"}
|
|
@@ -14,6 +14,15 @@ function toHttpException(error) {
|
|
|
14
14
|
cause: error
|
|
15
15
|
});
|
|
16
16
|
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Write error response.
|
|
20
|
+
*
|
|
21
|
+
* @param error The error.
|
|
22
|
+
* @param response The response.
|
|
23
|
+
* @param requestId The request id.
|
|
24
|
+
* @returns The write error response result.
|
|
25
|
+
*/
|
|
17
26
|
export async function writeErrorResponse(error, response, requestId) {
|
|
18
27
|
if (response.committed) {
|
|
19
28
|
return;
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
import type { RequestScopeContainer } from '@fluojs/di';
|
|
1
2
|
import type { Binder, HandlerDescriptor, RequestContext } from '../types.js';
|
|
2
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Invoke controller handler.
|
|
5
|
+
*
|
|
6
|
+
* @param handler The handler.
|
|
7
|
+
* @param requestContext The request context.
|
|
8
|
+
* @param binder The binder.
|
|
9
|
+
* @param controllerContainer Container used for resolving the controller instance before handler invocation.
|
|
10
|
+
* @returns The invoke controller handler result.
|
|
11
|
+
*/
|
|
12
|
+
export declare function invokeControllerHandler(handler: HandlerDescriptor, requestContext: RequestContext, binder?: Binder, controllerContainer?: RequestScopeContainer): Promise<unknown>;
|
|
3
13
|
//# sourceMappingURL=dispatch-handler-policy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch-handler-policy.d.ts","sourceRoot":"","sources":["../../src/dispatch/dispatch-handler-policy.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dispatch-handler-policy.d.ts","sourceRoot":"","sources":["../../src/dispatch/dispatch-handler-policy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAKxD,OAAO,KAAK,EAA2B,MAAM,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAKtG;;;;;;;;GAQG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,iBAAiB,EAC1B,cAAc,EAAE,cAAc,EAC9B,MAAM,GAAE,MAAsB,EAC9B,mBAAmB,GAAE,qBAAgD,GACpE,OAAO,CAAC,OAAO,CAAC,CAwBlB"}
|
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
import { InvariantError } from '@fluojs/core';
|
|
2
2
|
import { DefaultBinder } from '../adapters/binding.js';
|
|
3
|
+
import { getCompiledDtoBindingPlan } from '../adapters/dto-binding-plan.js';
|
|
3
4
|
import { HttpDtoValidationAdapter } from '../adapters/dto-validation-adapter.js';
|
|
4
5
|
const defaultBinder = new DefaultBinder();
|
|
5
6
|
const defaultValidator = new HttpDtoValidationAdapter();
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Invoke controller handler.
|
|
10
|
+
*
|
|
11
|
+
* @param handler The handler.
|
|
12
|
+
* @param requestContext The request context.
|
|
13
|
+
* @param binder The binder.
|
|
14
|
+
* @param controllerContainer Container used for resolving the controller instance before handler invocation.
|
|
15
|
+
* @returns The invoke controller handler result.
|
|
16
|
+
*/
|
|
17
|
+
export async function invokeControllerHandler(handler, requestContext, binder = defaultBinder, controllerContainer = requestContext.container) {
|
|
18
|
+
const controller = await controllerContainer.resolve(handler.controllerToken);
|
|
8
19
|
const method = controller[handler.methodName];
|
|
9
20
|
if (typeof method !== 'function') {
|
|
10
21
|
throw new InvariantError(`Controller ${handler.controllerToken.name} does not expose handler method ${handler.methodName}.`);
|
|
@@ -13,9 +24,10 @@ export async function invokeControllerHandler(handler, requestContext, binder =
|
|
|
13
24
|
handler,
|
|
14
25
|
requestContext
|
|
15
26
|
};
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
27
|
+
const requestDto = handler.route.request;
|
|
28
|
+
const input = requestDto ? await binder.bind(requestDto, argumentResolverContext) : undefined;
|
|
29
|
+
if (requestDto && getCompiledDtoBindingPlan(requestDto).needsValidation) {
|
|
30
|
+
await defaultValidator.validate(input, requestDto);
|
|
19
31
|
}
|
|
20
32
|
return method.call(controller, input, requestContext);
|
|
21
33
|
}
|
|
@@ -1,7 +1,17 @@
|
|
|
1
1
|
import { resolveContentNegotiation, type ResolvedContentNegotiation } from './dispatch-content-negotiation.js';
|
|
2
2
|
import { writeErrorResponse } from './dispatch-error-policy.js';
|
|
3
3
|
import type { FrameworkRequest, FrameworkResponse, HandlerDescriptor } from '../types.js';
|
|
4
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Write success response.
|
|
6
|
+
*
|
|
7
|
+
* @param handler The handler.
|
|
8
|
+
* @param request The request.
|
|
9
|
+
* @param response The response.
|
|
10
|
+
* @param value The value.
|
|
11
|
+
* @param contentNegotiation The content negotiation.
|
|
12
|
+
* @returns The write success response result.
|
|
13
|
+
*/
|
|
14
|
+
export declare function writeSuccessResponse(handler: HandlerDescriptor, request: FrameworkRequest, response: FrameworkResponse, value: unknown, contentNegotiation: ResolvedContentNegotiation | undefined): ReturnType<FrameworkResponse['send']> | void;
|
|
5
15
|
export { resolveContentNegotiation, writeErrorResponse };
|
|
6
16
|
export type { ResolvedContentNegotiation };
|
|
7
17
|
//# sourceMappingURL=dispatch-response-policy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch-response-policy.d.ts","sourceRoot":"","sources":["../../src/dispatch/dispatch-response-policy.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EAEzB,KAAK,0BAA0B,EAChC,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"dispatch-response-policy.d.ts","sourceRoot":"","sources":["../../src/dispatch/dispatch-response-policy.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,yBAAyB,EAEzB,KAAK,0BAA0B,EAChC,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,aAAa,CAAC;AAgErB;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,iBAAiB,EAC1B,OAAO,EAAE,gBAAgB,EACzB,QAAQ,EAAE,iBAAiB,EAC3B,KAAK,EAAE,OAAO,EACd,kBAAkB,EAAE,0BAA0B,GAAG,SAAS,GACzD,UAAU,CAAC,iBAAiB,CAAC,MAAM,CAAC,CAAC,GAAG,IAAI,CAwC9C;AAED,OAAO,EAAE,yBAAyB,EAAE,kBAAkB,EAAE,CAAC;AACzD,YAAY,EAAE,0BAA0B,EAAE,CAAC"}
|
|
@@ -11,7 +11,46 @@ function resolveDefaultSuccessStatus(handler, value) {
|
|
|
11
11
|
return 200;
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
|
-
|
|
14
|
+
function canUseSimpleJsonFastPath(response, value) {
|
|
15
|
+
return isSimpleJsonResponseBody(value) && !isResponseBodyForbidden(response.statusCode) && hasJsonCompatibleContentType(response);
|
|
16
|
+
}
|
|
17
|
+
function hasSimpleJsonResponseWriter(response) {
|
|
18
|
+
return typeof response.sendSimpleJson === 'function';
|
|
19
|
+
}
|
|
20
|
+
function isSimpleJsonResponseBody(value) {
|
|
21
|
+
if (Array.isArray(value)) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
return typeof value === 'object' && value !== null && Object.getPrototypeOf(value) === Object.prototype;
|
|
25
|
+
}
|
|
26
|
+
function isResponseBodyForbidden(status) {
|
|
27
|
+
return status === 204 || status === 205 || status === 304;
|
|
28
|
+
}
|
|
29
|
+
function hasJsonCompatibleContentType(response) {
|
|
30
|
+
const contentType = readHeader(response.headers, 'content-type');
|
|
31
|
+
return contentType === undefined || isJsonContentType(contentType);
|
|
32
|
+
}
|
|
33
|
+
function readHeader(headers, name) {
|
|
34
|
+
const lowerName = name.toLowerCase();
|
|
35
|
+
const entry = Object.entries(headers).find(([headerName]) => headerName.toLowerCase() === lowerName);
|
|
36
|
+
const value = entry?.[1];
|
|
37
|
+
return typeof value === 'string' ? value : undefined;
|
|
38
|
+
}
|
|
39
|
+
function isJsonContentType(contentType) {
|
|
40
|
+
return contentType.toLowerCase().includes('application/json') || contentType.toLowerCase().endsWith('+json');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Write success response.
|
|
45
|
+
*
|
|
46
|
+
* @param handler The handler.
|
|
47
|
+
* @param request The request.
|
|
48
|
+
* @param response The response.
|
|
49
|
+
* @param value The value.
|
|
50
|
+
* @param contentNegotiation The content negotiation.
|
|
51
|
+
* @returns The write success response result.
|
|
52
|
+
*/
|
|
53
|
+
export function writeSuccessResponse(handler, request, response, value, contentNegotiation) {
|
|
15
54
|
if (response.committed) {
|
|
16
55
|
return;
|
|
17
56
|
}
|
|
@@ -39,7 +78,10 @@ export async function writeSuccessResponse(handler, request, response, value, co
|
|
|
39
78
|
} else if (response.statusSet !== true) {
|
|
40
79
|
response.setStatus(resolveDefaultSuccessStatus(handler, value));
|
|
41
80
|
}
|
|
81
|
+
if (!formatter && hasSimpleJsonResponseWriter(response) && canUseSimpleJsonFastPath(response, value)) {
|
|
82
|
+
return response.sendSimpleJson(value);
|
|
83
|
+
}
|
|
42
84
|
const responseBody = formatter ? formatter.format(value) : value;
|
|
43
|
-
|
|
85
|
+
return response.send(responseBody);
|
|
44
86
|
}
|
|
45
87
|
export { resolveContentNegotiation, writeErrorResponse };
|