@xenterprises/fastify-xconfig 2.1.4 → 2.2.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/LICENSE +60 -0
- package/README.md +140 -141
- package/examples/.env.example +17 -0
- package/examples/basic.js +127 -0
- package/index.d.ts +75 -0
- package/package.json +9 -5
- package/src/integrations/prisma.js +13 -17
- package/src/lifecycle/xFastifyAfter.js +4 -15
- package/src/middleware/bugsnag.js +7 -7
- package/src/middleware/cors.js +14 -15
- package/src/middleware/fancyErrors.js +17 -22
- package/src/middleware/multipart.js +4 -4
- package/src/middleware/rateLimit.js +4 -4
- package/src/middleware/underPressure.js +4 -4
- package/src/utils/formatBytes.js +3 -1
- package/src/utils/health.js +10 -36
- package/src/xConfig.js +21 -39
- package/dist/integrations/cloudinary.d.ts +0 -1
- package/dist/integrations/cloudinary.js +0 -25
- package/dist/integrations/cloudinary.js.map +0 -1
- package/dist/integrations/prisma.d.ts +0 -1
- package/dist/integrations/prisma.js +0 -13
- package/dist/integrations/prisma.js.map +0 -1
- package/dist/integrations/sendgrid.d.ts +0 -1
- package/dist/integrations/sendgrid.js +0 -22
- package/dist/integrations/sendgrid.js.map +0 -1
- package/dist/integrations/stripe.d.ts +0 -1
- package/dist/integrations/stripe.js +0 -15
- package/dist/integrations/stripe.js.map +0 -1
- package/dist/integrations/twilio.d.ts +0 -1
- package/dist/integrations/twilio.js +0 -17
- package/dist/integrations/twilio.js.map +0 -1
- package/dist/middleware/bugsnag.d.ts +0 -2
- package/dist/middleware/bugsnag.js +0 -9
- package/dist/middleware/bugsnag.js.map +0 -1
- package/dist/middleware/cors.d.ts +0 -2
- package/dist/middleware/cors.js +0 -11
- package/dist/middleware/cors.js.map +0 -1
- package/dist/middleware/errorHandler.d.ts +0 -2
- package/dist/middleware/errorHandler.js +0 -19
- package/dist/middleware/errorHandler.js.map +0 -1
- package/dist/middleware/multipart.d.ts +0 -2
- package/dist/middleware/multipart.js +0 -7
- package/dist/middleware/multipart.js.map +0 -1
- package/dist/middleware/rateLimit.d.ts +0 -2
- package/dist/middleware/rateLimit.js +0 -7
- package/dist/middleware/rateLimit.js.map +0 -1
- package/dist/middleware/underPressure.d.ts +0 -2
- package/dist/middleware/underPressure.js +0 -7
- package/dist/middleware/underPressure.js.map +0 -1
- package/dist/utils/colorize.d.ts +0 -4
- package/dist/utils/colorize.js +0 -33
- package/dist/utils/colorize.js.map +0 -1
- package/dist/utils/formatBytes.d.ts +0 -1
- package/dist/utils/formatBytes.js +0 -10
- package/dist/utils/formatBytes.js.map +0 -1
- package/dist/utils/randomUUID.d.ts +0 -1
- package/dist/utils/randomUUID.js +0 -3
- package/dist/utils/randomUUID.js.map +0 -1
- package/dist/utils/statAsync.d.ts +0 -2
- package/dist/utils/statAsync.js +0 -4
- package/dist/utils/statAsync.js.map +0 -1
- package/dist/xConfig.d.ts +0 -3
- package/dist/xConfig.js +0 -9
- package/dist/xConfig.js.map +0 -1
- package/ts-reference/integrations/cloudinary.ts +0 -26
- package/ts-reference/integrations/prisma.ts +0 -13
- package/ts-reference/integrations/sendgrid.ts +0 -27
- package/ts-reference/integrations/stripe.ts +0 -15
- package/ts-reference/integrations/twilio.ts +0 -20
- package/ts-reference/middleware/bugsnag.ts +0 -10
- package/ts-reference/middleware/cors.ts +0 -13
- package/ts-reference/middleware/errorHandler.ts +0 -24
- package/ts-reference/middleware/multipart.ts +0 -8
- package/ts-reference/middleware/rateLimit.ts +0 -8
- package/ts-reference/middleware/underPressure.ts +0 -11
- package/ts-reference/utils/colorize.ts +0 -45
- package/ts-reference/utils/formatBytes.ts +0 -8
- package/ts-reference/utils/randomUUID.ts +0 -3
- package/ts-reference/utils/statAsync.ts +0 -4
- package/tsconfig.json +0 -14
- package/xConfigReference.js +0 -119
package/LICENSE
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
PROPRIETARY SOFTWARE LICENSE
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2026 X Enterprises LLC. All Rights Reserved.
|
|
4
|
+
|
|
5
|
+
This software and associated documentation files (the "Software") are the
|
|
6
|
+
exclusive property of X Enterprises LLC, a Washington limited liability
|
|
7
|
+
company.
|
|
8
|
+
|
|
9
|
+
TERMS AND CONDITIONS
|
|
10
|
+
|
|
11
|
+
1. OWNERSHIP
|
|
12
|
+
All rights, title, and interest in and to the Software, including all
|
|
13
|
+
intellectual property rights, are and shall remain the exclusive property
|
|
14
|
+
of X Enterprises LLC.
|
|
15
|
+
|
|
16
|
+
2. RESTRICTIONS
|
|
17
|
+
Without the prior written consent of X Enterprises LLC, you may not:
|
|
18
|
+
- Copy, modify, or distribute the Software
|
|
19
|
+
- Reverse engineer, decompile, or disassemble the Software
|
|
20
|
+
- Sublicense, sell, lease, or otherwise transfer the Software
|
|
21
|
+
- Remove or alter any proprietary notices or labels
|
|
22
|
+
|
|
23
|
+
3. AUTHORIZED USE
|
|
24
|
+
Use of this Software is limited to authorized employees, contractors, and
|
|
25
|
+
agents of X Enterprises LLC, solely for purposes approved by X Enterprises
|
|
26
|
+
LLC.
|
|
27
|
+
|
|
28
|
+
4. NO WARRANTY
|
|
29
|
+
THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
30
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
31
|
+
FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
32
|
+
X ENTERPRISES LLC BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY,
|
|
33
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF,
|
|
34
|
+
OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
35
|
+
SOFTWARE.
|
|
36
|
+
|
|
37
|
+
5. LIMITATION OF LIABILITY
|
|
38
|
+
IN NO EVENT SHALL X ENTERPRISES LLC BE LIABLE FOR ANY INDIRECT, INCIDENTAL,
|
|
39
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
40
|
+
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
41
|
+
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
42
|
+
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
43
|
+
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
44
|
+
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
45
|
+
|
|
46
|
+
6. GOVERNING LAW
|
|
47
|
+
This license shall be governed by and construed in accordance with the laws
|
|
48
|
+
of the State of Washington, United States, without regard to its conflict
|
|
49
|
+
of law provisions.
|
|
50
|
+
|
|
51
|
+
7. TERMINATION
|
|
52
|
+
This license is effective until terminated. X Enterprises LLC may terminate
|
|
53
|
+
this license at any time without notice. Upon termination, you must destroy
|
|
54
|
+
all copies of the Software in your possession.
|
|
55
|
+
|
|
56
|
+
For licensing inquiries, contact: legal@x.enterprises
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
X Enterprises LLC
|
|
60
|
+
Bothell, Washington, United States
|
package/README.md
CHANGED
|
@@ -1,18 +1,8 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @xenterprises/fastify-xconfig
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Fastify plugin for centralized middleware orchestration, health checks, and utility decorators.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
xConfig is a lightweight configuration and middleware orchestration plugin for Fastify. It handles:
|
|
8
|
-
|
|
9
|
-
- **Middleware Setup**: CORS, rate limiting, multipart handling, error handling
|
|
10
|
-
- **Health Checks**: Disk space monitoring and application uptime tracking
|
|
11
|
-
- **Error Tracking**: Bugsnag integration for production error monitoring
|
|
12
|
-
- **Prisma Integration**: Database connectivity through @prisma/client
|
|
13
|
-
- **Back Pressure Handling**: Automatic shutdown management for overloaded servers
|
|
14
|
-
|
|
15
|
-
## Installation
|
|
5
|
+
## Install
|
|
16
6
|
|
|
17
7
|
```bash
|
|
18
8
|
npm install @xenterprises/fastify-xconfig
|
|
@@ -20,155 +10,164 @@ npm install @xenterprises/fastify-xconfig
|
|
|
20
10
|
|
|
21
11
|
## Usage
|
|
22
12
|
|
|
23
|
-
### Basic Configuration
|
|
24
|
-
|
|
25
13
|
```javascript
|
|
26
14
|
import Fastify from 'fastify';
|
|
27
15
|
import xConfig from '@xenterprises/fastify-xconfig';
|
|
28
16
|
|
|
29
|
-
const fastify = Fastify();
|
|
17
|
+
const fastify = Fastify({ logger: true });
|
|
30
18
|
|
|
31
19
|
await fastify.register(xConfig, {
|
|
32
|
-
prisma: {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
},
|
|
38
|
-
rateLimit: {
|
|
39
|
-
max: 100,
|
|
40
|
-
timeWindow: '1 minute'
|
|
41
|
-
},
|
|
42
|
-
bugsnag: {
|
|
43
|
-
apiKey: process.env.BUGSNAG_API_KEY // Optional
|
|
44
|
-
}
|
|
20
|
+
prisma: { active: false },
|
|
21
|
+
bugsnag: { active: false },
|
|
22
|
+
cors: { origin: ['http://localhost:3000'], credentials: true },
|
|
23
|
+
rateLimit: { max: 100, timeWindow: '1 minute' },
|
|
24
|
+
multipart: { limits: { fileSize: 52428800 } },
|
|
25
|
+
underPressure: { maxEventLoopDelay: 1000 },
|
|
45
26
|
});
|
|
46
27
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
### Core Options
|
|
53
|
-
|
|
54
|
-
| Option | Type | Required | Description |
|
|
55
|
-
|--------|------|----------|-------------|
|
|
56
|
-
| `prisma` | Object | No | Prisma client configuration. If empty, plugin uses default connection pool. |
|
|
57
|
-
| `professional` | Boolean | No | Enable professional mode features (default: false) |
|
|
58
|
-
| `fancyErrors` | Boolean | No | Enable formatted error responses (default: true) |
|
|
28
|
+
// Utility decorators are now available:
|
|
29
|
+
fastify.xSlugify('Hello World'); // "hello-world"
|
|
30
|
+
fastify.xRandomUUID(); // "a1b2c3d4-..."
|
|
31
|
+
fastify.xFormatBytes(1048576); // "1 MB"
|
|
32
|
+
fastify.xEcho(); // "Hello from X Enterprises!"
|
|
59
33
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
| Option | Type | Required | Description |
|
|
63
|
-
|--------|------|----------|-------------|
|
|
64
|
-
| `cors` | Object | No | CORS configuration with `active`, `origin`, and `credentials` |
|
|
65
|
-
| `rateLimit` | Object | No | Rate limiting with `max` requests and `timeWindow` |
|
|
66
|
-
| `multipart` | Object | No | Multipart form handling configuration |
|
|
67
|
-
| `underPressure` | Object | No | Back pressure monitoring configuration |
|
|
68
|
-
|
|
69
|
-
### Observability Options
|
|
70
|
-
|
|
71
|
-
| Option | Type | Required | Description |
|
|
72
|
-
|--------|------|----------|-------------|
|
|
73
|
-
| `bugsnag` | Object | No | Bugsnag error tracking with `apiKey` |
|
|
74
|
-
|
|
75
|
-
## Available Decorators
|
|
76
|
-
|
|
77
|
-
After registration, the following are available on the fastify instance:
|
|
78
|
-
|
|
79
|
-
```javascript
|
|
80
|
-
// Health check function
|
|
81
|
-
fastify.health.check() // Returns { status, diskSpace, uptime }
|
|
82
|
-
|
|
83
|
-
// Prisma client (if configured)
|
|
84
|
-
fastify.prisma
|
|
34
|
+
await fastify.listen({ port: 3000 });
|
|
85
35
|
```
|
|
86
36
|
|
|
87
|
-
##
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
37
|
+
## Options
|
|
38
|
+
|
|
39
|
+
| Name | Type | Default | Required | Description |
|
|
40
|
+
|------|------|---------|----------|-------------|
|
|
41
|
+
| `professional` | `boolean` | `false` | No | Disable route listing on startup |
|
|
42
|
+
| `fancyErrors` | `boolean` | `true` | No | Enable formatted error responses with status codes |
|
|
43
|
+
| `prisma` | `object` | `{}` | No | Prisma client configuration (see below) |
|
|
44
|
+
| `bugsnag` | `object` | `{}` | No | Bugsnag error tracking configuration (see below) |
|
|
45
|
+
| `cors` | `object` | `{}` | No | CORS configuration passed to @fastify/cors |
|
|
46
|
+
| `rateLimit` | `object` | `{}` | No | Rate limiting configuration passed to @fastify/rate-limit |
|
|
47
|
+
| `multipart` | `object` | `{}` | No | Multipart configuration passed to @fastify/multipart |
|
|
48
|
+
| `underPressure` | `object` | `{}` | No | Back-pressure configuration passed to @fastify/under-pressure |
|
|
49
|
+
|
|
50
|
+
### `prisma` Options
|
|
51
|
+
|
|
52
|
+
| Name | Type | Default | Required | Description |
|
|
53
|
+
|------|------|---------|----------|-------------|
|
|
54
|
+
| `active` | `boolean` | `true` | No | Enable/disable Prisma integration |
|
|
55
|
+
| `client` | `PrismaClient` | — | Yes (if active) | Your generated PrismaClient class |
|
|
56
|
+
| `...rest` | `object` | — | No | Passed directly to `new PrismaClient(...)` |
|
|
57
|
+
|
|
58
|
+
### `bugsnag` Options
|
|
59
|
+
|
|
60
|
+
| Name | Type | Default | Required | Description |
|
|
61
|
+
|------|------|---------|----------|-------------|
|
|
62
|
+
| `active` | `boolean` | `true` | No | Enable/disable Bugsnag integration |
|
|
63
|
+
| `apiKey` | `string` | — | Yes (if active) | Bugsnag project API key |
|
|
64
|
+
|
|
65
|
+
### `cors` Options
|
|
66
|
+
|
|
67
|
+
| Name | Type | Default | Required | Description |
|
|
68
|
+
|------|------|---------|----------|-------------|
|
|
69
|
+
| `active` | `boolean` | `true` | No | Enable/disable CORS |
|
|
70
|
+
| `origin` | `string\|array` | env-based | No | Allowed origins (production reads `CORS_ORIGIN` env var) |
|
|
71
|
+
| `credentials` | `boolean` | `true` | No | Allow credentials |
|
|
72
|
+
| `methods` | `array` | `["GET","POST","PUT","DELETE","OPTIONS"]` | No | Allowed HTTP methods |
|
|
73
|
+
| `...rest` | `object` | — | No | Passed directly to @fastify/cors |
|
|
74
|
+
|
|
75
|
+
### Middleware `active` Flag
|
|
76
|
+
|
|
77
|
+
All middleware options (`cors`, `rateLimit`, `multipart`, `underPressure`, `bugsnag`, `prisma`) accept `active: false` to disable the middleware entirely. When omitted, the middleware is enabled by default.
|
|
78
|
+
|
|
79
|
+
## Decorated Properties
|
|
80
|
+
|
|
81
|
+
| Name | Type | Description |
|
|
82
|
+
|------|------|-------------|
|
|
83
|
+
| `fastify.prisma` | `PrismaClient` | Prisma client instance (only if prisma is active) |
|
|
84
|
+
| `fastify.xEcho()` | `function` | Returns `"Hello from X Enterprises!"` |
|
|
85
|
+
| `fastify.xSlugify(str)` | `function` | Converts string to URL-safe slug |
|
|
86
|
+
| `fastify.xRandomUUID()` | `function` | Generates a UUID v4 string |
|
|
87
|
+
| `fastify.xGenerateUUID()` | `function` | Alias for `xRandomUUID` |
|
|
88
|
+
| `fastify.xFormatBytes(bytes, decimals?)` | `function` | Formats bytes to human-readable string |
|
|
89
|
+
|
|
90
|
+
## Routes
|
|
91
|
+
|
|
92
|
+
| Method | Path | Description |
|
|
93
|
+
|--------|------|-------------|
|
|
94
|
+
| `GET` | `/health` | Health check endpoint with system metrics |
|
|
95
|
+
|
|
96
|
+
### Health Check Response
|
|
97
|
+
|
|
98
|
+
```json
|
|
99
|
+
{
|
|
100
|
+
"status": "healthy",
|
|
101
|
+
"timestamp": "2025-01-15T12:00:00.000Z",
|
|
102
|
+
"uptime": 3600,
|
|
103
|
+
"environment": "production",
|
|
104
|
+
"dependencies": {
|
|
105
|
+
"database": "up",
|
|
106
|
+
"redis": "not configured"
|
|
107
|
+
},
|
|
108
|
+
"resources": {
|
|
109
|
+
"memory": { "rss": "50 MB", "heapTotal": "30 MB", "heapUsed": "25 MB" },
|
|
110
|
+
"cpu": { "loadAverage": [1.2, 0.8, 0.5], "cpus": 4 },
|
|
111
|
+
"disk": { "free": "100 GB", "size": "500 GB" }
|
|
112
|
+
},
|
|
113
|
+
"details": {}
|
|
114
|
+
}
|
|
107
115
|
```
|
|
108
116
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
The following services have been extracted to separate, dedicated plugins:
|
|
112
|
-
|
|
113
|
-
| Service | Plugin | Package |
|
|
114
|
-
|---------|--------|---------|
|
|
115
|
-
| Authentication/JWKS | xAuthJWSK | @xenterprises/fastify-xauth-jwks |
|
|
116
|
-
| Geocoding | xGeocode | @xenterprises/fastify-xgeocode |
|
|
117
|
-
| SMS/Email | xTwilio | @xenterprises/fastify-xtwilio (separate module) |
|
|
118
|
-
| File Storage | xStorage | @xenterprises/fastify-xstorage (separate module) |
|
|
119
|
-
| Payment Processing | xStripe | @xenterprises/fastify-xstripe (separate module) |
|
|
120
|
-
|
|
121
|
-
## Development
|
|
122
|
-
|
|
123
|
-
### Running Tests
|
|
117
|
+
Returns `200` when healthy, `503` when degraded.
|
|
124
118
|
|
|
125
|
-
|
|
126
|
-
npm test
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
### Starting Development Server
|
|
119
|
+
## Environment Variables
|
|
130
120
|
|
|
131
|
-
|
|
132
|
-
|
|
121
|
+
| Name | Required | Description |
|
|
122
|
+
|------|----------|-------------|
|
|
123
|
+
| `NODE_ENV` | No | `development` or `production` (affects error stack traces, CORS defaults) |
|
|
124
|
+
| `PORT` | No | Server port (default: `3000`) |
|
|
125
|
+
| `FASTIFY_ADDRESS` | No | Server bind address (default: `0.0.0.0`) |
|
|
126
|
+
| `CORS_ORIGIN` | No | Comma-separated CORS origins for production |
|
|
127
|
+
| `RATE_LIMIT_MAX` | No | Max requests per window (default: `100`) |
|
|
128
|
+
| `RATE_LIMIT_TIME_WINDOW` | No | Rate limit window (default: `1 minute`) |
|
|
129
|
+
| `BUGSNAG_API_KEY` | If bugsnag active | Bugsnag project API key |
|
|
130
|
+
| `DATABASE_URL` | If prisma active | Database connection string |
|
|
131
|
+
|
|
132
|
+
## Error Reference
|
|
133
|
+
|
|
134
|
+
| Error | When |
|
|
135
|
+
|-------|------|
|
|
136
|
+
| `[xConfig] professional must be a boolean` | `professional` option is not a boolean |
|
|
137
|
+
| `[xConfig] fancyErrors must be a boolean` | `fancyErrors` option is not a boolean |
|
|
138
|
+
| `[xConfig] prisma.client is required - pass your PrismaClient class from your generated client` | Prisma is active but `client` not provided |
|
|
139
|
+
| `[xConfig] prisma.client must be a PrismaClient constructor` | `prisma.client` is not a function/class |
|
|
140
|
+
| `[xConfig] bugsnag.apiKey is required and must be a string` | Bugsnag is active but `apiKey` missing or not a string |
|
|
141
|
+
|
|
142
|
+
## Fancy Error Response Format
|
|
143
|
+
|
|
144
|
+
When `fancyErrors: true` (default), unhandled errors return:
|
|
145
|
+
|
|
146
|
+
```json
|
|
147
|
+
{
|
|
148
|
+
"status": 500,
|
|
149
|
+
"message": "Error description",
|
|
150
|
+
"stack": "..."
|
|
151
|
+
}
|
|
133
152
|
```
|
|
134
153
|
|
|
135
|
-
The
|
|
154
|
+
The `stack` field is only included when `NODE_ENV !== "production"`.
|
|
136
155
|
|
|
137
|
-
|
|
156
|
+
## How It Works
|
|
138
157
|
|
|
139
|
-
|
|
140
|
-
npm start
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
## Architecture
|
|
144
|
-
|
|
145
|
-
```
|
|
146
|
-
fastify app
|
|
147
|
-
↓
|
|
148
|
-
└─→ xConfig (core middleware & config)
|
|
149
|
-
├─→ @fastify/cors
|
|
150
|
-
├─→ @fastify/rate-limit
|
|
151
|
-
├─→ @fastify/multipart
|
|
152
|
-
├─→ @fastify/under-pressure
|
|
153
|
-
├─→ @fastify/sensible
|
|
154
|
-
├─→ fastify-bugsnag (optional)
|
|
155
|
-
└─→ Prisma client
|
|
156
|
-
```
|
|
158
|
+
xConfig is a single Fastify plugin that orchestrates registration of multiple sub-plugins in a specific order:
|
|
157
159
|
|
|
158
|
-
|
|
160
|
+
1. **Prisma** — decorates `fastify.prisma` with a connected PrismaClient and registers an `onClose` hook to disconnect on shutdown.
|
|
161
|
+
2. **Middleware** — registers CORS, under-pressure monitoring, rate limiting, and multipart handling (each skippable via `active: false`).
|
|
162
|
+
3. **Bugsnag** — optional error tracking that integrates with the fancy error handler.
|
|
163
|
+
4. **Fancy Errors** — sets a custom `errorHandler` that normalizes error responses and optionally reports to Bugsnag.
|
|
164
|
+
5. **@fastify/sensible** — adds `.httpErrors`, `.to()`, and other HTTP utilities.
|
|
165
|
+
6. **Utilities** — registers `xEcho`, `xSlugify`, `xRandomUUID`, and `xFormatBytes` decorators.
|
|
166
|
+
7. **Health Check** — registers `GET /health` with dependency checks (database, redis), resource monitoring (memory, CPU, disk), and environment validation.
|
|
167
|
+
8. **Lifecycle** — sets up route listing on startup (unless `professional: true`) and a goodbye log on shutdown.
|
|
159
168
|
|
|
160
|
-
|
|
161
|
-
- `@fastify/cors` - CORS middleware
|
|
162
|
-
- `@fastify/rate-limit` - Rate limiting
|
|
163
|
-
- `@fastify/multipart` - Multipart form handling
|
|
164
|
-
- `@fastify/sensible` - HTTP utilities
|
|
165
|
-
- `@fastify/under-pressure` - Back pressure handling
|
|
166
|
-
- `@prisma/client` - ORM for database access
|
|
167
|
-
- `fastify-bugsnag` - Error tracking (optional)
|
|
168
|
-
- `fastify-plugin` - Plugin wrapper
|
|
169
|
-
- `uncrypto` - Crypto utilities
|
|
170
|
-
- `check-disk-space` - Disk space monitoring
|
|
169
|
+
The plugin is wrapped with `fastify-plugin` so all decorators and routes are available in the parent scope.
|
|
171
170
|
|
|
172
171
|
## License
|
|
173
172
|
|
|
174
|
-
|
|
173
|
+
UNLICENSED
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# Server
|
|
2
|
+
PORT=3000
|
|
3
|
+
FASTIFY_ADDRESS=0.0.0.0
|
|
4
|
+
NODE_ENV=development
|
|
5
|
+
|
|
6
|
+
# CORS
|
|
7
|
+
CORS_ORIGIN=http://localhost:3000,http://localhost:3001
|
|
8
|
+
|
|
9
|
+
# Rate Limiting
|
|
10
|
+
RATE_LIMIT_MAX=100
|
|
11
|
+
RATE_LIMIT_TIME_WINDOW=1 minute
|
|
12
|
+
|
|
13
|
+
# Bugsnag (optional)
|
|
14
|
+
# BUGSNAG_API_KEY=your-bugsnag-api-key
|
|
15
|
+
|
|
16
|
+
# Database (required only if prisma.active is true)
|
|
17
|
+
# DATABASE_URL=postgresql://user:password@localhost:5432/mydb
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* fastify-x-config — Basic Example
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates all features of the xConfig plugin:
|
|
5
|
+
* - CORS, rate limiting, multipart, under-pressure middleware
|
|
6
|
+
* - Fancy error handling
|
|
7
|
+
* - Health check endpoint
|
|
8
|
+
* - Utility decorators: xEcho, xSlugify, xRandomUUID, xFormatBytes
|
|
9
|
+
*
|
|
10
|
+
* Run: node examples/basic.js
|
|
11
|
+
* Requires: .env or environment variables (see .env.example)
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import Fastify from "fastify";
|
|
15
|
+
import xConfig from "../src/xConfig.js";
|
|
16
|
+
|
|
17
|
+
const fastify = Fastify({
|
|
18
|
+
logger: true,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// ─── Register xConfig with all options ───────────────────────────────────────
|
|
22
|
+
|
|
23
|
+
await fastify.register(xConfig, {
|
|
24
|
+
// Disable route listing in production
|
|
25
|
+
professional: process.env.NODE_ENV === "production",
|
|
26
|
+
|
|
27
|
+
// Enable fancy error formatting (default: true)
|
|
28
|
+
fancyErrors: true,
|
|
29
|
+
|
|
30
|
+
// Prisma — disabled for this demo; pass { client: PrismaClient } to enable
|
|
31
|
+
prisma: { active: false },
|
|
32
|
+
|
|
33
|
+
// Bugsnag — disabled for this demo; pass { apiKey: "..." } to enable
|
|
34
|
+
bugsnag: { active: false },
|
|
35
|
+
|
|
36
|
+
// CORS configuration
|
|
37
|
+
cors: {
|
|
38
|
+
origin: process.env.CORS_ORIGIN
|
|
39
|
+
? process.env.CORS_ORIGIN.split(",")
|
|
40
|
+
: ["http://localhost:3000"],
|
|
41
|
+
credentials: true,
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
// Rate limiting
|
|
45
|
+
rateLimit: {
|
|
46
|
+
max: parseInt(process.env.RATE_LIMIT_MAX || "100", 10),
|
|
47
|
+
timeWindow: process.env.RATE_LIMIT_TIME_WINDOW || "1 minute",
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
// Multipart file uploads
|
|
51
|
+
multipart: {
|
|
52
|
+
limits: {
|
|
53
|
+
fileSize: 52428800, // 50 MB
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
// Back-pressure monitoring
|
|
58
|
+
underPressure: {
|
|
59
|
+
maxEventLoopDelay: 1000,
|
|
60
|
+
maxHeapUsedBytes: 1_000_000_000,
|
|
61
|
+
maxRssBytes: 1_000_000_000,
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// ─── 1. Health check (auto-registered by xConfig at /health) ─────────────────
|
|
66
|
+
|
|
67
|
+
// Try: curl http://localhost:3000/health
|
|
68
|
+
|
|
69
|
+
// ─── 2. xEcho — simple ping utility ─────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
fastify.get("/echo", async () => {
|
|
72
|
+
return { message: fastify.xEcho() };
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
// ─── 3. xSlugify — URL-safe slugification ───────────────────────────────────
|
|
76
|
+
|
|
77
|
+
fastify.get("/slug/:text", async (request) => {
|
|
78
|
+
const { text } = request.params;
|
|
79
|
+
return {
|
|
80
|
+
original: text,
|
|
81
|
+
slug: fastify.xSlugify(text),
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// ─── 4. xRandomUUID — UUID generation ───────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
fastify.get("/uuid", async () => {
|
|
88
|
+
return {
|
|
89
|
+
uuid: fastify.xRandomUUID(),
|
|
90
|
+
// xGenerateUUID is also available as an alias
|
|
91
|
+
uuid2: fastify.xGenerateUUID(),
|
|
92
|
+
};
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// ─── 5. xFormatBytes — human-readable byte formatting ───────────────────────
|
|
96
|
+
|
|
97
|
+
fastify.get("/format-bytes/:bytes", async (request) => {
|
|
98
|
+
const bytes = parseInt(request.params.bytes, 10);
|
|
99
|
+
return {
|
|
100
|
+
bytes,
|
|
101
|
+
formatted: fastify.xFormatBytes(bytes),
|
|
102
|
+
};
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// ─── 6. Fancy error handling demo ────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
fastify.get("/error", async () => {
|
|
108
|
+
throw new Error("This demonstrates fancy error formatting");
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
fastify.get("/error/:code", async (request) => {
|
|
112
|
+
const err = new Error(`Custom ${request.params.code} error`);
|
|
113
|
+
err.statusCode = parseInt(request.params.code, 10);
|
|
114
|
+
throw err;
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// ─── Start server ────────────────────────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
const port = parseInt(process.env.PORT || "3000", 10);
|
|
120
|
+
const address = process.env.FASTIFY_ADDRESS || "0.0.0.0";
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
await fastify.listen({ port, host: address });
|
|
124
|
+
} catch (err) {
|
|
125
|
+
fastify.log.error(err);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { FastifyPluginAsync } from 'fastify';
|
|
2
|
+
|
|
3
|
+
declare module 'fastify' {
|
|
4
|
+
interface FastifyInstance {
|
|
5
|
+
/** Prisma client instance (only available if prisma option is active) */
|
|
6
|
+
prisma?: any;
|
|
7
|
+
/** Returns "Hello from X Enterprises!" */
|
|
8
|
+
xEcho(): string;
|
|
9
|
+
/** Converts a string to a URL-safe slug */
|
|
10
|
+
xSlugify(input: string | number): string;
|
|
11
|
+
/** Generates a UUID v4 string */
|
|
12
|
+
xRandomUUID(): string;
|
|
13
|
+
/** Generates a UUID v4 string (alias for xRandomUUID) */
|
|
14
|
+
xGenerateUUID(): string;
|
|
15
|
+
/** Formats bytes into a human-readable string (e.g. "1.5 MB") */
|
|
16
|
+
xFormatBytes(bytes: number, decimals?: number): string;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface XConfigCorsOptions {
|
|
21
|
+
active?: boolean;
|
|
22
|
+
origin?: string | string[] | boolean;
|
|
23
|
+
credentials?: boolean;
|
|
24
|
+
methods?: string[];
|
|
25
|
+
[key: string]: any;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface XConfigRateLimitOptions {
|
|
29
|
+
active?: boolean;
|
|
30
|
+
max?: number;
|
|
31
|
+
timeWindow?: string | number;
|
|
32
|
+
[key: string]: any;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface XConfigMultipartOptions {
|
|
36
|
+
active?: boolean;
|
|
37
|
+
limits?: {
|
|
38
|
+
fileSize?: number;
|
|
39
|
+
[key: string]: any;
|
|
40
|
+
};
|
|
41
|
+
[key: string]: any;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface XConfigUnderPressureOptions {
|
|
45
|
+
active?: boolean;
|
|
46
|
+
maxEventLoopDelay?: number;
|
|
47
|
+
maxHeapUsedBytes?: number;
|
|
48
|
+
maxRssBytes?: number;
|
|
49
|
+
[key: string]: any;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
interface XConfigPrismaOptions {
|
|
53
|
+
active?: boolean;
|
|
54
|
+
client?: any;
|
|
55
|
+
[key: string]: any;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface XConfigBugsnagOptions {
|
|
59
|
+
active?: boolean;
|
|
60
|
+
apiKey?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
interface XConfigOptions {
|
|
64
|
+
professional?: boolean;
|
|
65
|
+
fancyErrors?: boolean;
|
|
66
|
+
prisma?: XConfigPrismaOptions;
|
|
67
|
+
bugsnag?: XConfigBugsnagOptions;
|
|
68
|
+
cors?: XConfigCorsOptions;
|
|
69
|
+
rateLimit?: XConfigRateLimitOptions;
|
|
70
|
+
multipart?: XConfigMultipartOptions;
|
|
71
|
+
underPressure?: XConfigUnderPressureOptions;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
declare const xConfig: FastifyPluginAsync<XConfigOptions>;
|
|
75
|
+
export default xConfig;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xenterprises/fastify-xconfig",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.2.0",
|
|
5
5
|
"description": "Fastify configuration plugin for setting up middleware, services, and route handling.",
|
|
6
6
|
"main": "src/xConfig.js",
|
|
7
7
|
"scripts": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"test": "node --test test/xConfig.test.js"
|
|
11
11
|
},
|
|
12
12
|
"author": "Tim Mushen",
|
|
13
|
-
"license": "
|
|
13
|
+
"license": "UNLICENSED",
|
|
14
14
|
"repository": {
|
|
15
15
|
"type": "git",
|
|
16
16
|
"url": "https://gitlab.com/x-enterprises/fastify-plugins/fastify-x-config"
|
|
@@ -33,14 +33,18 @@
|
|
|
33
33
|
"@fastify/rate-limit": "^10.0.0",
|
|
34
34
|
"@fastify/sensible": "^6.0.0",
|
|
35
35
|
"@fastify/under-pressure": "^9.0.0",
|
|
36
|
-
"@prisma/client": "^7.3.0",
|
|
37
36
|
"check-disk-space": "^3.4.0",
|
|
38
|
-
"fastify": "^5.1.0",
|
|
39
37
|
"fastify-bugsnag": "^5.0.0",
|
|
40
38
|
"fastify-plugin": "^5.0.0",
|
|
41
39
|
"uncrypto": "^0.1.3"
|
|
42
40
|
},
|
|
43
41
|
"peerDependencies": {
|
|
44
|
-
"fastify": "
|
|
42
|
+
"fastify": ">=5.0.0",
|
|
43
|
+
"@prisma/client": ">=5.0.0"
|
|
44
|
+
},
|
|
45
|
+
"peerDependenciesMeta": {
|
|
46
|
+
"@prisma/client": {
|
|
47
|
+
"optional": true
|
|
48
|
+
}
|
|
45
49
|
}
|
|
46
50
|
}
|
|
@@ -1,24 +1,20 @@
|
|
|
1
1
|
export async function setupPrisma(fastify, options) {
|
|
2
|
-
if (options.active
|
|
3
|
-
const { client: PrismaClient, active, ...prismaOptions } = options;
|
|
4
|
-
if (!PrismaClient) {
|
|
5
|
-
throw new Error("prisma.client is required - pass your PrismaClient class from your generated client");
|
|
6
|
-
}
|
|
2
|
+
if (options.active === false) return;
|
|
7
3
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
fastify.addHook("onClose", async () => {
|
|
13
|
-
await fastify.prisma.$disconnect();
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
console.info(" ✅ Prisma Enabled");
|
|
4
|
+
const { client: PrismaClient, active, ...prismaOptions } = options;
|
|
5
|
+
if (!PrismaClient) {
|
|
6
|
+
throw new Error("[xConfig] prisma.client is required - pass your PrismaClient class from your generated client");
|
|
17
7
|
}
|
|
8
|
+
if (typeof PrismaClient !== 'function') {
|
|
9
|
+
throw new Error("[xConfig] prisma.client must be a PrismaClient constructor");
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const prisma = new PrismaClient(prismaOptions);
|
|
13
|
+
fastify.decorate("prisma", prisma);
|
|
18
14
|
|
|
19
15
|
fastify.addHook("onClose", async () => {
|
|
20
|
-
|
|
21
|
-
await fastify.prisma.$disconnect();
|
|
22
|
-
}
|
|
16
|
+
await fastify.prisma.$disconnect();
|
|
23
17
|
});
|
|
18
|
+
|
|
19
|
+
fastify.log.info("[xConfig] Prisma enabled");
|
|
24
20
|
}
|