@gasket/plugin-fastify 0.0.0-canary-20250922152503
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/EXAMPLES.md +144 -0
- package/LICENSE.md +21 -0
- package/README.md +125 -0
- package/generator/app/plugins/README.md +11 -0
- package/generator/app/plugins/routes-plugin.js +30 -0
- package/generator/app/plugins/routes-plugin.ts +29 -0
- package/generator/jest/jest.config.js +21 -0
- package/generator/jest/test/index.test.js +20 -0
- package/generator/jest/test/index.test.ts +26 -0
- package/generator/mocha/test/index.test.js +23 -0
- package/generator/mocha/test/index.test.ts +23 -0
- package/generator/vitest/test/index.test.js +21 -0
- package/generator/vitest/test/index.test.ts +21 -0
- package/lib/actions.js +17 -0
- package/lib/create-servers.js +32 -0
- package/lib/create.js +56 -0
- package/lib/index.d.ts +67 -0
- package/lib/index.js +74 -0
- package/lib/utils.js +50 -0
- package/package.json +55 -0
package/EXAMPLES.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# @gasket/plugin-fastify Examples
|
|
2
|
+
|
|
3
|
+
This document provides working examples for all exported interfaces from `@gasket/plugin-fastify`.
|
|
4
|
+
|
|
5
|
+
## Plugin Installation and Configuration
|
|
6
|
+
|
|
7
|
+
### Basic Plugin Setup
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
// gasket.js
|
|
11
|
+
import { makeGasket } from '@gasket/core';
|
|
12
|
+
import pluginFastify from '@gasket/plugin-fastify';
|
|
13
|
+
|
|
14
|
+
export default makeGasket({
|
|
15
|
+
plugins: [
|
|
16
|
+
pluginFastify
|
|
17
|
+
]
|
|
18
|
+
});
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Plugin Configuration
|
|
22
|
+
|
|
23
|
+
```js
|
|
24
|
+
// gasket.js
|
|
25
|
+
export default makeGasket({
|
|
26
|
+
plugins: [
|
|
27
|
+
pluginFastify
|
|
28
|
+
],
|
|
29
|
+
fastify: {
|
|
30
|
+
compression: true,
|
|
31
|
+
trustProxy: true,
|
|
32
|
+
disableRequestLogging: false
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Lifecycle Hooks
|
|
38
|
+
|
|
39
|
+
### `fastify` Lifecycle Hook
|
|
40
|
+
|
|
41
|
+
Access and modify the Fastify instance:
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
// Example plugin or lifecycle file
|
|
45
|
+
export default {
|
|
46
|
+
name: 'my-fastify-plugin',
|
|
47
|
+
hooks: {
|
|
48
|
+
fastify: async function (gasket, app) {
|
|
49
|
+
// Register plugins
|
|
50
|
+
app.register(require('@fastify/cors'), {
|
|
51
|
+
origin: true
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Add hooks
|
|
55
|
+
app.addHook('preHandler', async (request, reply) => {
|
|
56
|
+
console.log('Processing request:', request.url);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// Add custom decorators
|
|
60
|
+
app.decorate('customMethod', function() {
|
|
61
|
+
return 'custom value';
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
// Add routes
|
|
65
|
+
app.get('/hello', async (request, reply) => {
|
|
66
|
+
return { hello: 'world' };
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### `errorMiddleware` Lifecycle Hook
|
|
74
|
+
|
|
75
|
+
Add Express-style error handling middleware:
|
|
76
|
+
|
|
77
|
+
```js
|
|
78
|
+
// Example plugin
|
|
79
|
+
export default {
|
|
80
|
+
name: 'my-error-handler',
|
|
81
|
+
hooks: {
|
|
82
|
+
errorMiddleware: function (gasket) {
|
|
83
|
+
return (error, req, res, next) => {
|
|
84
|
+
gasket.logger.error('Request error:', error);
|
|
85
|
+
|
|
86
|
+
// Handle different error types
|
|
87
|
+
if (error.statusCode) {
|
|
88
|
+
res.status(error.statusCode);
|
|
89
|
+
} else {
|
|
90
|
+
res.status(500);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
res.send({
|
|
94
|
+
error: 'Internal Server Error',
|
|
95
|
+
message: error.message
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Configuration Examples
|
|
104
|
+
|
|
105
|
+
### Route Plugin Example
|
|
106
|
+
|
|
107
|
+
```js
|
|
108
|
+
// plugins/routes-plugin.js
|
|
109
|
+
export default {
|
|
110
|
+
name: 'routes-plugin',
|
|
111
|
+
hooks: {
|
|
112
|
+
fastify: async function (gasket, app) {
|
|
113
|
+
// API routes
|
|
114
|
+
app.get('/api/health', async (request, reply) => {
|
|
115
|
+
return {
|
|
116
|
+
status: 'healthy',
|
|
117
|
+
timestamp: new Date().toISOString(),
|
|
118
|
+
version: process.env.npm_package_version
|
|
119
|
+
};
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
app.get('/api/config', async (request, reply) => {
|
|
123
|
+
return {
|
|
124
|
+
environment: gasket.config.env,
|
|
125
|
+
features: gasket.config.features || {}
|
|
126
|
+
};
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Protected route example
|
|
130
|
+
app.get('/api/protected', {
|
|
131
|
+
preHandler: async (request, reply) => {
|
|
132
|
+
const auth = request.headers.authorization;
|
|
133
|
+
if (!auth || !auth.startsWith('Bearer ')) {
|
|
134
|
+
reply.code(401).send({ error: 'Unauthorized' });
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}, async (request, reply) => {
|
|
139
|
+
return { message: 'Protected data' };
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
```
|
package/LICENSE.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2019 GoDaddy Operating Company, LLC.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# @gasket/plugin-fastify
|
|
2
|
+
|
|
3
|
+
Adds Fastify to your application.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
#### New apps
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
npm i @gasket/plugin-fastify
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Update your `gasket` file plugin configuration:
|
|
14
|
+
|
|
15
|
+
```diff
|
|
16
|
+
// gasket.js
|
|
17
|
+
|
|
18
|
+
+ import pluginFastify from '@gasket/plugin-fastify';
|
|
19
|
+
|
|
20
|
+
export default makeGasket({
|
|
21
|
+
plugins: [
|
|
22
|
+
+ pluginFastify
|
|
23
|
+
]
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Configuration
|
|
28
|
+
|
|
29
|
+
All the configurations for the plugin are added under `fastify` in the config:
|
|
30
|
+
|
|
31
|
+
- `compression`: true by default. Can be set to false if applying compression
|
|
32
|
+
differently.
|
|
33
|
+
- `trustProxy`: Enable trust proxy option, [see Fastify documentation for possible values](https://fastify.dev/docs/latest/Reference/Server/#trustproxy)
|
|
34
|
+
- `disableRequestLogging`: Turn off request logging, true by default
|
|
35
|
+
|
|
36
|
+
#### Example configuration
|
|
37
|
+
|
|
38
|
+
```js
|
|
39
|
+
export default makeGasket({
|
|
40
|
+
plugins: [
|
|
41
|
+
pluginFastify
|
|
42
|
+
],
|
|
43
|
+
fastify: {
|
|
44
|
+
compression: false,
|
|
45
|
+
excludedRoutesRegex: /^(?!\/_next\/)/,
|
|
46
|
+
trustProxy: true
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Route Definition
|
|
52
|
+
|
|
53
|
+
Routes can be defined in a in-app plugin in the `plugins` directory. The plugin will hook the `fastify` lifecycle to add the routes to the fastify app.
|
|
54
|
+
|
|
55
|
+
```js
|
|
56
|
+
// plugins/routes-plugin.js
|
|
57
|
+
export default {
|
|
58
|
+
name: 'routes-plugin',
|
|
59
|
+
hooks: {
|
|
60
|
+
fastify: async function (gasket, app) {
|
|
61
|
+
app.get('/hello', (req, res) => {
|
|
62
|
+
res.send('Hello World!');
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Lifecycles
|
|
70
|
+
|
|
71
|
+
### fastify
|
|
72
|
+
|
|
73
|
+
Executed **after** the `middleware` event for when you need full control over
|
|
74
|
+
the `fastify` instance.
|
|
75
|
+
|
|
76
|
+
```js
|
|
77
|
+
export default {
|
|
78
|
+
name: 'sample-plugin',
|
|
79
|
+
hooks: {
|
|
80
|
+
/**
|
|
81
|
+
* Update Fastify app instance
|
|
82
|
+
*
|
|
83
|
+
* @param {Gasket} gasket The Gasket API
|
|
84
|
+
* @param {Fastify} fastify Fastify app instance
|
|
85
|
+
* @returns {function|function[]} middleware(s)
|
|
86
|
+
*/
|
|
87
|
+
fastify: async function (gasket, fastify) {
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### errorMiddleware
|
|
94
|
+
|
|
95
|
+
Executed after the `fastify` event. All middleware functions returned from this
|
|
96
|
+
hook will be applied to Fastify.
|
|
97
|
+
|
|
98
|
+
```js
|
|
99
|
+
export default {
|
|
100
|
+
name: 'sample-plugin',
|
|
101
|
+
hooks: {
|
|
102
|
+
/**
|
|
103
|
+
* Add Fastify error middlewares
|
|
104
|
+
*
|
|
105
|
+
* @param {Gasket} gasket The Gasket API
|
|
106
|
+
* @returns {function|function[]} error middleware(s)
|
|
107
|
+
*/
|
|
108
|
+
errorMiddleware: function (gasket) {
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## How it works
|
|
115
|
+
|
|
116
|
+
This plugins hooks the [createServers] lifecycles from [@gasket/plugin-https].
|
|
117
|
+
|
|
118
|
+
## License
|
|
119
|
+
|
|
120
|
+
[MIT](./LICENSE.md)
|
|
121
|
+
|
|
122
|
+
<!-- LINKS -->
|
|
123
|
+
|
|
124
|
+
[@gasket/plugin-https]:/packages/gasket-plugin-https/README.md
|
|
125
|
+
[createServers]:/packages/gasket-plugin-https/README.md#createservers
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export const defaultHandler = async (req, res) => {
|
|
2
|
+
if (res.statusCode === 200) {
|
|
3
|
+
res.send({ message: 'Welcome to your default route...' });
|
|
4
|
+
}
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
name: 'routes-plugin',
|
|
9
|
+
hooks: {
|
|
10
|
+
fastify(gasket, app) {
|
|
11
|
+
{{#if useSwagger }}
|
|
12
|
+
/**
|
|
13
|
+
* @swagger
|
|
14
|
+
*
|
|
15
|
+
* /default:
|
|
16
|
+
* get:
|
|
17
|
+
* summary: "Get default route"
|
|
18
|
+
* produces:
|
|
19
|
+
* - "application/json"
|
|
20
|
+
* responses:
|
|
21
|
+
* "200":
|
|
22
|
+
* description: "Returns welcome message."
|
|
23
|
+
* content:
|
|
24
|
+
* application/json
|
|
25
|
+
*/
|
|
26
|
+
{{/if }}
|
|
27
|
+
app.get('/default', defaultHandler);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export const defaultHandler = async (req, res) => {
|
|
2
|
+
if (res.statusCode === 200) {
|
|
3
|
+
res.send({ message: 'Welcome to your default route...' });
|
|
4
|
+
}
|
|
5
|
+
};
|
|
6
|
+
export default {
|
|
7
|
+
name: 'routes-plugin',
|
|
8
|
+
hooks: {
|
|
9
|
+
fastify(gasket, app) {
|
|
10
|
+
{{#if useSwagger }}
|
|
11
|
+
/**
|
|
12
|
+
* @swagger
|
|
13
|
+
*
|
|
14
|
+
* /default:
|
|
15
|
+
* get:
|
|
16
|
+
* summary: "Get default route"
|
|
17
|
+
* produces:
|
|
18
|
+
* - "application/json"
|
|
19
|
+
* responses:
|
|
20
|
+
* "200":
|
|
21
|
+
* description: "Returns welcome message."
|
|
22
|
+
* content:
|
|
23
|
+
* application/json
|
|
24
|
+
*/
|
|
25
|
+
{{/if }}
|
|
26
|
+
app.get('/default', defaultHandler);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{{#if typescript}}
|
|
2
|
+
const config = {
|
|
3
|
+
preset: 'ts-jest/presets/default-esm',
|
|
4
|
+
testEnvironment: 'node',
|
|
5
|
+
injectGlobals: true,
|
|
6
|
+
moduleDirectories: ['node_modules', '<rootDir>'],
|
|
7
|
+
moduleNameMapper: {
|
|
8
|
+
'^(\\.{1,2}/.*)\\.js$': '$1'
|
|
9
|
+
},
|
|
10
|
+
transform: {
|
|
11
|
+
'^.+.ts$': ['ts-jest', { useESM: true }]
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default config;
|
|
16
|
+
{{else}}
|
|
17
|
+
export default {
|
|
18
|
+
testEnvironment: 'node',
|
|
19
|
+
injectGlobals: true
|
|
20
|
+
};
|
|
21
|
+
{{/if}}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { jest } from '@jest/globals';
|
|
2
|
+
import { defaultHandler } from '../plugins/routes-plugin.js';
|
|
3
|
+
|
|
4
|
+
describe('Routes', () => {
|
|
5
|
+
let mockRequest, mockResponse;
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
mockRequest = {};
|
|
8
|
+
mockResponse = {
|
|
9
|
+
statusCode: 200,
|
|
10
|
+
send: jest.fn()
|
|
11
|
+
};
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('defaultHandler should use expeced message', async () => {
|
|
15
|
+
await defaultHandler(mockRequest, mockResponse);
|
|
16
|
+
expect(mockResponse.send).toHaveBeenCalledWith({
|
|
17
|
+
message: 'Welcome to your default route...'
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import {
|
|
2
|
+
jest,
|
|
3
|
+
describe,
|
|
4
|
+
beforeEach,
|
|
5
|
+
it,
|
|
6
|
+
expect
|
|
7
|
+
} from '@jest/globals';
|
|
8
|
+
import { defaultHandler } from '../plugins/routes-plugin.js';
|
|
9
|
+
|
|
10
|
+
describe('Routes', () => {
|
|
11
|
+
let mockRequest, mockResponse;
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
mockRequest = {};
|
|
14
|
+
mockResponse = {
|
|
15
|
+
statusCode: 200,
|
|
16
|
+
send: jest.fn()
|
|
17
|
+
};
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('defaultHandler should use expeced message', async () => {
|
|
21
|
+
await defaultHandler(mockRequest, mockResponse);
|
|
22
|
+
expect(mockResponse.send).toHaveBeenCalledWith({
|
|
23
|
+
message: 'Welcome to your default route...'
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { expect } from 'chai';
|
|
2
|
+
import sinon from 'sinon';
|
|
3
|
+
import { defaultHandler } from '../plugins/routes-plugin.js';
|
|
4
|
+
|
|
5
|
+
describe('Routes', () => {
|
|
6
|
+
let mockRequest, mockResponse, sendSpy;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
mockRequest = {};
|
|
10
|
+
sendSpy = sinon.spy();
|
|
11
|
+
mockResponse = {
|
|
12
|
+
statusCode: 200,
|
|
13
|
+
send: sendSpy
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('defaultHandler should use expected message', async () => {
|
|
18
|
+
await defaultHandler(mockRequest, mockResponse);
|
|
19
|
+
expect(sendSpy.calledWith({
|
|
20
|
+
message: 'Welcome to your default route...'
|
|
21
|
+
})).to.be.true;
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { expect } from 'chai';
|
|
2
|
+
import sinon from 'sinon';
|
|
3
|
+
import { defaultHandler } from '../plugins/routes-plugin.js';
|
|
4
|
+
|
|
5
|
+
describe('Routes', () => {
|
|
6
|
+
let mockRequest, mockResponse, sendSpy;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
mockRequest = {};
|
|
10
|
+
sendSpy = sinon.spy();
|
|
11
|
+
mockResponse = {
|
|
12
|
+
statusCode: 200,
|
|
13
|
+
send: sendSpy
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('defaultHandler should use expected message', async () => {
|
|
18
|
+
await defaultHandler(mockRequest, mockResponse);
|
|
19
|
+
expect(sendSpy.calledWith({
|
|
20
|
+
message: 'Welcome to your default route...'
|
|
21
|
+
})).to.be.true;
|
|
22
|
+
});
|
|
23
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { vi, expect } from 'vitest';
|
|
2
|
+
import { defaultHandler } from '../plugins/routes-plugin.js';
|
|
3
|
+
|
|
4
|
+
describe('Routes', () => {
|
|
5
|
+
let mockRequest, mockResponse;
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
mockRequest = {};
|
|
9
|
+
mockResponse = {
|
|
10
|
+
statusCode: 200,
|
|
11
|
+
send: vi.fn()
|
|
12
|
+
};
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('defaultHeader should use expected message', async () => {
|
|
16
|
+
await defaultHandler(mockRequest, mockResponse);
|
|
17
|
+
expect(mockResponse.send).toHaveBeenCalledWith({
|
|
18
|
+
message: 'Welcome to your default route...'
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
2
|
+
import { defaultHandler } from '../plugins/routes-plugin.js';
|
|
3
|
+
|
|
4
|
+
describe('Routes', () => {
|
|
5
|
+
let mockRequest, mockResponse;
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
mockRequest = {};
|
|
9
|
+
mockResponse = {
|
|
10
|
+
statusCode: 200,
|
|
11
|
+
send: vi.fn()
|
|
12
|
+
};
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('defaultHeader should use expected message', async () => {
|
|
16
|
+
await defaultHandler(mockRequest, mockResponse);
|
|
17
|
+
expect(mockResponse.send).toHaveBeenCalledWith({
|
|
18
|
+
message: 'Welcome to your default route...'
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
});
|
package/lib/actions.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const { getAppInstance } = require('./utils.js');
|
|
2
|
+
|
|
3
|
+
const actions = {
|
|
4
|
+
/**
|
|
5
|
+
* @deprecated
|
|
6
|
+
* @param {import('@gasket/core').Gasket} gasket - Gasket instance
|
|
7
|
+
* @returns {import('fastify').FastifyInstance} - Fastify instance
|
|
8
|
+
*/
|
|
9
|
+
getFastifyApp(gasket) {
|
|
10
|
+
gasket.logger.warn(
|
|
11
|
+
`DEPRECATED \`getFastifyApp\` action will not be support in future major release.`
|
|
12
|
+
);
|
|
13
|
+
return getAppInstance(gasket);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
module.exports = actions;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/// <reference types="@gasket/plugin-https" />
|
|
2
|
+
/// <reference types="@gasket/plugin-logger" />
|
|
3
|
+
|
|
4
|
+
const { getAppInstance } = require('./utils.js');
|
|
5
|
+
/**
|
|
6
|
+
* Create the Fastify instance and setup the lifecycle hooks.
|
|
7
|
+
* Fastify is compatible with express middleware out of the box, so we can
|
|
8
|
+
* use the same middleware lifecycles.
|
|
9
|
+
* @type {import('@gasket/core').HookHandler<'createServers'>}
|
|
10
|
+
*/
|
|
11
|
+
module.exports = async function createServers(gasket, serverOpts) {
|
|
12
|
+
/** Cast to Fastify + Express hybrid because Gasket adds `.use()` via @fastify/express plugin */
|
|
13
|
+
const app = /** @type {import('fastify').FastifyInstance & { use: Function }} */ (getAppInstance(gasket));
|
|
14
|
+
|
|
15
|
+
// allow consuming apps to directly append options to their server
|
|
16
|
+
await gasket.exec('fastify', app);
|
|
17
|
+
|
|
18
|
+
const postRenderingStacks = (await gasket.exec('errorMiddleware')).filter(Boolean);
|
|
19
|
+
postRenderingStacks.forEach((stack) => {
|
|
20
|
+
/** @type {import('connect').NextHandleFunction} */
|
|
21
|
+
const middleware = stack;
|
|
22
|
+
app.use(middleware);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
...serverOpts,
|
|
27
|
+
handler: async function handler(...args) {
|
|
28
|
+
await app.ready();
|
|
29
|
+
app.server.emit('request', ...args);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
};
|
package/lib/create.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/// <reference types="@gasket/core" />
|
|
2
|
+
/// <reference types="create-gasket-app" />
|
|
3
|
+
|
|
4
|
+
const { name, version, devDependencies } = require('../package.json');
|
|
5
|
+
|
|
6
|
+
const createTestFiles = ({ files, generatorDir, testPlugins, globIgnore }) => {
|
|
7
|
+
if (!testPlugins || testPlugins.length === 0) return;
|
|
8
|
+
const unit = ['jest', 'mocha', 'vitest'];
|
|
9
|
+
const integration = ['cypress'];
|
|
10
|
+
const frameworks = [...unit, ...integration];
|
|
11
|
+
const frameworksRegex = new RegExp(frameworks.join('|'));
|
|
12
|
+
|
|
13
|
+
testPlugins.forEach((testPlugin) => {
|
|
14
|
+
const match = frameworksRegex.exec(testPlugin);
|
|
15
|
+
if (match) {
|
|
16
|
+
const matchedFramework = match[0];
|
|
17
|
+
if (unit.includes(matchedFramework)) {
|
|
18
|
+
files.add(`${generatorDir}/${matchedFramework}/*`, `${generatorDir}/${matchedFramework}/**/${globIgnore}`);
|
|
19
|
+
} else {
|
|
20
|
+
files.add(`${generatorDir}/${matchedFramework}/*`, `${generatorDir}/${matchedFramework}/**/*`);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Add files & extend package.json for new apps.
|
|
28
|
+
* @type {import('@gasket/core').HookHandler<'create'>}
|
|
29
|
+
*/
|
|
30
|
+
module.exports = async function create(gasket, context) {
|
|
31
|
+
const {
|
|
32
|
+
files,
|
|
33
|
+
typescript,
|
|
34
|
+
apiApp,
|
|
35
|
+
addApiRoutes = true,
|
|
36
|
+
testPlugins,
|
|
37
|
+
pkg,
|
|
38
|
+
gasketConfig
|
|
39
|
+
} = context;
|
|
40
|
+
const generatorDir = `${__dirname}/../generator`;
|
|
41
|
+
|
|
42
|
+
gasketConfig.addPlugin('pluginFastify', name);
|
|
43
|
+
|
|
44
|
+
pkg.add('dependencies', {
|
|
45
|
+
[name]: `^${version}`,
|
|
46
|
+
fastify: devDependencies.fastify
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
if (apiApp && addApiRoutes) {
|
|
50
|
+
const globIgnore = typescript ? '!(*.js)' : '!(*.ts)';
|
|
51
|
+
files.add(`${generatorDir}/app/**/${globIgnore}`);
|
|
52
|
+
gasketConfig.addPlugin('pluginRoutes', './plugins/routes-plugin.js');
|
|
53
|
+
|
|
54
|
+
createTestFiles({ files, generatorDir, testPlugins, globIgnore });
|
|
55
|
+
}
|
|
56
|
+
};
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { MaybeAsync, MaybeMultiple, Plugin } from '@gasket/core';
|
|
2
|
+
import type { Logger } from '@gasket/plugin-logger';
|
|
3
|
+
import type {
|
|
4
|
+
FastifyInstance,
|
|
5
|
+
FastifyRequest,
|
|
6
|
+
FastifyReply,
|
|
7
|
+
FastifyServerOptions,
|
|
8
|
+
FastifyBaseLogger,
|
|
9
|
+
FastifyTypeProviderDefault,
|
|
10
|
+
RawServerDefault
|
|
11
|
+
} from 'fastify';
|
|
12
|
+
import type { IncomingMessage, ServerResponse } from 'http';
|
|
13
|
+
|
|
14
|
+
export interface FastifyConfig {
|
|
15
|
+
/** Enable compression */
|
|
16
|
+
compression?: boolean;
|
|
17
|
+
/** Trust proxy configuration */
|
|
18
|
+
trustProxy?: FastifyServerOptions['trustProxy'];
|
|
19
|
+
/** Fastify request logging per route */
|
|
20
|
+
disableRequestLogging?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type FastifyOptions = FastifyServerOptions & {
|
|
24
|
+
https?: any,
|
|
25
|
+
http2?: boolean | object
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
declare module '@gasket/core' {
|
|
29
|
+
export interface GasketActions {
|
|
30
|
+
/** @deprecated */
|
|
31
|
+
getFastifyApp(): FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, FastifyTypeProviderDefault>;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface GasketConfig {
|
|
35
|
+
fastify?: FastifyConfig
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
/** Error handler function type */
|
|
40
|
+
type ErrorHandler = (
|
|
41
|
+
error: Error,
|
|
42
|
+
req: FastifyRequest,
|
|
43
|
+
res: FastifyReply,
|
|
44
|
+
next: (error?: Error) => void
|
|
45
|
+
) => void;
|
|
46
|
+
|
|
47
|
+
export interface HookExecTypes {
|
|
48
|
+
fastify(app: FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, FastifyTypeProviderDefault>): MaybeAsync<void>;
|
|
49
|
+
errorMiddleware(): MaybeAsync<MaybeMultiple<ErrorHandler>>;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function alignLogger(
|
|
54
|
+
logger: Logger
|
|
55
|
+
): FastifyBaseLogger
|
|
56
|
+
|
|
57
|
+
declare module 'create-gasket-app' {
|
|
58
|
+
export interface CreateContext {
|
|
59
|
+
/** Flag indicating if API app is enabled */
|
|
60
|
+
apiApp?: boolean;
|
|
61
|
+
addApiRoutes?: boolean;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
declare const plugin: Plugin;
|
|
66
|
+
|
|
67
|
+
export default plugin;
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/// <reference types="@gasket/core" />
|
|
2
|
+
/// <reference types="@gasket/plugin-metadata" />
|
|
3
|
+
|
|
4
|
+
const { name, version, description } = require('../package.json');
|
|
5
|
+
const create = require('./create');
|
|
6
|
+
const createServers = require('./create-servers');
|
|
7
|
+
const actions = require('./actions');
|
|
8
|
+
|
|
9
|
+
/** @type {import('@gasket/core').Plugin} */
|
|
10
|
+
const plugin = {
|
|
11
|
+
name,
|
|
12
|
+
version,
|
|
13
|
+
description,
|
|
14
|
+
actions,
|
|
15
|
+
hooks: {
|
|
16
|
+
create,
|
|
17
|
+
createServers,
|
|
18
|
+
metadata(gasket, meta) {
|
|
19
|
+
return {
|
|
20
|
+
...meta,
|
|
21
|
+
actions: [
|
|
22
|
+
{
|
|
23
|
+
name: 'getFastifyApp',
|
|
24
|
+
description: 'Get the Fastify app instance',
|
|
25
|
+
link: 'README.md#getFastifyApp',
|
|
26
|
+
deprecated: true
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
lifecycles: [
|
|
30
|
+
{
|
|
31
|
+
name: 'fastify',
|
|
32
|
+
method: 'exec',
|
|
33
|
+
description: 'Modify the Fastify instance to for adding endpoints',
|
|
34
|
+
link: 'README.md#express',
|
|
35
|
+
parent: 'createServers',
|
|
36
|
+
after: 'middleware'
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'errorMiddleware',
|
|
40
|
+
method: 'exec',
|
|
41
|
+
description:
|
|
42
|
+
'Add Express style middleware for handling errors with Fastify',
|
|
43
|
+
link: 'README.md#errorMiddleware',
|
|
44
|
+
parent: 'createServers',
|
|
45
|
+
after: 'express'
|
|
46
|
+
}
|
|
47
|
+
],
|
|
48
|
+
configurations: [
|
|
49
|
+
{
|
|
50
|
+
name: 'fastify',
|
|
51
|
+
link: 'README.md#configuration',
|
|
52
|
+
description: 'Fastify configuration object',
|
|
53
|
+
type: 'object'
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'fastify.compression',
|
|
57
|
+
link: 'README.md#configuration',
|
|
58
|
+
description: 'Automatic compression',
|
|
59
|
+
type: 'boolean',
|
|
60
|
+
default: true
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: 'fastify.excludedRoutesRegex',
|
|
64
|
+
link: 'README.md#configuration',
|
|
65
|
+
description: 'Routes to be excluded based on a regex',
|
|
66
|
+
type: 'RegExp'
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
module.exports = plugin;
|
package/lib/utils.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
|
|
3
|
+
const fallbackMap = {
|
|
4
|
+
fatal: 'error',
|
|
5
|
+
trace: 'debug'
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* A helper function to ensure that all the required log levels
|
|
10
|
+
* for a Fastify server are present.
|
|
11
|
+
* @type {import('@gasket/plugin-fastify').alignLogger}
|
|
12
|
+
*/
|
|
13
|
+
function alignLogger(logger) {
|
|
14
|
+
const fastifyLogger = logger;
|
|
15
|
+
['fatal', 'trace'].map(level => {
|
|
16
|
+
if (!logger[level]) {
|
|
17
|
+
fastifyLogger[level] = logger[fallbackMap[level]];
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
return fastifyLogger;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let instance = null;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Get the Fastify instance.
|
|
28
|
+
* @param {import('@gasket/core').Gasket} gasket - Gasket instance
|
|
29
|
+
* @returns {import('fastify').FastifyInstance} - Fastify instance
|
|
30
|
+
*/
|
|
31
|
+
function getAppInstance(gasket) {
|
|
32
|
+
if (!instance) {
|
|
33
|
+
const fastify = require('fastify');
|
|
34
|
+
const { fastify: fastifyConfig = {}, http2, https } = gasket.config;
|
|
35
|
+
const { trustProxy = false, disableRequestLogging = true } = fastifyConfig;
|
|
36
|
+
const fastifyLogger = alignLogger(gasket.logger);
|
|
37
|
+
|
|
38
|
+
instance = fastify({ logger: fastifyLogger, trustProxy, https, http2, disableRequestLogging });
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return instance;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = {
|
|
45
|
+
alignLogger,
|
|
46
|
+
getAppInstance,
|
|
47
|
+
testClearAppInstance: () => {
|
|
48
|
+
instance = null;
|
|
49
|
+
}
|
|
50
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@gasket/plugin-fastify",
|
|
3
|
+
"version": "0.0.0-canary-20250922152503",
|
|
4
|
+
"description": "Adds fastify support to your application",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"types": "lib/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"generator",
|
|
9
|
+
"lib",
|
|
10
|
+
"EXAMPLES.md"
|
|
11
|
+
],
|
|
12
|
+
"repository": "godaddy/gasket.git",
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"fastify",
|
|
18
|
+
"gasket",
|
|
19
|
+
"plugin"
|
|
20
|
+
],
|
|
21
|
+
"author": "GoDaddy Operating Company, LLC",
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"bugs": "https://github.com/godaddy/gasket/issues",
|
|
24
|
+
"homepage": "https://github.com/godaddy/gasket/tree/main/packages/gasket-plugin-fastify",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@fastify/express": "^3.0.0",
|
|
27
|
+
"fastify": "^4.29.1"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/connect": "^3.4.38",
|
|
31
|
+
"fastify": "^4.29.1",
|
|
32
|
+
"jest": "^29.7.0",
|
|
33
|
+
"@gasket/core": "^0.0.0-canary-20250922152503",
|
|
34
|
+
"@gasket/plugin-https": "^0.0.0-canary-20250922152503",
|
|
35
|
+
"@gasket/plugin-logger": "^0.0.0-canary-20250922152503",
|
|
36
|
+
"create-gasket-app": "^0.0.0-canary-20250922152503",
|
|
37
|
+
"@gasket/plugin-metadata": "^0.0.0-canary-20250922152503"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"fastify": "^4.29.1"
|
|
41
|
+
},
|
|
42
|
+
"jest": {
|
|
43
|
+
"rootDir": "test"
|
|
44
|
+
},
|
|
45
|
+
"scripts": {
|
|
46
|
+
"lint": "eslint .",
|
|
47
|
+
"lint:fix": "pnpm run lint --fix",
|
|
48
|
+
"posttest": "pnpm run lint && pnpm run typecheck",
|
|
49
|
+
"test": "cross-env NODE_OPTIONS='--unhandled-rejections=strict' jest",
|
|
50
|
+
"test:coverage": "jest --coverage",
|
|
51
|
+
"test:watch": "jest --watch",
|
|
52
|
+
"typecheck": "tsc",
|
|
53
|
+
"typecheck:watch": "tsc --watch"
|
|
54
|
+
}
|
|
55
|
+
}
|