galbe 0.1.7 → 0.1.8

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.
@@ -16,5 +16,5 @@ jobs:
16
16
  -H "Accept: application/vnd.github+json" \
17
17
  -H "Authorization: Bearer ${{ secrets.GH_WEBSITE_TOKEN }}" \
18
18
  -H "X-GitHub-Api-Version: 2022-11-28" \
19
- https://api.github.com/repos/pierre-cm/galbe-website/actions/workflows/ci.yml/dispatches \
19
+ https://api.github.com/repos/pierre-cm/galbe-website/actions/workflows/deploy.yml/dispatches \
20
20
  -d '{"ref":"main"}'
package/README.md CHANGED
@@ -12,7 +12,9 @@ Galbe is a fast, lightweight and highly customizable JavaScript web framework ba
12
12
  ## Getting started
13
13
 
14
14
  ```bash
15
- bun create galbe
15
+ bun create galbe app
16
+ cd app
17
+ bun install && bun dev
16
18
  ```
17
19
 
18
20
  ## Documentation
@@ -1,8 +1,8 @@
1
1
  # Getting started
2
2
 
3
- Galbe is a Javascript web framework to build fast and versatile backend servers with Bun.
3
+ Galbe is a Javascript web framework for building fast and versatile backend servers with Bun.
4
4
 
5
- It was designed with simplicity in mind, allowing you to quickly create and setup a project. In addition, Galbe also offers usefull features, allowing you to focus on your application logic rather than the rest.
5
+ Designed with simplicity in mind, Galbe allows you to quickly create and set up a project. In addition to its ease of use, Galbe also offers a range of useful features that help you focus on the core logic of your application.
6
6
 
7
7
  ## Requirements
8
8
 
@@ -26,12 +26,12 @@ Now you can start the dev server by running:
26
26
  bun dev
27
27
  ```
28
28
 
29
- This will start a web server on `loclahost:3000`.
29
+ This will start a web server on `localhost:3000`.
30
30
 
31
- To verify that the project was setup correctly and is running, try to reach `loclahost:3000/hello` endpoint, this should return following greeting message:
31
+ To verify that the project was setup correctly and is running, try to reach `localhost:3000/hello` endpoint, this should return following greeting message:
32
32
 
33
33
  ```bash
34
- $ curl localhost:3000/hello
34
+ curl localhost:3000/hello
35
35
  Hello from Galbe!
36
36
  ```
37
37
 
@@ -74,7 +74,7 @@ export default galbe
74
74
 
75
75
  This is the recommended way to proceed but it is not mandatory. Galbe instances also provide a `listen` method that will allow you to manually start your server instance from the code.
76
76
 
77
- > [!WARNING]
77
+ > [!WARNING]
78
78
  > In the case you decide to not rely on Galbe CLI to run/build your app, you will not have access to [Automatic Route Analyzer](routes.md#automatic-route-analyzer) feature.
79
79
 
80
80
  ### Galbe CLI
@@ -85,7 +85,7 @@ galbe <command> <argument> [options]
85
85
 
86
86
  Here are the available commands:
87
87
 
88
- #### dev
88
+ **dev**
89
89
 
90
90
  Start a dev server running your Galbe application.
91
91
 
@@ -98,7 +98,7 @@ _options_
98
98
  - `--port` or `-p`: port number (default: 3000)
99
99
  - `--watch` or `-w`: watch file changes (default: true)
100
100
 
101
- #### build
101
+ **build**
102
102
 
103
103
  Bundle your Galbe application.
104
104
 
@@ -121,25 +121,25 @@ const galbe = new Galbe(configuration)
121
121
 
122
122
  ### Properties
123
123
 
124
- #### port
124
+ **port**
125
125
 
126
126
  The port number that the server will be listening on. Default is `3000`.
127
127
 
128
- #### basePath
128
+ **basePath**
129
129
 
130
130
  The base path is added as a prefix to all the routes created.
131
131
 
132
- #### routes
132
+ **routes**
133
133
 
134
134
  A Glob Pattern or a list of Glob patterns defining the route files to be analyzed by the [Automatic Route Analyzer](routes.md#automatic-route-analyzer). Default is `src/**/*.route.{js,ts}`.
135
135
 
136
- #### plugin
136
+ **plugin**
137
137
 
138
138
  A property that can be used by plugins to add plugin's specific configuration. Every key should correspond to a [Unique Plugin Identifier](plugins.md).
139
139
 
140
140
  ### Examples
141
141
 
142
- An common way to handle server configuration is to create new file a `galbe.config.(js|ts|json)` at the root of your project directory and import it in your code. Here is an example:
142
+ A common way to handle server configuration is to create new file `galbe.config.(js|ts|json)` at the root of your project directory and import it in your code. Here is an example:
143
143
 
144
144
  galbe.config.js
145
145
 
@@ -217,7 +217,7 @@ Here are two examples of valid project structures by default:
217
217
 
218
218
  In both cases, the [Automatic Route Analyzer](routes.md#automatic-route-analyzer) will analyze `foo.route.ts` and `bar.route.ts` Route Files to find route definitions.
219
219
 
220
- You can find more info about Route Files definition under the [Routes](routes.md) section.
220
+ You can find more info about Route Files definition in the [Routes Files](routes.md#route-files) section.
221
221
 
222
- > [!NOTE]
223
- > Those are just examples that will work with the default configuration. You can of course redefine `routes` property with your own pattern(s) to fit your own project structure.
222
+ > [!NOTE]
223
+ > The examples provided above will work with the default configuration, but you can easily customize the routes property to fit your own project structure. Simply redefine the `routes` property with your own pattern(s) to to fit your own project structure.
@@ -0,0 +1 @@
1
+ # Handler
package/docs/hooks.md ADDED
@@ -0,0 +1,88 @@
1
+ # Hooks
2
+
3
+ Hooks provide a simple way to perform specific actions before and/or after reaching a specific route endpoint.
4
+
5
+ ## Hook Definition
6
+
7
+ ```ts
8
+ const hook = (context, next) => {
9
+ context.state['foo'] = 'bar'
10
+ await next()
11
+ console.log('Hook end')
12
+ }
13
+ ```
14
+
15
+ The hook takes only two arguments, a `context` object and a `next` function.
16
+
17
+ **context**
18
+
19
+ The `context` object contains the request information along with a state property that is modifiable and preserved across all hooks and the handler. It is useful for sharing information or objects across hooks and handler. You can find more information about it in the [Context]() section.
20
+
21
+ **next**
22
+
23
+ The `next` function calls the next hook in the hook list or the handler if the current hook is the last one declared. The `next` function should be called at most once. If it is omitted, Galbe will call it automatically at the end of the execution of the current hook.
24
+
25
+ > [!TIP]
26
+ > Hooks are interruptible objects, meaning they can return a response at any moment. This provides a powerful mechanism for implementing custom logic, such as authentication, authorization, caching, and more."
27
+
28
+ ## Hooks Declaration
29
+
30
+ Hooks should be declared just before the handler method in the [Route Definition]() method as a list of Hooks.
31
+
32
+ ```ts
33
+ galbe.get('foo', [ hook1, hook2, ... ], ctx => {})
34
+ ```
35
+
36
+ Hooks are called just before the [Handler]() in the order that they have been declared in the hook list of the [Route Definition](). To get a better understanding of hooks execution during the request lifecycle, you can refer to the [Lifecycle]() section.
37
+
38
+ ### Examples
39
+
40
+ Linear hooks declaration:
41
+
42
+ ```ts
43
+ const hook1 = context => {
44
+ console.log('hook1 called')
45
+ }
46
+ const hook2 = context => {
47
+ console.log('hook2 called')
48
+ }
49
+
50
+ galbe.get('example', [hook1, hook2], ctx => {
51
+ console.log('handler')
52
+ })
53
+ ```
54
+
55
+ ```bash
56
+ curl http://localhost:3000/example
57
+ hook1
58
+ hook2
59
+ handler
60
+ ```
61
+
62
+ Nested hooks declaration:
63
+
64
+ ```ts
65
+ const hook1 = (context, next) => {
66
+ console.log('hook1 start')
67
+ await next()
68
+ console.log('hook1 end')
69
+ }
70
+ const hook2 = context => {
71
+ console.log('hook2 start')
72
+ await next()
73
+ console.log('hook2 end')
74
+ }
75
+
76
+ galbe.get('example', [hook1, hook2], ctx => {
77
+ console.log('handler')
78
+ })
79
+ ```
80
+
81
+ ```bash
82
+ curl http://localhost:3000/example
83
+ hook1 start
84
+ hook2 start
85
+ handler
86
+ hook2 end
87
+ hook1 end
88
+ ```
@@ -0,0 +1 @@
1
+ # Plugins
package/docs/router.md ADDED
@@ -0,0 +1 @@
1
+ # Router
package/docs/routes.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Routes
2
2
 
3
+ Routes are the entry points for handling client requests in a Galbe application. In this section, we'll cover how to define routes, the various options available for route definitions, and how to use the Automatic Route Analyzer to simplify route setup.
4
+
3
5
  ## Route Definition
4
6
 
5
7
  Here is how to define routes in Galbe.
@@ -23,15 +25,15 @@ There are two special segments:
23
25
 
24
26
  **schema** (Schema) _Optional_
25
27
 
26
- See [Schemas](schemas) section.
28
+ See [Schemas](schemas.md) section.
27
29
 
28
30
  **hooks** (Hook[]) _Optional_
29
31
 
30
- See [Hooks](hooks) section.
32
+ See [Hooks](hooks.md) section.
31
33
 
32
34
  **handler** (Handler)
33
35
 
34
- See [Handler](handler) section.
36
+ See [Handler](handler.md) section.
35
37
 
36
38
  ### Examples
37
39
 
@@ -78,15 +80,13 @@ galbe.get(
78
80
  ## Automatic Route Analyzer
79
81
 
80
82
  > [!NOTE]
81
- > This feature is only available if you run/build the app via the [Galbe CLI](), which is the case by default if you created your app following the [Automatic Installation]() step or properly configured your package.json to do so.
82
-
83
- The Automatic Route Analyzer is in charge of analyzing all the Route Files of your project and set up the routes defintions to your Glabe server automatically.
83
+ > This feature is only available if you run/build the app via the [Galbe CLI](getting-started.md#galbe-cli), which is the case by default if you created your app following the [Automatic Installation](getting-started.md#automatic-installation) step or if you configured your package.json to do so.
84
84
 
85
- By default, the analyzer will search for route files matching paths like `'src/**/*.route.{js,ts}'`. This can be configured by modifying the value of `routes` property of your Galbe configuration. A value of `false` will disable the analyzer.
85
+ The Automatic Route Analyzer is responsible for analyzing all the Route Files of your project and setting up the route definitions for your Galbe server automatically. By default, the analyzer will search for Route Files matching paths like `src/**/*.route.{js,ts}`. This can be configured by modifying the value of `routes` property of your Galbe configuration. A value of `false` will disable the analyzer.
86
86
 
87
87
  ### Route Files
88
88
 
89
- In order to be properly analyzed, Route Files must export a default function that takes a Galbe instance as unique argument. Your routes should be defined using that Galbe instance. Here a basic js example:
89
+ In order to be properly analyzed, Route Files must export a default function that takes a Galbe instance as unique argument. Your routes should be defined using that Galbe instance. Here's a basic example in JavaScript:
90
90
 
91
91
  ```ts
92
92
  export default g => {
@@ -94,7 +94,7 @@ export default g => {
94
94
  }
95
95
  ```
96
96
 
97
- The same example using Typescript:
97
+ The same example in Typescript:
98
98
 
99
99
  ```ts
100
100
  import type { Galbe } from 'galbe'
@@ -103,7 +103,7 @@ export default (g: Galbe) => {
103
103
  }
104
104
  ```
105
105
 
106
- The Automatic Route Analyzer is also capable of collecting metadata about your Routefile and your routes by analyzing multiline comments. This can be used by some plugins to perform specific tasks. Here is an example of Routefile with multiline comments metadata.
106
+ The Automatic Route Analyzer can also collect metadata about your Route File and your routes by analyzing multiline comments. This can be used by some plugins to perform specific tasks. Here's an example of a Route File with multiline comment metadata:
107
107
 
108
108
  ```js
109
109
  /**
@@ -121,4 +121,4 @@ export default g => {
121
121
  }
122
122
  ```
123
123
 
124
- You will find more information about comment's metadata and how to use them along with examples in a plugin in the [Plugin](plugins) section.
124
+ You will find more information about comment metadata and how to use them along with examples in the [Plugin](plugins.md) section.
package/docs/schemas.md CHANGED
@@ -1,12 +1,10 @@
1
1
  # Shemas
2
2
 
3
- Galbe offers a custom Schema Type processor that provides type safety along with data parsing and validation.
4
-
5
- The prime intention of that features is to offer an easy way to manage automatically request inputs validation and error handling. Moreover, it also greatly improve developper's experience by infering static Typescript types from schema definitions.
3
+ Galbe provides a custom Schema Type processor that offers type safety, data parsing, and validation. The primary purpose of this feature is to simplify request input validation and error handling automatically. Additionally, it enhances the developer's experience by inferring static TypeScript types from schema definitions.
6
4
 
7
5
  ## Schema Types
8
6
 
9
- To get started with Schema defintion, just import `$T` from `galbe` library:
7
+ To start using Schema definitions, import `$T` from the `galbe` library:
10
8
 
11
9
  ```js
12
10
  import { $T } from 'galbe'
@@ -62,32 +60,32 @@ Schema Type matching `array` values.
62
60
  const arraySchema = $T.array($T.any(), options)
63
61
  ```
64
62
 
65
- #### Union
63
+ #### Optional
66
64
 
67
- Creates an union of Schema Types. .
65
+ Makes any type optional. This allows for `undefined` values.
68
66
 
69
67
  ```ts
70
- const unionSchema = $T.union([$T.string(), $T.number()])
68
+ const optionalSchema = $T.optional($T.string())
71
69
  ```
72
70
 
73
- #### TOptional
71
+ #### Union
74
72
 
75
- Makes any type optional. In practice, this allows for `undefined` values.
73
+ Creates an union of Schema Types.
76
74
 
77
75
  ```ts
78
- const optionalSchema = $T.optional($T.string())
76
+ const unionSchema = $T.union([$T.string(), $T.number()])
79
77
  ```
80
78
 
81
79
  ## Request Schema definition
82
80
 
83
- The Request Schema definition allows you to define a schema for your request on your [Route Definition](). It must be defined right after the [path]() of your route.
81
+ The Request Schema definition allows you to define a schema for your request on your [Route Definition](routes.md#route-defintion). It must be defined right after the path of your route.
84
82
 
85
83
  ```js
86
84
  const schema = {}
87
85
  galbe.get('/foo/:bar', schema, ctx => {})
88
86
  ```
89
87
 
90
- The Request Schema has 4 optional properties
88
+ The Request Schema has four optional properties:
91
89
 
92
90
  ### headers
93
91
 
@@ -95,7 +93,7 @@ The Request Schema has 4 optional properties
95
93
  headers: { [key: string]: STString | STBoolean | STNumber | STInteger | STLiteral }
96
94
  ```
97
95
 
98
- This is a key-value object where each key represents a request `header` name and the value the Schema associated.
96
+ This is a key-value object where each key represents a request header name, and the value is the associated Schema.
99
97
 
100
98
  **Example**:
101
99
 
@@ -113,7 +111,7 @@ const schema = {
113
111
  params: { [key: string]: STString | STBoolean | STNumber | STInteger | STLiteral }
114
112
  ```
115
113
 
116
- This is a key-value object where each key represents a request `path parameter` name and the value the Schema associated.
114
+ This is a key-value object where each key represents a request path parameter name, and the value is the associated Schema.
117
115
 
118
116
  **Example**:
119
117
 
@@ -127,7 +125,7 @@ const schema = {
127
125
  ```
128
126
 
129
127
  > [!WARNING]
130
- > Every key should match an existing [route path]() parameter. Otherwise Typescript will show you an error.
128
+ > Every key should match an existing [route path](routes.md#route-defintion) parameter. Otherwise Typescript will show an error.
131
129
  >
132
130
  > By default, if no schema is defined for a given parameter. Galbe will assume it is of type `string`.
133
131
 
@@ -137,7 +135,7 @@ const schema = {
137
135
  query: { [key: string]: STString | STBoolean | STNumber | STInteger | STLiteral }
138
136
  ```
139
137
 
140
- This is a key-value object where each key represents a request `query parameter` name and the value the Schema associated.
138
+ This is a key-value object where each key represents a request query parameter name, and the value is the associated Schema.
141
139
 
142
140
  **Example**:
143
141
 
@@ -152,13 +150,15 @@ const schema = {
152
150
 
153
151
  ### body
154
152
 
153
+ <!-- prettier-ignore -->
155
154
  ```ts
156
- body: STByteArray | STString | STBoolean | STNumber | STInteger | STLiteral | STObject | STMulripartForm | STUrlForm
155
+ body: STByteArray | STString | STBoolean | STNumber | STInteger | STLiteral |
156
+ STObject | STMulripartForm | STUrlForm
157
157
  ```
158
158
 
159
159
  #### Json
160
160
 
161
- To define an `application/json` request body. You must use `STObject` Schema Type. Example:
161
+ To define an `application/json` request body, use `STObject` Schema Type. Example:
162
162
 
163
163
  ```ts
164
164
  const jsonBody = $T.object({
@@ -169,7 +169,7 @@ const jsonBody = $T.object({
169
169
 
170
170
  #### Multipart
171
171
 
172
- To define a `multipart/form-data` request body. You must use `TMultipartForm` Schema Type. Example:
172
+ To define a `multipart/form-data` request body, use `TMultipartForm` Schema Type. Example:
173
173
 
174
174
  ```ts
175
175
  const multipartBody = $T.multipartForm({
@@ -180,7 +180,7 @@ const multipartBody = $T.multipartForm({
180
180
 
181
181
  #### Url Form
182
182
 
183
- To define an `application/x-www-form-urlencoded` request body. You must use `TUrlForm` Schema Type. Example:
183
+ To define an `application/x-www-form-urlencoded` request body, use `TUrlForm` Schema Type. Example:
184
184
 
185
185
  ```ts
186
186
  const urlBody = $T.urlForm({
@@ -191,11 +191,9 @@ const urlBody = $T.urlForm({
191
191
 
192
192
  #### Stream
193
193
 
194
- Some body request types can be streamed by using `STStream` Schema Type wrapper. Streamable Schema Types are `STByteArray`, `STString`, `STUrlForm` and `STMultipartForm`.
194
+ Some body request types can be streamed by using `STStream` Schema Type wrapper. The streamable Schema Types are `STByteArray`, `STString`, `STUrlForm` and `STMultipartForm`. This can be usefull to imporve performances in case you have heavy body payloads and you want to perform early validations on the body.
195
195
 
196
- This can be usefull to imporve performances in case you have heavy body payloads and you want to perform early validations on the body.
197
-
198
- Let's see a concrete example where that could be usefull. Imagine you want a `multipart/form-data` body request that has two properties `username` and `heavyImageFile`. In the normal case you would define something like that:
196
+ Let's look at a concrete example where this could be useful. Imagine you want a `multipart/form-data` body request that has two properties: `username` and `heavyImageFile`. In a normal case, you would define something like this:
199
197
 
200
198
  ```ts
201
199
  galbe.post(
@@ -215,11 +213,9 @@ galbe.post(
215
213
  })
216
214
  ```
217
215
 
218
- This means that in the case where the username wouldn't pass the validation, the full request body including the `heavyImageFile` would have been processed for nothing as it is not used. Inducing unnecessary time and resource consumption.
219
-
220
- The `STStream` Schema Type wrapper was created to remediate to that issue. In practice it allows you to perform validations on the fly.
216
+ This means that in the case where the username wouldn't pass the validation, the full request body, including the `heavyImageFile`, would have been processed for nothing, as it is not used. This would induce unnecessary time and resource consumption.
221
217
 
222
- Now in your handler, instead of receiving an object as ctx.body, you'll receive an [AsyncGenerator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator).
218
+ The `STStream` Schema Type wrapper was created to remediate to remediate this issue. In practice it allows you to perform validations on the fly. Now in your handler, instead of receiving an object as `ctx.body`, you will receive an [AsyncGenerator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator).
223
219
 
224
220
  ```ts
225
221
  galbe.post(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "galbe",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Fast, lightweight and highly customizable JavaScript web framework based on Bun",
5
5
  "author": "Pierre Caillaud M (https://github.com/pierre-cm)",
6
6
  "type": "module",
package/src/server.ts CHANGED
@@ -127,7 +127,11 @@ export default async (galbe: Galbe, port?: number) => {
127
127
  call: async () => {
128
128
  let nextCalled = false
129
129
  let next = async () => {
130
- await callChain[idx + 1].call()
130
+ if (nextCalled) console.error('Hook already called - ignored')
131
+ else {
132
+ nextCalled = true
133
+ await callChain[idx + 1].call()
134
+ }
131
135
  }
132
136
  await hook(context, next)
133
137
  if (!nextCalled && !handlerCalled) await next()