express-fast-json-stringify 1.3.0 → 2.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.
- package/CHANGELOG.md +26 -17
- package/README.md +64 -31
- package/build/main/index.d.ts +1 -1
- package/build/main/index.js +4 -1
- package/build/main/lib/install.d.ts +77 -0
- package/build/main/lib/install.js +98 -0
- package/build/main/lib/middleware.d.ts +8 -38
- package/build/main/lib/middleware.js +13 -36
- package/build/main/lib/openapi.d.ts +30 -36
- package/build/main/lib/openapi.js +82 -53
- package/build/main/lib/send.d.ts +7 -8
- package/build/main/lib/send.js +12 -40
- package/build/module/index.d.ts +1 -1
- package/build/module/index.js +2 -1
- package/build/module/lib/install.d.ts +77 -0
- package/build/module/lib/install.js +92 -0
- package/build/module/lib/middleware.d.ts +8 -38
- package/build/module/lib/middleware.js +14 -26
- package/build/module/lib/openapi.d.ts +30 -36
- package/build/module/lib/openapi.js +81 -53
- package/build/module/lib/send.d.ts +7 -8
- package/build/module/lib/send.js +10 -37
- package/package.json +9 -2
- package/build/main/lib/override.d.ts +0 -17
- package/build/main/lib/override.js +0 -49
- package/build/module/lib/override.d.ts +0 -17
- package/build/module/lib/override.js +0 -44
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { type
|
|
3
|
-
import { type OverrideErrorHandler } from './override';
|
|
1
|
+
import { type Options, type Schema } from 'fast-json-stringify';
|
|
2
|
+
import { type Application, type FastJsonOptions } from './install';
|
|
4
3
|
/**
|
|
5
4
|
* The parts of an OpenAPI 3.x or Swagger 2.0 document this package reads.
|
|
6
5
|
*
|
|
@@ -16,35 +15,9 @@ export type OpenApiDocument = {
|
|
|
16
15
|
readonly components?: Readonly<Record<string, unknown>>;
|
|
17
16
|
readonly definitions?: Readonly<Record<string, unknown>>;
|
|
18
17
|
};
|
|
19
|
-
export type OpenApiOptions = Omit<Options, 'mode'> & {
|
|
18
|
+
export type OpenApiOptions = Omit<Options, 'mode'> & FastJsonOptions & {
|
|
20
19
|
/** Media type to read the schema from. Defaults to `application/json`. */
|
|
21
20
|
readonly contentType?: string;
|
|
22
|
-
/** Pin the OpenAPI path instead of deriving it from the matched route. */
|
|
23
|
-
readonly path?: string;
|
|
24
|
-
/** Pin the OpenAPI method instead of using the request method. */
|
|
25
|
-
readonly method?: string;
|
|
26
|
-
/**
|
|
27
|
-
* Throw when the document describes no schema for a response, instead of
|
|
28
|
-
* quietly falling back to `res.json()`. Only applies to explicit
|
|
29
|
-
* `res.fastJson()` calls — an overridden `res.json()` always falls back.
|
|
30
|
-
*/
|
|
31
|
-
readonly strict?: boolean;
|
|
32
|
-
/**
|
|
33
|
-
* Also route `res.json()` — and therefore `res.send(object)`, which Express
|
|
34
|
-
* implements on top of it — through the compiled serializer whenever the
|
|
35
|
-
* document describes the response.
|
|
36
|
-
*
|
|
37
|
-
* Off by default. Turning it on lets an existing codebase benefit without
|
|
38
|
-
* rewriting a single call site: responses the document covers are serialized
|
|
39
|
-
* from the schema, everything else keeps the stock behavior.
|
|
40
|
-
*/
|
|
41
|
-
readonly overrideJson?: boolean;
|
|
42
|
-
/**
|
|
43
|
-
* Called when an overridden `res.json()` could not use the fast path because
|
|
44
|
-
* the serializer threw. The response falls back to the stock `res.json()`
|
|
45
|
-
* either way; this is only so the mismatch is visible.
|
|
46
|
-
*/
|
|
47
|
-
readonly onError?: OverrideErrorHandler;
|
|
48
21
|
};
|
|
49
22
|
/**
|
|
50
23
|
* Translate an Express route pattern into the OpenAPI equivalent:
|
|
@@ -53,14 +26,35 @@ export type OpenApiOptions = Omit<Options, 'mode'> & {
|
|
|
53
26
|
*/
|
|
54
27
|
export declare const toOpenApiPath: (path: string) => string;
|
|
55
28
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
29
|
+
* The schema the document declares for one operation and status, with the
|
|
30
|
+
* shared schemas attached so `$ref` resolves, ready for `fastJsonSchema`. For
|
|
31
|
+
* a route whose Express path does not match the document.
|
|
58
32
|
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
33
|
+
* @param {OpenApiDocument} document The OpenAPI 3.x or Swagger 2.0 document
|
|
34
|
+
* @param {string} path The OpenAPI path, `/users/{id}`
|
|
35
|
+
* @param {string} method The operation, `get`
|
|
36
|
+
* @param {number} status The response status, `200` when omitted
|
|
37
|
+
* @param {string} contentType The media type, `application/json` when omitted
|
|
38
|
+
* @returns {Schema | undefined} undefined when the document describes no such response
|
|
39
|
+
*
|
|
40
|
+
* Examples:
|
|
41
|
+
* ```ts
|
|
42
|
+
* app.get('/v2/people/:id', fastJsonSchema(openApiSchema(document, '/users/{id}', 'get')!), handler);
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare const openApiSchema: (document: OpenApiDocument, path: string, method: string, status?: number, contentType?: string) => Schema | undefined;
|
|
46
|
+
/**
|
|
47
|
+
* Serialize every documented response of an application from its OpenAPI or
|
|
48
|
+
* Swagger document, so the contract you already publish is the one used to
|
|
49
|
+
* serialize. Once per app, at setup: it calls `installFastJson(app)` itself, and no
|
|
50
|
+
* middleware runs per request.
|
|
51
|
+
*
|
|
52
|
+
* The operation is resolved from the matched Express route, and the schema
|
|
53
|
+
* from the response status code, which means `res.status(201).fastJson()`
|
|
61
54
|
* serializes with the `201` schema. Routes the document does not describe fall
|
|
62
55
|
* back to `res.json()` unless `strict` is set.
|
|
63
56
|
*
|
|
57
|
+
* @param {Application} app The application
|
|
64
58
|
* @param {OpenApiDocument} document The OpenAPI 3.x or Swagger 2.0 document
|
|
65
59
|
* @param {OpenApiOptions} options The options to use (optional)
|
|
66
60
|
*
|
|
@@ -73,7 +67,7 @@ export declare const toOpenApiPath: (path: string) => string;
|
|
|
73
67
|
* const app = express();
|
|
74
68
|
* const document = swaggerJsdoc({ definition: { openapi: '3.1.0', info: { title: 'API', version: '1.0.0' } }, apis: ['./routes/*.ts'] });
|
|
75
69
|
*
|
|
76
|
-
*
|
|
70
|
+
* fastJsonOpenApi(app, document);
|
|
77
71
|
*
|
|
78
72
|
* app.get('/users/:id', (req, res, next) => {
|
|
79
73
|
* try {
|
|
@@ -84,4 +78,4 @@ export declare const toOpenApiPath: (path: string) => string;
|
|
|
84
78
|
* });
|
|
85
79
|
* ```
|
|
86
80
|
*/
|
|
87
|
-
export declare const fastJsonOpenApi: (document: OpenApiDocument, options?: OpenApiOptions) =>
|
|
81
|
+
export declare const fastJsonOpenApi: (app: Application, document: OpenApiDocument, options?: OpenApiOptions) => void;
|
|
@@ -14,10 +14,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
14
14
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.fastJsonOpenApi = exports.toOpenApiPath = void 0;
|
|
17
|
+
exports.fastJsonOpenApi = exports.openApiSchema = exports.toOpenApiPath = void 0;
|
|
18
18
|
const fast_json_stringify_1 = __importDefault(require("fast-json-stringify"));
|
|
19
|
-
const
|
|
20
|
-
const send_1 = require("./send");
|
|
19
|
+
const install_1 = require("./install");
|
|
21
20
|
/**
|
|
22
21
|
* Translate an Express route pattern into the OpenAPI equivalent:
|
|
23
22
|
* `/users/:id` becomes `/users/{id}`. Express parameter modifiers — a trailing
|
|
@@ -81,15 +80,46 @@ const withSharedSchemas = (document, schema) => {
|
|
|
81
80
|
}
|
|
82
81
|
return result;
|
|
83
82
|
};
|
|
83
|
+
const validateDocument = (document) => {
|
|
84
|
+
if (!document || typeof document !== 'object' || typeof document.paths !== 'object' || document.paths === null) {
|
|
85
|
+
throw new TypeError(`express-fast-json-stringify: invalid OpenAPI document`);
|
|
86
|
+
}
|
|
87
|
+
};
|
|
84
88
|
/**
|
|
85
|
-
*
|
|
86
|
-
*
|
|
89
|
+
* The schema the document declares for one operation and status, with the
|
|
90
|
+
* shared schemas attached so `$ref` resolves, ready for `fastJsonSchema`. For
|
|
91
|
+
* a route whose Express path does not match the document.
|
|
92
|
+
*
|
|
93
|
+
* @param {OpenApiDocument} document The OpenAPI 3.x or Swagger 2.0 document
|
|
94
|
+
* @param {string} path The OpenAPI path, `/users/{id}`
|
|
95
|
+
* @param {string} method The operation, `get`
|
|
96
|
+
* @param {number} status The response status, `200` when omitted
|
|
97
|
+
* @param {string} contentType The media type, `application/json` when omitted
|
|
98
|
+
* @returns {Schema | undefined} undefined when the document describes no such response
|
|
87
99
|
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
100
|
+
* Examples:
|
|
101
|
+
* ```ts
|
|
102
|
+
* app.get('/v2/people/:id', fastJsonSchema(openApiSchema(document, '/users/{id}', 'get')!), handler);
|
|
103
|
+
* ```
|
|
104
|
+
*/
|
|
105
|
+
const openApiSchema = (document, path, method, status = 200, contentType = 'application/json') => {
|
|
106
|
+
validateDocument(document);
|
|
107
|
+
const schema = findResponseSchema(document, path, method.toLowerCase(), status, contentType);
|
|
108
|
+
return schema ? withSharedSchemas(document, schema) : undefined;
|
|
109
|
+
};
|
|
110
|
+
exports.openApiSchema = openApiSchema;
|
|
111
|
+
/**
|
|
112
|
+
* Serialize every documented response of an application from its OpenAPI or
|
|
113
|
+
* Swagger document, so the contract you already publish is the one used to
|
|
114
|
+
* serialize. Once per app, at setup: it calls `installFastJson(app)` itself, and no
|
|
115
|
+
* middleware runs per request.
|
|
116
|
+
*
|
|
117
|
+
* The operation is resolved from the matched Express route, and the schema
|
|
118
|
+
* from the response status code, which means `res.status(201).fastJson()`
|
|
90
119
|
* serializes with the `201` schema. Routes the document does not describe fall
|
|
91
120
|
* back to `res.json()` unless `strict` is set.
|
|
92
121
|
*
|
|
122
|
+
* @param {Application} app The application
|
|
93
123
|
* @param {OpenApiDocument} document The OpenAPI 3.x or Swagger 2.0 document
|
|
94
124
|
* @param {OpenApiOptions} options The options to use (optional)
|
|
95
125
|
*
|
|
@@ -102,7 +132,7 @@ const withSharedSchemas = (document, schema) => {
|
|
|
102
132
|
* const app = express();
|
|
103
133
|
* const document = swaggerJsdoc({ definition: { openapi: '3.1.0', info: { title: 'API', version: '1.0.0' } }, apis: ['./routes/*.ts'] });
|
|
104
134
|
*
|
|
105
|
-
*
|
|
135
|
+
* fastJsonOpenApi(app, document);
|
|
106
136
|
*
|
|
107
137
|
* app.get('/users/:id', (req, res, next) => {
|
|
108
138
|
* try {
|
|
@@ -113,54 +143,53 @@ const withSharedSchemas = (document, schema) => {
|
|
|
113
143
|
* });
|
|
114
144
|
* ```
|
|
115
145
|
*/
|
|
116
|
-
const fastJsonOpenApi = (document, options = {}) => {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
const
|
|
121
|
-
// One compiled serializer per operation and status code. Misses are cached as
|
|
122
|
-
// `null` so an undocumented route costs a single lookup.
|
|
123
|
-
const serializers = new Map();
|
|
124
|
-
const routePath = (req) => { var _a, _b; return pinnedPath !== null && pinnedPath !== void 0 ? pinnedPath : (0, exports.toOpenApiPath)(`${req.baseUrl}${(_b = (_a = req.route) === null || _a === void 0 ? void 0 : _a.path) !== null && _b !== void 0 ? _b : req.path}`); };
|
|
125
|
-
const serializerFor = (req, status) => {
|
|
126
|
-
const path = routePath(req);
|
|
127
|
-
const method = (pinnedMethod !== null && pinnedMethod !== void 0 ? pinnedMethod : req.method).toLowerCase();
|
|
128
|
-
const key = `${method} ${path} ${status}`;
|
|
129
|
-
const cached = serializers.get(key);
|
|
130
|
-
if (cached !== undefined) {
|
|
131
|
-
return cached;
|
|
132
|
-
}
|
|
146
|
+
const fastJsonOpenApi = (app, document, options = {}) => {
|
|
147
|
+
validateDocument(document);
|
|
148
|
+
const { contentType = 'application/json', overrideJson, onError, strict } = options, fastJsonOptions = __rest(options, ["contentType", "overrideJson", "onError", "strict"]);
|
|
149
|
+
(0, install_1.installFastJson)(app, { overrideJson, onError, strict });
|
|
150
|
+
const compile = (path, method, status) => {
|
|
133
151
|
const schema = findResponseSchema(document, path, method, status, contentType);
|
|
134
|
-
|
|
135
|
-
serializers.set(key, serializer);
|
|
136
|
-
return serializer;
|
|
152
|
+
return schema ? (0, fast_json_stringify_1.default)(withSharedSchemas(document, schema), fastJsonOptions) : null;
|
|
137
153
|
};
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
|
|
154
|
+
// One compiled serializer per route, mount and status, found from the route object the
|
|
155
|
+
// framework already matched: no path is rebuilt per request. Misses are cached as `null`.
|
|
156
|
+
// A router mounted twice answers under two mounts, so the mount is a key of its own.
|
|
157
|
+
const byRoute = new WeakMap();
|
|
158
|
+
// Answered from plain middleware, before any route matched: the request path is what names
|
|
159
|
+
// the operation, and a string key is all there is.
|
|
160
|
+
const byPath = new Map();
|
|
161
|
+
(0, install_1.setResolver)(app, (res) => {
|
|
162
|
+
const req = res.req;
|
|
163
|
+
const status = res.statusCode;
|
|
164
|
+
const method = req.method.toLowerCase();
|
|
165
|
+
const route = req.route;
|
|
166
|
+
if (route === undefined) {
|
|
167
|
+
const key = `${method} ${req.baseUrl}${req.path} ${status}`;
|
|
168
|
+
let serialize = byPath.get(key);
|
|
169
|
+
if (serialize === undefined) {
|
|
170
|
+
serialize = compile((0, exports.toOpenApiPath)(`${req.baseUrl}${req.path}`), method, status);
|
|
171
|
+
byPath.set(key, serialize);
|
|
156
172
|
}
|
|
157
|
-
return
|
|
158
|
-
};
|
|
159
|
-
if (overrideJson) {
|
|
160
|
-
(0, override_1.overrideResJson)(req, res, (status) => serializerFor(req, status), onError);
|
|
173
|
+
return serialize;
|
|
161
174
|
}
|
|
162
|
-
|
|
163
|
-
|
|
175
|
+
let byMount = byRoute.get(route);
|
|
176
|
+
if (byMount === undefined) {
|
|
177
|
+
byMount = new Map();
|
|
178
|
+
byRoute.set(route, byMount);
|
|
179
|
+
}
|
|
180
|
+
const baseUrl = req.baseUrl;
|
|
181
|
+
let byStatus = byMount.get(baseUrl);
|
|
182
|
+
if (byStatus === undefined) {
|
|
183
|
+
byStatus = new Map();
|
|
184
|
+
byMount.set(baseUrl, byStatus);
|
|
185
|
+
}
|
|
186
|
+
let serialize = byStatus.get(status);
|
|
187
|
+
if (serialize === undefined) {
|
|
188
|
+
serialize = compile((0, exports.toOpenApiPath)(`${baseUrl}${route.path}`), method, status);
|
|
189
|
+
byStatus.set(status, serialize);
|
|
190
|
+
}
|
|
191
|
+
return serialize;
|
|
192
|
+
});
|
|
164
193
|
};
|
|
165
194
|
exports.fastJsonOpenApi = fastJsonOpenApi;
|
|
166
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
195
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BlbmFwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9saWIvb3BlbmFwaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7Ozs7OztBQUNBLDhFQUFtRjtBQUVuRix1Q0FBa0g7QUF3QmxIOzs7O0dBSUc7QUFDSSxNQUFNLGFBQWEsR0FBRyxDQUFDLElBQVksRUFBVSxFQUFFLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxrQ0FBa0MsRUFBRSxNQUFNLENBQUMsQ0FBQztBQUFuRyxRQUFBLGFBQWEsaUJBQXNGO0FBRWhIOzs7R0FHRztBQUNILE1BQU0sZ0JBQWdCLEdBQUcsQ0FBQyxNQUFjLEVBQXFCLEVBQUUsQ0FBQyxDQUFDLE1BQU0sS0FBSyxNQUFNLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxFQUFFLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUM7QUFFakg7OztHQUdHO0FBQ0gsTUFBTSxnQkFBZ0IsR0FBRyxDQUFDLE1BQWMsRUFBcUIsRUFBRTtJQUM3RCxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLE1BQU0sR0FBRyxHQUFHLENBQUMsQ0FBQztJQUN2QyxPQUFPLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FBQyxFQUFFLEdBQUcsS0FBSyxJQUFJLEVBQUUsR0FBRyxLQUFLLElBQUksRUFBRSxTQUFTLENBQUMsQ0FBQztBQUNqRSxDQUFDLENBQUM7QUFRRixNQUFNLGtCQUFrQixHQUFHLENBQUMsUUFBeUIsRUFBRSxJQUFZLEVBQUUsTUFBYyxFQUFFLE1BQWMsRUFBRSxXQUFtQixFQUFXLEVBQUU7O0lBQ25JLE1BQU0sVUFBVSxHQUFHLE1BQUEsUUFBUSxDQUFDLEtBQUssMENBQUcsSUFBSSxDQUFrRCxDQUFDO0lBQzNGLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUNoQixPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0lBRUQsS0FB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
|
package/build/main/lib/send.d.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Response } from 'express';
|
|
2
2
|
/**
|
|
3
|
-
* Write an already serialized JSON payload
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* apart from each other or from Express.
|
|
3
|
+
* Write an already serialized JSON payload the way `res.json()` does once the
|
|
4
|
+
* body is a string: the type when the route set none, then `res.send()`, which
|
|
5
|
+
* owns `Content-Length`, the `ETag`, conditional requests, `204`/`304` and
|
|
6
|
+
* `HEAD` in Express and in the frameworks that stand in for it. Nothing is
|
|
7
|
+
* reimplemented here, so nothing can drift.
|
|
9
8
|
*/
|
|
10
|
-
export declare const
|
|
9
|
+
export declare const sendSerialized: (res: Response, json: string) => Response;
|
package/build/main/lib/send.js
CHANGED
|
@@ -1,46 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.sendSerialized = void 0;
|
|
4
4
|
/**
|
|
5
|
-
* Write an already serialized JSON payload
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* apart from each other or from Express.
|
|
5
|
+
* Write an already serialized JSON payload the way `res.json()` does once the
|
|
6
|
+
* body is a string: the type when the route set none, then `res.send()`, which
|
|
7
|
+
* owns `Content-Length`, the `ETag`, conditional requests, `204`/`304` and
|
|
8
|
+
* `HEAD` in Express and in the frameworks that stand in for it. Nothing is
|
|
9
|
+
* reimplemented here, so nothing can drift.
|
|
11
10
|
*/
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
// Do not clobber a content type the route set on purpose (eg. res.type('application/vnd.api+json')).
|
|
16
|
-
if (!res.getHeader('Content-Type')) {
|
|
17
|
-
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
11
|
+
const sendSerialized = (res, json) => {
|
|
12
|
+
if (!res.get('Content-Type')) {
|
|
13
|
+
res.set('Content-Type', 'application/json; charset=utf-8');
|
|
18
14
|
}
|
|
19
|
-
|
|
20
|
-
// res.json() for res.fastJson() keeps conditional requests working.
|
|
21
|
-
const etagFn = (_a = res.app) === null || _a === void 0 ? void 0 : _a.get('etag fn');
|
|
22
|
-
if (typeof etagFn === 'function' && !res.getHeader('ETag')) {
|
|
23
|
-
const etag = etagFn(payload, 'utf-8');
|
|
24
|
-
if (etag) {
|
|
25
|
-
res.setHeader('ETag', etag);
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
if (req.fresh) {
|
|
29
|
-
res.statusCode = 304;
|
|
30
|
-
}
|
|
31
|
-
// 204 No Content and 304 Not Modified must not carry a body, nor describe one.
|
|
32
|
-
if (res.statusCode === 204 || res.statusCode === 304) {
|
|
33
|
-
res.removeHeader('Content-Type');
|
|
34
|
-
res.removeHeader('Content-Length');
|
|
35
|
-
res.removeHeader('Transfer-Encoding');
|
|
36
|
-
payload = '';
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
// Without this the response falls back to chunked encoding, and HEAD
|
|
40
|
-
// requests answer with no length at all.
|
|
41
|
-
res.setHeader('Content-Length', Buffer.byteLength(payload));
|
|
42
|
-
}
|
|
43
|
-
return res.end(payload);
|
|
15
|
+
return res.send(json);
|
|
44
16
|
};
|
|
45
|
-
exports.
|
|
46
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
17
|
+
exports.sendSerialized = sendSerialized;
|
|
18
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VuZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9saWIvc2VuZC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFFQTs7Ozs7O0dBTUc7QUFDSSxNQUFNLGNBQWMsR0FBRyxDQUFDLEdBQWEsRUFBRSxJQUFZLEVBQVksRUFBRTtJQUN0RSxJQUFJLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxjQUFjLENBQUMsRUFBRSxDQUFDO1FBQzdCLEdBQUcsQ0FBQyxHQUFHLENBQUMsY0FBYyxFQUFFLGlDQUFpQyxDQUFDLENBQUM7SUFDN0QsQ0FBQztJQUNELE9BQU8sR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztBQUN4QixDQUFDLENBQUM7QUFMVyxRQUFBLGNBQWMsa0JBS3pCIn0=
|
package/build/module/index.d.ts
CHANGED
package/build/module/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { installFastJson } from './lib/install';
|
|
1
2
|
export * from './lib/middleware';
|
|
2
3
|
export * from './lib/openapi';
|
|
3
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
4
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFvQixlQUFlLEVBQW1ELE1BQU0sZUFBZSxDQUFDO0FBQ25ILGNBQWMsa0JBQWtCLENBQUM7QUFDakMsY0FBYyxlQUFlLENBQUMifQ==
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { Express, Request, Response } from 'express';
|
|
2
|
+
/** What the install needs of an application: the prototype its responses share. Fulmine has one too. */
|
|
3
|
+
export type Application = Pick<Express, 'response'>;
|
|
4
|
+
/** A compiled serializer, as fast-json-stringify builds it. */
|
|
5
|
+
export type Serializer = (body: any) => string;
|
|
6
|
+
/** Finds the serializer for a response from something other than the route, an OpenAPI document. */
|
|
7
|
+
export type SerializerResolver = (res: Response) => Serializer | null;
|
|
8
|
+
/** Notified when an overridden `res.json()` could not use the fast path. */
|
|
9
|
+
export type OverrideErrorHandler = (error: unknown, req: Request) => void;
|
|
10
|
+
export type FastJsonOptions = {
|
|
11
|
+
/**
|
|
12
|
+
* Also route `res.json()`, and `res.send(object)` which Express implements on
|
|
13
|
+
* top of it, through the serializer in force.
|
|
14
|
+
*
|
|
15
|
+
* Off by default. A route's own schema describes the successful payload, so
|
|
16
|
+
* under it only `2xx` responses take the fast path: an error body would
|
|
17
|
+
* otherwise be rewritten into the wrong shape.
|
|
18
|
+
*/
|
|
19
|
+
readonly overrideJson?: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* Called when an overridden `res.json()` could not use the fast path because
|
|
22
|
+
* the serializer threw. The response falls back to the stock `res.json()`
|
|
23
|
+
* either way; this is only so the mismatch is visible.
|
|
24
|
+
*/
|
|
25
|
+
readonly onError?: OverrideErrorHandler;
|
|
26
|
+
/**
|
|
27
|
+
* Make `res.fastJson()` throw when no schema is known for the response,
|
|
28
|
+
* instead of quietly falling back to `res.json()`. An overridden `res.json()`
|
|
29
|
+
* always falls back.
|
|
30
|
+
*/
|
|
31
|
+
readonly strict?: boolean;
|
|
32
|
+
};
|
|
33
|
+
/** Where `fastJsonSchema` leaves the serializer of the route, on `res.locals`. */
|
|
34
|
+
export declare const kSerializer: unique symbol;
|
|
35
|
+
/**
|
|
36
|
+
* Give an application `res.fastJson()`, and with `overrideJson` a `res.json()`
|
|
37
|
+
* that serializes through the schema in force. Once per app, at setup: the
|
|
38
|
+
* methods go on `app.response`, so a request pays nothing to have them.
|
|
39
|
+
*
|
|
40
|
+
* `fastJsonSchema` chooses the schema per route, `fastJsonOpenApi` per
|
|
41
|
+
* operation from a document and calls this itself.
|
|
42
|
+
*
|
|
43
|
+
* @param {Application} app The application to extend
|
|
44
|
+
* @param {FastJsonOptions} options The options to use (optional)
|
|
45
|
+
*
|
|
46
|
+
* Examples:
|
|
47
|
+
* ```ts
|
|
48
|
+
* import express from 'express';
|
|
49
|
+
* import { installFastJson, fastJsonSchema } from 'express-fast-json-stringify';
|
|
50
|
+
*
|
|
51
|
+
* const app = express();
|
|
52
|
+
* installFastJson(app);
|
|
53
|
+
*
|
|
54
|
+
* app.get('/', fastJsonSchema(schema), (req, res) => {
|
|
55
|
+
* res.fastJson({ firstName: 'Simone', lastName: 'Nigro', age: 40 });
|
|
56
|
+
* });
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export declare const installFastJson: (app: Application, options?: FastJsonOptions) => void;
|
|
60
|
+
/** Registers where the document based serializers come from, see fastJsonOpenApi. */
|
|
61
|
+
export declare const setResolver: (app: Application, resolver: SerializerResolver) => void;
|
|
62
|
+
declare global {
|
|
63
|
+
namespace Express {
|
|
64
|
+
interface Response {
|
|
65
|
+
/**
|
|
66
|
+
* Send JSON response, serialized with the schema in force for the route.
|
|
67
|
+
*
|
|
68
|
+
* Examples:
|
|
69
|
+
* ```ts
|
|
70
|
+
* res.fastJson({ user: 'Simone Nigro' });
|
|
71
|
+
* res.status(200).fastJson({ user: 'Simone Nigro' });
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
fastJson: (body: any) => Response;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { sendSerialized } from './send';
|
|
2
|
+
/** Where `fastJsonSchema` leaves the serializer of the route, on `res.locals`. */
|
|
3
|
+
export const kSerializer = Symbol('express-fast-json-stringify');
|
|
4
|
+
// one state per response prototype, which is one per app
|
|
5
|
+
const states = new WeakMap();
|
|
6
|
+
const routeOf = (req) => `${req.baseUrl}${req.route?.path ?? req.path}`;
|
|
7
|
+
// the route's own serializer first, then what the document says
|
|
8
|
+
const pick = (state, res, successOnly) => {
|
|
9
|
+
const own = res.locals[kSerializer];
|
|
10
|
+
if (own !== undefined && (!successOnly || (res.statusCode >= 200 && res.statusCode < 300))) {
|
|
11
|
+
return own;
|
|
12
|
+
}
|
|
13
|
+
return state.resolver === null ? null : state.resolver(res);
|
|
14
|
+
};
|
|
15
|
+
const fastJsonMethod = (state) => function installFastJson(body) {
|
|
16
|
+
const serialize = pick(state, this, false);
|
|
17
|
+
if (serialize === null) {
|
|
18
|
+
if (state.strict) {
|
|
19
|
+
throw new Error(`express-fast-json-stringify: no schema for ${this.req.method} ${routeOf(this.req)}`);
|
|
20
|
+
}
|
|
21
|
+
return state.stockJson.call(this, body);
|
|
22
|
+
}
|
|
23
|
+
return sendSerialized(this, serialize(body));
|
|
24
|
+
};
|
|
25
|
+
// The stock res.json() runs whenever the fast path does not apply: a setting that changes the
|
|
26
|
+
// bytes (json replacer, spaces, escape), no schema for this response, or a body the schema refuses
|
|
27
|
+
const jsonMethod = (state) => function json(body) {
|
|
28
|
+
const app = this.app;
|
|
29
|
+
if (!app.get('json replacer') && !app.get('json spaces') && !app.get('json escape')) {
|
|
30
|
+
const serialize = pick(state, this, true);
|
|
31
|
+
if (serialize !== null) {
|
|
32
|
+
let out;
|
|
33
|
+
try {
|
|
34
|
+
out = serialize(body);
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
state.onError?.(error, this.req);
|
|
38
|
+
}
|
|
39
|
+
if (out !== undefined) {
|
|
40
|
+
return sendSerialized(this, out);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return state.stockJson.call(this, body);
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Give an application `res.fastJson()`, and with `overrideJson` a `res.json()`
|
|
48
|
+
* that serializes through the schema in force. Once per app, at setup: the
|
|
49
|
+
* methods go on `app.response`, so a request pays nothing to have them.
|
|
50
|
+
*
|
|
51
|
+
* `fastJsonSchema` chooses the schema per route, `fastJsonOpenApi` per
|
|
52
|
+
* operation from a document and calls this itself.
|
|
53
|
+
*
|
|
54
|
+
* @param {Application} app The application to extend
|
|
55
|
+
* @param {FastJsonOptions} options The options to use (optional)
|
|
56
|
+
*
|
|
57
|
+
* Examples:
|
|
58
|
+
* ```ts
|
|
59
|
+
* import express from 'express';
|
|
60
|
+
* import { installFastJson, fastJsonSchema } from 'express-fast-json-stringify';
|
|
61
|
+
*
|
|
62
|
+
* const app = express();
|
|
63
|
+
* installFastJson(app);
|
|
64
|
+
*
|
|
65
|
+
* app.get('/', fastJsonSchema(schema), (req, res) => {
|
|
66
|
+
* res.fastJson({ firstName: 'Simone', lastName: 'Nigro', age: 40 });
|
|
67
|
+
* });
|
|
68
|
+
* ```
|
|
69
|
+
*/
|
|
70
|
+
export const installFastJson = (app, options = {}) => {
|
|
71
|
+
const proto = app?.response;
|
|
72
|
+
if (!proto || typeof proto.json !== 'function') {
|
|
73
|
+
throw new TypeError('express-fast-json-stringify: an Express application is required');
|
|
74
|
+
}
|
|
75
|
+
let state = states.get(proto);
|
|
76
|
+
if (state === undefined) {
|
|
77
|
+
state = { stockJson: proto.json, resolver: null, overrideJson: false, onError: undefined, strict: false };
|
|
78
|
+
states.set(proto, state);
|
|
79
|
+
proto.fastJson = fastJsonMethod(state);
|
|
80
|
+
}
|
|
81
|
+
state.overrideJson = options.overrideJson === true;
|
|
82
|
+
state.onError = options.onError;
|
|
83
|
+
state.strict = options.strict === true;
|
|
84
|
+
// the stock method is put back when the override is off, so nothing runs on res.json() that
|
|
85
|
+
// was not there before, and a framework reading the prototype sees the method it knows
|
|
86
|
+
proto.json = state.overrideJson ? jsonMethod(state) : state.stockJson;
|
|
87
|
+
};
|
|
88
|
+
/** Registers where the document based serializers come from, see fastJsonOpenApi. */
|
|
89
|
+
export const setResolver = (app, resolver) => {
|
|
90
|
+
states.get(app.response).resolver = resolver;
|
|
91
|
+
};
|
|
92
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5zdGFsbC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9saWIvaW5zdGFsbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQSxPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0sUUFBUSxDQUFDO0FBc0N4QyxrRkFBa0Y7QUFDbEYsTUFBTSxDQUFDLE1BQU0sV0FBVyxHQUFrQixNQUFNLENBQUMsNkJBQTZCLENBQUMsQ0FBQztBQVloRix5REFBeUQ7QUFDekQsTUFBTSxNQUFNLEdBQUcsSUFBSSxPQUFPLEVBQWlCLENBQUM7QUFFNUMsTUFBTSxPQUFPLEdBQUcsQ0FBQyxHQUFZLEVBQVUsRUFBRSxDQUFDLEdBQUcsR0FBRyxDQUFDLE9BQU8sR0FBRyxHQUFHLENBQUMsS0FBSyxFQUFFLElBQUksSUFBSSxHQUFHLENBQUMsSUFBSSxFQUFFLENBQUM7QUFFekYsZ0VBQWdFO0FBQ2hFLE1BQU0sSUFBSSxHQUFHLENBQUMsS0FBWSxFQUFFLEdBQWEsRUFBRSxXQUFvQixFQUFxQixFQUFFO0lBQ3BGLE1BQU0sR0FBRyxHQUFJLEdBQUcsQ0FBQyxNQUFpQixDQUFDLFdBQVcsQ0FBQyxDQUFDO0lBQ2hELElBQUksR0FBRyxLQUFLLFNBQVMsSUFBSSxDQUFDLENBQUMsV0FBVyxJQUFJLENBQUMsR0FBRyxDQUFDLFVBQVUsSUFBSSxHQUFHLElBQUksR0FBRyxDQUFDLFVBQVUsR0FBRyxHQUFHLENBQUMsQ0FBQyxFQUFFLENBQUM7UUFDM0YsT0FBTyxHQUFHLENBQUM7SUFDYixDQUFDO0lBQ0QsT0FBTyxLQUFLLENBQUMsUUFBUSxLQUFLLElBQUksQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxDQUFDO0FBQzlELENBQUMsQ0FBQztBQUVGLE1BQU0sY0FBYyxHQUFHLENBQUMsS0FBWSxFQUFFLEVBQUUsQ0FDdEMsU0FBUyxlQUFlLENBQWlCLElBQVM7SUFDaEQsTUFB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
|
|
@@ -1,36 +1,22 @@
|
|
|
1
1
|
import type { NextFunction, Request, Response } from 'express';
|
|
2
2
|
import { type Options, type Schema } from 'fast-json-stringify';
|
|
3
|
-
import { type OverrideErrorHandler } from './override';
|
|
4
3
|
export type { Schema, Options } from 'fast-json-stringify';
|
|
5
|
-
export type FastJsonSchemaOptions = Omit<Options, 'mode'
|
|
6
|
-
/**
|
|
7
|
-
* Also route `res.json()` — and therefore `res.send(object)`, which Express
|
|
8
|
-
* implements on top of it — through the compiled serializer.
|
|
9
|
-
*
|
|
10
|
-
* Off by default. Because a single schema describes the successful payload,
|
|
11
|
-
* only `2xx` responses take the fast path: an error body would otherwise be
|
|
12
|
-
* rewritten into the shape of the success schema.
|
|
13
|
-
*/
|
|
14
|
-
readonly overrideJson?: boolean;
|
|
15
|
-
/**
|
|
16
|
-
* Called when an overridden `res.json()` could not use the fast path because
|
|
17
|
-
* the serializer threw. The response falls back to the stock `res.json()`
|
|
18
|
-
* either way; this is only so the mismatch is visible.
|
|
19
|
-
*/
|
|
20
|
-
readonly onError?: OverrideErrorHandler;
|
|
21
|
-
};
|
|
4
|
+
export type FastJsonSchemaOptions = Omit<Options, 'mode'>;
|
|
22
5
|
/**
|
|
23
|
-
* Build a
|
|
6
|
+
* Build a middleware that gives its route a serializer compiled from the schema.
|
|
7
|
+
* The application needs `installFastJson(app)` once for `res.fastJson()` to exist.
|
|
8
|
+
*
|
|
24
9
|
* @param {Schema} schema The schema used to stringify values
|
|
25
|
-
* @param {
|
|
10
|
+
* @param {FastJsonSchemaOptions} options The fast-json-stringify options (optional)
|
|
26
11
|
* @see https://www.npmjs.com/package/fast-json-stringify
|
|
27
12
|
*
|
|
28
13
|
* Examples:
|
|
29
14
|
* ```ts
|
|
30
15
|
* import express from 'express';
|
|
31
|
-
* import { fastJsonSchema, Schema } from 'express-fast-json-stringify';
|
|
16
|
+
* import { installFastJson, fastJsonSchema, Schema } from 'express-fast-json-stringify';
|
|
32
17
|
*
|
|
33
18
|
* const app = express();
|
|
19
|
+
* installFastJson(app);
|
|
34
20
|
*
|
|
35
21
|
* const schema: Schema = {
|
|
36
22
|
* title: 'Example Schema',
|
|
@@ -62,20 +48,4 @@ export type FastJsonSchemaOptions = Omit<Options, 'mode'> & {
|
|
|
62
48
|
* });
|
|
63
49
|
* ```
|
|
64
50
|
*/
|
|
65
|
-
export declare const fastJsonSchema: (schema: Schema, options?: FastJsonSchemaOptions) => (
|
|
66
|
-
declare global {
|
|
67
|
-
namespace Express {
|
|
68
|
-
interface Response {
|
|
69
|
-
/**
|
|
70
|
-
* Send JSON response.
|
|
71
|
-
*
|
|
72
|
-
* Examples:
|
|
73
|
-
* ```ts
|
|
74
|
-
* res.fastJson({ user: 'Simone Nigro' });
|
|
75
|
-
* res.status(200).fastJson({ user: 'Simone Nigro' });
|
|
76
|
-
* ```
|
|
77
|
-
*/
|
|
78
|
-
fastJson: (body: any) => Response;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
51
|
+
export declare const fastJsonSchema: (schema: Schema, options?: FastJsonSchemaOptions) => (_req: Request, res: Response, next: NextFunction) => void;
|