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.
@@ -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.2.8",
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 src --ext .ts --fix",
25
+ "fix:prettier": "prettier \"{src,example}/**/*.ts\" --write",
26
+ "fix:lint": "eslint . --fix",
21
27
  "test": "run-s build test:*",
22
- "test:lint": "eslint src --ext .ts",
23
- "test:prettier": "prettier \"src/**/*.ts\" --list-different",
24
- "test:spelling": "cspell \"{README.md,.github/*.md,src/**/*.ts}\"",
25
- "test:unit": "nyc --silent ava",
26
- "check-cli": "run-s test diff-integration-tests check-integration-tests",
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": "nyc --silent ava --watch",
31
- "cov": "run-s build test:unit cov:html cov:lcov && open-cli coverage/index.html",
32
- "cov:html": "nyc report --reporter=html",
33
- "cov:lcov": "nyc report --reporter=lcov",
34
- "cov:send": "run-s cov:lcov && codecov",
35
- "cov:check": "nyc report && nyc check-coverage --lines 100 --functions 100 --branches 100",
36
- "doc": "run-s doc:html && open-cli build/docs/index.html",
37
- "doc:html": "typedoc src/ --exclude **/*.spec.ts --target ES6 --mode file --out build/docs",
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 version doc:publish"
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": ">=18"
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
- "@ava/typescript": "^4.1.0",
52
- "@istanbuljs/nyc-config-typescript": "^1.0.1",
53
- "@types/express": "^5.0.0",
54
- "@types/node": "^20.9.0",
55
- "@types/supertest": "^6.0.2",
56
- "@typescript-eslint/eslint-plugin": "^6.10.0",
57
- "@typescript-eslint/parser": "^6.10.0",
58
- "ava": "^5.3.1",
59
- "codecov": "^3.5.0",
60
- "cspell": "^8.0.0",
61
- "cz-conventional-changelog": "^3.3.0",
62
- "eslint": "^8.53.0",
63
- "eslint-config-prettier": "^9.0.0",
64
- "eslint-plugin-eslint-comments": "^3.2.0",
65
- "eslint-plugin-functional": "^6.0.0",
66
- "eslint-plugin-import": "^2.22.0",
67
- "express": "^4.21.0",
68
- "gh-pages": "^6.0.0",
69
- "npm-run-all": "^4.1.5",
70
- "nyc": "^15.1.0",
71
- "open-cli": "^7.2.0",
72
- "prettier": "^3.0.3",
73
- "standard-version": "^9.0.0",
74
- "supertest": "^7.0.0",
75
- "ts-node": "^10.9.1",
76
- "typedoc": "^0.25.3",
77
- "typescript": "^5.2.2"
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 {};
@@ -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=