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,18 +1,20 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import { sendJson } from './send';
|
|
1
|
+
import fastJsonStringify from 'fast-json-stringify';
|
|
2
|
+
import { kSerializer } from './install';
|
|
4
3
|
/**
|
|
5
|
-
* Build a
|
|
4
|
+
* Build a middleware that gives its route a serializer compiled from the schema.
|
|
5
|
+
* The application needs `installFastJson(app)` once for `res.fastJson()` to exist.
|
|
6
|
+
*
|
|
6
7
|
* @param {Schema} schema The schema used to stringify values
|
|
7
|
-
* @param {
|
|
8
|
+
* @param {FastJsonSchemaOptions} options The fast-json-stringify options (optional)
|
|
8
9
|
* @see https://www.npmjs.com/package/fast-json-stringify
|
|
9
10
|
*
|
|
10
11
|
* Examples:
|
|
11
12
|
* ```ts
|
|
12
13
|
* import express from 'express';
|
|
13
|
-
* import { fastJsonSchema, Schema } from 'express-fast-json-stringify';
|
|
14
|
+
* import { installFastJson, fastJsonSchema, Schema } from 'express-fast-json-stringify';
|
|
14
15
|
*
|
|
15
16
|
* const app = express();
|
|
17
|
+
* installFastJson(app);
|
|
16
18
|
*
|
|
17
19
|
* const schema: Schema = {
|
|
18
20
|
* title: 'Example Schema',
|
|
@@ -48,26 +50,12 @@ export const fastJsonSchema = (schema, options) => {
|
|
|
48
50
|
if (!schema || (typeof schema !== 'object' && typeof schema !== 'boolean')) {
|
|
49
51
|
throw new TypeError(`express-fast-json-stringify: invalid schema`);
|
|
50
52
|
}
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
*
|
|
57
|
-
* Examples:
|
|
58
|
-
* ```ts
|
|
59
|
-
* res.fastJson({ user: 'Simone Nigro' });
|
|
60
|
-
* res.status(200).fastJson({ user: 'Simone Nigro' });
|
|
61
|
-
* ```
|
|
62
|
-
*/
|
|
63
|
-
res.fastJson = (body) => sendJson(req, res, fjs(body));
|
|
64
|
-
if (overrideJson) {
|
|
65
|
-
// A single schema describes the successful payload, so applying it to an
|
|
66
|
-
// error body would rewrite it into the wrong shape. Only 2xx responses
|
|
67
|
-
// take the fast path; everything else keeps the stock res.json().
|
|
68
|
-
overrideResJson(req, res, (status) => (status >= 200 && status < 300 ? fjs : null), onError);
|
|
69
|
-
}
|
|
53
|
+
const serialize = fastJsonStringify(schema, options);
|
|
54
|
+
// One write on res.locals and a next(), nothing else on req or res: a framework that reads a
|
|
55
|
+
// middleware's source to skip work, fulmine does, must be able to see this touches no header
|
|
56
|
+
return (_req, res, next) => {
|
|
57
|
+
res.locals[kSerializer] = serialize;
|
|
70
58
|
next();
|
|
71
59
|
};
|
|
72
60
|
};
|
|
73
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
61
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWlkZGxld2FyZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9saWIvbWlkZGxld2FyZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLGlCQUFnRCxNQUFNLHFCQUFxQixDQUFDO0FBRW5GLE9BQU8sRUFBRSxXQUFXLEVBQW1CLE1BQU0sV0FBVyxDQUFDO0FBTXpEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0E2Q0c7QUFDSCxNQUFNLENBQUMsTUFBTSxjQUFjLEdBQUcsQ0FBQyxNQUFjLEVBQUUsT0FBK0IsRUFBRSxFQUFFO0lBQ2hGLElBQUksQ0FBQyxNQUFNLElBQUksQ0FBQyxPQUFPLE1BQU0sS0FBSyxRQUFRLElBQUksT0FBTyxNQUFNLEtBQUssU0FBUyxDQUFDLEVBQUUsQ0FBQztRQUMzRSxNQUFNLElBQUksU0FBUyxDQUFDLDZDQUE2QyxDQUFDLENBQUM7SUFDckUsQ0FBQztJQUNELE1BQU0sU0FBUyxHQUFlLGlCQUFpQixDQUFDLE1BQU0sRUFBRSxPQUFPLENBQUMsQ0FBQztJQUNqRSw2RkFBNkY7SUFDN0YsNkZBQTZGO0lBQzdGLE9BQU8sQ0FBQyxJQUFhLEVBQUUsR0FBYSxFQUFFLElBQWtCLEVBQVEsRUFBRTtRQUMvRCxHQUFHLENBQUMsTUFBaUQsQ0FBQyxXQUFXLENBQUMsR0FBRyxTQUFTLENBQUM7UUFDaEYsSUFBSSxFQUFFLENBQUM7SUFDVCxDQUFDLENBQUM7QUFDSixDQUFDLENBQUMifQ==
|
|
@@ -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;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import { sendJson } from './send';
|
|
1
|
+
import fastJsonStringify from 'fast-json-stringify';
|
|
2
|
+
import { installFastJson, setResolver } from './install';
|
|
4
3
|
/**
|
|
5
4
|
* Translate an Express route pattern into the OpenAPI equivalent:
|
|
6
5
|
* `/users/:id` becomes `/users/{id}`. Express parameter modifiers — a trailing
|
|
@@ -62,15 +61,45 @@ const withSharedSchemas = (document, schema) => {
|
|
|
62
61
|
}
|
|
63
62
|
return result;
|
|
64
63
|
};
|
|
64
|
+
const validateDocument = (document) => {
|
|
65
|
+
if (!document || typeof document !== 'object' || typeof document.paths !== 'object' || document.paths === null) {
|
|
66
|
+
throw new TypeError(`express-fast-json-stringify: invalid OpenAPI document`);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
65
69
|
/**
|
|
66
|
-
*
|
|
67
|
-
*
|
|
70
|
+
* The schema the document declares for one operation and status, with the
|
|
71
|
+
* shared schemas attached so `$ref` resolves, ready for `fastJsonSchema`. For
|
|
72
|
+
* a route whose Express path does not match the document.
|
|
73
|
+
*
|
|
74
|
+
* @param {OpenApiDocument} document The OpenAPI 3.x or Swagger 2.0 document
|
|
75
|
+
* @param {string} path The OpenAPI path, `/users/{id}`
|
|
76
|
+
* @param {string} method The operation, `get`
|
|
77
|
+
* @param {number} status The response status, `200` when omitted
|
|
78
|
+
* @param {string} contentType The media type, `application/json` when omitted
|
|
79
|
+
* @returns {Schema | undefined} undefined when the document describes no such response
|
|
68
80
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
81
|
+
* Examples:
|
|
82
|
+
* ```ts
|
|
83
|
+
* app.get('/v2/people/:id', fastJsonSchema(openApiSchema(document, '/users/{id}', 'get')!), handler);
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export const openApiSchema = (document, path, method, status = 200, contentType = 'application/json') => {
|
|
87
|
+
validateDocument(document);
|
|
88
|
+
const schema = findResponseSchema(document, path, method.toLowerCase(), status, contentType);
|
|
89
|
+
return schema ? withSharedSchemas(document, schema) : undefined;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Serialize every documented response of an application from its OpenAPI or
|
|
93
|
+
* Swagger document, so the contract you already publish is the one used to
|
|
94
|
+
* serialize. Once per app, at setup: it calls `installFastJson(app)` itself, and no
|
|
95
|
+
* middleware runs per request.
|
|
96
|
+
*
|
|
97
|
+
* The operation is resolved from the matched Express route, and the schema
|
|
98
|
+
* from the response status code, which means `res.status(201).fastJson()`
|
|
71
99
|
* serializes with the `201` schema. Routes the document does not describe fall
|
|
72
100
|
* back to `res.json()` unless `strict` is set.
|
|
73
101
|
*
|
|
102
|
+
* @param {Application} app The application
|
|
74
103
|
* @param {OpenApiDocument} document The OpenAPI 3.x or Swagger 2.0 document
|
|
75
104
|
* @param {OpenApiOptions} options The options to use (optional)
|
|
76
105
|
*
|
|
@@ -83,7 +112,7 @@ const withSharedSchemas = (document, schema) => {
|
|
|
83
112
|
* const app = express();
|
|
84
113
|
* const document = swaggerJsdoc({ definition: { openapi: '3.1.0', info: { title: 'API', version: '1.0.0' } }, apis: ['./routes/*.ts'] });
|
|
85
114
|
*
|
|
86
|
-
*
|
|
115
|
+
* fastJsonOpenApi(app, document);
|
|
87
116
|
*
|
|
88
117
|
* app.get('/users/:id', (req, res, next) => {
|
|
89
118
|
* try {
|
|
@@ -94,53 +123,52 @@ const withSharedSchemas = (document, schema) => {
|
|
|
94
123
|
* });
|
|
95
124
|
* ```
|
|
96
125
|
*/
|
|
97
|
-
export const fastJsonOpenApi = (document, options = {}) => {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
const
|
|
102
|
-
// One compiled serializer per operation and status code. Misses are cached as
|
|
103
|
-
// `null` so an undocumented route costs a single lookup.
|
|
104
|
-
const serializers = new Map();
|
|
105
|
-
const routePath = (req) => pinnedPath ?? toOpenApiPath(`${req.baseUrl}${req.route?.path ?? req.path}`);
|
|
106
|
-
const serializerFor = (req, status) => {
|
|
107
|
-
const path = routePath(req);
|
|
108
|
-
const method = (pinnedMethod ?? req.method).toLowerCase();
|
|
109
|
-
const key = `${method} ${path} ${status}`;
|
|
110
|
-
const cached = serializers.get(key);
|
|
111
|
-
if (cached !== undefined) {
|
|
112
|
-
return cached;
|
|
113
|
-
}
|
|
126
|
+
export const fastJsonOpenApi = (app, document, options = {}) => {
|
|
127
|
+
validateDocument(document);
|
|
128
|
+
const { contentType = 'application/json', overrideJson, onError, strict, ...fastJsonOptions } = options;
|
|
129
|
+
installFastJson(app, { overrideJson, onError, strict });
|
|
130
|
+
const compile = (path, method, status) => {
|
|
114
131
|
const schema = findResponseSchema(document, path, method, status, contentType);
|
|
115
|
-
|
|
116
|
-
serializers.set(key, serializer);
|
|
117
|
-
return serializer;
|
|
132
|
+
return schema ? fastJsonStringify(withSharedSchemas(document, schema), fastJsonOptions) : null;
|
|
118
133
|
};
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
|
134
|
+
// One compiled serializer per route, mount and status, found from the route object the
|
|
135
|
+
// framework already matched: no path is rebuilt per request. Misses are cached as `null`.
|
|
136
|
+
// A router mounted twice answers under two mounts, so the mount is a key of its own.
|
|
137
|
+
const byRoute = new WeakMap();
|
|
138
|
+
// Answered from plain middleware, before any route matched: the request path is what names
|
|
139
|
+
// the operation, and a string key is all there is.
|
|
140
|
+
const byPath = new Map();
|
|
141
|
+
setResolver(app, (res) => {
|
|
142
|
+
const req = res.req;
|
|
143
|
+
const status = res.statusCode;
|
|
144
|
+
const method = req.method.toLowerCase();
|
|
145
|
+
const route = req.route;
|
|
146
|
+
if (route === undefined) {
|
|
147
|
+
const key = `${method} ${req.baseUrl}${req.path} ${status}`;
|
|
148
|
+
let serialize = byPath.get(key);
|
|
149
|
+
if (serialize === undefined) {
|
|
150
|
+
serialize = compile(toOpenApiPath(`${req.baseUrl}${req.path}`), method, status);
|
|
151
|
+
byPath.set(key, serialize);
|
|
137
152
|
}
|
|
138
|
-
return
|
|
139
|
-
};
|
|
140
|
-
if (overrideJson) {
|
|
141
|
-
overrideResJson(req, res, (status) => serializerFor(req, status), onError);
|
|
153
|
+
return serialize;
|
|
142
154
|
}
|
|
143
|
-
|
|
144
|
-
|
|
155
|
+
let byMount = byRoute.get(route);
|
|
156
|
+
if (byMount === undefined) {
|
|
157
|
+
byMount = new Map();
|
|
158
|
+
byRoute.set(route, byMount);
|
|
159
|
+
}
|
|
160
|
+
const baseUrl = req.baseUrl;
|
|
161
|
+
let byStatus = byMount.get(baseUrl);
|
|
162
|
+
if (byStatus === undefined) {
|
|
163
|
+
byStatus = new Map();
|
|
164
|
+
byMount.set(baseUrl, byStatus);
|
|
165
|
+
}
|
|
166
|
+
let serialize = byStatus.get(status);
|
|
167
|
+
if (serialize === undefined) {
|
|
168
|
+
serialize = compile(toOpenApiPath(`${baseUrl}${route.path}`), method, status);
|
|
169
|
+
byStatus.set(status, serialize);
|
|
170
|
+
}
|
|
171
|
+
return serialize;
|
|
172
|
+
});
|
|
145
173
|
};
|
|
146
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
174
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BlbmFwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9saWIvb3BlbmFwaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLGlCQUFnRCxNQUFNLHFCQUFxQixDQUFDO0FBRW5GLE9BQU8sRUFBMEMsZUFBZSxFQUFtQixXQUFXLEVBQUUsTUFBTSxXQUFXLENBQUM7QUF3QmxIOzs7O0dBSUc7QUFDSCxNQUFNLENBQUMsTUFBTSxhQUFhLEdBQUcsQ0FBQyxJQUFZLEVBQVUsRUFBRSxDQUFDLElBQUksQ0FBQyxPQUFPLENBQUMsa0NBQWtDLEVBQUUsTUFBTSxDQUFDLENBQUM7QUFFaEg7OztHQUdHO0FBQ0gsTUFBTSxnQkFBZ0IsR0FBRyxDQUFDLE1BQWMsRUFBcUIsRUFBRSxDQUFDLENBQUMsTUFBTSxLQUFLLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLEVBQUUsS0FBSyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQztBQUVqSDs7O0dBR0c7QUFDSCxNQUFNLGdCQUFnQixHQUFHLENBQUMsTUFBYyxFQUFxQixFQUFFO0lBQzdELE1BQU0sS0FBSyxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsTUFBTSxHQUFHLEdBQUcsQ0FBQyxDQUFDO0lBQ3ZDLE9BQU8sQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLEVBQUUsR0FBRyxLQUFLLElBQUksRUFBRSxHQUFHLEtBQUssSUFBSSxFQUFFLFNBQVMsQ0FBQyxDQUFDO0FBQ2pFLENBQUMsQ0FBQztBQVFGLE1BQU0sa0JBQWtCLEdBQUcsQ0FBQyxRQUF5QixFQUFFLElBQVksRUFBRSxNQUFjLEVBQUUsTUFBYyxFQUFFLFdBQW1CLEVBQVcsRUFBRTtJQUNuSSxNQUFNLFVBQVUsR0FBRyxRQUFRLENBQUMsS0FBSyxFQUFFLENBQUMsSUFBSSxDQUFrRCxDQUFDO0lBQzNGLElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztRQUNoQixPQUFP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
|
|
@@ -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/module/lib/send.js
CHANGED
|
@@ -1,41 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Write an already serialized JSON payload
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* apart from each other or from Express.
|
|
2
|
+
* Write an already serialized JSON payload the way `res.json()` does once the
|
|
3
|
+
* body is a string: the type when the route set none, then `res.send()`, which
|
|
4
|
+
* owns `Content-Length`, the `ETag`, conditional requests, `204`/`304` and
|
|
5
|
+
* `HEAD` in Express and in the frameworks that stand in for it. Nothing is
|
|
6
|
+
* reimplemented here, so nothing can drift.
|
|
8
7
|
*/
|
|
9
|
-
export const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
if (!res.getHeader('Content-Type')) {
|
|
13
|
-
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
8
|
+
export const sendSerialized = (res, json) => {
|
|
9
|
+
if (!res.get('Content-Type')) {
|
|
10
|
+
res.set('Content-Type', 'application/json; charset=utf-8');
|
|
14
11
|
}
|
|
15
|
-
|
|
16
|
-
// res.json() for res.fastJson() keeps conditional requests working.
|
|
17
|
-
const etagFn = res.app?.get('etag fn');
|
|
18
|
-
if (typeof etagFn === 'function' && !res.getHeader('ETag')) {
|
|
19
|
-
const etag = etagFn(payload, 'utf-8');
|
|
20
|
-
if (etag) {
|
|
21
|
-
res.setHeader('ETag', etag);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
if (req.fresh) {
|
|
25
|
-
res.statusCode = 304;
|
|
26
|
-
}
|
|
27
|
-
// 204 No Content and 304 Not Modified must not carry a body, nor describe one.
|
|
28
|
-
if (res.statusCode === 204 || res.statusCode === 304) {
|
|
29
|
-
res.removeHeader('Content-Type');
|
|
30
|
-
res.removeHeader('Content-Length');
|
|
31
|
-
res.removeHeader('Transfer-Encoding');
|
|
32
|
-
payload = '';
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
// Without this the response falls back to chunked encoding, and HEAD
|
|
36
|
-
// requests answer with no length at all.
|
|
37
|
-
res.setHeader('Content-Length', Buffer.byteLength(payload));
|
|
38
|
-
}
|
|
39
|
-
return res.end(payload);
|
|
12
|
+
return res.send(json);
|
|
40
13
|
};
|
|
41
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
14
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VuZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9saWIvc2VuZC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQTs7Ozs7O0dBTUc7QUFDSCxNQUFNLENBQUMsTUFBTSxjQUFjLEdBQUcsQ0FBQyxHQUFhLEVBQUUsSUFBWSxFQUFZLEVBQUU7SUFDdEUsSUFBSSxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsY0FBYyxDQUFDLEVBQUUsQ0FBQztRQUM3QixHQUFHLENBQUMsR0FBRyxDQUFDLGNBQWMsRUFBRSxpQ0FBaUMsQ0FBQyxDQUFDO0lBQzdELENBQUM7SUFDRCxPQUFPLEdBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7QUFDeEIsQ0FBQyxDQUFDIn0=
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "express-fast-json-stringify",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "With express-fast-json-stringify, you can leverage fast-json-stringify in your Express application to improve JSON serialization performance",
|
|
5
5
|
"main": "build/main/index.js",
|
|
6
6
|
"typings": "build/main/index.d.ts",
|
|
@@ -44,7 +44,8 @@
|
|
|
44
44
|
"prepare-release": "run-s reset-hard test cov:check doc:html release doc:publish",
|
|
45
45
|
"example": "tsx example/index.ts",
|
|
46
46
|
"release": "release-it",
|
|
47
|
-
"example:openapi": "tsx example/openapi.ts"
|
|
47
|
+
"example:openapi": "tsx example/openapi.ts",
|
|
48
|
+
"prepare": "husky"
|
|
48
49
|
},
|
|
49
50
|
"engines": {
|
|
50
51
|
"node": ">=20"
|
|
@@ -66,7 +67,10 @@
|
|
|
66
67
|
"eslint-plugin-functional": "^10.0.0",
|
|
67
68
|
"eslint-plugin-import-x": "^4.17.1",
|
|
68
69
|
"express": "^5.2.1",
|
|
70
|
+
"fulmine.js": "^5.21.2",
|
|
69
71
|
"gh-pages": "^6.3.0",
|
|
72
|
+
"husky": "^9.1.7",
|
|
73
|
+
"lint-staged": "^17.2.0",
|
|
70
74
|
"npm-run-all2": "^9.0.2",
|
|
71
75
|
"open-cli": "^9.0.0",
|
|
72
76
|
"prettier": "^3.9.6",
|
|
@@ -93,5 +97,8 @@
|
|
|
93
97
|
"prettier": {
|
|
94
98
|
"singleQuote": true,
|
|
95
99
|
"printWidth": 180
|
|
100
|
+
},
|
|
101
|
+
"lint-staged": {
|
|
102
|
+
"*": "prettier --write --ignore-unknown"
|
|
96
103
|
}
|
|
97
104
|
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { Request, Response } from 'express';
|
|
2
|
-
/** Resolves the serializer to use for a response status, or `null` for none. */
|
|
3
|
-
export type SerializerResolver = (status: number) => ((body: any) => string) | null;
|
|
4
|
-
/** Notified when an overridden `res.json()` could not use the fast path. */
|
|
5
|
-
export type OverrideErrorHandler = (error: unknown, req: Request) => void;
|
|
6
|
-
/**
|
|
7
|
-
* Route `res.json()` through a compiled serializer when one is available.
|
|
8
|
-
*
|
|
9
|
-
* Only `res.json` is replaced: Express implements `res.send(object)` by calling
|
|
10
|
-
* `res.json(object)`, so both entry points are covered by the single hook.
|
|
11
|
-
*
|
|
12
|
-
* The override is deliberately incapable of breaking a route. Whenever the fast
|
|
13
|
-
* path does not apply — no schema for this response, custom JSON settings, or a
|
|
14
|
-
* serializer that throws on the given body — the original `res.json()` runs and
|
|
15
|
-
* the response is exactly what it was before.
|
|
16
|
-
*/
|
|
17
|
-
export declare const overrideResJson: (req: Request, res: Response, resolve: SerializerResolver, onError?: OverrideErrorHandler) => void;
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.overrideResJson = void 0;
|
|
4
|
-
const send_1 = require("./send");
|
|
5
|
-
/**
|
|
6
|
-
* Settings that change the bytes `res.json()` writes. A compiled serializer
|
|
7
|
-
* cannot reproduce any of them, so the fast path has to step aside when one is
|
|
8
|
-
* configured rather than silently change the output.
|
|
9
|
-
*/
|
|
10
|
-
const hasCustomJsonSettings = (res) => {
|
|
11
|
-
var _a, _b;
|
|
12
|
-
const app = res.app;
|
|
13
|
-
if (!app) {
|
|
14
|
-
return false;
|
|
15
|
-
}
|
|
16
|
-
return Boolean((_b = (_a = app.get('json replacer')) !== null && _a !== void 0 ? _a : app.get('json spaces')) !== null && _b !== void 0 ? _b : app.get('json escape'));
|
|
17
|
-
};
|
|
18
|
-
/**
|
|
19
|
-
* Route `res.json()` through a compiled serializer when one is available.
|
|
20
|
-
*
|
|
21
|
-
* Only `res.json` is replaced: Express implements `res.send(object)` by calling
|
|
22
|
-
* `res.json(object)`, so both entry points are covered by the single hook.
|
|
23
|
-
*
|
|
24
|
-
* The override is deliberately incapable of breaking a route. Whenever the fast
|
|
25
|
-
* path does not apply — no schema for this response, custom JSON settings, or a
|
|
26
|
-
* serializer that throws on the given body — the original `res.json()` runs and
|
|
27
|
-
* the response is exactly what it was before.
|
|
28
|
-
*/
|
|
29
|
-
const overrideResJson = (req, res, resolve, onError) => {
|
|
30
|
-
const original = res.json.bind(res);
|
|
31
|
-
res.json = (body) => {
|
|
32
|
-
if (!hasCustomJsonSettings(res)) {
|
|
33
|
-
const serialize = resolve(res.statusCode);
|
|
34
|
-
if (serialize) {
|
|
35
|
-
try {
|
|
36
|
-
return (0, send_1.sendJson)(req, res, serialize(body));
|
|
37
|
-
}
|
|
38
|
-
catch (error) {
|
|
39
|
-
// A body that does not fit the schema must not turn into a failed
|
|
40
|
-
// request: report it, then fall through to the stock res.json().
|
|
41
|
-
onError === null || onError === void 0 ? void 0 : onError(error, req);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
return original(body);
|
|
46
|
-
};
|
|
47
|
-
};
|
|
48
|
-
exports.overrideResJson = overrideResJson;
|
|
49
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3ZlcnJpZGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL292ZXJyaWRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUVBLGlDQUFrQztBQVFsQzs7OztHQUlHO0FBQ0gsTUFBTSxxQkFBcUIsR0FBRyxDQUFDLEdBQWEsRUFBVyxFQUFFOztJQUN2RCxNQUFNLEdBQUcsR0FBRyxHQUFHLENBQUMsR0FBRyxDQUFDO0lBQ3BCLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUNULE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztJQUNELE9BQU8sT0FBTyxDQUFDLE1BQUEsTUFBQSxHQUFHLENBQUMsR0FBRyxDQUFDLGVBQWUsQ0FBQyxtQ0FBSSxHQUFHLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxtQ0FBSSxHQUFHLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUM7QUFDL0YsQ0FBQyxDQUFDO0FBRUY7Ozs7Ozs7Ozs7R0FVRztBQUNJLE1BQU0sZUFBZSxHQUFHLENBQUMsR0FBWSxFQUFFLEdBQWEsRUFBRSxPQUEyQixFQUFFLE9BQThCLEVBQVEsRUFBRTtJQUNoSSxNQUFNLFFBQVEsR0FBRyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQztJQUVwQyxHQUFHLENBQUMsSUFBSSxHQUFHLENBQUMsSUFBUyxFQUFZLEVBQUU7UUFDakMsSUFBSSxDQUFDLHFCQUFxQixDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7WUFDaEMsTUFBTSxTQUFTLEdBQUcsT0FBTyxDQUFDLEdBQUcsQ0FBQyxVQUFVLENBQUMsQ0FBQztZQUMxQyxJQUFJLFNBQVMsRUFBRSxDQUFDO2dCQUNkLElBQUksQ0FBQztvQkFDSCxPQUFPLElBQUEsZUFBUSxFQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsU0FBUyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7Z0JBQzdDLENBQUM7Z0JBQUMsT0FBTyxLQUFLLEVBQUUsQ0FBQztvQkFDZixrRUFBa0U7b0JBQ2xFLGlFQUFpRTtvQkFDakUsT0FBTyxhQUFQLE9BQU8sdUJBQVAsT0FBTyxDQUFHLEtBQUssRUFBRSxHQUFHLENBQUMsQ0FBQztnQkFDeEIsQ0FBQztZQUNILENBQUM7UUFDSCxDQUFDO1FBQ0QsT0FBTyxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDeEIsQ0FBQyxDQUFDO0FBQ0osQ0FBQyxDQUFDO0FBbEJXLFFBQUEsZUFBZSxtQkFrQjFCIn0=
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import type { Request, Response } from 'express';
|
|
2
|
-
/** Resolves the serializer to use for a response status, or `null` for none. */
|
|
3
|
-
export type SerializerResolver = (status: number) => ((body: any) => string) | null;
|
|
4
|
-
/** Notified when an overridden `res.json()` could not use the fast path. */
|
|
5
|
-
export type OverrideErrorHandler = (error: unknown, req: Request) => void;
|
|
6
|
-
/**
|
|
7
|
-
* Route `res.json()` through a compiled serializer when one is available.
|
|
8
|
-
*
|
|
9
|
-
* Only `res.json` is replaced: Express implements `res.send(object)` by calling
|
|
10
|
-
* `res.json(object)`, so both entry points are covered by the single hook.
|
|
11
|
-
*
|
|
12
|
-
* The override is deliberately incapable of breaking a route. Whenever the fast
|
|
13
|
-
* path does not apply — no schema for this response, custom JSON settings, or a
|
|
14
|
-
* serializer that throws on the given body — the original `res.json()` runs and
|
|
15
|
-
* the response is exactly what it was before.
|
|
16
|
-
*/
|
|
17
|
-
export declare const overrideResJson: (req: Request, res: Response, resolve: SerializerResolver, onError?: OverrideErrorHandler) => void;
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { sendJson } from './send';
|
|
2
|
-
/**
|
|
3
|
-
* Settings that change the bytes `res.json()` writes. A compiled serializer
|
|
4
|
-
* cannot reproduce any of them, so the fast path has to step aside when one is
|
|
5
|
-
* configured rather than silently change the output.
|
|
6
|
-
*/
|
|
7
|
-
const hasCustomJsonSettings = (res) => {
|
|
8
|
-
const app = res.app;
|
|
9
|
-
if (!app) {
|
|
10
|
-
return false;
|
|
11
|
-
}
|
|
12
|
-
return Boolean(app.get('json replacer') ?? app.get('json spaces') ?? app.get('json escape'));
|
|
13
|
-
};
|
|
14
|
-
/**
|
|
15
|
-
* Route `res.json()` through a compiled serializer when one is available.
|
|
16
|
-
*
|
|
17
|
-
* Only `res.json` is replaced: Express implements `res.send(object)` by calling
|
|
18
|
-
* `res.json(object)`, so both entry points are covered by the single hook.
|
|
19
|
-
*
|
|
20
|
-
* The override is deliberately incapable of breaking a route. Whenever the fast
|
|
21
|
-
* path does not apply — no schema for this response, custom JSON settings, or a
|
|
22
|
-
* serializer that throws on the given body — the original `res.json()` runs and
|
|
23
|
-
* the response is exactly what it was before.
|
|
24
|
-
*/
|
|
25
|
-
export const overrideResJson = (req, res, resolve, onError) => {
|
|
26
|
-
const original = res.json.bind(res);
|
|
27
|
-
res.json = (body) => {
|
|
28
|
-
if (!hasCustomJsonSettings(res)) {
|
|
29
|
-
const serialize = resolve(res.statusCode);
|
|
30
|
-
if (serialize) {
|
|
31
|
-
try {
|
|
32
|
-
return sendJson(req, res, serialize(body));
|
|
33
|
-
}
|
|
34
|
-
catch (error) {
|
|
35
|
-
// A body that does not fit the schema must not turn into a failed
|
|
36
|
-
// request: report it, then fall through to the stock res.json().
|
|
37
|
-
onError?.(error, req);
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return original(body);
|
|
42
|
-
};
|
|
43
|
-
};
|
|
44
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3ZlcnJpZGUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvbGliL292ZXJyaWRlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUVBLE9BQU8sRUFBRSxRQUFRLEVBQUUsTUFBTSxRQUFRLENBQUM7QUFRbEM7Ozs7R0FJRztBQUNILE1BQU0scUJBQXFCLEdBQUcsQ0FBQyxHQUFhLEVBQVcsRUFBRTtJQUN2RCxNQUFNLEdBQUcsR0FBRyxHQUFHLENBQUMsR0FBRyxDQUFDO0lBQ3BCLElBQUksQ0FBQyxHQUFHLEVBQUUsQ0FBQztRQUNULE9BQU8sS0FBSyxDQUFDO0lBQ2YsQ0FBQztJQUNELE9BQU8sT0FBTyxDQUFDLEdBQUcsQ0FBQyxHQUFHLENBQUMsZUFBZSxDQUFDLElBQUksR0FBRyxDQUFDLEdBQUcsQ0FBQyxhQUFhLENBQUMsSUFBSSxHQUFHLENBQUMsR0FBRyxDQUFDLGFBQWEsQ0FBQyxDQUFDLENBQUM7QUFDL0YsQ0FBQyxDQUFDO0FBRUY7Ozs7Ozs7Ozs7R0FVRztBQUNILE1BQU0sQ0FBQyxNQUFNLGVBQWUsR0FBRyxDQUFDLEdBQVksRUFBRSxHQUFhLEVBQUUsT0FBMkIsRUFBRSxPQUE4QixFQUFRLEVBQUU7SUFDaEksTUFBTSxRQUFRLEdBQUcsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7SUFFcEMsR0FBRyxDQUFDLElBQUksR0FBRyxDQUFDLElBQVMsRUFBWSxFQUFFO1FBQ2pDLElBQUksQ0FBQyxxQkFBcUIsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQ2hDLE1BQU0sU0FBUyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsVUFBVSxDQUFDLENBQUM7WUFDMUMsSUFBSSxTQUFTLEVBQUUsQ0FBQztnQkFDZCxJQUFJLENBQUM7b0JBQ0gsT0FBTyxRQUFRLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztnQkFDN0MsQ0FBQztnQkFBQyxPQUFPLEtBQUssRUFBRSxDQUFDO29CQUNmLGtFQUFrRTtvQkFDbEUsaUVBQWlFO29CQUNqRSxPQUFPLEVBQUUsQ0FBQyxLQUFLLEVBQUUsR0FBRyxDQUFDLENBQUM7Z0JBQ3hCLENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQztRQUNELE9BQU8sUUFBUSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3hCLENBQUMsQ0FBQztBQUNKLENBQUMsQ0FBQyJ9
|