galbe 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/bin/commands/build.ts +13 -1
- package/docs/cli.md +25 -22
- package/docs/configuration.md +80 -0
- package/docs/context.md +38 -52
- package/docs/error-handler.md +20 -31
- package/docs/getting-started.md +40 -157
- package/docs/handler.md +35 -21
- package/docs/hooks.md +16 -16
- package/docs/plugins.md +50 -57
- package/docs/router.md +2 -2
- package/docs/routes.md +56 -39
- package/docs/schemas.md +60 -51
- package/package.json +1 -1
- package/src/parser.ts +1 -1
- package/src/router.ts +2 -2
- package/src/server.ts +7 -1
- package/test/parser.test.ts +23 -8
- package/test/router.test.ts +14 -0
package/docs/plugins.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Plugins
|
|
2
2
|
|
|
3
|
-
Galbe provides a powerful plugin system that allows developers to extend and customize the behavior
|
|
3
|
+
Galbe provides a powerful plugin system that allows developers to extend and customize the framework’s behavior. The plugin capabilities integrate with the [Request Lifecycle](https://galbe.dev/documentation/lifecycle).
|
|
4
4
|
|
|
5
5
|
## Definition
|
|
6
6
|
|
|
7
|
-
### Signature
|
|
7
|
+
### Plugin Signature
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
10
|
type GalbePlugin = {
|
|
@@ -17,52 +17,49 @@ type GalbePlugin = {
|
|
|
17
17
|
}
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
### name
|
|
21
|
+
The plugin name should be a Unique Plugin Identifier to prevent conflicts with other plugins. Ideally, it follows the format `com.example.myplugin`.
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
### init
|
|
24
|
+
This method is called immediately after the server starts. It receives two arguments:
|
|
25
|
+
- `config`: The plugin-specific configuration (See [Configuration](getting-started.md#properties) `plugin` property).
|
|
26
|
+
- `galbe`: The Galbe server instance, from which you can retrieve routes using `galbe.router.routes`.
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
### onFetch
|
|
29
|
+
This method is executed at the beginning of an incoming request. It receives a `context` object representing the [Request Context](context.md).
|
|
25
30
|
|
|
26
|
-
|
|
31
|
+
It is **preemptable**, meaning that if a response is returned, it will be sent to the client immediately, bypassing further processing.
|
|
27
32
|
|
|
28
|
-
|
|
33
|
+
### onRoute
|
|
34
|
+
Executed after the router identifies a matching route for the request. It takes a `context` argument and is **preemptable**, meaning it can return an early response.
|
|
29
35
|
|
|
30
|
-
|
|
36
|
+
### beforeHandle
|
|
37
|
+
Runs after request validation but before route hooks and the handler are called. Like the previous lifecycle methods, it is **preemptable**.
|
|
31
38
|
|
|
32
|
-
|
|
39
|
+
### afterHandle
|
|
40
|
+
Called after the route handler is executed but before sending the response. It receives two arguments:
|
|
41
|
+
- `response`: The [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) object from the handler.
|
|
42
|
+
- `context`: The request [Context](context.md).
|
|
33
43
|
|
|
34
|
-
**
|
|
44
|
+
It is also **preemptable**, meaning any returned response will override the original handler response.
|
|
35
45
|
|
|
36
|
-
|
|
46
|
+
## Plugin Registration
|
|
37
47
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
**beforeHandle**
|
|
41
|
-
|
|
42
|
-
This method is called after the request has been validated and before the route hooks and the handler are called. It takes a single `context` argument representing the current request [Context](context.md).
|
|
43
|
-
|
|
44
|
-
It is preemptable, meaning that any returned value will be interpreted as a response to send back to the client. Therefore, the method should only return [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) instances or nothing.
|
|
45
|
-
|
|
46
|
-
**afterHandle**
|
|
47
|
-
|
|
48
|
-
This method is called after the route handler has been called and before the response is sent. Its takes two arguments: a `response` object containing the [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) returned by the handler, and a `context` argument representing the current request [Context](context.md).
|
|
49
|
-
|
|
50
|
-
It is preemptable, meaning that any returned value will be interpreted as a response to send back to the client. Therefore, the method should only return [Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) instances or nothing.
|
|
51
|
-
|
|
52
|
-
### Usage
|
|
53
|
-
|
|
54
|
-
To register a plugin with your Galbe server, you must use the `use` method from you galbe instance.
|
|
48
|
+
To register a plugin with your Galbe server, use the `use` method on your Galbe instance.
|
|
55
49
|
|
|
56
50
|
```js
|
|
57
51
|
const galbe = new Galbe()
|
|
58
52
|
galbe.use(plugin)
|
|
59
53
|
```
|
|
60
54
|
|
|
61
|
-
## Create a
|
|
55
|
+
## How to Create a Plugin
|
|
56
|
+
|
|
57
|
+
This section will walk you through the process of creating a plugin.
|
|
58
|
+
Before creating a plugin, don't forget to check if there's an existing plugin that can be used. You can take a look at the official [Plugin List](https://galbe.dev/plugins).
|
|
62
59
|
|
|
63
60
|
### 1. Scaffolding
|
|
64
61
|
|
|
65
|
-
The Galbe starter CLI provides a template
|
|
62
|
+
The Galbe starter CLI provides a template for setting up a plugin project:
|
|
66
63
|
|
|
67
64
|
```bash
|
|
68
65
|
$ bun create galbe my-plugin --template plugin
|
|
@@ -70,45 +67,40 @@ $ cd my-plugin
|
|
|
70
67
|
$ bun install
|
|
71
68
|
```
|
|
72
69
|
|
|
73
|
-
Now you should be ready to start developing your plugin. Following section will present an example of a plugin implementation.
|
|
74
|
-
|
|
75
70
|
### 2. Implementation
|
|
76
71
|
|
|
77
|
-
|
|
72
|
+
Below is an example of a plugin that handles routes tagged with `@deprecated` metadata (See [Route Files](routes.md#route-files) for metadata usage).
|
|
78
73
|
|
|
79
74
|
deprecated.plugin.ts
|
|
80
|
-
|
|
81
75
|
```ts
|
|
82
|
-
import type { GalbePlugin } from 'galbe'
|
|
76
|
+
import type { GalbePlugin, Route } from 'galbe'
|
|
83
77
|
import { walkMetaRoutes } from 'galbe/utils'
|
|
84
78
|
|
|
85
79
|
const PLUGIN_ID = 'dev.galbe.deprecated'
|
|
86
80
|
|
|
87
|
-
export default () => {
|
|
81
|
+
export default (): GalbePlugin => {
|
|
88
82
|
let deprecateds = new Set<string>()
|
|
83
|
+
const isRouteDeprecated = (route?: Route) =>
|
|
84
|
+
deprecateds.has(JSON.stringify({ method: route?.method, path: route?.path }))
|
|
85
|
+
|
|
89
86
|
return {
|
|
90
87
|
name: PLUGIN_ID,
|
|
91
88
|
// Init the plugin, check for deprecated metadata tags
|
|
92
89
|
init(_config, galbe) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
})
|
|
97
|
-
}
|
|
90
|
+
walkMetaRoutes(galbe.meta || [], (method, path, meta) => {
|
|
91
|
+
if (meta.deprecated) deprecateds.add(JSON.stringify({ method, path }))
|
|
92
|
+
})
|
|
98
93
|
},
|
|
99
94
|
// Check if the current route is deprecated; if so, flag it as such and log it
|
|
100
95
|
onRoute(context) {
|
|
101
96
|
let r = context.route
|
|
102
|
-
if (
|
|
103
|
-
|
|
104
|
-
console.warn(`Call to deprecated route "${r.method} ${r.path}"`)
|
|
97
|
+
if (isRouteDeprecated(r)) {
|
|
98
|
+
console.warn(`Call to deprecated route "${r?.method} ${r?.path}"`)
|
|
105
99
|
}
|
|
106
100
|
},
|
|
107
101
|
// Add a header to the response if the route has been flagged as deprecated
|
|
108
102
|
afterHandle(response, context) {
|
|
109
|
-
|
|
110
|
-
if (!r) return
|
|
111
|
-
if (deprecateds.has(JSON.stringify({ method: r.method, path: r.path }))) {
|
|
103
|
+
if (isRouteDeprecated(context.route)) {
|
|
112
104
|
response.headers.set('x-deprecated', 'true')
|
|
113
105
|
}
|
|
114
106
|
}
|
|
@@ -116,6 +108,8 @@ export default () => {
|
|
|
116
108
|
}
|
|
117
109
|
```
|
|
118
110
|
|
|
111
|
+
Register the plugin with your Galbe server:
|
|
112
|
+
|
|
119
113
|
```ts
|
|
120
114
|
import { Galbe } from 'galbe'
|
|
121
115
|
import deprecatedPlugin from './deprecated.plugin'
|
|
@@ -128,26 +122,25 @@ export default galbe
|
|
|
128
122
|
|
|
129
123
|
### 3. Publishing
|
|
130
124
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
1. Create a **public** Github repository for your plugin. Make sure to include the following information in the README at the root of your repository:
|
|
125
|
+
To submit your plugin to the [official plugin list](https://galbe.dev/plugins), follow these steps:
|
|
134
126
|
|
|
135
|
-
|
|
136
|
-
-
|
|
137
|
-
-
|
|
127
|
+
1. **Create a public GitHub repository** for your plugin, ensuring that the `README.md` includes:
|
|
128
|
+
- A clear description of your plugin.
|
|
129
|
+
- Installation and configuration instructions.
|
|
130
|
+
- Usage examples.
|
|
138
131
|
|
|
139
|
-
2. (
|
|
132
|
+
2. _(Optional)_ Publish your plugin to [NPM](https://npmjs.com).
|
|
140
133
|
|
|
141
|
-
3.
|
|
134
|
+
3. **Submit a Pull Request** to add your plugin configuration to [plugins.json](https://github.com/pierre-cm/galbe-website/blob/main/plugins.json) in the following format:
|
|
142
135
|
|
|
143
136
|
```json
|
|
144
137
|
"plugin-id": {
|
|
145
138
|
"name": "Plugin Name",
|
|
146
139
|
"description": "Plugin description",
|
|
147
140
|
"repo": "https://github.com/<username>/<repo-name>",
|
|
148
|
-
"npm": "https://www.npmjs.com/package/<package-name>"
|
|
141
|
+
"npm": "https://www.npmjs.com/package/<package-name>"
|
|
149
142
|
}
|
|
150
143
|
```
|
|
151
144
|
|
|
152
145
|
> [!IMPORTANT]
|
|
153
|
-
>
|
|
146
|
+
> Provide all relevant details in the Pull Request description. It will be reviewed by project maintainers as soon as possible. Check the [Galbe Contributing Guide](https://github.com/pierre-cm/galbe/blob/main/docs/CONTRIBUTING.md) before submitting.
|
package/docs/router.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Router
|
|
2
2
|
|
|
3
|
-
Galbe router employs a hybrid approach to store and
|
|
3
|
+
Galbe router employs a hybrid approach to store and locate routes.
|
|
4
4
|
|
|
5
|
-
The static routes are maintained in a Map structure. This
|
|
5
|
+
The static routes are maintained in a Map structure. This ensures that any incoming request path matching a static route is resolved in a constant time `O(1)`.
|
|
6
6
|
|
|
7
7
|
> [!NOTE]
|
|
8
8
|
> A static route is a route that doesn't contain any parameter (e.g.,`:param`) or wildcards `*`.
|
package/docs/routes.md
CHANGED
|
@@ -1,71 +1,64 @@
|
|
|
1
1
|
# Routes
|
|
2
2
|
|
|
3
|
-
Routes
|
|
3
|
+
Routes serve as the entry points for handling client requests in a Galbe application. This section covers route definition, available configuration options, and the Automatic Route Analyzer, which simplifies route setup.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Defining Routes
|
|
6
6
|
|
|
7
|
-
Here
|
|
7
|
+
Here's how to define routes in Galbe:
|
|
8
8
|
|
|
9
9
|
```ts
|
|
10
10
|
galbe.[method](path: string, schema?: Schema, hooks?: Hooks[], handler: Handler)
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
**method** (
|
|
13
|
+
- **method** (`get` | `post` | `put` | `delete` | `patch` | `options` | `head`)
|
|
14
|
+
- The [HTTP request method](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods) for the route.
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
- **path** (string)
|
|
17
|
+
- The URL path of the route, composed of segments separated by `/`. Each segment may contain alphanumeric characters and dashes but should not start or end with a dash.
|
|
18
|
+
- Special segments:
|
|
19
|
+
- `:param` → A segment starting with `:` represents a parameter.
|
|
20
|
+
- `*` → A wildcard segment matching any sequence of segments.
|
|
16
21
|
|
|
17
|
-
**
|
|
22
|
+
- **schema** (Schema) _(Optional)_
|
|
23
|
+
- See the [Schemas](schemas.md) section.
|
|
18
24
|
|
|
19
|
-
|
|
25
|
+
- **hooks** (Hook[]) _(Optional)_
|
|
26
|
+
- See the [Hooks](hooks.md) section.
|
|
20
27
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
- `:param` Any segment starting with `:` indicates a parameter segment.
|
|
24
|
-
- `*` To indicate a wildcard segment. This will match any segment or sequence of segments.
|
|
25
|
-
|
|
26
|
-
**schema** (Schema) _Optional_
|
|
27
|
-
|
|
28
|
-
See [Schemas](schemas.md) section.
|
|
29
|
-
|
|
30
|
-
**hooks** (Hook[]) _Optional_
|
|
31
|
-
|
|
32
|
-
See [Hooks](hooks.md) section.
|
|
33
|
-
|
|
34
|
-
**handler** (Handler)
|
|
35
|
-
|
|
36
|
-
See [Handler](handler.md) section.
|
|
28
|
+
- **handler** (Handler)
|
|
29
|
+
- See the [Handler](handler.md) section.
|
|
37
30
|
|
|
38
31
|
### Examples
|
|
39
32
|
|
|
40
|
-
|
|
33
|
+
#### Basic Route
|
|
41
34
|
|
|
42
35
|
```js
|
|
43
|
-
galbe.get('/foo', ctx => 'Hello World!')
|
|
36
|
+
galbe.get('/foo', ctx => 'Hello, World!')
|
|
44
37
|
```
|
|
45
38
|
|
|
46
|
-
|
|
39
|
+
#### Route with a Schema
|
|
47
40
|
|
|
48
41
|
<!-- prettier-ignore -->
|
|
49
42
|
```js
|
|
50
43
|
galbe.get(
|
|
51
44
|
'/foo/:bar',
|
|
52
45
|
{ params: { bar: $T.string() } },
|
|
53
|
-
ctx => `Hello ${ctx.params.bar}
|
|
46
|
+
ctx => `Hello, ${ctx.params.bar}!`
|
|
54
47
|
)
|
|
55
48
|
```
|
|
56
49
|
|
|
57
|
-
|
|
50
|
+
#### Route with Hooks
|
|
58
51
|
|
|
59
52
|
<!-- prettier-ignore -->
|
|
60
53
|
```js
|
|
61
54
|
galbe.get(
|
|
62
55
|
'/foo/:bar',
|
|
63
56
|
[() => console.log('Hook1'), () => console.log('Hook2')],
|
|
64
|
-
ctx => `Hello ${ctx.params.bar}
|
|
57
|
+
ctx => `Hello, ${ctx.params.bar}!`
|
|
65
58
|
)
|
|
66
59
|
```
|
|
67
60
|
|
|
68
|
-
|
|
61
|
+
#### Route with Schemas and Hooks
|
|
69
62
|
|
|
70
63
|
<!-- prettier-ignore -->
|
|
71
64
|
```js
|
|
@@ -73,20 +66,43 @@ galbe.get(
|
|
|
73
66
|
'/foo/:bar',
|
|
74
67
|
{ params: { bar: $T.string() } },
|
|
75
68
|
[() => console.log('Hook')],
|
|
76
|
-
ctx => `Hello ${ctx.params.bar}
|
|
69
|
+
ctx => `Hello, ${ctx.params.bar}!`
|
|
77
70
|
)
|
|
78
71
|
```
|
|
79
72
|
|
|
73
|
+
## Defining Static Routes
|
|
74
|
+
|
|
75
|
+
Static routes serve files from the filesystem.
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
galbe.static(path: string, target: string, options?: StaticEndpointOptions)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
- **path** (string): The URL path of the route.
|
|
82
|
+
|
|
83
|
+
- **target** (string): The path to the directory or file to serve.
|
|
84
|
+
|
|
85
|
+
- **options** (StaticEndpointOptions) _(Optional)_:
|
|
86
|
+
- **resolve** ((path: string, target: string) => string | null | undefined | void) _(Optional)_
|
|
87
|
+
A function that resolves the path to the file to serve. The function may return a string corresponding to the new target.
|
|
88
|
+
|
|
89
|
+
### Examples
|
|
90
|
+
|
|
91
|
+
<!-- prettier-ignore -->
|
|
92
|
+
```js
|
|
93
|
+
galbe.static('/static', './public')
|
|
94
|
+
```
|
|
95
|
+
|
|
80
96
|
## Automatic Route Analyzer
|
|
81
97
|
|
|
82
98
|
> [!NOTE]
|
|
83
|
-
> This feature is only available if you run
|
|
99
|
+
> This feature is only available if you run or build the app using the [Galbe CLI](getting-started.md#galbe-cli). The CLI is used by default if you followed the [Automatic Installation](getting-started.md#automatic-installation) or configured `package.json` accordingly.
|
|
84
100
|
|
|
85
|
-
The Automatic Route Analyzer
|
|
101
|
+
The Automatic Route Analyzer scans all Route Files in your project and sets up route definitions automatically. By default, it looks for files matching `src/**/*.route.{js,ts}`. This behavior can be customized via the `routes` property in your Galbe configuration. Setting it to `false` disables the analyzer.
|
|
86
102
|
|
|
87
103
|
### Route Files
|
|
88
104
|
|
|
89
|
-
|
|
105
|
+
To be analyzed correctly, a Route File must export a default function that accepts a Galbe instance as its only argument. Define your routes within this function. Example in JavaScript:
|
|
90
106
|
|
|
91
107
|
```ts
|
|
92
108
|
export default g => {
|
|
@@ -94,16 +110,16 @@ export default g => {
|
|
|
94
110
|
}
|
|
95
111
|
```
|
|
96
112
|
|
|
97
|
-
The Automatic Route Analyzer can also
|
|
113
|
+
The Automatic Route Analyzer can also extract metadata from multiline comments. Some plugins utilize this metadata for specific tasks. Example:
|
|
98
114
|
|
|
99
115
|
```js
|
|
100
116
|
/**
|
|
101
|
-
*
|
|
102
|
-
* @annotation
|
|
117
|
+
* Header metadata description
|
|
118
|
+
* @annotation Example of a header annotation
|
|
103
119
|
*/
|
|
104
120
|
export default g => {
|
|
105
121
|
/**
|
|
106
|
-
*
|
|
122
|
+
* Route-specific metadata
|
|
107
123
|
* @deprecated
|
|
108
124
|
* @operationId fooBar
|
|
109
125
|
* @tags tag1 tag2
|
|
@@ -113,4 +129,5 @@ export default g => {
|
|
|
113
129
|
```
|
|
114
130
|
|
|
115
131
|
> [!TIP]
|
|
116
|
-
>
|
|
132
|
+
> To exclude a route from analysis, add `//@galbe-ignore` before its definition. This is useful for preventing certain routes from being included in automatic analysis or documentation generation.
|
|
133
|
+
|