@xenterprises/fastify-xconfig 1.1.9 → 2.0.1

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.
@@ -103,17 +103,16 @@ export async function setupHealth(fastify, options) {
103
103
  }
104
104
 
105
105
  // Configuration validation
106
- const requiredEnvVars = [
107
- "DATABASE_URL",
108
- "ADMIN_JWT_SECRET",
109
- "USER_JWT_SECRET",
110
- ];
111
- const missingEnvVars = requiredEnvVars.filter(
112
- (varName) => !process.env[varName]
113
- );
114
- if (missingEnvVars.length > 0) {
115
- status.status = "degraded";
116
- status.details.missingEnvVars = missingEnvVars;
106
+ // Only check required env vars if Prisma is enabled
107
+ if (fastify.prisma) {
108
+ const requiredEnvVars = ["DATABASE_URL"];
109
+ const missingEnvVars = requiredEnvVars.filter(
110
+ (varName) => !process.env[varName]
111
+ );
112
+ if (missingEnvVars.length > 0) {
113
+ status.status = "degraded";
114
+ status.details.missingEnvVars = missingEnvVars;
115
+ }
117
116
  }
118
117
 
119
118
  // Determine overall health
package/src/xConfig.js CHANGED
@@ -1,28 +1,17 @@
1
1
  // src/xConfig.js
2
2
  import fp from "fastify-plugin";
3
3
  const isProduction = process.env.NODE_ENV === 'production';
4
- /*
5
- * Auth
6
- */
7
- import { setupAdminAuth } from "./auth/admin.js";
8
- import { setupAuth } from "./auth/portal.js";
9
-
10
4
 
11
5
  /*
12
6
  * Integrations
13
7
  */
14
8
  import { setupPrisma } from "./integrations/prisma.js";
15
- import { setupSendgrid } from "./integrations/sendgrid.js";
16
- import { setupTwilio } from "./integrations/twilio.js";
17
- import { setupCloudinary } from "./integrations/cloudinary.js";
18
- import { setupGeocode } from "./integrations/geocode.js";
19
9
 
20
10
  /*
21
11
  * Lifecycle
22
12
  */
23
13
  import { xFastifyAfter } from "./lifecycle/xFastifyAfter.js";
24
14
 
25
-
26
15
  /*
27
16
  * Middleware
28
17
  */
@@ -47,16 +36,10 @@ async function xConfig(fastify, options) {
47
36
  fancyErrors = true,
48
37
  prisma: prismaOptions = {},
49
38
  bugsnag: bugsnagOptions = {},
50
- stripe: stripeOptions = {},
51
- sendGrid: sendGridOptions = {},
52
- twilio: twilioOptions = {},
53
- cloudinary: cloudinaryOptions = {},
54
- auth: authOptions = {},
55
39
  cors: corsOptions = {},
56
40
  underPressure: underPressureOptions = {},
57
41
  multipart: multipartOptions = {},
58
42
  rateLimit: rateLimitOptions = {},
59
- geocode: geocodeOptions = {},
60
43
  } = options;
61
44
 
62
45
  // add starting console with emoji
@@ -73,10 +56,6 @@ async function xConfig(fastify, options) {
73
56
  * Integrations
74
57
  */
75
58
  await setupPrisma(fastify, prismaOptions);
76
- await setupSendgrid(fastify, sendGridOptions);
77
- await setupTwilio(fastify, twilioOptions);
78
- await setupCloudinary(fastify, cloudinaryOptions);
79
- await setupGeocode(fastify, geocodeOptions);
80
59
 
81
60
  /*
82
61
  * Middleware
@@ -98,13 +77,6 @@ async function xConfig(fastify, options) {
98
77
  fastify.register(xUUID);
99
78
  await setupHealth(fastify);
100
79
 
101
- /*
102
- * Auth
103
- */
104
- await setupAuth(fastify, authOptions);
105
- await setupAdminAuth(fastify, authOptions);
106
-
107
-
108
80
  //.after() method to ensure this runs after all plugins are registered.
109
81
  await xFastifyAfter(fastify, { professional, routes });
110
82
 
@@ -112,4 +84,4 @@ async function xConfig(fastify, options) {
112
84
 
113
85
  export default fp(xConfig, {
114
86
  name: "xConfig",
115
- });
87
+ });
package/test/index.js CHANGED
@@ -8,10 +8,10 @@ fastify.get('/', async (request, reply) => {
8
8
  return { message: fastify.myPluginMethod() };
9
9
  });
10
10
 
11
- fastify.listen(3000, (err, address) => {
12
- if (err) {
13
- console.error(err);
14
- process.exit(1);
15
- }
11
+ try {
12
+ const address = await fastify.listen({ port: 3000 });
16
13
  console.log(`Server running at ${address}`);
17
- });
14
+ } catch (err) {
15
+ console.error(err);
16
+ process.exit(1);
17
+ }
@@ -0,0 +1,278 @@
1
+ // test/xConfig.test.js
2
+ import { test } from "node:test";
3
+ import assert from "node:assert";
4
+ import Fastify from "fastify";
5
+ import xConfig from "../src/xConfig.js";
6
+
7
+ // Minimal config for testing
8
+ const minimalConfig = {
9
+ prisma: { active: false },
10
+ bugsnag: { active: false },
11
+ };
12
+
13
+ test("xConfig Plugin - registers successfully", async () => {
14
+ const fastify = Fastify({ logger: false });
15
+ try {
16
+ await fastify.register(xConfig, minimalConfig);
17
+ assert.ok(true, "Plugin registered successfully");
18
+ } finally {
19
+ await fastify.close();
20
+ }
21
+ });
22
+
23
+ test("xConfig Plugin - provides utility decorators", async () => {
24
+ const fastify = Fastify({ logger: false });
25
+ try {
26
+ await fastify.register(xConfig, minimalConfig);
27
+ // At least one utility decorator should be available
28
+ const hasUtilityDecorators =
29
+ fastify.xEcho !== undefined ||
30
+ fastify.xUUID !== undefined;
31
+ assert.ok(hasUtilityDecorators, "At least one utility decorator is available");
32
+ } finally {
33
+ await fastify.close();
34
+ }
35
+ });
36
+
37
+ test("xConfig Plugin - has health route registered", async () => {
38
+ const fastify = Fastify({ logger: false });
39
+ try {
40
+ const routes = [];
41
+ fastify.addHook("onRoute", (r) => routes.push(r));
42
+ await fastify.register(xConfig, minimalConfig);
43
+
44
+ // Check that health route is present
45
+ const hasHealthRoute = routes.some((r) => r.url === "/health");
46
+ assert.ok(hasHealthRoute, "Health endpoint route is registered");
47
+ } finally {
48
+ await fastify.close();
49
+ }
50
+ });
51
+
52
+ test("xConfig Plugin - accepts custom CORS config", async () => {
53
+ const fastify = Fastify({ logger: false });
54
+ const customConfig = {
55
+ ...minimalConfig,
56
+ cors: { origin: "http://example.com" },
57
+ };
58
+ try {
59
+ await fastify.register(xConfig, customConfig);
60
+ assert.ok(true, "Custom CORS config accepted");
61
+ } finally {
62
+ await fastify.close();
63
+ }
64
+ });
65
+
66
+ test("xConfig Plugin - accepts custom rate limit config", async () => {
67
+ const fastify = Fastify({ logger: false });
68
+ const customConfig = {
69
+ ...minimalConfig,
70
+ rateLimit: { max: 50, timeWindow: "1 minute" },
71
+ };
72
+ try {
73
+ await fastify.register(xConfig, customConfig);
74
+ assert.ok(true, "Custom rate limit config accepted");
75
+ } finally {
76
+ await fastify.close();
77
+ }
78
+ });
79
+
80
+ test("xConfig Plugin - under pressure monitoring available", async () => {
81
+ const fastify = Fastify({ logger: false });
82
+ try {
83
+ const customConfig = {
84
+ ...minimalConfig,
85
+ underPressure: {
86
+ maxEventLoopDelay: 1000,
87
+ maxHeapUsedBytes: 1000000000,
88
+ maxRssBytes: 1000000000
89
+ }
90
+ };
91
+ await fastify.register(xConfig, customConfig);
92
+ assert.ok(true, "Under pressure config accepted");
93
+ } finally {
94
+ await fastify.close();
95
+ }
96
+ });
97
+
98
+ test("xConfig Plugin - multipart handling available", async () => {
99
+ const fastify = Fastify({ logger: false });
100
+ try {
101
+ const customConfig = {
102
+ ...minimalConfig,
103
+ multipart: { limits: { fileSize: 52428800 } }
104
+ };
105
+ await fastify.register(xConfig, customConfig);
106
+ assert.ok(true, "Multipart config accepted");
107
+ } finally {
108
+ await fastify.close();
109
+ }
110
+ });
111
+
112
+ test("xConfig Plugin - health check endpoint responds", async () => {
113
+ const fastify = Fastify({ logger: false });
114
+ try {
115
+ const routes = [];
116
+ fastify.addHook("onRoute", (r) => routes.push(r));
117
+ await fastify.register(xConfig, minimalConfig);
118
+
119
+ const response = await fastify.inject({
120
+ method: "GET",
121
+ url: "/health"
122
+ });
123
+ assert.equal(response.statusCode, 200, "Health endpoint returns 200");
124
+ const body = JSON.parse(response.body);
125
+ assert.ok(body.status, "Health response includes status field");
126
+ } finally {
127
+ await fastify.close();
128
+ }
129
+ });
130
+
131
+ test("xConfig Utilities - xUUID generates valid UUIDs", async () => {
132
+ const fastify = Fastify({ logger: false });
133
+ try {
134
+ await fastify.register(xConfig, minimalConfig);
135
+
136
+ const uuid1 = fastify.randomUUID();
137
+ const uuid2 = fastify.randomUUID();
138
+
139
+ // Check that randomUUID is a function
140
+ assert.equal(typeof fastify.randomUUID, "function", "randomUUID is a function");
141
+
142
+ // Check that UUIDs are generated
143
+ assert.ok(uuid1, "UUID is generated");
144
+ assert.ok(uuid2, "Second UUID is generated");
145
+
146
+ // Check that UUIDs are different
147
+ assert.notEqual(uuid1, uuid2, "UUIDs are unique");
148
+
149
+ // Check UUID format (36 chars, with dashes at positions 8, 13, 18, 23)
150
+ assert.match(uuid1, /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i, "UUID matches format");
151
+ } finally {
152
+ await fastify.close();
153
+ }
154
+ });
155
+
156
+ test("xConfig Utilities - xSlugify handles various inputs", async () => {
157
+ const fastify = Fastify({ logger: false });
158
+ try {
159
+ await fastify.register(xConfig, minimalConfig);
160
+
161
+ // Test basic slugification
162
+ assert.equal(fastify.slugify("Hello World"), "hello-world", "Converts to lowercase and replaces spaces");
163
+ assert.equal(fastify.slugify("UPPERCASE"), "uppercase", "Converts uppercase to lowercase");
164
+ assert.equal(fastify.slugify("Multiple Spaces"), "multiple-spaces", "Collapses multiple spaces");
165
+
166
+ // Test special character removal
167
+ assert.equal(fastify.slugify("Hello@World!"), "helloworld", "Removes special characters");
168
+ assert.equal(fastify.slugify("Test-String"), "test-string", "Preserves dashes");
169
+
170
+ // Test edge cases
171
+ assert.equal(fastify.slugify(" Trim "), "trim", "Trims leading and trailing spaces");
172
+ assert.equal(fastify.slugify("---multiple-dashes---"), "multiple-dashes", "Collapses multiple dashes");
173
+ assert.equal(fastify.slugify(""), "", "Handles empty string");
174
+
175
+ // Test with numbers
176
+ assert.equal(fastify.slugify("Test123"), "test123", "Preserves numbers");
177
+ } finally {
178
+ await fastify.close();
179
+ }
180
+ });
181
+
182
+ test("xConfig Utilities - xEcho returns status message", async () => {
183
+ const fastify = Fastify({ logger: false });
184
+ try {
185
+ await fastify.register(xConfig, minimalConfig);
186
+
187
+ const echo = fastify.xEcho();
188
+ assert.ok(echo, "xEcho returns a value");
189
+ assert.equal(typeof echo, "string", "xEcho returns a string");
190
+ } finally {
191
+ await fastify.close();
192
+ }
193
+ });
194
+
195
+ test("xConfig Health Endpoint - response structure validation", async () => {
196
+ const fastify = Fastify({ logger: false });
197
+ try {
198
+ await fastify.register(xConfig, minimalConfig);
199
+
200
+ const response = await fastify.inject({
201
+ method: "GET",
202
+ url: "/health"
203
+ });
204
+
205
+ assert.equal(response.statusCode, 200, "Returns 200 status");
206
+
207
+ const body = JSON.parse(response.body);
208
+ assert.ok(body.status, "Has status field");
209
+ assert.ok(body.timestamp, "Has timestamp field");
210
+ assert.ok(body.details, "Has details object");
211
+ assert.ok(typeof body.details === "object", "Details is an object");
212
+ } finally {
213
+ await fastify.close();
214
+ }
215
+ });
216
+
217
+ test("xConfig Configuration - CORS disabled when active is false", async () => {
218
+ const fastify = Fastify({ logger: false });
219
+ const customConfig = {
220
+ ...minimalConfig,
221
+ cors: { active: false },
222
+ };
223
+ try {
224
+ await fastify.register(xConfig, customConfig);
225
+ assert.ok(true, "Plugin accepts cors.active: false");
226
+ } finally {
227
+ await fastify.close();
228
+ }
229
+ });
230
+
231
+ test("xConfig Configuration - rate limit disabled when active is false", async () => {
232
+ const fastify = Fastify({ logger: false });
233
+ const customConfig = {
234
+ ...minimalConfig,
235
+ rateLimit: { active: false },
236
+ };
237
+ try {
238
+ await fastify.register(xConfig, customConfig);
239
+ assert.ok(true, "Plugin accepts rateLimit.active: false");
240
+ } finally {
241
+ await fastify.close();
242
+ }
243
+ });
244
+
245
+ test("xConfig Configuration - fancy errors can be disabled", async () => {
246
+ const fastify = Fastify({ logger: false });
247
+ const customConfig = {
248
+ ...minimalConfig,
249
+ fancyErrors: false,
250
+ };
251
+ try {
252
+ await fastify.register(xConfig, customConfig);
253
+ assert.ok(true, "Plugin accepts fancyErrors: false");
254
+ } finally {
255
+ await fastify.close();
256
+ }
257
+ });
258
+
259
+ test("xConfig Integration - all decorators available together", async () => {
260
+ const fastify = Fastify({ logger: false });
261
+ try {
262
+ await fastify.register(xConfig, minimalConfig);
263
+
264
+ // Verify all expected decorators exist
265
+ assert.ok(fastify.xEcho, "xEcho decorator available");
266
+ assert.ok(fastify.randomUUID, "randomUUID decorator available");
267
+ assert.ok(fastify.generateUUID, "generateUUID decorator available");
268
+ assert.ok(fastify.slugify, "slugify decorator available");
269
+
270
+ // Verify they're functions
271
+ assert.equal(typeof fastify.xEcho, "function", "xEcho is a function");
272
+ assert.equal(typeof fastify.randomUUID, "function", "randomUUID is a function");
273
+ assert.equal(typeof fastify.generateUUID, "function", "generateUUID is a function");
274
+ assert.equal(typeof fastify.slugify, "function", "slugify is a function");
275
+ } finally {
276
+ await fastify.close();
277
+ }
278
+ });