@lokalise/fastify-extras 16.0.0 → 16.1.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,18 @@
1
+ import type { ErrorReporter } from '@lokalise/node-core';
2
+ import type { FastifyRequest, FastifyReply, FastifyInstance } from 'fastify';
3
+ export type FreeformRecord = Record<string, any>;
4
+ type ResponseObject = {
5
+ statusCode: number;
6
+ payload: {
7
+ message: string;
8
+ errorCode: string;
9
+ details?: FreeformRecord;
10
+ };
11
+ };
12
+ export type ErrorHandlerParams = {
13
+ errorReporter: ErrorReporter;
14
+ resolveResponseObject?: (error: FreeformRecord) => ResponseObject | undefined;
15
+ resolveLogObject?: (error: unknown) => FreeformRecord | undefined;
16
+ };
17
+ export declare function createErrorHandler(params: ErrorHandlerParams): (this: FastifyInstance, error: FreeformRecord, request: FastifyRequest, reply: FastifyReply) => void;
18
+ export {};
@@ -0,0 +1,91 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createErrorHandler = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const node_core_1 = require("@lokalise/node-core");
6
+ const pino_1 = tslib_1.__importDefault(require("pino"));
7
+ const zod_1 = require("zod");
8
+ const knownAuthErrors = new Set([
9
+ 'FST_JWT_NO_AUTHORIZATION_IN_HEADER',
10
+ 'FST_JWT_AUTHORIZATION_TOKEN_EXPIRED',
11
+ 'FST_JWT_AUTHORIZATION_TOKEN_INVALID',
12
+ ]);
13
+ function resolveLogObject(error) {
14
+ if ((0, node_core_1.isInternalError)(error)) {
15
+ return {
16
+ message: error.message,
17
+ code: error.errorCode,
18
+ details: error.details ? JSON.stringify(error.details) : undefined,
19
+ error: pino_1.default.stdSerializers.err({
20
+ name: error.name,
21
+ message: error.message,
22
+ stack: error.stack,
23
+ }),
24
+ };
25
+ }
26
+ return {
27
+ message: (0, node_core_1.isObject)(error) ? error.message : JSON.stringify(error),
28
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
29
+ error: error instanceof Error ? pino_1.default.stdSerializers.err(error) : error,
30
+ };
31
+ }
32
+ function resolveResponseObject(error) {
33
+ if ((0, node_core_1.isPublicNonRecoverableError)(error)) {
34
+ return {
35
+ statusCode: error.httpStatusCode ?? 500,
36
+ payload: {
37
+ message: error.message,
38
+ errorCode: error.errorCode,
39
+ details: error.details,
40
+ },
41
+ };
42
+ }
43
+ if (error instanceof zod_1.ZodError) {
44
+ return {
45
+ statusCode: 400,
46
+ payload: {
47
+ message: 'Invalid params',
48
+ errorCode: 'VALIDATION_ERROR',
49
+ details: {
50
+ error: error.issues,
51
+ },
52
+ },
53
+ };
54
+ }
55
+ if ((0, node_core_1.isStandardizedError)(error)) {
56
+ if (knownAuthErrors.has(error.code)) {
57
+ const message = error.code === 'FST_JWT_AUTHORIZATION_TOKEN_INVALID'
58
+ ? 'Authorization token is invalid'
59
+ : error.message;
60
+ return {
61
+ statusCode: 401,
62
+ payload: {
63
+ message,
64
+ errorCode: 'AUTH_FAILED',
65
+ },
66
+ };
67
+ }
68
+ }
69
+ return {
70
+ statusCode: 500,
71
+ payload: {
72
+ message: 'Internal server error',
73
+ errorCode: 'INTERNAL_SERVER_ERROR',
74
+ },
75
+ };
76
+ }
77
+ function createErrorHandler(params) {
78
+ return function errorHandler(error, request, reply) {
79
+ const logObject = params.resolveLogObject?.(error) ?? resolveLogObject(error);
80
+ if ((0, node_core_1.isInternalError)(error)) {
81
+ params.errorReporter.report({ error });
82
+ }
83
+ const responseObject = params.resolveResponseObject?.(error) ?? resolveResponseObject(error);
84
+ if (responseObject.statusCode >= 500) {
85
+ request.log.error(logObject);
86
+ }
87
+ void reply.status(responseObject.statusCode).send(responseObject.payload);
88
+ };
89
+ }
90
+ exports.createErrorHandler = createErrorHandler;
91
+ //# sourceMappingURL=errorHandler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errorHandler.js","sourceRoot":"","sources":["../../lib/errors/errorHandler.ts"],"names":[],"mappings":";;;;AACA,mDAK4B;AAE5B,wDAAuB;AACvB,6BAA8B;AAK9B,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC;IAC9B,oCAAoC;IACpC,qCAAqC;IACrC,qCAAqC;CACtC,CAAC,CAAA;AAWF,SAAS,gBAAgB,CAAC,KAAc;IACtC,IAAI,IAAA,2BAAe,EAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO;YACL,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,IAAI,EAAE,KAAK,CAAC,SAAS;YACrB,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS;YAClE,KAAK,EAAE,cAAI,CAAC,cAAc,CAAC,GAAG,CAAC;gBAC7B,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,KAAK,EAAE,KAAK,CAAC,KAAK;aACnB,CAAC;SACH,CAAA;IACH,CAAC;IAED,OAAO;QACL,OAAO,EAAE,IAAA,oBAAQ,EAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC;QAChE,mEAAmE;QACnE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,cAAI,CAAC,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK;KACvE,CAAA;AACH,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAqB;IAClD,IAAI,IAAA,uCAA2B,EAAC,KAAK,CAAC,EAAE,CAAC;QACvC,OAAO;YACL,UAAU,EAAE,KAAK,CAAC,cAAc,IAAI,GAAG;YACvC,OAAO,EAAE;gBACP,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;aACvB;SACF,CAAA;IACH,CAAC;IAED,IAAI,KAAK,YAAY,cAAQ,EAAE,CAAC;QAC9B,OAAO;YACL,UAAU,EAAE,GAAG;YACf,OAAO,EAAE;gBACP,OAAO,EAAE,gBAAgB;gBACzB,SAAS,EAAE,kBAAkB;gBAC7B,OAAO,EAAE;oBACP,KAAK,EAAE,KAAK,CAAC,MAAM;iBACpB;aACF;SACF,CAAA;IACH,CAAC;IAED,IAAI,IAAA,+BAAmB,EAAC,KAAK,CAAC,EAAE,CAAC;QAC/B,IAAI,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACpC,MAAM,OAAO,GACX,KAAK,CAAC,IAAI,KAAK,qCAAqC;gBAClD,CAAC,CAAC,gCAAgC;gBAClC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAA;YAEnB,OAAO;gBACL,UAAU,EAAE,GAAG;gBACf,OAAO,EAAE;oBACP,OAAO;oBACP,SAAS,EAAE,aAAa;iBACzB;aACF,CAAA;QACH,CAAC;IACH,CAAC;IAED,OAAO;QACL,UAAU,EAAE,GAAG;QACf,OAAO,EAAE;YACP,OAAO,EAAE,uBAAuB;YAChC,SAAS,EAAE,uBAAuB;SACnC;KACF,CAAA;AACH,CAAC;AAQD,SAAgB,kBAAkB,CAAC,MAA0B;IAC3D,OAAO,SAAS,YAAY,CAE1B,KAAqB,EACrB,OAAuB,EACvB,KAAmB;QAEnB,MAAM,SAAS,GAAG,MAAM,CAAC,gBAAgB,EAAE,CAAC,KAAK,CAAC,IAAI,gBAAgB,CAAC,KAAK,CAAC,CAAA;QAE7E,IAAI,IAAA,2BAAe,EAAC,KAAK,CAAC,EAAE,CAAC;YAC3B,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,CAAC,CAAA;QACxC,CAAC;QAED,MAAM,cAAc,GAAG,MAAM,CAAC,qBAAqB,EAAE,CAAC,KAAK,CAAC,IAAI,qBAAqB,CAAC,KAAK,CAAC,CAAA;QAC5F,IAAI,cAAc,CAAC,UAAU,IAAI,GAAG,EAAE,CAAC;YACrC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,SAAS,CAAC,CAAA;QAC9B,CAAC;QAED,KAAK,KAAK,CAAC,MAAM,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAA;IAC3E,CAAC,CAAA;AACH,CAAC;AApBD,gDAoBC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,206 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const node_core_1 = require("@lokalise/node-core");
5
+ const fastify_1 = tslib_1.__importDefault(require("fastify"));
6
+ const vitest_1 = require("vitest");
7
+ const zod_1 = require("zod");
8
+ const errorHandler_1 = require("./errorHandler");
9
+ async function initApp(routeHandler, errorHandlerParams = {}, awaitApp = true) {
10
+ const app = (0, fastify_1.default)({
11
+ logger: true,
12
+ });
13
+ app.setErrorHandler((0, errorHandler_1.createErrorHandler)({
14
+ errorReporter: {
15
+ report: () => { },
16
+ },
17
+ ...errorHandlerParams,
18
+ }));
19
+ app.route({
20
+ method: 'GET',
21
+ url: '/',
22
+ handler: routeHandler,
23
+ });
24
+ if (awaitApp) {
25
+ await app.ready();
26
+ }
27
+ return app;
28
+ }
29
+ describe('errorHandler', () => {
30
+ let app;
31
+ afterAll(async () => {
32
+ await app.close();
33
+ });
34
+ it('returns 500 internal error by default', async () => {
35
+ app = await initApp(() => {
36
+ throw new Error('Generic error');
37
+ });
38
+ const response = await app.inject().get('/').end();
39
+ expect(response.statusCode).toBe(500);
40
+ expect(response.json()).toEqual({
41
+ errorCode: 'INTERNAL_SERVER_ERROR',
42
+ message: 'Internal server error',
43
+ });
44
+ });
45
+ it('can override response resolution', async () => {
46
+ app = await initApp(() => {
47
+ throw new node_core_1.PublicNonRecoverableError({
48
+ message: 'Auth failed',
49
+ errorCode: 'AUTH_FAILED',
50
+ httpStatusCode: 401,
51
+ details: { someDetails: 'details' },
52
+ });
53
+ }, {
54
+ resolveResponseObject: (error) => {
55
+ return {
56
+ statusCode: 502,
57
+ payload: {
58
+ message: error.message + '1',
59
+ errorCode: 'TEST_ERR',
60
+ details: {
61
+ someValues: 1,
62
+ },
63
+ },
64
+ };
65
+ },
66
+ });
67
+ const response = await app.inject().get('/').end();
68
+ expect(response.statusCode).toBe(502);
69
+ expect(response.json()).toEqual({
70
+ details: {
71
+ someValues: 1,
72
+ },
73
+ errorCode: 'TEST_ERR',
74
+ message: 'Auth failed1',
75
+ });
76
+ });
77
+ it('can override logged object resolution', async () => {
78
+ let logSpy;
79
+ app = await initApp((req) => {
80
+ logSpy = vitest_1.vitest.spyOn(req.log, 'error');
81
+ throw new node_core_1.InternalError({
82
+ message: 'Internal error',
83
+ errorCode: 'INTERNAL',
84
+ });
85
+ }, {
86
+ resolveLogObject: (error) => {
87
+ return {
88
+ message: error.message + '22',
89
+ };
90
+ },
91
+ });
92
+ const response = await app.inject().get('/').end();
93
+ expect(response.statusCode).toBe(500);
94
+ expect(logSpy.mock.calls).toHaveLength(1);
95
+ expect(logSpy.mock.calls[0]).toEqual([
96
+ {
97
+ message: 'Internal error22',
98
+ },
99
+ ]);
100
+ });
101
+ it('sends error to the reporter', async () => {
102
+ const logs = [];
103
+ app = await initApp(() => {
104
+ throw new node_core_1.InternalError({
105
+ message: 'Internal error',
106
+ errorCode: 'INTERNAL',
107
+ });
108
+ }, {
109
+ errorReporter: {
110
+ report: (err) => {
111
+ logs.push(err);
112
+ },
113
+ },
114
+ });
115
+ const response = await app.inject().get('/').end();
116
+ expect(response.statusCode).toBe(500);
117
+ expect(logs).toHaveLength(1);
118
+ expect(logs[0].error).toMatchObject({
119
+ message: 'Internal error',
120
+ errorCode: 'INTERNAL',
121
+ });
122
+ });
123
+ it('responds with AUTH_FAILED in case of internal auth failed error', async () => {
124
+ app = await initApp(() => {
125
+ throw new node_core_1.PublicNonRecoverableError({
126
+ message: 'Auth failed',
127
+ errorCode: 'AUTH_FAILED',
128
+ httpStatusCode: 401,
129
+ details: { someDetails: 'details' },
130
+ });
131
+ });
132
+ const response = await app.inject().get('/').end();
133
+ expect(response.statusCode).toBe(401);
134
+ expect(response.json()).toEqual({
135
+ message: 'Auth failed',
136
+ errorCode: 'AUTH_FAILED',
137
+ details: { someDetails: 'details' },
138
+ });
139
+ });
140
+ it('responds with 401 for standardized token error with invalid token', async () => {
141
+ app = await initApp(() => {
142
+ const err = new Error('Auth failed');
143
+ // @ts-expect-error
144
+ err.code = 'FST_JWT_AUTHORIZATION_TOKEN_INVALID';
145
+ throw err;
146
+ });
147
+ const response = await app.inject().get('/').end();
148
+ expect(response.statusCode).toBe(401);
149
+ expect(response.json()).toEqual({
150
+ message: 'Authorization token is invalid',
151
+ errorCode: 'AUTH_FAILED',
152
+ });
153
+ });
154
+ it('responds with 401 for standardized token error with expired token', async () => {
155
+ app = await initApp(() => {
156
+ const err = new Error('Auth failed');
157
+ // @ts-expect-error
158
+ err.code = 'FST_JWT_NO_AUTHORIZATION_IN_HEADER';
159
+ throw err;
160
+ });
161
+ const response = await app.inject().get('/').end();
162
+ expect(response.statusCode).toBe(401);
163
+ expect(response.json()).toEqual({
164
+ message: 'Auth failed',
165
+ errorCode: 'AUTH_FAILED',
166
+ });
167
+ });
168
+ it('returns 500 for InternalError', async () => {
169
+ app = await initApp(() => {
170
+ throw new node_core_1.InternalError({
171
+ message: 'Auth failed',
172
+ details: { userId: 4 },
173
+ errorCode: 'INT_ERR',
174
+ });
175
+ });
176
+ const response = await app.inject().get('/').end();
177
+ expect(response.statusCode).toBe(500);
178
+ expect(response.json()).toEqual({
179
+ errorCode: 'INTERNAL_SERVER_ERROR',
180
+ message: 'Internal server error',
181
+ });
182
+ });
183
+ it('returns 400 for Zod errors', async () => {
184
+ app = await initApp(() => {
185
+ zod_1.z.string().parse(45);
186
+ });
187
+ const response = await app.inject().get('/').end();
188
+ expect(response.statusCode).toBe(400);
189
+ expect(response.json()).toEqual({
190
+ errorCode: 'VALIDATION_ERROR',
191
+ message: 'Invalid params',
192
+ details: {
193
+ error: [
194
+ {
195
+ code: 'invalid_type',
196
+ expected: 'string',
197
+ message: 'Expected string, received number',
198
+ path: [],
199
+ received: 'number',
200
+ },
201
+ ],
202
+ },
203
+ });
204
+ });
205
+ });
206
+ //# sourceMappingURL=errorHandler.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errorHandler.spec.js","sourceRoot":"","sources":["../../lib/errors/errorHandler.spec.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -20,3 +20,5 @@ export { amplitudePlugin, Amplitude, type AmplitudeConfig, type CreateApiTrackin
20
20
  export type { FastifyReplyWithPayload } from './types';
21
21
  export { unhandledExceptionPlugin } from './plugins/unhandledExceptionPlugin';
22
22
  export type { UnhandledExceptionPluginOptions } from './plugins/unhandledExceptionPlugin';
23
+ export { createErrorHandler } from './errors/errorHandler';
24
+ export type { ErrorHandlerParams, FreeformRecord } from './errors/errorHandler';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.unhandledExceptionPlugin = exports.Amplitude = exports.amplitudePlugin = exports.wrapHealthCheck = exports.publicHealthcheckPlugin = exports.prismaOtelTracingPlugin = exports.metricsPlugin = exports.wrapHealthCheckForPrometheus = exports.healthcheckMetricsPlugin = exports.SplitIOFeatureManager = exports.splitIOFeatureManagerPlugin = exports.NewRelicTransactionManager = exports.newrelicTransactionManagerPlugin = exports.REQUEST_ID_STORE_KEY = exports.getRequestIdFastifyAppConfig = exports.requestContextProviderPlugin = exports.reportErrorToBugsnag = exports.bugsnagPlugin = void 0;
3
+ exports.createErrorHandler = exports.unhandledExceptionPlugin = exports.Amplitude = exports.amplitudePlugin = exports.wrapHealthCheck = exports.publicHealthcheckPlugin = exports.prismaOtelTracingPlugin = exports.metricsPlugin = exports.wrapHealthCheckForPrometheus = exports.healthcheckMetricsPlugin = exports.SplitIOFeatureManager = exports.splitIOFeatureManagerPlugin = exports.NewRelicTransactionManager = exports.newrelicTransactionManagerPlugin = exports.REQUEST_ID_STORE_KEY = exports.getRequestIdFastifyAppConfig = exports.requestContextProviderPlugin = exports.reportErrorToBugsnag = exports.bugsnagPlugin = void 0;
4
4
  var bugsnagPlugin_1 = require("./plugins/bugsnagPlugin");
5
5
  Object.defineProperty(exports, "bugsnagPlugin", { enumerable: true, get: function () { return bugsnagPlugin_1.bugsnagPlugin; } });
6
6
  Object.defineProperty(exports, "reportErrorToBugsnag", { enumerable: true, get: function () { return bugsnagPlugin_1.reportErrorToBugsnag; } });
@@ -30,4 +30,6 @@ Object.defineProperty(exports, "amplitudePlugin", { enumerable: true, get: funct
30
30
  Object.defineProperty(exports, "Amplitude", { enumerable: true, get: function () { return amplitudePlugin_1.Amplitude; } });
31
31
  var unhandledExceptionPlugin_1 = require("./plugins/unhandledExceptionPlugin");
32
32
  Object.defineProperty(exports, "unhandledExceptionPlugin", { enumerable: true, get: function () { return unhandledExceptionPlugin_1.unhandledExceptionPlugin; } });
33
+ var errorHandler_1 = require("./errors/errorHandler");
34
+ Object.defineProperty(exports, "createErrorHandler", { enumerable: true, get: function () { return errorHandler_1.createErrorHandler; } });
33
35
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":";;;AAAA,yDAA6E;AAApE,8GAAA,aAAa,OAAA;AAAE,qHAAA,oBAAoB,OAAA;AAG5C,uFAI+C;AAH7C,4IAAA,4BAA4B,OAAA;AAC5B,4IAAA,4BAA4B,OAAA;AAC5B,oIAAA,oBAAoB,OAAA;AAItB,+FAGmD;AAFjD,oJAAA,gCAAgC,OAAA;AAChC,8IAAA,0BAA0B,OAAA;AAI5B,qFAG8C;AAF5C,0IAAA,2BAA2B,OAAA;AAC3B,oIAAA,qBAAqB,OAAA;AAIvB,2FAGuD;AAFrD,oIAAA,wBAAwB,OAAA;AACxB,wIAAA,4BAA4B,OAAA;AAQ9B,yDAAuD;AAA9C,8GAAA,aAAa,OAAA;AAGtB,2FAAyF;AAAhF,kIAAA,uBAAuB,OAAA;AAGhC,yFAAuF;AAA9E,kIAAA,uBAAuB,OAAA;AAMhC,+EAA0E;AAAjE,qHAAA,eAAe,OAAA;AAGxB,6DAKkC;AAJhC,kHAAA,eAAe,OAAA;AACf,4GAAA,SAAS,OAAA;AAOX,+EAA6E;AAApE,oIAAA,wBAAwB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":";;;AAAA,yDAA6E;AAApE,8GAAA,aAAa,OAAA;AAAE,qHAAA,oBAAoB,OAAA;AAG5C,uFAI+C;AAH7C,4IAAA,4BAA4B,OAAA;AAC5B,4IAAA,4BAA4B,OAAA;AAC5B,oIAAA,oBAAoB,OAAA;AAItB,+FAGmD;AAFjD,oJAAA,gCAAgC,OAAA;AAChC,8IAAA,0BAA0B,OAAA;AAI5B,qFAG8C;AAF5C,0IAAA,2BAA2B,OAAA;AAC3B,oIAAA,qBAAqB,OAAA;AAIvB,2FAGuD;AAFrD,oIAAA,wBAAwB,OAAA;AACxB,wIAAA,4BAA4B,OAAA;AAQ9B,yDAAuD;AAA9C,8GAAA,aAAa,OAAA;AAGtB,2FAAyF;AAAhF,kIAAA,uBAAuB,OAAA;AAGhC,yFAAuF;AAA9E,kIAAA,uBAAuB,OAAA;AAMhC,+EAA0E;AAAjE,qHAAA,eAAe,OAAA;AAGxB,6DAKkC;AAJhC,kHAAA,eAAe,OAAA;AACf,4GAAA,SAAS,OAAA;AAOX,+EAA6E;AAApE,oIAAA,wBAAwB,OAAA;AAGjC,sDAA0D;AAAjD,kHAAA,kBAAkB,OAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lokalise/fastify-extras",
3
- "version": "16.0.0",
3
+ "version": "16.1.0",
4
4
  "description": "Opinionated set of fastify plugins, commonly used in Lokalise",
5
5
  "author": {
6
6
  "name": "Lokalise",
@@ -62,7 +62,8 @@
62
62
  "@fastify/request-context": "^5.0.0",
63
63
  "fastify": "^4.24.3",
64
64
  "newrelic": "^11.0.0",
65
- "pino": "^8.16.2"
65
+ "pino": "^8.16.2",
66
+ "zod": "^3.22.4"
66
67
  },
67
68
  "devDependencies": {
68
69
  "@fastify/request-context": "^5.0.0",
@@ -87,7 +88,8 @@
87
88
  "ts-node": "^10.9.1",
88
89
  "typescript": "^5.3.2",
89
90
  "vitest": "^0.34.6",
90
- "vite": "4.5.0"
91
+ "vite": "4.5.0",
92
+ "zod": "^3.22.4"
91
93
  },
92
94
  "engines": {
93
95
  "node": ">=18"