@xenterprises/fastify-xconfig 2.1.0 → 2.1.2
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/.env +33 -0
- package/package.json +1 -1
- package/CHANGELOG.md +0 -189
- package/server/app.js +0 -43
- package/test/index.js +0 -17
- package/test/xConfig.test.js +0 -278
package/.env
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Environment variables for xConfig Plugin
|
|
2
|
+
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
|
|
3
|
+
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
|
|
4
|
+
|
|
5
|
+
# Database Configuration (Required)
|
|
6
|
+
DATABASE_URL="postgresql://user:password@localhost:5432/dbname?sslmode=require"
|
|
7
|
+
|
|
8
|
+
# Server Configuration
|
|
9
|
+
NODE_ENV=development
|
|
10
|
+
PORT=3002
|
|
11
|
+
FASTIFY_ADDRESS=0.0.0.0
|
|
12
|
+
|
|
13
|
+
# Error Tracking (optional)
|
|
14
|
+
BUGSNAG_API_KEY="your-bugsnag-api-key"
|
|
15
|
+
|
|
16
|
+
# CORS Configuration (optional)
|
|
17
|
+
CORS_ORIGIN="http://localhost:3000,https://example.com"
|
|
18
|
+
RATE_LIMIT_MAX=100
|
|
19
|
+
RATE_LIMIT_TIME_WINDOW="1 minute"
|
|
20
|
+
|
|
21
|
+
# ========================================
|
|
22
|
+
# DEPRECATED / NO LONGER USED BY XCONFIG
|
|
23
|
+
# ========================================
|
|
24
|
+
# The following services have been extracted to separate plugins:
|
|
25
|
+
#
|
|
26
|
+
# Authentication → Use @xenterprises/fastify-xauth-jwks
|
|
27
|
+
# Geolocation → Use @xenterprises/fastify-xgeocode
|
|
28
|
+
# SMS Services → Use xTwilio plugin (separate module)
|
|
29
|
+
# Email Services → Use separate email plugin
|
|
30
|
+
# Image/File Storage → Use xStorage plugin (separate module)
|
|
31
|
+
# Payment Processing → Use xStripe plugin (separate module)
|
|
32
|
+
#
|
|
33
|
+
# Old Stack Auth variables are deprecated - use xAuthJWSK instead
|
package/package.json
CHANGED
package/CHANGELOG.md
DELETED
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
# Changelog
|
|
2
|
-
|
|
3
|
-
All notable changes to this project will be documented in this file.
|
|
4
|
-
|
|
5
|
-
## [2.0.0] - 2025-12-28
|
|
6
|
-
|
|
7
|
-
### BREAKING CHANGES
|
|
8
|
-
|
|
9
|
-
This is a major version release with significant architectural changes.
|
|
10
|
-
|
|
11
|
-
#### Removed Services
|
|
12
|
-
|
|
13
|
-
The following services have been extracted to dedicated plugins and are **no longer** available in xConfig:
|
|
14
|
-
|
|
15
|
-
- **SendGrid Integration** - Use `@xenterprises/fastify-xsendgrid` (planned) or implement separately
|
|
16
|
-
- **Twilio Integration** - Use `@xenterprises/fastify-xtwilio` (planned) or implement separately
|
|
17
|
-
- **Cloudinary Integration** - Use `@xenterprises/fastify-xstorage` (planned) or implement separately
|
|
18
|
-
- **Stripe Integration** - Use `@xenterprises/fastify-xstripe` (planned) or implement separately
|
|
19
|
-
- **Stack Auth Integration** - Use `@xenterprises/fastify-xauth-jwks` instead
|
|
20
|
-
|
|
21
|
-
#### Configuration Changes
|
|
22
|
-
|
|
23
|
-
The following options are **no longer supported** in xConfig registration:
|
|
24
|
-
|
|
25
|
-
```javascript
|
|
26
|
-
// ❌ These options are now REMOVED
|
|
27
|
-
fastify.register(xConfig, {
|
|
28
|
-
sendGrid: { ... }, // REMOVED - use separate plugin
|
|
29
|
-
twilio: { ... }, // REMOVED - use separate plugin
|
|
30
|
-
cloudinary: { ... }, // REMOVED - use separate plugin
|
|
31
|
-
stripe: { ... }, // REMOVED - use separate plugin
|
|
32
|
-
auth: { ... }, // REMOVED - use xAuthJWSK plugin
|
|
33
|
-
geocode: { ... }, // REMOVED - use xGeocode plugin
|
|
34
|
-
});
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
#### New Plugin-Based Architecture
|
|
38
|
-
|
|
39
|
-
xConfig now focuses on core middleware only. Use these dedicated plugins for removed services:
|
|
40
|
-
|
|
41
|
-
```javascript
|
|
42
|
-
import Fastify from 'fastify';
|
|
43
|
-
import xConfig from '@xenterprises/fastify-xconfig';
|
|
44
|
-
import xAuthJWSK from '@xenterprises/fastify-xauth-jwks';
|
|
45
|
-
import xGeocode from '@xenterprises/fastify-xgeocode';
|
|
46
|
-
|
|
47
|
-
const fastify = Fastify();
|
|
48
|
-
|
|
49
|
-
// Core config (middleware & database)
|
|
50
|
-
await fastify.register(xConfig, {
|
|
51
|
-
prisma: {},
|
|
52
|
-
cors: { origin: ['https://example.com'] },
|
|
53
|
-
rateLimit: { max: 100, timeWindow: '1 minute' },
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
// Dedicated plugins for other services
|
|
57
|
-
await fastify.register(xAuthJWSK, { /* config */ });
|
|
58
|
-
await fastify.register(xGeocode, { /* config */ });
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### Added
|
|
62
|
-
|
|
63
|
-
- ✅ Enhanced CORS security - Environment-aware origin defaults
|
|
64
|
-
- Production: Only allows configured domain
|
|
65
|
-
- Development: Allows localhost:3000 and localhost:3001
|
|
66
|
-
- ✅ Improved health check endpoint - Better environment validation
|
|
67
|
-
- ✅ Test suite - 8 comprehensive tests covering core functionality
|
|
68
|
-
- ✅ Production hardening - Security audit and best practices applied
|
|
69
|
-
|
|
70
|
-
### Changed
|
|
71
|
-
|
|
72
|
-
- **Core Focus**: xConfig now provides only essential middleware and configuration
|
|
73
|
-
- **Plugin Architecture**: Services moved to dedicated, focused plugins
|
|
74
|
-
- **Dependencies**: Reduced from 22 to 11 (50% reduction)
|
|
75
|
-
- **Bundle Size**: ~42% smaller with removed integrations
|
|
76
|
-
- **Code Quality**: Improved maintainability and single responsibility
|
|
77
|
-
|
|
78
|
-
### Fixed
|
|
79
|
-
|
|
80
|
-
- ⚠️ Prisma disconnect hook - Now properly called only once on shutdown
|
|
81
|
-
- ✅ Health endpoint - No longer fails when Prisma is disabled
|
|
82
|
-
- ✅ Lifecycle hooks - Fixed "already listening" errors in test scenarios
|
|
83
|
-
|
|
84
|
-
### Security
|
|
85
|
-
|
|
86
|
-
- **npm audit**: 0 vulnerabilities
|
|
87
|
-
- **CORS**: Secure defaults based on environment
|
|
88
|
-
- **Error Handling**: Stack traces hidden in production
|
|
89
|
-
- **Error Reporting**: Optional Bugsnag integration
|
|
90
|
-
|
|
91
|
-
### Migration Guide
|
|
92
|
-
|
|
93
|
-
#### Before (v1.x)
|
|
94
|
-
|
|
95
|
-
```javascript
|
|
96
|
-
fastify.register(xConfig, {
|
|
97
|
-
sendGrid: { apiKey: '...' },
|
|
98
|
-
twilio: { accountSid: '...', authToken: '...', phoneNumber: '...' },
|
|
99
|
-
cloudinary: { cloudName: '...' },
|
|
100
|
-
auth: { admin: { ... }, user: { ... } },
|
|
101
|
-
geocode: { apiKey: '...' },
|
|
102
|
-
});
|
|
103
|
-
```
|
|
104
|
-
|
|
105
|
-
#### After (v2.0)
|
|
106
|
-
|
|
107
|
-
```javascript
|
|
108
|
-
// Step 1: Core config only
|
|
109
|
-
fastify.register(xConfig, {
|
|
110
|
-
prisma: {},
|
|
111
|
-
cors: { active: true, origin: ['https://yourdomain.com'] },
|
|
112
|
-
rateLimit: { max: 100, timeWindow: '1 minute' },
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
// Step 2: Add service plugins
|
|
116
|
-
fastify.register(xAuthJWSK, {
|
|
117
|
-
paths: {
|
|
118
|
-
admin: { pathPattern: '/admin', jwksUrl: '...' },
|
|
119
|
-
api: { pathPattern: '/api', jwksUrl: '...' }
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
fastify.register(xGeocode, {
|
|
124
|
-
apiKey: process.env.GEOCODIO_API_KEY
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
// Step 3: Implement other services separately or wait for dedicated plugins
|
|
128
|
-
// - Email: Use SendGrid SDK directly or wait for @xenterprises/fastify-xsendgrid
|
|
129
|
-
// - SMS: Use Twilio SDK directly or wait for @xenterprises/fastify-xtwilio
|
|
130
|
-
// - Storage: Use AWS S3 SDK directly or wait for @xenterprises/fastify-xstorage
|
|
131
|
-
// - Payments: Use Stripe SDK directly or wait for @xenterprises/fastify-xstripe
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
### Deprecation Notes
|
|
135
|
-
|
|
136
|
-
- The `auth` option is deprecated. Use `@xenterprises/fastify-xauth-jwks` instead
|
|
137
|
-
- The `geocode` option is deprecated. Use `@xenterprises/fastify-xgeocode` instead
|
|
138
|
-
- Stack Auth integration is deprecated. Use `@xenterprises/fastify-xauth-jwks` instead
|
|
139
|
-
|
|
140
|
-
### Environment Variables
|
|
141
|
-
|
|
142
|
-
**Removed** (no longer supported):
|
|
143
|
-
- `ADMIN_STACK_PROJECT_ID`
|
|
144
|
-
- `ADMIN_STACK_PUBLISHABLE_CLIENT_KEY`
|
|
145
|
-
- `ADMIN_STACK_SECRET_SERVER_KEY`
|
|
146
|
-
- `USER_STACK_PROJECT_ID`
|
|
147
|
-
- `USER_STACK_PUBLISHABLE_CLIENT_KEY`
|
|
148
|
-
- `USER_STACK_SECRET_SERVER_KEY`
|
|
149
|
-
- `SENDGRID_API_KEY`
|
|
150
|
-
- `SENDGRID_API_EMAIL_VALIDATION_KEY`
|
|
151
|
-
- `SENDGRID_FROM_EMAIL`
|
|
152
|
-
- `TWILIO_ACCOUNT_SID`
|
|
153
|
-
- `TWILIO_AUTH_TOKEN`
|
|
154
|
-
- `TWILIO_PHONE_NUMBER`
|
|
155
|
-
- `CLOUDINARY_CLOUD_NAME`
|
|
156
|
-
- `CLOUDINARY_API_KEY`
|
|
157
|
-
- `CLOUDINARY_API_SECRET`
|
|
158
|
-
- `CLOUDINARY_BASE_PATH`
|
|
159
|
-
- `STRIPE_API_KEY`
|
|
160
|
-
|
|
161
|
-
**Still Supported**:
|
|
162
|
-
- `DATABASE_URL` - PostgreSQL connection string (if Prisma enabled)
|
|
163
|
-
- `NODE_ENV` - Application environment
|
|
164
|
-
- `PORT` - Server port
|
|
165
|
-
- `FASTIFY_ADDRESS` - Server address
|
|
166
|
-
- `BUGSNAG_API_KEY` - Error tracking (optional)
|
|
167
|
-
- `CORS_ORIGIN` - Allowed CORS origins
|
|
168
|
-
- `RATE_LIMIT_MAX` - Rate limit threshold
|
|
169
|
-
- `RATE_LIMIT_TIME_WINDOW` - Rate limit window
|
|
170
|
-
|
|
171
|
-
### Testing
|
|
172
|
-
|
|
173
|
-
- ✅ All tests passing (8/8)
|
|
174
|
-
- ✅ npm audit clean (0 vulnerabilities)
|
|
175
|
-
- ✅ Production hardening complete
|
|
176
|
-
- ✅ Graceful shutdown verified
|
|
177
|
-
- ✅ Health endpoint validated
|
|
178
|
-
|
|
179
|
-
### Dependencies Updated
|
|
180
|
-
|
|
181
|
-
- Updated `@prisma/client` to ^7.2.0
|
|
182
|
-
- All @fastify packages verified and up-to-date
|
|
183
|
-
- Removed: @sendgrid/mail, @sendgrid/client, twilio, cloudinary, jose, stripe
|
|
184
|
-
|
|
185
|
-
---
|
|
186
|
-
|
|
187
|
-
## [1.x] - Previous Versions
|
|
188
|
-
|
|
189
|
-
See git history for previous releases.
|
package/server/app.js
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
// server/app.js
|
|
2
|
-
import Fastify from 'fastify';
|
|
3
|
-
import xConfig from '../src/xConfig.js'; // Import your plugin correctly
|
|
4
|
-
|
|
5
|
-
const fastify = Fastify();
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export default async function (fastify, opts) {
|
|
9
|
-
fastify.register(xConfig, {
|
|
10
|
-
professional: false,
|
|
11
|
-
fancyErrors: true,
|
|
12
|
-
prisma: {
|
|
13
|
-
active: false,
|
|
14
|
-
},
|
|
15
|
-
bugsnag: {
|
|
16
|
-
apiKey: process.env.BUGSNAG_API_KEY
|
|
17
|
-
},
|
|
18
|
-
rateLimit: {
|
|
19
|
-
max: process.env.RATE_LIMIT_MAX || 100,
|
|
20
|
-
timeWindow: process.env.RATE_LIMIT_TIME_WINDOW || '1 minute'
|
|
21
|
-
},
|
|
22
|
-
cors: {
|
|
23
|
-
active: true,
|
|
24
|
-
origin: process.env.CORS_ORIGIN || ['http://localhost:3000'],
|
|
25
|
-
credentials: true
|
|
26
|
-
},
|
|
27
|
-
multipart: {
|
|
28
|
-
limits: {
|
|
29
|
-
fileSize: 52428800 // 50MB
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
underPressure: {
|
|
33
|
-
maxEventLoopDelay: 1000,
|
|
34
|
-
maxHeapUsedBytes: 1000000000,
|
|
35
|
-
maxRssBytes: 1000000000
|
|
36
|
-
}
|
|
37
|
-
}); // Register the default export, which should be a function
|
|
38
|
-
fastify.get('/', async (request, reply) => {
|
|
39
|
-
console.log(fastify.xEcho())
|
|
40
|
-
return { status: fastify.xEcho() }
|
|
41
|
-
})
|
|
42
|
-
|
|
43
|
-
};
|
package/test/index.js
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
// test.js
|
|
2
|
-
const fastify = require('fastify')();
|
|
3
|
-
const myPlugin = require('../src/xConfig');
|
|
4
|
-
|
|
5
|
-
fastify.register(myPlugin);
|
|
6
|
-
|
|
7
|
-
fastify.get('/', async (request, reply) => {
|
|
8
|
-
return { message: fastify.myPluginMethod() };
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
try {
|
|
12
|
-
const address = await fastify.listen({ port: 3000 });
|
|
13
|
-
console.log(`Server running at ${address}`);
|
|
14
|
-
} catch (err) {
|
|
15
|
-
console.error(err);
|
|
16
|
-
process.exit(1);
|
|
17
|
-
}
|
package/test/xConfig.test.js
DELETED
|
@@ -1,278 +0,0 @@
|
|
|
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.xRandomUUID();
|
|
137
|
-
const uuid2 = fastify.xRandomUUID();
|
|
138
|
-
|
|
139
|
-
// Check that randomUUID is a function
|
|
140
|
-
assert.equal(typeof fastify.xRandomUUID, "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.xSlugify("Hello World"), "hello-world", "Converts to lowercase and replaces spaces");
|
|
163
|
-
assert.equal(fastify.xSlugify("UPPERCASE"), "uppercase", "Converts uppercase to lowercase");
|
|
164
|
-
assert.equal(fastify.xSlugify("Multiple Spaces"), "multiple-spaces", "Collapses multiple spaces");
|
|
165
|
-
|
|
166
|
-
// Test special character removal
|
|
167
|
-
assert.equal(fastify.xSlugify("Hello@World!"), "helloworld", "Removes special characters");
|
|
168
|
-
assert.equal(fastify.xSlugify("Test-String"), "test-string", "Preserves dashes");
|
|
169
|
-
|
|
170
|
-
// Test edge cases
|
|
171
|
-
assert.equal(fastify.xSlugify(" Trim "), "trim", "Trims leading and trailing spaces");
|
|
172
|
-
assert.equal(fastify.xSlugify("---multiple-dashes---"), "multiple-dashes", "Collapses multiple dashes");
|
|
173
|
-
assert.equal(fastify.xSlugify(""), "", "Handles empty string");
|
|
174
|
-
|
|
175
|
-
// Test with numbers
|
|
176
|
-
assert.equal(fastify.xSlugify("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.xRandomUUID, "randomUUID decorator available");
|
|
267
|
-
assert.ok(fastify.xGenerateUUID, "generateUUID decorator available");
|
|
268
|
-
assert.ok(fastify.xSlugify, "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.xRandomUUID, "function", "randomUUID is a function");
|
|
273
|
-
assert.equal(typeof fastify.xGenerateUUID, "function", "generateUUID is a function");
|
|
274
|
-
assert.equal(typeof fastify.xSlugify, "function", "slugify is a function");
|
|
275
|
-
} finally {
|
|
276
|
-
await fastify.close();
|
|
277
|
-
}
|
|
278
|
-
});
|