@lokalise/fastify-extras 25.2.0 → 25.4.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/README.md CHANGED
@@ -70,6 +70,39 @@ Add the plugin to your Fastify instance by registering it with the following opt
70
70
 
71
71
  Your Fastify app will reply with the status of the app when hitting the `GET /` route.
72
72
 
73
+ ### Common Healthcheck Plugin
74
+
75
+ Plugin to monitor app status through public and private healthchecks.
76
+
77
+ Add the plugin to your Fastify instance by registering it with the following options:
78
+
79
+ - `healthChecks`, a list of promises with healthcheck in the callback;
80
+ - `responsePayload` (optional), the response payload that the healthcheck should return. If no response payload is provided, the default response is:
81
+ ```json
82
+ { "heartbeat": "HEALTHY", "checks": {} }
83
+ ```
84
+
85
+ Your Fastify app will reply with the status of the app when hitting the `GET /` public route with aggregated heartbeat from healthchecks provided, example:
86
+ ```json
87
+ {
88
+ "heartbeat": "HEALTHY"
89
+ }
90
+ ```
91
+
92
+
93
+
94
+ Your Fastify app will reply with the status of the app when hitting the `GET /health` private route with detailed results from healthchecks provided, example:
95
+ ```json
96
+ {
97
+ "heartbeat": "PARTIALLY_HEALTHY",
98
+ "checks": {
99
+ "check1": "HEALTHY",
100
+ "check2": "HEALTHY",
101
+ "check3": "FAIL"
102
+ }
103
+ }
104
+ ```
105
+
73
106
  ### Split IO Plugin
74
107
 
75
108
  Plugin to handle feature flags in Split IO.
@@ -0,0 +1,18 @@
1
+ import type { FastifyPluginCallback } from 'fastify';
2
+ import type { HealthChecker } from './healthcheckCommons';
3
+ export interface CommonHealthcheckPluginOptions {
4
+ responsePayload?: Record<string, unknown>;
5
+ logLevel?: 'fatal' | 'error' | 'warn' | 'info' | 'debug' | 'trace' | 'silent';
6
+ healthChecks: readonly HealthCheck[];
7
+ infoProviders?: readonly InfoProvider[];
8
+ }
9
+ export type InfoProvider = {
10
+ name: string;
11
+ dataResolver: () => Record<string, unknown>;
12
+ };
13
+ export type HealthCheck = {
14
+ name: string;
15
+ isMandatory: boolean;
16
+ checker: HealthChecker;
17
+ };
18
+ export declare const commonHealthcheckPlugin: FastifyPluginCallback<CommonHealthcheckPluginOptions>;
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.commonHealthcheckPlugin = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const fastify_plugin_1 = tslib_1.__importDefault(require("fastify-plugin"));
6
+ function resolveHealthcheckResults(results, opts) {
7
+ const healthChecks = {};
8
+ let isFullyHealthy = true;
9
+ let isPartiallyHealthy = false;
10
+ // Return detailed healthcheck results
11
+ for (let i = 0; i < results.length; i++) {
12
+ const entry = results[i];
13
+ healthChecks[entry.name] = entry.result.error ? 'FAIL' : 'HEALTHY';
14
+ if (entry.result.error && opts.healthChecks[i].isMandatory) {
15
+ isFullyHealthy = false;
16
+ isPartiallyHealthy = false;
17
+ }
18
+ // Check if we are only partially healthy (only optional dependencies are failing)
19
+ if (isFullyHealthy && entry.result.error && !opts.healthChecks[i].isMandatory) {
20
+ isFullyHealthy = false;
21
+ isPartiallyHealthy = true;
22
+ }
23
+ }
24
+ return {
25
+ isFullyHealthy,
26
+ isPartiallyHealthy,
27
+ healthChecks,
28
+ };
29
+ }
30
+ function addRoute(app, opts, routeOpts) {
31
+ const responsePayload = opts.responsePayload ?? {};
32
+ app.route({
33
+ url: routeOpts.url,
34
+ method: 'GET',
35
+ logLevel: opts.logLevel ?? 'info',
36
+ schema: {
37
+ // hide route from swagger plugins
38
+ // @ts-expect-error
39
+ hide: true,
40
+ },
41
+ handler: async (_, reply) => {
42
+ let isFullyHealthy = true;
43
+ let isPartiallyHealthy = false;
44
+ let healthChecks = {};
45
+ if (opts.healthChecks.length) {
46
+ const results = await Promise.all(opts.healthChecks.map((healthcheck) => {
47
+ return healthcheck.checker(app).then((result) => {
48
+ if (result.error) {
49
+ app.log.error(result.error, `${healthcheck.name} healthcheck has failed`);
50
+ }
51
+ return {
52
+ name: healthcheck.name,
53
+ result,
54
+ isMandatory: healthcheck.isMandatory,
55
+ };
56
+ });
57
+ }));
58
+ const resolvedHealthcheckResponse = resolveHealthcheckResults(results, opts);
59
+ healthChecks = resolvedHealthcheckResponse.healthChecks;
60
+ isFullyHealthy = resolvedHealthcheckResponse.isFullyHealthy;
61
+ isPartiallyHealthy = resolvedHealthcheckResponse.isPartiallyHealthy;
62
+ }
63
+ const extraInfo = opts.infoProviders
64
+ ? opts.infoProviders.map((infoProvider) => {
65
+ return {
66
+ name: infoProvider.name,
67
+ value: infoProvider.dataResolver(),
68
+ };
69
+ })
70
+ : undefined;
71
+ const heartbeat = isFullyHealthy
72
+ ? 'HEALTHY'
73
+ : isPartiallyHealthy
74
+ ? 'PARTIALLY_HEALTHY'
75
+ : 'FAIL';
76
+ const response = {
77
+ ...responsePayload,
78
+ heartbeat,
79
+ ...(routeOpts.isPublicRoute
80
+ ? {}
81
+ : { checks: healthChecks, ...(extraInfo && { extraInfo }) }),
82
+ };
83
+ return reply.status(isFullyHealthy || isPartiallyHealthy ? 200 : 500).send(response);
84
+ },
85
+ });
86
+ }
87
+ function plugin(app, opts, done) {
88
+ addRoute(app, opts, {
89
+ url: '/',
90
+ isPublicRoute: true,
91
+ });
92
+ addRoute(app, opts, {
93
+ url: '/health',
94
+ isPublicRoute: false,
95
+ });
96
+ done();
97
+ }
98
+ exports.commonHealthcheckPlugin = (0, fastify_plugin_1.default)(plugin, {
99
+ fastify: '5.x',
100
+ name: 'common-healthcheck-plugin',
101
+ });
102
+ //# sourceMappingURL=commonHealthcheckPlugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commonHealthcheckPlugin.js","sourceRoot":"","sources":["../../../lib/plugins/healthcheck/commonHealthcheckPlugin.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lokalise/fastify-extras",
3
- "version": "25.2.0",
3
+ "version": "25.4.0",
4
4
  "description": "Opinionated set of fastify plugins, commonly used in Lokalise",
5
5
  "author": {
6
6
  "name": "Lokalise",
@@ -34,9 +34,8 @@
34
34
  "dependencies": {
35
35
  "@amplitude/analytics-node": "^1.3.6",
36
36
  "@bugsnag/js": "^8.1.2",
37
- "@lokalise/background-jobs-common": "^8.0.0",
38
37
  "@lokalise/error-utils": "^2.2.0",
39
- "@splitsoftware/splitio": "^10.28.0",
38
+ "@splitsoftware/splitio": "^11.0.1",
40
39
  "@supercharge/promise-pool": "^3.2.0",
41
40
  "fastify-metrics": "^12.1.0",
42
41
  "fastify-plugin": "^5.0.1",
@@ -47,6 +46,7 @@
47
46
  },
48
47
  "peerDependencies": {
49
48
  "@fastify/jwt": "^9.0.1",
49
+ "@lokalise/background-jobs-common": ">=8.0.0",
50
50
  "@lokalise/node-core": ">=12.0.0",
51
51
  "bullmq": "^5.19.0",
52
52
  "fastify": "^5.0.0",
@@ -59,6 +59,7 @@
59
59
  "@amplitude/analytics-types": "^2.8.4",
60
60
  "@biomejs/biome": "^1.9.4",
61
61
  "@lokalise/backend-http-client": "^2.4.0",
62
+ "@lokalise/background-jobs-common": "^9.0.0",
62
63
  "@lokalise/biome-config": "^1.5.0",
63
64
  "@lokalise/node-core": "^13.1.0",
64
65
  "@types/newrelic": "^9.14.5",
@@ -69,9 +70,9 @@
69
70
  "fastify": "^5.1.0",
70
71
  "fastify-type-provider-zod": "^4.0.2",
71
72
  "ioredis": "^5.4.1",
72
- "newrelic": "12.5.2",
73
+ "newrelic": "12.8.0",
73
74
  "pino": "^9.4.0",
74
- "pino-pretty": "^11.2.2",
75
+ "pino-pretty": "^13.0.0",
75
76
  "shx": "^0.3.4",
76
77
  "typescript": "^5.6.3",
77
78
  "vitest": "^2.1.4",