express-fast-json-stringify 1.2.8 → 1.3.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 +13 -0
- package/LICENSE +21 -21
- package/README.md +274 -113
- package/build/main/index.d.ts +2 -0
- package/build/main/index.js +2 -1
- package/build/main/lib/middleware.d.ts +24 -6
- package/build/main/lib/middleware.js +29 -14
- package/build/main/lib/openapi.d.ts +87 -0
- package/build/main/lib/openapi.js +166 -0
- package/build/main/lib/override.d.ts +17 -0
- package/build/main/lib/override.js +49 -0
- package/build/main/lib/send.d.ts +10 -0
- package/build/main/lib/send.js +46 -0
- package/build/module/index.d.ts +2 -0
- package/build/module/index.js +2 -1
- package/build/module/lib/middleware.d.ts +24 -6
- package/build/module/lib/middleware.js +18 -14
- package/build/module/lib/openapi.d.ts +87 -0
- package/build/module/lib/openapi.js +146 -0
- package/build/module/lib/override.d.ts +17 -0
- package/build/module/lib/override.js +44 -0
- package/build/module/lib/send.d.ts +10 -0
- package/build/module/lib/send.js +41 -0
- package/package.json +59 -80
- package/build/main/index.spec.d.ts +0 -1
- package/build/main/index.spec.js +0 -66
- package/build/module/index.spec.d.ts +0 -1
- package/build/module/index.spec.js +0 -61
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import fastJson from 'fast-json-stringify';
|
|
2
|
+
import { overrideResJson } from './override';
|
|
3
|
+
import { sendJson } from './send';
|
|
4
|
+
/**
|
|
5
|
+
* Translate an Express route pattern into the OpenAPI equivalent:
|
|
6
|
+
* `/users/:id` becomes `/users/{id}`. Express parameter modifiers — a trailing
|
|
7
|
+
* `?` or an inline `(regex)` — are dropped, since OpenAPI has no notion of them.
|
|
8
|
+
*/
|
|
9
|
+
export const toOpenApiPath = (path) => path.replace(/:([A-Za-z0-9_]+)(\([^)]*\))?\??/g, '{$1}');
|
|
10
|
+
/**
|
|
11
|
+
* Express answers a HEAD request with the GET handler, and documents rarely
|
|
12
|
+
* describe a `head` operation, so fall back to `get`.
|
|
13
|
+
*/
|
|
14
|
+
const methodCandidates = (method) => (method === 'head' ? ['head', 'get'] : [method]);
|
|
15
|
+
/**
|
|
16
|
+
* Response keys to try, most specific first. OpenAPI allows a wildcard range
|
|
17
|
+
* (`2XX`) and a catch all (`default`) next to explicit codes.
|
|
18
|
+
*/
|
|
19
|
+
const statusCandidates = (status) => {
|
|
20
|
+
const range = Math.floor(status / 100);
|
|
21
|
+
return [String(status), `${range}XX`, `${range}xx`, 'default'];
|
|
22
|
+
};
|
|
23
|
+
const findResponseSchema = (document, path, method, status, contentType) => {
|
|
24
|
+
const operations = document.paths?.[path];
|
|
25
|
+
if (!operations) {
|
|
26
|
+
return undefined;
|
|
27
|
+
}
|
|
28
|
+
for (const candidate of methodCandidates(method)) {
|
|
29
|
+
const operation = operations[candidate];
|
|
30
|
+
const responses = operation?.responses;
|
|
31
|
+
if (!responses) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
for (const key of statusCandidates(status)) {
|
|
35
|
+
const response = responses[key];
|
|
36
|
+
if (!response) {
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
// OpenAPI 3.x keys the schema by media type; Swagger 2.0 does not.
|
|
40
|
+
const schema = response.content?.[contentType]?.schema ?? response.schema;
|
|
41
|
+
if (schema) {
|
|
42
|
+
return schema;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return undefined;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* fast-json-stringify resolves `$ref` as a JSON pointer against the root of the
|
|
50
|
+
* schema it is given, so the document's shared schemas only have to be reachable
|
|
51
|
+
* under the key the references already use — `components` for OpenAPI 3.x,
|
|
52
|
+
* `definitions` for Swagger 2.0. No rewriting needed, and recursive references
|
|
53
|
+
* keep working.
|
|
54
|
+
*/
|
|
55
|
+
const withSharedSchemas = (document, schema) => {
|
|
56
|
+
const result = { ...schema };
|
|
57
|
+
if (document.components && result.components === undefined) {
|
|
58
|
+
result.components = document.components;
|
|
59
|
+
}
|
|
60
|
+
if (document.definitions && result.definitions === undefined) {
|
|
61
|
+
result.definitions = document.definitions;
|
|
62
|
+
}
|
|
63
|
+
return result;
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Build a stringify middleware that takes its schemas from an OpenAPI or Swagger
|
|
67
|
+
* document, so the contract you already publish is the one used to serialize.
|
|
68
|
+
*
|
|
69
|
+
* The operation is resolved per request from the matched Express route, and the
|
|
70
|
+
* schema from the response status code, which means `res.status(201).fastJson()`
|
|
71
|
+
* serializes with the `201` schema. Routes the document does not describe fall
|
|
72
|
+
* back to `res.json()` unless `strict` is set.
|
|
73
|
+
*
|
|
74
|
+
* @param {OpenApiDocument} document The OpenAPI 3.x or Swagger 2.0 document
|
|
75
|
+
* @param {OpenApiOptions} options The options to use (optional)
|
|
76
|
+
*
|
|
77
|
+
* Examples:
|
|
78
|
+
* ```ts
|
|
79
|
+
* import express from 'express';
|
|
80
|
+
* import swaggerJsdoc from 'swagger-jsdoc';
|
|
81
|
+
* import { fastJsonOpenApi } from 'express-fast-json-stringify';
|
|
82
|
+
*
|
|
83
|
+
* const app = express();
|
|
84
|
+
* const document = swaggerJsdoc({ definition: { openapi: '3.1.0', info: { title: 'API', version: '1.0.0' } }, apis: ['./routes/*.ts'] });
|
|
85
|
+
*
|
|
86
|
+
* app.use(fastJsonOpenApi(document));
|
|
87
|
+
*
|
|
88
|
+
* app.get('/users/:id', (req, res, next) => {
|
|
89
|
+
* try {
|
|
90
|
+
* res.fastJson({ id: Number(req.params.id), firstName: 'Simone' });
|
|
91
|
+
* } catch (error) {
|
|
92
|
+
* next(error);
|
|
93
|
+
* }
|
|
94
|
+
* });
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export const fastJsonOpenApi = (document, options = {}) => {
|
|
98
|
+
if (!document || typeof document !== 'object' || typeof document.paths !== 'object' || document.paths === null) {
|
|
99
|
+
throw new TypeError(`express-fast-json-stringify: invalid OpenAPI document`);
|
|
100
|
+
}
|
|
101
|
+
const { contentType = 'application/json', path: pinnedPath, method: pinnedMethod, strict = false, overrideJson = false, onError, ...fastJsonOptions } = options;
|
|
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
|
+
}
|
|
114
|
+
const schema = findResponseSchema(document, path, method, status, contentType);
|
|
115
|
+
const serializer = schema ? fastJson(withSharedSchemas(document, schema), fastJsonOptions) : null;
|
|
116
|
+
serializers.set(key, serializer);
|
|
117
|
+
return serializer;
|
|
118
|
+
};
|
|
119
|
+
return (req, res, next) => {
|
|
120
|
+
/**
|
|
121
|
+
* Send JSON response, serialized with the schema the document declares for
|
|
122
|
+
* this route and status code.
|
|
123
|
+
*
|
|
124
|
+
* Examples:
|
|
125
|
+
* ```ts
|
|
126
|
+
* res.fastJson({ user: 'Simone Nigro' });
|
|
127
|
+
* res.status(201).fastJson({ user: 'Simone Nigro' });
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
res.fastJson = (body) => {
|
|
131
|
+
const serializer = serializerFor(req, res.statusCode);
|
|
132
|
+
if (!serializer) {
|
|
133
|
+
if (strict) {
|
|
134
|
+
throw new Error(`express-fast-json-stringify: no ${contentType} schema for ${req.method} ${routePath(req)} with status ${res.statusCode}`);
|
|
135
|
+
}
|
|
136
|
+
return res.json(body);
|
|
137
|
+
}
|
|
138
|
+
return sendJson(req, res, serializer(body));
|
|
139
|
+
};
|
|
140
|
+
if (overrideJson) {
|
|
141
|
+
overrideResJson(req, res, (status) => serializerFor(req, status), onError);
|
|
142
|
+
}
|
|
143
|
+
next();
|
|
144
|
+
};
|
|
145
|
+
};
|
|
146
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoib3BlbmFwaS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9saWIvb3BlbmFwaS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLFFBQXVDLE1BQU0scUJBQXFCLENBQUM7QUFFMUUsT0FBTyxFQUE2QixlQUFlLEVBQUUsTUFBTSxZQUFZLENBQUM7QUFDeEUsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLFFBQVEsQ0FBQztBQWlEbEM7Ozs7R0FJRztBQUNILE1BQU0sQ0FBQyxNQUFNLGFBQWEsR0FBRyxDQUFDLElBQVksRUFBVSxFQUFFLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxrQ0FBa0MsRUFBRSxNQUFNLENBQUMsQ0FBQztBQUVoSDs7O0dBR0c7QUFDSCxNQUFNLGdCQUFnQixHQUFHLENBQUMsTUFBYyxFQUFxQixFQUFFLENBQUMsQ0FBQyxNQUFNLEtBQUssTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFDO0FBRWpIOzs7R0FHRztBQUNILE1BQU0sZ0JBQWdCLEdBQUcsQ0FBQyxNQUFjLEVBQXFCLEVBQUU7SUFDN0QsTUFBTSxLQUFLLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxNQUFNLEdBQUcsR0FBRyxDQUFDLENBQUM7SUFDdkMsT0FBTyxDQUFDLE1BQU0sQ0FBQyxNQUFNLENBQUMsRUFBRSxHQUFHLEtBQUssSUFBSSxFQUFFLEdBQUcsS0FBSyxJQUFJLEVBQUUsU0FBUyxDQUFDLENBQUM7QUFDakUsQ0FBQyxDQUFDO0FBUUYsTUFBTSxrQkFBa0IsR0FBRyxDQUFDLFFBQXlCLEVBQUUsSUFBWSxFQUFFLE1BQWMsRUFBRSxNQUFjLEVBQUUsV0FBbUIsRUFBVyxFQUFFO0lBQ25JLE1BQU0sVUFBVSxHQUFHLFFBQVEsQ0FBQyxLQUFLLEVBQUUsQ0FBQyxJQUFJLENBQWtELENBQUM7SUFDM0YsSUFBSSxD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
|
|
@@ -0,0 +1,17 @@
|
|
|
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;
|
|
@@ -0,0 +1,44 @@
|
|
|
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
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Request, Response } from 'express';
|
|
2
|
+
/**
|
|
3
|
+
* Write an already serialized JSON payload with the same HTTP semantics as
|
|
4
|
+
* `res.json()`: charset, `Content-Length`, `ETag`, conditional requests and the
|
|
5
|
+
* empty body rules for `204`/`304`.
|
|
6
|
+
*
|
|
7
|
+
* Every middleware in this package goes through here, so they cannot drift
|
|
8
|
+
* apart from each other or from Express.
|
|
9
|
+
*/
|
|
10
|
+
export declare const sendJson: (req: Request, res: Response, serialized: string) => Response;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Write an already serialized JSON payload with the same HTTP semantics as
|
|
3
|
+
* `res.json()`: charset, `Content-Length`, `ETag`, conditional requests and the
|
|
4
|
+
* empty body rules for `204`/`304`.
|
|
5
|
+
*
|
|
6
|
+
* Every middleware in this package goes through here, so they cannot drift
|
|
7
|
+
* apart from each other or from Express.
|
|
8
|
+
*/
|
|
9
|
+
export const sendJson = (req, res, serialized) => {
|
|
10
|
+
let payload = serialized;
|
|
11
|
+
// Do not clobber a content type the route set on purpose (eg. res.type('application/vnd.api+json')).
|
|
12
|
+
if (!res.getHeader('Content-Type')) {
|
|
13
|
+
res.setHeader('Content-Type', 'application/json; charset=utf-8');
|
|
14
|
+
}
|
|
15
|
+
// Mirror res.send(): honour the app `etag` setting so that swapping
|
|
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);
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VuZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9saWIvc2VuZC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFFQTs7Ozs7OztHQU9HO0FBQ0gsTUFBTSxDQUFDLE1BQU0sUUFBUSxHQUFHLENBQUMsR0FBWSxFQUFFLEdBQWEsRUFBRSxVQUFrQixFQUFZLEVBQUU7SUFDcEYsSUFBSSxPQUFPLEdBQUcsVUFBVSxDQUFDO0lBRXpCLHFHQUFxRztJQUNyRyxJQUFJLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxjQUFjLENBQUMsRUFBRSxDQUFDO1FBQ25DLEdBQUcsQ0FBQyxTQUFTLENBQUMsY0FBYyxFQUFFLGlDQUFpQyxDQUFDLENBQUM7SUFDbkUsQ0FBQztJQUVELG9FQUFvRTtJQUNwRSxvRUFBb0U7SUFDcEUsTUFBTSxNQUFNLEdBQUcsR0FBRyxDQUFDLEdBQUcsRUFBRSxHQUFHLENBQUMsU0FBUyxDQUE0RSxDQUFDO0lBQ2xILElBQUksT0FBTyxNQUFNLEtBQUssVUFBVSxJQUFJLENBQUMsR0FBRyxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDO1FBQzNELE1BQU0sSUFBSSxHQUFHLE1BQU0sQ0FBQyxPQUFPLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDdEMsSUFBSSxJQUFJLEVBQUUsQ0FBQztZQUNULEdBQUcsQ0FBQyxTQUFTLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQyxDQUFDO1FBQzlCLENBQUM7SUFDSCxDQUFDO0lBRUQsSUFBSSxHQUFHLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDZCxHQUFHLENBQUMsVUFBVSxHQUFHLEdBQUcsQ0FBQztJQUN2QixDQUFDO0lBRUQsK0VBQStFO0lBQy9FLElBQUksR0FBRyxDQUFDLFVBQVUsS0FBSyxHQUFHLElBQUksR0FBRyxDQUFDLFVBQVUsS0FBSyxHQUFHLEVBQUUsQ0FBQztRQUNyRCxHQUFHLENBQUMsWUFBWSxDQUFDLGNBQWMsQ0FBQyxDQUFDO1FBQ2pDLEdBQUcsQ0FBQyxZQUFZLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUNuQyxHQUFHLENBQUMsWUFBWSxDQUFDLG1CQUFtQixDQUFDLENBQUM7UUFDdEMsT0FBTyxHQUFHLEVBQUUsQ0FBQztJQUNmLENBQUM7U0FBTSxDQUFDO1FBQ04scUVBQXFFO1FBQ3JFLHlDQUF5QztRQUN6QyxHQUFHLENBQUMsU0FBUyxDQUFDLGdCQUFnQixFQUFFLE1BQU0sQ0FBQyxVQUFVLENBQUMsT0FBTyxDQUFDLENBQUMsQ0FBQztJQUM5RCxDQUFDO0lBRUQsT0FBTyxHQUFHLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0FBQzFCLENBQUMsQ0FBQyJ9
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "express-fast-json-stringify",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"description": "With express-fast-json-stringify, you can leverage fast-json-stringify in your Express application",
|
|
3
|
+
"version": "1.3.0",
|
|
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",
|
|
7
7
|
"module": "build/module/index.js",
|
|
@@ -9,72 +9,77 @@
|
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"keywords": [
|
|
11
11
|
"express",
|
|
12
|
-
"fast-json-stringify"
|
|
12
|
+
"fast-json-stringify",
|
|
13
|
+
"json",
|
|
14
|
+
"stringify",
|
|
15
|
+
"schema",
|
|
16
|
+
"jsonschema",
|
|
17
|
+
"validator",
|
|
18
|
+
"json-schema"
|
|
13
19
|
],
|
|
14
20
|
"scripts": {
|
|
15
21
|
"build": "run-p build:*",
|
|
16
22
|
"build:main": "tsc -p tsconfig.json",
|
|
17
23
|
"build:module": "tsc -p tsconfig.module.json",
|
|
18
24
|
"fix": "run-s fix:*",
|
|
19
|
-
"fix:prettier": "prettier \"src/**/*.ts\" --write",
|
|
20
|
-
"fix:lint": "eslint
|
|
25
|
+
"fix:prettier": "prettier \"{src,example}/**/*.ts\" --write",
|
|
26
|
+
"fix:lint": "eslint . --fix",
|
|
21
27
|
"test": "run-s build test:*",
|
|
22
|
-
"test:lint": "eslint
|
|
23
|
-
"test:prettier": "prettier \"src/**/*.ts\" --list-different",
|
|
24
|
-
"test:spelling": "cspell \"{README.md,.github
|
|
25
|
-
"test:
|
|
26
|
-
"
|
|
27
|
-
"check-integration-tests": "run-s check-integration-test:*",
|
|
28
|
-
"diff-integration-tests": "mkdir -p diff && rm -rf diff/test && cp -r test diff/test && rm -rf diff/test/test-*/.git && cd diff && git init --quiet && git add -A && git commit --quiet --no-verify --allow-empty -m 'WIP' && echo '\\n\\nCommitted most recent integration test output in the \"diff\" directory. Review the changes with \"cd diff && git diff HEAD\" or your preferred git diff viewer.'",
|
|
28
|
+
"test:lint": "eslint .",
|
|
29
|
+
"test:prettier": "prettier \"{src,example}/**/*.ts\" --list-different",
|
|
30
|
+
"test:spelling": "cspell \"{README.md,.github/**/*.md,src/**/*.ts,example/**/*.ts}\"",
|
|
31
|
+
"test:types": "tsc -p tsconfig.spec.json",
|
|
32
|
+
"test:unit": "vitest run",
|
|
29
33
|
"watch:build": "tsc -p tsconfig.json -w",
|
|
30
|
-
"watch:test": "
|
|
31
|
-
"cov": "run-s
|
|
32
|
-
"cov:
|
|
33
|
-
"cov:
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"doc": "
|
|
37
|
-
"doc:
|
|
38
|
-
"doc:json": "typedoc src/ --exclude **/*.spec.ts --target ES6 --mode file --json build/docs/typedoc.json",
|
|
34
|
+
"watch:test": "vitest",
|
|
35
|
+
"cov": "run-s cov:check cov:open",
|
|
36
|
+
"cov:check": "vitest run --coverage",
|
|
37
|
+
"cov:open": "open-cli coverage/index.html",
|
|
38
|
+
"doc": "run-s doc:html doc:open",
|
|
39
|
+
"doc:html": "typedoc src/index.ts --out build/docs",
|
|
40
|
+
"doc:open": "open-cli build/docs/index.html",
|
|
41
|
+
"doc:json": "typedoc src/index.ts --json build/docs/typedoc.json",
|
|
39
42
|
"doc:publish": "gh-pages -m \"[ci skip] Updates\" -d build/docs",
|
|
40
|
-
"version": "standard-version",
|
|
41
43
|
"reset-hard": "git clean -dfx && git reset --hard && npm i",
|
|
42
|
-
"prepare-release": "run-s reset-hard test cov:check doc:html
|
|
44
|
+
"prepare-release": "run-s reset-hard test cov:check doc:html release doc:publish",
|
|
45
|
+
"example": "tsx example/index.ts",
|
|
46
|
+
"release": "release-it",
|
|
47
|
+
"example:openapi": "tsx example/openapi.ts"
|
|
43
48
|
},
|
|
44
49
|
"engines": {
|
|
45
|
-
"node": ">=
|
|
50
|
+
"node": ">=20"
|
|
46
51
|
},
|
|
47
52
|
"dependencies": {
|
|
48
|
-
"fast-json-stringify": "^6.0.0"
|
|
53
|
+
"fast-json-stringify": "^6.2.0 || ^7.0.0"
|
|
49
54
|
},
|
|
50
55
|
"devDependencies": {
|
|
51
|
-
"@
|
|
52
|
-
"@
|
|
53
|
-
"@
|
|
54
|
-
"@types/
|
|
55
|
-
"@types/
|
|
56
|
-
"@
|
|
57
|
-
"@
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"eslint": "^
|
|
63
|
-
"
|
|
64
|
-
"
|
|
65
|
-
"
|
|
66
|
-
"
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
"
|
|
56
|
+
"@eslint-community/eslint-plugin-eslint-comments": "^4.7.2",
|
|
57
|
+
"@eslint/js": "^10.0.1",
|
|
58
|
+
"@release-it/conventional-changelog": "^11.0.1",
|
|
59
|
+
"@types/express": "^5.0.6",
|
|
60
|
+
"@types/node": "^26.1.1",
|
|
61
|
+
"@types/supertest": "^7.2.1",
|
|
62
|
+
"@vitest/coverage-v8": "^4.1.10",
|
|
63
|
+
"cspell": "^10.0.1",
|
|
64
|
+
"eslint": "^10.8.0",
|
|
65
|
+
"eslint-config-prettier": "^10.1.8",
|
|
66
|
+
"eslint-plugin-functional": "^10.0.0",
|
|
67
|
+
"eslint-plugin-import-x": "^4.17.1",
|
|
68
|
+
"express": "^5.2.1",
|
|
69
|
+
"gh-pages": "^6.3.0",
|
|
70
|
+
"npm-run-all2": "^9.0.2",
|
|
71
|
+
"open-cli": "^9.0.0",
|
|
72
|
+
"prettier": "^3.9.6",
|
|
73
|
+
"release-it": "^20.2.1",
|
|
74
|
+
"supertest": "^7.2.2",
|
|
75
|
+
"tsx": "^4.23.1",
|
|
76
|
+
"typedoc": "^0.28.20",
|
|
77
|
+
"typescript": "^6.0.3",
|
|
78
|
+
"typescript-eslint": "^8.65.0",
|
|
79
|
+
"vitest": "^4.1.10"
|
|
80
|
+
},
|
|
81
|
+
"peerDependencies": {
|
|
82
|
+
"express": "^4.16.0 || ^5.0.0"
|
|
78
83
|
},
|
|
79
84
|
"files": [
|
|
80
85
|
"build/main",
|
|
@@ -85,34 +90,8 @@
|
|
|
85
90
|
"LICENSE",
|
|
86
91
|
"README.md"
|
|
87
92
|
],
|
|
88
|
-
"ava": {
|
|
89
|
-
"failFast": true,
|
|
90
|
-
"timeout": "60s",
|
|
91
|
-
"typescript": {
|
|
92
|
-
"compile": false,
|
|
93
|
-
"rewritePaths": {
|
|
94
|
-
"src/": "build/main/"
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
"files": [
|
|
98
|
-
"!build/module/**"
|
|
99
|
-
]
|
|
100
|
-
},
|
|
101
|
-
"config": {
|
|
102
|
-
"commitizen": {
|
|
103
|
-
"path": "cz-conventional-changelog"
|
|
104
|
-
}
|
|
105
|
-
},
|
|
106
93
|
"prettier": {
|
|
107
|
-
"singleQuote": true
|
|
108
|
-
|
|
109
|
-
"nyc": {
|
|
110
|
-
"extends": "@istanbuljs/nyc-config-typescript",
|
|
111
|
-
"exclude": [
|
|
112
|
-
"**/*.spec.js"
|
|
113
|
-
]
|
|
114
|
-
},
|
|
115
|
-
"peerDependencies": {
|
|
116
|
-
"fast-json-stringify": "^6.0.0"
|
|
94
|
+
"singleQuote": true,
|
|
95
|
+
"printWidth": 180
|
|
117
96
|
}
|
|
118
97
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/build/main/index.spec.js
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const ava_1 = __importDefault(require("ava"));
|
|
7
|
-
const express_1 = __importDefault(require("express"));
|
|
8
|
-
const supertest_1 = __importDefault(require("supertest"));
|
|
9
|
-
const _1 = require("./");
|
|
10
|
-
const data = {
|
|
11
|
-
firstName: 'Simone',
|
|
12
|
-
lastName: 'Nigro',
|
|
13
|
-
age: 40,
|
|
14
|
-
};
|
|
15
|
-
const schema = {
|
|
16
|
-
title: 'Example Schema',
|
|
17
|
-
type: 'object',
|
|
18
|
-
properties: {
|
|
19
|
-
firstName: {
|
|
20
|
-
type: 'string',
|
|
21
|
-
},
|
|
22
|
-
lastName: {
|
|
23
|
-
type: 'string',
|
|
24
|
-
},
|
|
25
|
-
age: {
|
|
26
|
-
description: 'Age in years',
|
|
27
|
-
type: 'integer',
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
const app = (0, express_1.default)();
|
|
32
|
-
app.use(express_1.default.json());
|
|
33
|
-
app.post('/with-schema', (0, _1.fastJsonSchema)(schema), (req, res, next) => {
|
|
34
|
-
try {
|
|
35
|
-
res.fastJson(req.body.data);
|
|
36
|
-
}
|
|
37
|
-
catch (error) {
|
|
38
|
-
next(error);
|
|
39
|
-
}
|
|
40
|
-
});
|
|
41
|
-
app.post('/without-schema', (req, res, next) => {
|
|
42
|
-
try {
|
|
43
|
-
res.fastJson(req.body.data);
|
|
44
|
-
}
|
|
45
|
-
catch (error) {
|
|
46
|
-
next(error);
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
app.use((err, _req, res, _next) => {
|
|
50
|
-
res.status(500).json({ error: err.message });
|
|
51
|
-
});
|
|
52
|
-
(0, ava_1.default)('with schema', async (t) => {
|
|
53
|
-
t.plan(3);
|
|
54
|
-
const res = await (0, supertest_1.default)(app).post('/with-schema').send({ data });
|
|
55
|
-
t.like(res.body, data);
|
|
56
|
-
t.is(res.ok, true);
|
|
57
|
-
t.is(res.type, 'application/json');
|
|
58
|
-
});
|
|
59
|
-
(0, ava_1.default)('without schema', async (t) => {
|
|
60
|
-
t.plan(3);
|
|
61
|
-
const res = await (0, supertest_1.default)(app).post('/without-schema').send({ data });
|
|
62
|
-
t.like(res.body, { error: `res.fastJson is not a function` });
|
|
63
|
-
t.is(res.ok, false);
|
|
64
|
-
t.is(res.type, 'application/json');
|
|
65
|
-
});
|
|
66
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguc3BlYy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9pbmRleC5zcGVjLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7O0FBQUEsOENBQXVCO0FBQ3ZCLHNEQUFtRTtBQUNuRSwwREFBZ0M7QUFFaEMseUJBQTRDO0FBRTVDLE1BQU0sSUFBSSxHQUFHO0lBQ1gsU0FBUyxFQUFFLFFBQVE7SUFDbkIsUUFBUSxFQUFFLE9BQU87SUFDakIsR0FBRyxFQUFFLEVBQUU7Q0FDUixDQUFDO0FBRUYsTUFBTSxNQUFNLEdBQVc7SUFDckIsS0FBSyxFQUFFLGdCQUFnQjtJQUN2QixJQUFJLEVBQUUsUUFBUTtJQUNkLFVBQVUsRUFBRTtRQUNWLFNBQVMsRUFBRTtZQUNULElBQUksRUFBRSxRQUFRO1NBQ2Y7UUFDRCxRQUFRLEVBQUU7WUFDUixJQUFJLEVBQUUsUUFBUTtTQUNmO1FBQ0QsR0FBRyxFQUFFO1lBQ0gsV0FBVyxFQUFFLGNBQWM7WUFDM0IsSUFBSSxFQUFFLFNBQVM7U0FDaEI7S0FDRjtDQUNGLENBQUM7QUFFRixNQUFNLEdBQUcsR0FBRyxJQUFBLGlCQUFPLEdBQUUsQ0FBQztBQUV0QixHQUFHLENBQUMsR0FBRyxDQUFDLGlCQUFPLENBQUMsSUFBSSxFQUFFLENBQUMsQ0FBQztBQUV4QixHQUFHLENBQUMsSUFBSSxDQUFDLGNBQWMsRUFBRSxJQUFBLGlCQUFjLEVBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxFQUFFO0lBQ2xFLElBQUk7UUFDRixHQUFHLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7S0FDN0I7SUFBQyxPQUFPLEtBQUssRUFBRTtRQUNkLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztLQUNiO0FBQ0gsQ0FBQyxDQUFDLENBQUM7QUFFSCxHQUFHLENBQUMsSUFBSSxDQUFDLGlCQUFpQixFQUFFLENBQUMsR0FBRyxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsRUFBRTtJQUM3QyxJQUFJO1FBQ0YsR0FBRyxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0tBQzdCO0lBQUMsT0FBTyxLQUFLLEVBQUU7UUFDZCxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7S0FDYjtBQUNILENBQUMsQ0FBQyxDQUFDO0FBRUgsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDLEdBQVEsRUFBRSxJQUFhLEVBQUUsR0FBYSxFQUFFLEtBQW1CLEVBQUUsRUFBRTtJQUN0RSxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLEtBQUssRUFBRSxHQUFHLENBQUMsT0FBTyxFQUFFLENBQUMsQ0FBQztBQUMvQyxDQUFDLENBQUMsQ0FBQztBQUVILElBQUEsYUFBSSxFQUFDLGFBQWEsRUFBRSxLQUFLLEVBQUUsQ0FBQyxFQUFFLEVBQUU7SUFDOUIsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUVWLE1BQU0sR0FBRyxHQUFHLE1BQU0sSUFBQSxtQkFBTyxFQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBRW5FLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQztJQUN2QixDQUFDLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDbkIsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLGtCQUFrQixDQUFDLENBQUM7QUFDckMsQ0FBQyxDQUFDLENBQUM7QUFFSCxJQUFBLGFBQUksRUFBQyxnQkFBZ0IsRUFBRSxLQUFLLEVBQUUsQ0FBQyxFQUFFLEVBQUU7SUFDakMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUVWLE1BQU0sR0FBRyxHQUFHLE1BQU0sSUFBQSxtQkFBTyxFQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLElBQUksRUFBRSxDQUFDLENBQUM7SUFFdEUsQ0FBQyxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLEVBQUUsS0FBSyxFQUFFLGdDQUFnQyxFQUFFLENBQUMsQ0FBQztJQUM5RCxDQUFDLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsS0FBSyxDQUFDLENBQUM7SUFDcEIsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLGtCQUFrQixDQUFDLENBQUM7QUFDckMsQ0FBQyxDQUFDLENBQUMifQ==
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import test from 'ava';
|
|
2
|
-
import express from 'express';
|
|
3
|
-
import request from 'supertest';
|
|
4
|
-
import { fastJsonSchema } from './';
|
|
5
|
-
const data = {
|
|
6
|
-
firstName: 'Simone',
|
|
7
|
-
lastName: 'Nigro',
|
|
8
|
-
age: 40,
|
|
9
|
-
};
|
|
10
|
-
const schema = {
|
|
11
|
-
title: 'Example Schema',
|
|
12
|
-
type: 'object',
|
|
13
|
-
properties: {
|
|
14
|
-
firstName: {
|
|
15
|
-
type: 'string',
|
|
16
|
-
},
|
|
17
|
-
lastName: {
|
|
18
|
-
type: 'string',
|
|
19
|
-
},
|
|
20
|
-
age: {
|
|
21
|
-
description: 'Age in years',
|
|
22
|
-
type: 'integer',
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
};
|
|
26
|
-
const app = express();
|
|
27
|
-
app.use(express.json());
|
|
28
|
-
app.post('/with-schema', fastJsonSchema(schema), (req, res, next) => {
|
|
29
|
-
try {
|
|
30
|
-
res.fastJson(req.body.data);
|
|
31
|
-
}
|
|
32
|
-
catch (error) {
|
|
33
|
-
next(error);
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
app.post('/without-schema', (req, res, next) => {
|
|
37
|
-
try {
|
|
38
|
-
res.fastJson(req.body.data);
|
|
39
|
-
}
|
|
40
|
-
catch (error) {
|
|
41
|
-
next(error);
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
app.use((err, _req, res, _next) => {
|
|
45
|
-
res.status(500).json({ error: err.message });
|
|
46
|
-
});
|
|
47
|
-
test('with schema', async (t) => {
|
|
48
|
-
t.plan(3);
|
|
49
|
-
const res = await request(app).post('/with-schema').send({ data });
|
|
50
|
-
t.like(res.body, data);
|
|
51
|
-
t.is(res.ok, true);
|
|
52
|
-
t.is(res.type, 'application/json');
|
|
53
|
-
});
|
|
54
|
-
test('without schema', async (t) => {
|
|
55
|
-
t.plan(3);
|
|
56
|
-
const res = await request(app).post('/without-schema').send({ data });
|
|
57
|
-
t.like(res.body, { error: `res.fastJson is not a function` });
|
|
58
|
-
t.is(res.ok, false);
|
|
59
|
-
t.is(res.type, 'application/json');
|
|
60
|
-
});
|
|
61
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguc3BlYy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9pbmRleC5zcGVjLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sSUFBSSxNQUFNLEtBQUssQ0FBQztBQUN2QixPQUFPLE9BQTRDLE1BQU0sU0FBUyxDQUFDO0FBQ25FLE9BQU8sT0FBTyxNQUFNLFdBQVcsQ0FBQztBQUVoQyxPQUFPLEVBQUUsY0FBYyxFQUFVLE1BQU0sSUFBSSxDQUFDO0FBRTVDLE1BQU0sSUFBSSxHQUFHO0lBQ1gsU0FBUyxFQUFFLFFBQVE7SUFDbkIsUUFBUSxFQUFFLE9BQU87SUFDakIsR0FBRyxFQUFFLEVBQUU7Q0FDUixDQUFDO0FBRUYsTUFBTSxNQUFNLEdBQVc7SUFDckIsS0FBSyxFQUFFLGdCQUFnQjtJQUN2QixJQUFJLEVBQUUsUUFBUTtJQUNkLFVBQVUsRUFBRTtRQUNWLFNBQVMsRUFBRTtZQUNULElBQUksRUFBRSxRQUFRO1NBQ2Y7UUFDRCxRQUFRLEVBQUU7WUFDUixJQUFJLEVBQUUsUUFBUTtTQUNmO1FBQ0QsR0FBRyxFQUFFO1lBQ0gsV0FBVyxFQUFFLGNBQWM7WUFDM0IsSUFBSSxFQUFFLFNBQVM7U0FDaEI7S0FDRjtDQUNGLENBQUM7QUFFRixNQUFNLEdBQUcsR0FBRyxPQUFPLEVBQUUsQ0FBQztBQUV0QixHQUFHLENBQUMsR0FBRyxDQUFDLE9BQU8sQ0FBQyxJQUFJLEVBQUUsQ0FBQyxDQUFDO0FBRXhCLEdBQUcsQ0FBQyxJQUFJLENBQUMsY0FBYyxFQUFFLGNBQWMsQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUUsSUFBSSxFQUFFLEVBQUU7SUFDbEUsSUFBSTtRQUNGLEdBQUcsQ0FBQyxRQUFRLENBQUMsR0FBRyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQztLQUM3QjtJQUFDLE9BQU8sS0FBSyxFQUFFO1FBQ2QsSUFBSSxDQUFDLEtBQUssQ0FBQyxDQUFDO0tBQ2I7QUFDSCxDQUFDLENBQUMsQ0FBQztBQUVILEdBQUcsQ0FBQyxJQUFJLENBQUMsaUJBQWlCLEVBQUUsQ0FBQyxHQUFHLEVBQUUsR0FBRyxFQUFFLElBQUksRUFBRSxFQUFFO0lBQzdDLElBQUk7UUFDRixHQUFHLENBQUMsUUFBUSxDQUFDLEdBQUcsQ0FBQyxJQUFJLENBQUMsSUFBSSxDQUFDLENBQUM7S0FDN0I7SUFBQyxPQUFPLEtBQUssRUFBRTtRQUNkLElBQUksQ0FBQyxLQUFLLENBQUMsQ0FBQztLQUNiO0FBQ0gsQ0FBQyxDQUFDLENBQUM7QUFFSCxHQUFHLENBQUMsR0FBRyxDQUFDLENBQUMsR0FBUSxFQUFFLElBQWEsRUFBRSxHQUFhLEVBQUUsS0FBbUIsRUFBRSxFQUFFO0lBQ3RFLEdBQUcsQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBSSxDQUFDLEVBQUUsS0FBSyxFQUFFLEdBQUcsQ0FBQyxPQUFPLEVBQUUsQ0FBQyxDQUFDO0FBQy9DLENBQUMsQ0FBQyxDQUFDO0FBRUgsSUFBSSxDQUFDLGFBQWEsRUFBRSxLQUFLLEVBQUUsQ0FBQyxFQUFFLEVBQUU7SUFDOUIsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztJQUVWLE1BQU0sR0FBRyxHQUFHLE1BQU0sT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLElBQUksQ0FBQyxjQUFjLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBRW5FLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQztJQUN2QixDQUFDLENBQUMsRUFBRSxDQUFDLEdBQUcsQ0FBQyxFQUFFLEVBQUUsSUFBSSxDQUFDLENBQUM7SUFDbkIsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLGtCQUFrQixDQUFDLENBQUM7QUFDckMsQ0FBQyxDQUFDLENBQUM7QUFFSCxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsS0FBSyxFQUFFLENBQUMsRUFBRSxFQUFFO0lBQ2pDLENBQUMsQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7SUFFVixNQUFNLEdBQUcsR0FBRyxNQUFNLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsQ0FBQyxJQUFJLENBQUMsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBRXRFLENBQUMsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxFQUFFLEtBQUssRUFBRSxnQ0FBZ0MsRUFBRSxDQUFDLENBQUM7SUFDOUQsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxHQUFHLENBQUMsRUFBRSxFQUFFLEtBQUssQ0FBQyxDQUFDO0lBQ3BCLENBQUMsQ0FBQyxFQUFFLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxrQkFBa0IsQ0FBQyxDQUFDO0FBQ3JDLENBQUMsQ0FBQyxDQUFDIn0=
|