topbit 2.0.0 → 3.0.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.
Files changed (72) hide show
  1. package/README.cn.md +133 -176
  2. package/README.md +803 -592
  3. package/bin/new-ctl.js +7 -3
  4. package/demo/allow.js +13 -13
  5. package/demo/controller/api.js +15 -0
  6. package/demo/extends.js +5 -0
  7. package/demo/http2.js +34 -0
  8. package/demo/http2_proxy_backend.js +45 -0
  9. package/demo/http2proxy.js +48 -0
  10. package/demo/http_proxy_backend.js +44 -0
  11. package/demo/httpproxy.js +47 -0
  12. package/demo/loader.js +27 -0
  13. package/demo/log.js +1 -1
  14. package/demo/memlimit.js +1 -1
  15. package/demo/min.js +1 -1
  16. package/demo/serv.js +1 -1
  17. package/images/topbit-middleware.png +0 -0
  18. package/images/topbit.png +0 -0
  19. package/package.json +7 -6
  20. package/src/_loadExtends.js +21 -0
  21. package/src/bodyparser.js +1 -1
  22. package/src/context1.js +19 -19
  23. package/src/context2.js +11 -8
  24. package/src/ctxpool.js +1 -0
  25. package/src/extends/Http2Pool.js +365 -0
  26. package/src/extends/__randstring.js +24 -0
  27. package/src/extends/cookie.js +44 -0
  28. package/src/extends/cors.js +334 -0
  29. package/src/extends/errorlog.js +252 -0
  30. package/src/extends/http2limit.js +126 -0
  31. package/src/extends/http2proxy.js +691 -0
  32. package/src/extends/jwt.js +217 -0
  33. package/src/extends/mixlogger.js +63 -0
  34. package/src/extends/paramcheck.js +266 -0
  35. package/src/extends/proxy.js +662 -0
  36. package/src/extends/realip.js +34 -0
  37. package/src/extends/referer.js +68 -0
  38. package/src/extends/resource.js +398 -0
  39. package/src/extends/session.js +174 -0
  40. package/src/extends/setfinal.js +50 -0
  41. package/src/extends/sni.js +48 -0
  42. package/src/extends/sse.js +293 -0
  43. package/src/extends/timing.js +111 -0
  44. package/src/extends/tofile.js +123 -0
  45. package/src/http1.js +15 -16
  46. package/src/http2.js +5 -5
  47. package/src/httpc.js +3 -3
  48. package/src/lib/npargv.js +354 -0
  49. package/src/lib/zipdata.js +45 -0
  50. package/src/middleware1.js +15 -16
  51. package/src/middleware2.js +4 -9
  52. package/src/token/token.js +4 -5
  53. package/src/topbit.js +13 -11
  54. package/test/{test-helper.js → test-ext.js} +1 -1
  55. package/test/test-route.js +1 -1
  56. package/cache/allow.js +0 -130
  57. package/cache/errserv.js +0 -45
  58. package/cache/minserv.js +0 -167
  59. package/cache/router.js +0 -84
  60. package/cache/servsock.js +0 -286
  61. package/cache/sni.js +0 -66
  62. package/images/titbit-middleware.png +0 -0
  63. package/images/titbit.png +0 -0
  64. package/tmp/buff-code +0 -134
  65. package/tmp/devplan +0 -9
  66. package/tmp/evt-test.js +0 -34
  67. package/tmp/fastParseUrl.js +0 -302
  68. package/tmp/router-rule.js +0 -559
  69. package/tmp/test-cdps.js +0 -122
  70. package/tmp/titbit.js +0 -1286
  71. /package/{cache/rsa → demo/cert}/localhost-cert.pem +0 -0
  72. /package/{cache/rsa → demo/cert}/localhost-privkey.pem +0 -0
package/README.md CHANGED
@@ -1,305 +1,294 @@
1
- ![](images/titbit.png)
1
+ ![](images/topbit.png)
2
2
 
3
- # titbit
3
+ # Topbit
4
4
 
5
- **titbit** is a server-side web framework initially designed to simplify development in educational settings, but it has also been used in some production systems. It is not a heavyweight framework, but it is far from overly simplistic.
5
+ [中文文档](README.cn.md)
6
6
 
7
- > **About Type Systems and TypeScript Support**
8
- > If the ECMAScript proposal for a type system is approved, JavaScript will natively support types in the future, eliminating the need to consider TypeScript support.
9
- > If this proposal is not adopted, TypeScript support will be considered later.
10
-
11
- > Reference link: <a href="https://github.com/tc39/proposal-type-annotations" target="_blank">JavaScript Type Annotations Proposal</a>
12
-
13
- > For bugs or questions, please submit an issue or send a private message.
14
-
15
- > It is extremely fast, both in route lookup and middleware execution.
16
-
17
-
18
- A Node.js web development framework that supports both HTTP/1.1 and HTTP/2 protocols, providing a robust middleware mechanism.
7
+ Topbit is a server-side Web framework based on Node.js. It has no third-party dependencies and is optimized for extreme performance with a unique routing and middleware grouping execution mechanism.
19
8
 
20
9
  **Core Features:**
21
10
 
22
- * Request context design that abstracts interface differences.
23
- * Middleware pattern.
24
- * Route grouping and naming.
25
- * Middleware execution based on route groups, matching request methods and routes.
26
- * Support for running as a daemon using the `cluster` module.
27
- * Display of subprocess load information.
28
- * Automatic parsing of request body data.
29
- * Configurable support for HTTP/1.1 or HTTP/2 services, with a compatibility mode allowing simultaneous support for both.
30
- * Support for enabling HTTPS (required for HTTP/2 services).
31
- * Request rate limiting.
32
- * Limiting the maximum number of requests from a single IP within a time period.
33
- * IP blacklists and whitelists.
34
- * In cluster mode, automatic restart of subprocesses that exceed maximum memory limits.
35
- * Optional automatic load balancing: creates new subprocesses based on load and reverts to the initial state when idle.
36
- * Control over maximum memory usage for subprocesses, with automatic restarts when limits are exceeded or when memory usage surpasses a threshold and there are no active connections.
37
- * Default configurations for network security to mitigate DDoS attacks and other security issues at the software service layer.
38
-
39
- ## !Note
40
-
41
- Always use the latest version whenever possible. **titbit performs route lookup before creating the request context object. If no route is found, the request context object is not created.** This avoids unnecessary operations and includes detection for some erroneous or malicious requests, with error status codes 404 and 400. To customize error messages during this process, use the `notFound` and `badRequest` initialization options, which by default return simple text messages. (For routes you define, handle 404 errors internally as needed.)
42
-
43
- ## **v25.x Version Changes**
44
-
45
- Starting with v25.0.0, the request context and related details have been updated, flattening data attributes. The `res` object in the request context has been removed, and `ctx.res.body` is no longer used to collect response data; instead, `ctx.data` is used directly.
46
-
47
- Use the `ctx.send()` function to set the final response data. The code remains compatible, so no changes are required, and you can upgrade directly.
11
+ * **Request Context Design:** Shields interface differences.
12
+ * **Global Middleware Pattern.**
13
+ * **Route Grouping and Naming.**
14
+ * **Group-Based Middleware Execution:** Middleware is matched and executed based on request methods and route groups.
15
+ * **Daemon Mode:** Supports multi-process clustering and automated worker process load adjustment.
16
+ * **Load Monitoring:** Displays child process load status.
17
+ * **Body Parsing:** Parses body data by default.
18
+ * **HTTP/1.1 & HTTP/2:** Supports enabling HTTP/1.1 or HTTP/2 via configuration. Allows simultaneous support for HTTP/2 and HTTP/1.1.
19
+ * **HTTPS Support:** Supports HTTPS configuration (HTTP/2 service requires HTTPS).
20
+ * **Request Limiting:** Limits the maximum number of visits per single IP within a specific time period.
21
+ * **IP Blacklist and Whitelist.**
22
+ * **Memory Management:** In cluster mode, monitors child processes and restarts them if they exceed the maximum memory limit.
23
+ * **Auto-Scaling:** Optional automatic load mode: creates new child processes to handle requests based on load and restores the initial state when idle.
24
+ * **Security:** Default settings related to network security to avoid DDoS attacks and other network security issues at the software service level.
48
25
 
49
26
  ## Installation
50
27
 
51
28
  ```javascript
52
- npm i titbit
29
+ npm i topbit
53
30
  ```
54
31
 
55
- You can also install it using Yarn:
32
+ You can also install via yarn:
56
33
 
57
34
  ```javascript
58
- yarn add titbit
35
+ yarn add topbit
59
36
  ```
60
37
 
61
- ## Compatibility
62
-
63
- Since v21.8.1, updates have been kept compatible, allowing seamless version upgrades without compatibility concerns. If future technological changes require breaking updates, detailed explanations will be provided. Please review the documentation and Wiki.
64
-
65
- Before v21.8.1, major version numbers ensured compatibility.
66
-
67
- <a href="https://gitee.com/daoio/titbit/wikis/%E7%89%88%E6%9C%AC%E6%94%B9%E8%BF%9B%E8%AF%B4%E6%98%8E?sort_id=3220595" target="_blank">· Important Version Improvements</a>
68
-
69
38
  ## Minimal Example
70
39
 
71
40
  ```javascript
72
41
  'use strict'
73
42
 
74
- const Titbit = require('titbit')
43
+ const Topbit = require('topbit')
75
44
 
76
- const app = new Titbit({
45
+ const app = new Topbit({
77
46
  debug: true
78
47
  })
79
48
 
80
49
  app.run(1234)
81
50
  ```
82
51
 
83
- When no routes are added, titbit automatically adds a default route:
52
+ When no routes are added, Topbit adds a default route:
84
53
 
85
54
  `/*`
86
55
 
87
- Visiting this in a browser displays a simple page, intended for initial exploration and documentation access. It has no impact on actual development.
56
+ Visiting via a browser will show a very simple page. This is merely for initial understanding and documentation access; it will not affect actual development.
88
57
 
89
- ## Adding a Route
58
+ ## Add a Route
90
59
 
91
- ```javascript
60
+ ``` JavaScript
92
61
  'use strict'
93
62
 
94
- const Titbit = require('titbit')
63
+ const Topbit = require('topbit')
95
64
 
96
- const app = new Titbit({
65
+ const app = new Topbit({
97
66
  debug: true
98
67
  })
99
68
 
69
+
100
70
  app.get('/', async ctx => {
101
- ctx.send('success')
71
+ ctx.to('success')
102
72
  })
103
73
 
104
- // Listens on 0.0.0.0 by default; parameters are consistent with the native `listen` interface.
74
+ // Defaults to listening on 0.0.0.0, parameters are consistent with the native listen interface.
105
75
  app.run(1234)
76
+
106
77
  ```
107
78
 
108
- `ctx.data` holds the response data, or you can use `ctx.send(data)`.
109
- > Internally, `ctx.send()` sets the value of `ctx.data`.
110
- **It’s recommended to use `ctx.send()` to set response data, as versions prior to v25.0.0 used `ctx.res.body` for responses, and `send` ensures compatibility.**
79
+ `ctx.data` is the response data to be returned. You can also use `ctx.to(data)`.
80
+ > Actually, `ctx.to()` internally sets the value of `ctx.data`. **Using `ctx.to()` to set return data is recommended.**
111
81
 
112
- ## Using ES6 Imports
82
+ ## Using Import
113
83
 
114
- In `.mjs` files, you can use ES6 `import` syntax:
84
+ In `.mjs` files, you can use ES6 import:
115
85
 
116
86
  ```javascript
117
- import Titbit from 'titbit'
87
+ import Topbit from 'topbit'
118
88
 
119
- const app = new Titbit({
89
+ const app = new Topbit({
120
90
  debug: true
121
91
  })
122
92
 
123
93
  app.get('/', async ctx => {
124
- ctx.send('success')
94
+ ctx.to('success')
125
95
  })
126
96
 
127
97
  app.run(1234)
98
+
128
99
  ```
129
100
 
130
101
  ## Routes and Request Types
131
102
 
132
- HTTP request methods (also called request types) are specified in the HTTP start line. Supported request methods:
133
-
103
+ The HTTP start line defines the request type, also known as the Request Method. Current request methods:
134
104
  ```
135
- GET POST PUT PATCH DELETE OPTIONS TRACE HEAD
105
+ GET POST PUT PATCH DELETE OPTIONS TRACE HEAD
136
106
  ```
137
107
 
138
- The most commonly used are the first six. For each request type, the router provides a corresponding lowercase function for mounting routes. For convenience, these methods are also available directly on the `app` instance after initialization. (The framework only supports these methods.)
108
+ The first 6 are the most commonly used. For each request type, the router has a corresponding lowercase function for mounting routes. For convenience, after initializing the app, you can use the shortcut calls with the same names on the `app` instance. (The framework supports these types.)
139
109
 
140
110
  **Example:**
141
111
 
142
- ```javascript
112
+ ``` JavaScript
113
+
143
114
  'use strict'
144
115
 
145
- const Titbit = require('titbit')
116
+ const Topbit = require('titibit')
146
117
 
147
- const app = new Titbit({
118
+ const app = new Topbit({
148
119
  debug: true
149
120
  })
150
121
 
151
122
  app.get('/', async c => {
152
- c.send('success')
123
+ c.to('success')
153
124
  })
154
125
 
155
126
  app.get('/p', async c => {
156
- c.send(`${c.method} ${c.routepath}`)
127
+ c.to(`${c.method} ${c.routepath}`)
157
128
  })
158
129
 
159
130
  app.post('/', async c => {
160
- // Returns the submitted data
161
- c.send(c.body)
131
+ // Return uploaded data
132
+ c.to(c.body)
162
133
  })
163
134
 
164
135
  app.put('/p', async c => {
165
- c.send({
166
- method: c.method,
167
- body: c.body,
168
- query: c.query
136
+ c.to({
137
+ method : c.method,
138
+ body : c.body,
139
+ query : c.query
169
140
  })
170
141
  })
171
142
 
172
- // Listens on 0.0.0.0 by default; parameters are consistent with the native `listen` interface.
143
+ // Defaults to listening on 0.0.0.0
173
144
  app.run(8080)
145
+
174
146
  ```
175
147
 
176
- ## Retrieving URL Parameters
148
+ ## Get URL Parameters
177
149
 
178
- - Query string parameters (e.g., `?a=1&b=2`) are parsed into `c.query`.
179
- - Form-submitted data is parsed into `c.body`.
150
+ - Query strings in the URL (parameters like `?a=1&b=2`) are parsed into `c.query`.
151
+ - Form submitted data is parsed into `c.body`.
180
152
 
181
- > Form submissions typically have a `content-type` of `application/x-www-form-urlencoded`.
153
+ > The content-type for forms is `application/x-www-form-urlencoded`.
182
154
 
183
- ```javascript
184
- 'use strict'
155
+ ``` JavaScript
156
+ 'use strict';
185
157
 
186
- const titbit = require('titbit')
158
+ const Topbit = require('topbit');
187
159
 
188
- let app = new titbit({
189
- debug: true
160
+ let app = new Topbit({
161
+ debug: true
190
162
  })
191
163
 
192
164
  app.get('/q', async c => {
193
- // Query string parameters from the URL are parsed into `query`.
194
- // Returns JSON text with `content-type` set to `text/json`.
195
- c.send(c.query)
165
+ // Query strings after ? in URL are parsed into query.
166
+ // Returns JSON text, main difference is content-type in header is text/json
167
+ c.to(c.query)
196
168
  })
197
169
 
198
170
  app.post('/p', async c => {
199
- // Data from POST or PUT requests is stored in `body`. For forms, it is automatically parsed; otherwise, it’s raw text.
171
+ // Data submitted via POST/PUT is saved to body.
172
+ // If it's a form, it is automatically parsed; otherwise, raw text value is saved.
200
173
  // Middleware can be used to handle various data types.
201
- c.send(c.body)
174
+ c.to(c.body)
202
175
  })
203
176
 
204
177
  app.run(2019)
178
+
205
179
  ```
206
180
 
207
- ## Retrieving POST Data
181
+ ## Get POST Data
208
182
 
209
- POST and PUT requests submit data in the request body, typically from form submissions or asynchronous requests.
183
+ Requests that submit body data are POST and PUT. In front-end pages, this is usually form submission or asynchronous requests.
210
184
 
211
- - Form-submitted data is parsed into `c.body`.
185
+ - Form submitted data is parsed into `c.body`.
212
186
 
213
- > Forms have a `content-type` of `application/x-www-form-urlencoded`.
214
- > Asynchronous requests often use `content-type: application/json`.
187
+ > Form content-type is `application/x-www-form-urlencoded`.
215
188
 
216
- For both types, `c.body` is an object.
189
+ > Asynchronous request data often has a content-type of `application/json`.
217
190
 
218
- ```javascript
191
+ For both types above, `c.body` will be an object.
192
+
193
+ ``` JavaScript
219
194
  'use strict'
220
195
 
221
- const titbit = require('titbit')
196
+ const Topbit = require('topbit')
222
197
 
223
- let app = new titbit({ debug: true })
198
+ let app = new Topbit({debug: true})
224
199
 
225
200
  app.post('/p', async c => {
226
- // Data from POST or PUT requests is stored in `body`. For forms, it is automatically parsed into an object.
227
- // Middleware can be used to handle various data types.
228
- c.send(c.body)
229
- })
201
+ // POST/PUT data saved to body. Forms are auto-parsed to object.
202
+ // Middleware can be used to process various data.
203
+ c.to(c.body)
204
+ });
230
205
 
231
206
  app.run(2019)
207
+
232
208
  ```
233
209
 
234
- ## About `content-type`
210
+ ## About content-type
235
211
 
236
- **`application/x-www-form-urlencoded`**
237
- Basic form data is parsed into `c.body` as a JavaScript object.
212
+ **application/x-www-form-urlencoded**
238
213
 
239
- **`text/*`**
240
- For `content-type` starting with `text/`, such as `text/json`, the framework does not parse the data. It converts the uploaded data to a UTF-8 encoded string and assigns it to `c.body`. Further processing is left to the developer.
214
+ Basic form types are parsed into `c.body` as a JS object.
241
215
 
242
- **`multipart/form-data;boundary=xxx`**
243
- For file uploads, the framework parses the data by default, and the parsed file objects are stored in `c.files`, accessible via `c.getFile`.
216
+ **text/\***
244
217
 
245
- **`application/json`**
246
- This type is parsed using `JSON.parse`.
218
+ If content-type is `text/*` (starts with text/), such as `text/json`, the framework does not parse it. It simply converts the uploaded data to a string in utf8 format and assigns it to `c.body`. Subsequent processing is decided by the developer.
247
219
 
248
- **Other Types**
249
- For other `content-type` values, `c.body` points to `c.rawBody`, which is the raw `Buffer` data.
220
+ **multipart/form-data;boundary=xxx**
250
221
 
251
- The framework provides core support for basic types, leaving other types for developers to handle or extend. To disable default body parsing, set the `parseBody` initialization option to `false`. You can also extend body parsing as needed.
222
+ If content-type is a file upload type, it is parsed by default. The parsed file objects are placed in `c.files`, accessible via `c.getFile`.
252
223
 
253
- The body parsing module is essentially a middleware, designed to facilitate extensions and replacements.
224
+ **application/json**
254
225
 
255
- ## The `send` Function
226
+ This type will be parsed using `JSON.parse`.
256
227
 
257
- The `send` function is a wrapper for setting `c.data`. It supports an optional second parameter for the status code (default is 0, which uses the module’s default status code, 200 in Node.js HTTP and HTTP/2).
228
+ **Other types**
229
+
230
+ If content-type is any other type, `c.body` defaults to point to `c.rawBody`, which is the rawest Buffer data.
231
+
232
+ The framework provides basic core support. Other types need to be handled by the developer or via extensions.
233
+
234
+ To be easy to use while leaving enough space for developers, you can completely discard the default body parsing by setting the initialization option `parseBody` to `false`. You can also extend upon this.
235
+
236
+ The body parsing module is essentially a middleware designed to facilitate extension and replacement.
237
+
238
+ ## to Function Returning Data
239
+
240
+ The `to` function is a wrapper for `c.data`; it sets the value of `c.data`. There are two aliases: `ok` and `oo`. You can choose freely based on the scenario.
241
+
242
+ ``` JavaScript
258
243
 
259
- ```javascript
260
244
  app.get('/', async c => {
261
- c.send('success')
245
+ c.to('success')
262
246
  })
263
247
 
264
248
  app.get('/randerr', async c => {
265
249
  let n = parseInt(Math.random() * 10)
266
250
  if (n >= 5) {
267
- c.send('success')
251
+ c.ok('success')
268
252
  } else {
269
- // Returns 404 status code
253
+ // Return 404 status code
270
254
  /*
271
255
  Equivalent to:
272
256
  c.status(404).data = 'not found'
273
257
  */
274
- // Chainable calls are supported in v22.4.6 and above.
275
- c.status(404).send('not found')
258
+ // You can use chain calls in versions above v22.4.6.
259
+ c.status(404).oo('not found')
276
260
  }
277
261
  })
278
262
 
279
263
  app.run(1234)
264
+
280
265
  ```
281
266
 
282
- ## Chainable Calls
267
+ ## Chain Calls
283
268
 
284
- Starting with v22.4.6, `setHeader`, `status`, and `sendHeader` support chainable calls.
269
+ You can use chain calls for `setHeader`, `status`, and `sendHeader`.
285
270
 
286
271
  ```javascript
272
+
287
273
  app.get('/', async c => {
274
+
288
275
  c.setHeader('content-type', 'text/plain; charset=utf-8')
289
276
  .setHeader('x-server', 'nodejs server')
290
277
  .status(200)
291
- .send(`${Date.now()} Math.random()`)
278
+ .to(`${Date.now()} Math.random()}`)
279
+
292
280
  })
281
+
293
282
  ```
294
283
 
295
284
  ## Route Parameters
296
285
 
297
- ```javascript
286
+ ``` JavaScript
298
287
  app.get('/:name/:id', async c => {
299
- // Route parameters (denoted by `:`) are parsed into `c.param`.
300
- let username = c.param.name
301
- let uid = c.param.id
302
- c.send(`${username} ${uid}`)
288
+ // Use : to indicate route parameters, request params are parsed into c.param
289
+ let username = c.param.name;
290
+ let uid = c.param.id;
291
+ c.to(`${username} ${id}`)
303
292
  })
304
293
 
305
294
  app.run(8000)
@@ -307,273 +296,313 @@ app.run(8000)
307
296
 
308
297
  ## Wildcard Path Parameters
309
298
 
310
- The `*` wildcard indicates any path but must appear at the end of the route.
299
+ `*` represents any path, but it must appear at the end of the route.
300
+
301
+ ``` JavaScript
311
302
 
312
- ```javascript
313
303
  app.get('/static/*', async c => {
314
- // The wildcard path is parsed into `c.param.starPath`.
304
+ // Any path represented by * is parsed into c.param.starPath
315
305
  let spath = c.param.starPath
316
- c.send(spath)
306
+
307
+ c.to(spath)
317
308
  })
309
+
318
310
  ```
319
311
 
312
+ ----
313
+
320
314
  ## Route Lookup Rules
321
315
 
322
- Since v23.5.9, the route lookup process has been optimized, particularly for routes with parameters and wildcards, enforcing stricter order control instead of matching based on the order routes were added.
316
+ ----
323
317
 
324
- This change does not affect applications developed with earlier versions, ensuring compatibility. The stricter order reduces the likelihood of conflicts.
318
+ The route lookup process strictly controls the order of parameterized routes and routes with `*`, rather than matching based on addition order.
325
319
 
326
- **Route Lookup Strategy:**
320
+ Applications developed with previous versions are unaffected; there are no compatibility issues. Stricter ordering reduces the likelihood of conflicts.
327
321
 
328
- 1. Exact string paths.
329
- 2. Routes with parameters (fewer parameters match first).
330
- 3. Wildcard (`*`) routes, matched from longest to shortest.
322
+ Route Lookup Strategy:
323
+
324
+ 1. Ordinary string paths.
325
+ 2. Parameterized routes (routes with fewer parameters match first).
326
+ 3. Routes with `*` (matched in longest-to-shortest pattern).
331
327
 
332
328
  ```
333
329
  Example:
334
- Routes: /x/y/:id /x/y/* /x/* /x/:key/:id
330
+ Existing routes: /x/y/:id /x/y/* /x/* /x/:key/:id
331
+
332
+ /x/y/123 matches /x/y/:id first, stops matching.
333
+
334
+ /x/y/123/345 matches /x/y/* first, stops matching.
335
335
 
336
- /x/y/123 matches /x/y/:id and stops.
337
- /x/y/123/345 matches /x/y/* and stops.
338
- /x/q/123 matches /x/:key/:id.
339
- /x/a.jpg matches /x/*, as no other routes match.
340
- /x/static/images/a.jpg matches /x/*, as no other routes match.
336
+ /x/q/123 will match /x/:key/:id.
337
+
338
+ /x/a.jpg will match /x/*, other routes cannot match.
339
+
340
+ /x/static/images/a.jpg will match /x/*, other routes cannot match.
341
341
  ```
342
342
 
343
- ## Route Grouping
343
+ ----
344
+
345
+ ## Grouping Routes
344
346
 
345
- You can use `app.middleware` to specify middleware and use the returned `group` method to add grouped routes, or directly use `app.group` to add grouped routes.
347
+ You can use `app.middleware` to specify middleware and use the returned `group` method to add grouped routes, or use `app.group` directly.
346
348
 
347
- **`Titbit.prototype.middleware(mids, options=null)`**
349
+ **topbit.prototype.middleware(mids, options=null)**
348
350
 
349
- - `mids`: An array where each element is a middleware function or an array containing a middleware function and its options.
350
- - `options`: Defaults to `null`. Pass an object to apply options to all middleware, e.g., `{pre: true}`.
351
+ - `mids` is an array. Each element is a middleware function or an array where the first element is the middleware and the second is the options for adding that middleware.
352
+ - `options` defaults to `null`. Pass an object for options applying to all `mids`, e.g., `{pre: true}`.
351
353
 
352
- **`Titbit.prototype.group(group_name, callback, prefix=true)`**
354
+ **topbit.prototype.group(group_name, callback, prefix=true)**
353
355
 
354
- - `group_name`: A string representing the group name. If it’s a valid path, it also serves as a route prefix.
355
- - `callback`: A callback function that receives a parameter for adding middleware or routes using `get`, `post`, etc.
356
- - `prefix`: A boolean (default `true`) controlling whether `group_name` is used as a route prefix (only if it’s a valid route string).
356
+ - `group_name` is a string representing the route group name. If it is a valid path, it also serves as the route prefix.
357
+ - `callback` is a callback function. The parameters received by the callback can still call `middleware` and `group`, as well as `get`, `post`, etc., to add routes.
358
+ - `prefix` is a boolean, defaults to `true`. It controls whether `group_name` is added as a route prefix. However, it only acts as a prefix if `group_name` is a valid route string.
357
359
 
358
360
  ```javascript
359
361
  'use strict'
360
362
 
361
- const Titbit = require('titbit')
363
+ const Topbit = require('topbit')
362
364
 
363
- const app = new Titbit({
365
+ const app = new Topbit({
364
366
  debug: true
365
367
  })
366
368
 
367
369
  // Middleware function
368
370
  let mid_timing = async (c, next) => {
369
371
  console.time('request')
370
- await next()
372
+ await next(c)
371
373
  console.timeEnd('request')
372
374
  }
373
375
 
374
- // The group return value supports `use` and `pre` for adding middleware.
375
- // `/api` is also added as a route prefix.
376
+ // The return value of group can use 'use' and 'pre' to add middleware.
377
+ // /api is also added to the route prefix.
376
378
  app.group('/api', route => {
377
379
  route.get('/test', async c => {
378
- c.send('api test')
380
+ c.to('api test')
379
381
  })
380
382
 
381
383
  route.get('/:name', async c => {
382
- c.send(c.param)
384
+ c.to(c.param)
383
385
  })
384
386
  })
385
387
 
386
- // Add middleware to a specific group
388
+ // Add middleware to the corresponding group
387
389
  app.use(
388
390
  async (c, next) => {
389
391
  console.log(c.method, c.headers)
390
- await next()
391
- }, { group: '/sub' }
392
+ await next(c)
393
+ }, {group: '/sub'}
392
394
  ).group('/sub', route => {
393
395
  route.get('/:id', async c => {
394
- c.send(c.param.id)
396
+ c.to(c.param.id)
395
397
  })
396
398
  })
397
399
 
398
- // Test group name (not a valid route, so it wont be a prefix)
399
- app.group('test', route => {
400
+ // Test: Does not conform to route rules, so it won't be a path prefix.
401
+ app.group('Test', route => {
400
402
  route.get('/test', async c => {
401
403
  console.log(c.group, c.name)
402
- c.send('test ok')
404
+ c.to('test ok')
403
405
  }, 'test')
404
406
  })
405
407
 
406
408
  app.run(1234)
409
+
407
410
  ```
408
411
 
409
- This approach can be complex when specifying multiple middleware. The `middleware` method simplifies this, as shown below.
412
+ Specifying multiple middlewares in this way can be somewhat complex; you can use the `middleware` method. See the example below.
410
413
 
411
414
  ### Assigning Middleware to Groups and Subgroups
412
415
 
413
416
  ```javascript
414
417
  'use strict'
415
418
 
416
- const Titbit = require('titbit')
417
- const { ToFile } = require('titbit-toolkit')
419
+ const Topbit = require('topbit')
420
+ // Import ToFile extension
421
+ const {ToFile} = require('topbit-toolkit')
418
422
 
419
- const app = new Titbit({
423
+ const app = new Topbit({
420
424
  debug: true
421
425
  })
422
426
 
423
427
  // Middleware function
424
428
  let mid_timing = async (c, next) => {
425
429
  console.time('request')
426
- await next()
430
+ await next(c)
427
431
  console.timeEnd('request')
428
432
  }
429
433
 
430
434
  let sub_mid_test = async (c, next) => {
431
435
  console.log('mid test start')
432
- await next()
436
+ await next(c)
433
437
  console.log('mid test end')
434
438
  }
435
439
 
436
- // The group return value supports `use`, `pre`, and `middleware` for adding middleware.
437
- // `/api` is added as a route prefix.
438
- app.middleware([
439
- // Timing middleware runs before receiving request body data, so `pre` is set to `true`.
440
- [mid_timing, { pre: true }],
441
- // ToFile extension runs after receiving request body data, only for POST and PUT.
442
- [new ToFile(), { method: ['POST', 'PUT'] }]
443
- ]).group('/api', route => {
444
- route.get('/test', async c => {
445
- c.send('api test')
446
- })
447
-
448
- route.get('/:name', async c => {
449
- c.send(c.param)
450
- })
440
+ // group return value can use use, pre, middleware to add middleware.
441
+ // /api is also added to the route prefix.
451
442
 
452
- // Subgroup `/sub` enables `sub_mid_test` middleware and inherits parent middleware.
453
- route.middleware([sub_mid_test]).group('/sub', sub => {
454
- sub.get('/:key', async c => {
455
- c.send(c.param)
456
- })
443
+ app.middleware([
444
+ // Time recording middleware, runs before receiving body data, so set pre: true
445
+ [ mid_timing, {pre: true} ],
446
+
447
+ // ToFile extension runs after receiving body data, only for POST and PUT requests
448
+ [ new ToFile(), {method: ['POST', 'PUT']} ]
449
+ ])
450
+ .group('/api', route => {
451
+ route.get('/test', async c => {
452
+ c.to('api test')
453
+ })
454
+
455
+ route.get('/:name', async c => {
456
+ c.to(c.param)
457
+ })
458
+
459
+ // Subgroup /sub enables middleware sub_mid_test.
460
+ // Simultaneously, subgroups inherit all middleware from the upper layer.
461
+ route.middleware([sub_mid_test])
462
+ .group('/sub', sub => {
463
+ sub.get('/:key', async c => {
464
+ c.to(c.param)
465
+ })
466
+ })
457
467
  })
458
- })
459
468
 
460
469
  app.run(1234)
470
+
461
471
  ```
462
472
 
463
- Group nesting is supported but should not exceed 9 levels. Nesting beyond 3 levels is often a sign of poor design and should be reconsidered.
473
+ Groups support nested calls, but the hierarchy cannot exceed 9 levels. Usually, nesting more than 3 levels indicates a design issue and should be reconsidered.
474
+
475
+ **This feature is not as convenient and easy to use as the automatic loading mechanism of the `TopbitLoader` extension. However, in practice, requirements vary. Sometimes you have to use a single file for the service while still taking advantage of the framework's routing and middleware grouping, and also conveniently writing logic-clear, structured code. Therefore, `middleware` and `group` interfaces are convenient for handling this. Also, if you are not used to TopbitLoader's MCM pattern (Middleware - Controller - Model, similar to MVC), this method combines well with other module code.**
464
476
 
465
- **This feature is non-intrusive and does not affect existing code or conflict with `titbit-loader`.**
477
+ The function of assigning routing groups above is non-intrusive; it will not affect existing code, nor will it conflict with TopbitLoader.
466
478
 
467
- **Complex route handlers should be placed in separate modules and loaded using a unified automation function.**
479
+ **!! Complex route handling functions should be placed in separate modules and completed using a unified automated loading function.**
468
480
 
469
- Starting with v24.0.9, you can add routes using return values without passing a callback function:
481
+ It supports adding via return values, so passing a callback function is not mandatory:
470
482
 
471
483
  ```javascript
472
484
  'use strict'
473
485
 
474
- const Titbit = require('titbit')
475
- const { ToFile } = require('titbit-toolkit')
486
+ const Topbit = require('topbit')
487
+ // Import ToFile extension
488
+ const {ToFile} = require('topbit-toolkit')
476
489
 
477
- const app = new Titbit({
490
+ const app = new Topbit({
478
491
  debug: true
479
492
  })
480
493
 
481
494
  // Middleware function
482
495
  let mid_timing = async (c, next) => {
483
496
  console.time('request')
484
- await next()
497
+ await next(c)
485
498
  console.timeEnd('request')
486
499
  }
487
500
 
488
501
  let sub_mid_test = async (c, next) => {
489
502
  console.log('mid test start')
490
- await next()
503
+ await next(c)
491
504
  console.log('mid test end')
492
505
  }
493
506
 
494
507
  let route = app.middleware([
495
- // Timing middleware runs before receiving request body data, so `pre` is set to `true`.
496
- [mid_timing, { pre: true }],
497
- // ToFile extension runs after receiving request body data, only for POST and PUT.
498
- [new ToFile(), { method: ['POST', 'PUT'] }]
499
- ]).group('/api')
508
+ // Time recording middleware, pre set to true
509
+ [ mid_timing, {pre: true} ],
510
+
511
+ // ToFile extension runs after receiving body data, only for POST and PUT
512
+ [ new ToFile(), {method: ['POST', 'PUT']} ]
513
+ ])
514
+ .group('/api')
500
515
 
501
516
  route.get('/test', async c => {
502
- c.send('api test')
517
+ c.to('api test')
503
518
  })
504
519
 
505
520
  route.get('/:name', async c => {
506
- c.send(c.param)
521
+ c.to(c.param)
507
522
  })
508
523
 
509
- // Subgroup `/sub` enables `sub_mid_test` middleware and inherits parent middleware.
510
- route.middleware([sub_mid_test]).group('/sub', sub => {
511
- sub.get('/:key', async c => {
512
- c.send(c.param)
524
+ // Subgroup /sub enables middleware sub_mid_test.
525
+ // Subgroup inherits upper layer middleware.
526
+ route.middleware([sub_mid_test])
527
+ .group('/sub', sub => {
528
+ sub.get('/:key', async c => {
529
+ c.to(c.param)
530
+ })
513
531
  })
514
- })
515
532
 
516
533
  app.run(1234)
534
+
517
535
  ```
518
536
 
519
- ## File Uploads
537
+ ----
520
538
 
521
- File uploads are parsed by default. You can disable this by setting the `parseBody` option during initialization. Parsed file data is stored in `c.files`, with the structure detailed below.
539
+ ## Uploading Files
522
540
 
523
- ```javascript
541
+ Uploaded files are parsed by default. You can turn this off by passing the `parseBody` option when initializing the service (detailed in options below).
542
+ Parsed file data is stored in `c.files`. The specific structure is shown later.
543
+
544
+ ``` JavaScript
524
545
  'use strict'
525
546
 
526
- const titbit = require('titbit')
547
+ const Topbit = require('topbit')
527
548
 
528
- const app = new titbit()
549
+ const app = new Topbit()
529
550
 
530
551
  app.post('/upload', async c => {
552
+
531
553
  let f = c.getFile('image')
532
554
 
533
- // Helper function to generate a unique filename based on timestamp and file extension.
555
+ // Helper functions: makeName generates a name based on timestamp by default,
556
+ // extName parses the file extension.
557
+ // let fname = `${c.ext.makeName()}${c.ext.extName(f.filename)}`
558
+
559
+ // Generate a unique filename based on original extension + timestamp + random number.
534
560
  let fname = c.ext.makeName(f.filename)
535
561
 
536
562
  try {
537
- c.send(await c.moveFile(f, fname))
563
+ c.to(await c.moveFile(f, fname))
538
564
  } catch (err) {
539
- c.status(500).send(err.message)
565
+ c.status(500).to(err.message)
540
566
  }
541
- }, 'upload-image') // Names the route `upload-image`, accessible via `c.name`.
567
+
568
+ }, 'upload-image'); // Name the route 'upload-image', accessible in c.name.
542
569
 
543
570
  app.run(1234)
571
+
544
572
  ```
545
573
 
546
- ## `c.files` Data Structure
574
+ ## c.files Data Structure
547
575
 
548
- The structure is designed based on the HTTP protocol’s file upload data format. Since HTTP allows multiple files under the same upload name, files are parsed into an array. `c.getFile` returns the first file by default, as most cases involve a single file per upload name.
576
+ This structure is designed based on the data construction of HTTP protocol file uploads. HTTP protocol allows multiple files for the same upload name, so it parses into an array. Using `getFile` returns the first file by default, as usually, one upload name corresponds to one file.
549
577
 
550
- > For front-end developers, the upload name is the `name` attribute in the HTML form: `<input type="file" name="image">`.
551
- > Do not confuse the upload name with the filename.
578
+ > For the front-end, the upload name is the name attribute in the HTML form: `<input type="file" name="image">`.
579
+ > `image` is the upload name; do not confuse upload name with filename.
552
580
 
553
581
  ```javascript
554
582
  {
555
- image: [
583
+ image : [
556
584
  {
557
585
  'content-type': CONTENT_TYPE,
558
- // Available since v23.2.6, alias for content-type
586
+ // Available since 23.2.6, alias for content-type for easier access
559
587
  type: CONTENT_TYPE,
560
588
  filename: ORIGIN_FILENAME,
561
- start: START,
562
- end: END,
589
+ start : START,
590
+ end : END,
563
591
  length: LENGTH,
564
592
  rawHeader: HEADER_DATA,
565
593
  headers: {...}
566
594
  },
567
595
  ...
568
596
  ],
569
- video: [
597
+
598
+ video : [
570
599
  {
571
600
  'content-type': CONTENT_TYPE,
572
- // Available since v23.2.6, alias for content-type
601
+ // Available since 23.2.6, alias for content-type
573
602
  type: CONTENT_TYPE,
574
603
  filename: ORIGIN_FILENAME,
575
- start: START,
576
- end: END,
604
+ start : START,
605
+ end : END,
577
606
  length: LENGTH,
578
607
  rawHeader: HEADER_DATA,
579
608
  headers: {...}
@@ -583,690 +612,872 @@ The structure is designed based on the HTTP protocol’s file upload data format
583
612
  }
584
613
  ```
585
614
 
586
- `c.getFile(name)` retrieves file information by name, defaulting to index 0. If a negative index is provided, it returns the entire file array; if no files are found, it returns `null`.
615
+ `c.getFile` indexes by name. The default index value is 0. If a number less than 0 is passed, it gets the entire file array; returns null if not found.
587
616
 
588
- ## Body Size Limit
617
+ ## Max Body Data Limit
589
618
 
590
619
  ```javascript
591
620
  'use strict'
592
621
 
593
- const titbit = require('titbit')
622
+ const Topbit = require('topbit')
594
623
 
595
- const app = new titbit({
596
- // Sets the maximum data size for POST or PUT requests to ~20MB (in bytes).
624
+ const app = new Topbit({
625
+ // Allows POST or PUT request data max value to be approx 20MB.
626
+ // Unit is bytes.
597
627
  maxBody: 20000000
598
628
  })
599
629
 
630
+ //...
631
+
600
632
  app.run(1234)
633
+
601
634
  ```
602
635
 
603
636
  ## Middleware
604
637
 
605
- Middleware is a powerful pattern, with implementations varying slightly across languages but sharing the same essence. Middleware allows developers to organize code effectively and handle complex logic. The framework’s entire operation is based on the middleware pattern.
638
+ Middleware is a very useful pattern. Implementation varies slightly between languages, but the essence is the same. The middleware mechanism allows developers to organize code better and implement complex logic easily. In fact, the entire framework runs on a middleware pattern.
606
639
 
607
- **Middleware Diagram:**
640
+ Middleware Diagram:
608
641
 
609
642
  ![](images/middleware.jpg)
610
643
 
611
- The frameworks middleware is designed to execute based on route groups and request types, ensuring fast performance. Middleware is executed only when needed, avoiding unnecessary operations. Example:
644
+ This framework's middleware is designed to distinguish by route groups and recognize different request types to determine whether to execute or skip to the next layer. This makes it extremely fast. Multiple routes and groups have their own middleware, do not conflict, and avoid meaningless calls. Reference format:
645
+
646
+ ``` JavaScript
612
647
 
613
- ```javascript
614
648
  /*
615
- The second parameter is optional, indicating global middleware.
616
- Here, it specifies that the middleware only applies to POST requests in the `/api` group.
617
- This design ensures efficient execution without unnecessary operations.
649
+ The second parameter is optional; omitting it enables the middleware globally.
650
+ Here, the second parameter indicates: execute only for POST requests AND the route group must be /api.
651
+ This design ensures execution on demand, avoiding unnecessary operations.
618
652
  */
619
653
  app.add(async (c, next) => {
620
- console.log('before')
621
- await next()
622
- console.log('after')
623
- }, { method: 'POST', group: '/api' })
654
+ console.log('before');
655
+ await next(c);
656
+ console.log('after');
657
+ }, {method: 'POST', group: '/api'});
658
+
624
659
  ```
625
660
 
626
- Middleware added with `add` executes in reverse order of addition (standard onion model). To align with developer intuition, the `use` interface adds middleware that executes in the order it was added. Different frameworks handle execution order differently, but sequential execution is more intuitive.
661
+ Middleware added with `add` executes in reverse order of addition (LIFO), which is the standard onion model. To provide logic that is easier to understand, the `use` interface is provided. Middleware added with `use` executes in the order of addition (FIFO). Different frameworks have different logic for order implementation, but sequential execution suits developer habits better.
627
662
 
628
- **Recommendation: Use `use` to add middleware.**
663
+ **It is recommended to use `use` to add middleware:**
629
664
 
630
- ```javascript
665
+ ``` JavaScript
631
666
  // Executes first
632
667
  app.use(async (c, next) => {
633
668
  let start_time = Date.now()
634
- await next()
669
+ await next(c)
635
670
  let end_time = Date.now()
636
671
  console.log(end_time - start_time)
637
672
  })
638
673
 
639
- // Executes second
674
+ // Executes later
640
675
  app.use(async (c, next) => {
641
676
  console.log(c.method, c.path)
642
- await next()
677
+ await next(c)
643
678
  })
644
679
 
645
- // `use` supports chaining: app.use(m1).use(m2)
646
- // Available since v21.5.4, though this is less critical with `titbit-loader`.
680
+ // use can be cascaded: app.use(m1).use(m2)
681
+ // Available after v21.5.4, but this feature is not critical
682
+ // because the topbit-loader extension offers much more powerful functionality.
647
683
  ```
648
684
 
649
- ## titbit Complete Flow Diagram
685
+ ## Topbit Complete Flow Chart
650
686
 
651
- ![](images/titbit-middleware.png)
687
+ ![](images/topbit-middleware.png)
652
688
 
653
- > **Note: Internally, body data reception and parsing are also middleware, deliberately ordered and separated into `pre` and `use` interfaces.**
689
+ > **It is important to know that internally, body data reception and parsing are also middleware. The order is deliberately arranged, separating `pre` and `use` interfaces.**
654
690
 
655
691
  ## Middleware Parameters
656
692
 
657
- The `use` and `pre` interfaces support a second parameter for precise control:
693
+ Using `use` or `pre` interfaces to add middleware supports a second parameter for precise control via option properties:
658
694
 
659
- - `group`: Specifies the route group for the middleware.
660
- - `method`: Request method(s) as a string or array (must be uppercase).
661
- - `name`: Route name, restricting middleware to specific routes.
695
+ * `group`: Route group. Indicates which group to execute for.
696
+ * `method`: Request method. String or Array, must be uppercase.
697
+ * `name`: Request name. Indicates execution only for this request name.
662
698
 
663
699
  Example:
664
700
 
665
701
  ```javascript
702
+
666
703
  app.get('/xyz', async c => {
667
- // Route grouped under 'proxy'
668
- }, { group: 'proxy' })
704
+ //...
705
+ // Route group named proxy
706
+ }, {group: 'proxy'})
669
707
 
670
708
  app.use(proxy, {
671
- method: ['PUT', 'POST', 'GET', 'DELETE', 'OPTIONS'],
672
- group: 'proxy'
709
+ method : ['PUT', 'POST', 'GET', 'DELETE', 'OPTIONS'],
710
+ // Execute for requests in route group 'proxy'.
711
+ group : 'proxy'
673
712
  })
674
713
  ```
675
714
 
676
- ## `pre` Middleware (Before Body Parsing)
715
+ ## pre (Before Receiving Body Data)
677
716
 
678
- The main difference between `pre` and `use` is that `pre` middleware executes before receiving body data, useful for permission filtering. Its parameters are the same as `use`.
717
+ The main difference between middleware added via the `pre` interface and `use` is that `pre` executes before receiving body data. It can be used for permission filtering operations before receiving data. Its parameters are consistent with `use`.
679
718
 
680
- For a consistent experience, you can use `use` with the `pre` option:
719
+ For a consistent development experience, you can use the `use` interface and simply specify `pre` in the options:
681
720
 
682
721
  ```javascript
683
722
  let setbodysize = async (c, next) => {
684
- // Sets max body size to ~10KB.
685
- c.maxBody = 10000
686
- await next()
687
- }
723
+ // Set max body receive data to ~10k.
724
+ c.maxBody = 10000;
725
+ await next(c);
726
+ };
727
+
728
+ // Equivalent to app.pre(setbodysize);
729
+ app.use(setbodysize, {pre: true});
688
730
 
689
- // Equivalent to app.pre(setbodysize)
690
- app.use(setbodysize, { pre: true })
691
731
  ```
692
732
 
693
- `pre` middleware can handle complex logic and intercept requests without proceeding to the next layer. For example, the `titbit-toolkit` proxy module uses this to implement a high-performance proxy as a middleware.
733
+ Using `pre` allows for more complex processing, including intercepting and not executing the next layer. For example, the `proxy` module in the `topbit-toolkit` extension uses this feature to implement a high-performance proxy service directly as a framework middleware. Its main operation is to set the request's `data` event to receive data at this layer, handle other logic, and return directly.
694
734
 
695
- **Dynamic Body Size Limits by Request Type**
735
+ **Dynamically limiting request body size based on different request types**
696
736
 
697
- This can be achieved using `pre` middleware:
737
+ This requirement can be solved by adding middleware via `pre`:
698
738
 
699
739
  ```javascript
700
- const app = new titbit({
701
- // Default max body size ~10MB
740
+
741
+ const app = new Topbit({
742
+ // Default max body limit ~10M.
702
743
  maxBody: 10000000
703
744
  })
704
745
 
705
746
  app.pre(async (c, next) => {
747
+
706
748
  let ctype = c.headers['content-type'] || ''
707
749
 
708
750
  if (ctype.indexOf('text/') === 0) {
709
- // 50KB
751
+ // 50K
710
752
  c.maxBody = 50000
711
753
  } else if (ctype.indexOf('application/') === 0) {
712
- // 100KB
754
+ // 100K
713
755
  c.maxBody = 100000
714
756
  } else if (ctype.indexOf('multipart/form-data') < 0) {
715
- // 10KB
757
+ // 10K
716
758
  c.maxBody = 10000
717
759
  }
718
760
 
719
- await next()
720
- }, { method: ['POST', 'PUT'] })
761
+ await next(c)
762
+
763
+ }, {method: ['POST', 'PUT']})
764
+
765
+
721
766
  ```
722
767
 
723
- These parameters can make the code complex and hard to maintain, but they are powerful. For automation, use `titbit-loader`, which simplifies routing, model loading, and middleware orchestration: <a target="_blank" href="https://gitee.com/daoio/titbit-loader">titbit-loader</a>.
768
+ If these parameters appear in the file simultaneously, it can look complex and be hard to maintain, but the functionality is powerful. Therefore, leaving it to automated program completion can greatly simplify coding.
769
+
770
+ **For complete project structure setup, please use `topbit-loader`. This extension completes automatic loading of routes/models and automatic orchestration of middleware. <a target=_blank href="https://gitee.com/daoio/topbit-loader">topbit-loader</a>**
724
771
 
725
772
  ## HTTPS
726
773
 
727
774
  ```javascript
728
775
  'use strict'
729
776
 
730
- const Titbit = require('titbit')
777
+ const Topbit = require('topbit')
731
778
 
732
- // Specify paths to the certificate and key files
733
- const app = new Titbit({
734
- cert: './xxx.cert',
735
- key: './xxx.key'
779
+ // Just pass the path to the certificate and key files
780
+ const app = new Topbit({
781
+ // './xxx.pem' file also works
782
+ cert: './xxx.cert',
783
+ key: './xxx.key'
736
784
  })
737
785
 
738
786
  app.run(1234)
787
+
739
788
  ```
740
789
 
741
- ## Supporting HTTP/2 and HTTP/1.1 (Compatibility Mode)
790
+ ## Simultaneous Support for HTTP/2 and HTTP/1.1 (Compatibility Mode)
742
791
 
743
- Compatibility mode uses the ALPN protocol and requires HTTPS, so certificate and key files must be configured.
792
+ Compatibility mode utilizes the ALPN protocol and requires HTTPS, so certificates and keys must be configured.
744
793
 
745
794
  ```javascript
746
795
  'use strict'
747
796
 
748
- const Titbit = require('titbit')
797
+ const Topbit = require('topbit')
749
798
 
750
- // Specify paths to the certificate and key files
751
- const app = new Titbit({
752
- cert: './xxx.cert',
753
- key: './xxx.key',
754
- // Enable HTTP/2 and allow HTTP/1.1 compatibility
755
- http2: true,
756
- allowHTTP1: true
799
+ // Just pass the path to the certificate and key files
800
+ const app = new Topbit({
801
+ cert: './xxx.cert',
802
+ key: './xxx.key',
803
+ // Enable http2 and allow http1; compatibility mode is enabled automatically
804
+ http2: true,
805
+ allowHTTP1: true
757
806
  })
758
807
 
759
808
  app.run(1234)
809
+
760
810
  ```
761
811
 
762
812
  ## Configuration Options
763
813
 
764
- Complete configuration options for application initialization, with detailed comments:
814
+ Full configuration options for app initialization are as follows. Please read the comments carefully.
765
815
 
766
- ```javascript
767
- {
768
- // Maximum byte size for POST/PUT form submissions and file uploads.
769
- maxBody: 8000000,
816
+ ``` JavaScript
817
+ {
818
+ // This config represents max bytes for POST/PUT form submission and file uploads.
819
+ maxBody : 8000000,
770
820
 
771
- // Maximum number of files to parse.
772
- maxFiles: 12,
821
+ // Max number of files to parse
822
+ maxFiles : 12,
773
823
 
774
- daemon: false, // Enable daemon mode.
824
+ daemon : false, // Enable daemon mode
775
825
 
776
- // If set to a non-empty string in daemon mode, writes the PID to this file for service management.
777
- pidFile: '',
826
+ /*
827
+ After enabling daemon mode, if path is not empty string, pid is written to this file. Used for service management.
828
+ */
829
+ pidFile : '',
778
830
 
779
- // Enable global logging to output or save request information.
780
- globalLog: false,
831
+ // Whether to enable global logging. true enables it, outputting request info or writing to file.
832
+ globalLog: false,
781
833
 
782
- // Log output method: 'stdio' for terminal, 'file' for file.
783
- logType: 'stdio',
834
+ // Log output type: 'stdio' for terminal, 'file' for file output.
835
+ logType : 'stdio',
784
836
 
785
- // File path for successful request logs (2xx or 3xx status codes).
786
- logFile: '',
837
+ // File path for successful request logs
838
+ logFile : '',
787
839
 
788
- // File path for error request logs (4xx or 5xx status codes).
789
- errorLogFile: '',
840
+ // File path for error request logs
841
+ errorLogFile : '',
790
842
 
791
- // Maximum number of log entries per file.
792
- logMaxLines: 50000,
843
+ // Max lines per log file
844
+ logMaxLines: 50000,
793
845
 
794
- // Maximum number of historical log files.
795
- logHistory: 50,
846
+ // Max number of historical log files
847
+ logHistory: 50,
796
848
 
797
- // Custom log handling function.
798
- logHandle: null,
849
+ // Custom log handler function
850
+ logHandle: null,
799
851
 
800
- // Enable HTTPS.
801
- https: false,
852
+ // Enable HTTPS
853
+ https : false,
802
854
 
803
- http2: false,
855
+ http2 : false,
804
856
 
805
- allowHTTP1: false,
857
+ allowHTTP1: false,
806
858
 
807
- // File paths for HTTPS key and certificate. Setting these enables `https: true`.
808
- key: '',
809
- cert: '',
859
+ // HTTPS key and cert file paths. If paths are set, https is automatically set to true.
860
+ key : '',
861
+ cert : '',
810
862
 
811
- // Server options passed to `http2.createSecureServer` or `tls.createServer`.
812
- server: {
813
- handshakeTimeout: 8192, // TLS handshake timeout
814
- // sessionTimeout: 350,
815
- },
863
+ // Server options written in 'server', passed during http service initialization. Reference http2.createSecureServer, tls.createServer
864
+ server : {
865
+ handshakeTimeout: 8192, // TLS handshake timeout
866
+ //sessionTimeout: 350,
867
+ },
816
868
 
817
- // Server timeout (milliseconds). Can be overridden per request.
818
- timeout: 15000,
869
+ // Set server timeout in ms. Specific request timeouts can be set in specific requests.
870
+ timeout : 15000,
819
871
 
820
- debug: false,
872
+ debug : false,
821
873
 
822
- // Ignore trailing slashes in paths.
823
- ignoreSlash: true,
874
+ // Ignore trailing / in paths
875
+ ignoreSlash: true,
824
876
 
825
- // Enable request limiting.
826
- useLimit: false,
877
+ // Enable request limiting
878
+ useLimit: false,
827
879
 
828
- // Maximum connections (0 for unlimited).
829
- maxConn: 1024,
880
+ // Max connections, 0 means no limit
881
+ maxConn : 1024,
830
882
 
831
- // Maximum requests per IP within a time period (0 for unlimited).
832
- maxIPRequest: 0,
883
+ // Max connections per single IP within unit time, 0 means no limit
884
+ maxIPRequest: 0,
833
885
 
834
- // Time period for request limiting (default: 60 second).
835
- unitTime: 60,
886
+ // Rate limit unit time for IP, 1 means 1 second. Default 60 seconds. Range 0.1 ~ 86400.
887
+ unitTime : 60,
888
+
889
+ // Display load info, requires enabling cluster mode via daemon interface
890
+ loadMonitor : true,
836
891
 
837
- // Display load information (requires `daemon` mode).
838
- loadMonitor: true,
892
+ // Load info type: text, json, --null
893
+ // json type is for program communication, convenient for interface development
894
+ loadInfoType : 'text',
839
895
 
840
- // Load information type: 'text', 'json', or '--null'. JSON is for programmatic use.
841
- loadInfoType: 'text',
896
+ // Load info file path. If not set, outputs to terminal; otherwise saves to file.
897
+ loadInfoFile : '',
842
898
 
843
- // File path for load information (if unset, outputs to terminal).
844
- loadInfoFile: '',
899
+ // Data to return for 404
900
+ notFound: 'Not Found',
901
+
902
+ // Data to return for 400
903
+ badRequest : 'Bad Request',
845
904
 
846
- // Data for 404 responses.
847
- notFound: 'Not Found',
905
+ // Percentage parameter controlling max memory usage for child processes. Range -0.42 ~ 0.36. Base is 0.52, so default is 80%.
906
+ memFactor: 0.28,
848
907
 
849
- // Data for 400 responses.
850
- badRequest: 'Bad Request',
908
+ // Max URL length
909
+ maxUrlLength: 2048,
851
910
 
852
- // Memory usage percentage factor for subprocesses (-0.42 to 0.36; base is 0.52, default is 80%).
853
- memFactor: 0.28,
911
+ // Max number of request context cache pool.
912
+ maxpool: 4096,
854
913
 
855
- // Maximum URL length.
856
- maxUrlLength: 2048,
914
+ // Timer milliseconds for child process resource reporting.
915
+ monitorTimeSlice: 640,
857
916
 
858
- // Maximum request context cache pool size.
859
- maxpool: 4096,
917
+ // When globalLog is true, record real IP address? Mainly used in reverse proxy mode.
918
+ realIP: false,
860
919
 
861
- // Interval (milliseconds) for subprocess resource reporting.
862
- monitorTimeSlice: 640,
920
+ // Max allowed querystring parameters.
921
+ maxQuery: 25,
863
922
 
864
- // Log real IP addresses in global logs (useful in reverse proxy mode).
865
- realIP: false,
923
+ // Enable strong mode? If enabled, processes rejectionHandled and uncaughtException events,
924
+ // and captures errors: TypeError,ReferenceError,RangeError,AssertionError,URIError,Error.
925
+ strong: false,
866
926
 
867
- // Maximum number of query string parameters.
868
- maxQuery: 25,
927
+ // Fast querystring parsing. Multiple values with same name only set the first one, not parsed as array.
928
+ fastParseQuery: false,
929
+
930
+ // Whether to auto decode Query parameters using decodeURIComponent.
931
+ autoDecodeQuery: true,
869
932
 
870
- // Enable strong mode to handle `rejectionHandled` and `uncaughtException` events,
871
- // capturing errors like TypeError, ReferenceError, etc.
872
- strong: false,
933
+ // In multipart format, limit max length of a single form item.
934
+ maxFormLength: 1000000,
873
935
 
874
- // Fast query string parsing (uses only the first value for duplicate keys, not an array).
875
- fastParseQuery: false,
936
+ /* Error handler function. Unifies collection of runtime errors:
937
+ tlsClientError, server error, secureConnection error, clientError, thrown runtime errors.
938
+ errname is a string marking error info and location, format --ERR-CONNECTION--, --ERR-CLIENT--, etc.
876
939
 
877
- // Automatically decode query parameters using `decodeURIComponent`.
878
- autoDecodeQuery: true,
940
+ Usually Node.js thrown errors have code and message.
941
+ errname is optional but passed.
942
+ Pass custom function via config option to implement custom error collection/handling.
943
+ */
944
+ errorHandle: (err, errname) => {
945
+ this.config.debug && console.error(errname, err)
946
+ },
879
947
 
880
- // Maximum length for a single form item in multipart format.
881
- maxFormLength: 1000000,
948
+ // Max load rate percentage. Default 75 means if CPU usage > 75%, automatically create child process.
949
+ // Must enable auto load mode via autoWorker.
950
+ maxLoadRate: 75,
882
951
 
883
- // Error handling function for runtime errors (e.g., tlsClientError, server errors).
884
- // `errname` is a string like `--ERR-CONNECTION--` or `--ERR-CLIENT--`.
885
- errorHandle: (err, errname) => {
886
- this.config.debug && console.error(errname, err)
887
- },
952
+ // http2 protocol http2Stream timeout. If not set, -1 means consistent with timeout.
953
+ streamTimeout: -1,
888
954
 
889
- // Maximum CPU load percentage (default: 75%). Effective only with `autoWorker`.
890
- maxLoadRate: 75,
955
+ // Request timeout. This is total request time, mainly to counter malicious requests.
956
+ // e.g. Slowloris attacks where 1 byte is sent per second.
957
+ // Idle timeout won't work, allowing long-term resource occupation.
958
+ // This is a DDoS attack.
959
+ requestTimeout: 100000,
891
960
 
892
- // HTTP/2 stream timeout (-1 to match `timeout`).
893
- streamTimeout: -1,
961
+ };
962
+ // For HTTP status codes, only these two are needed here. Others don't strictly need full support,
963
+ // and you can handle them in your application implementation.
964
+ // Because once execution starts, corresponding status codes can be returned via runtime state.
965
+ // Before that, the framework prepares for the onion model execution, which is very fast.
894
966
 
895
- // Total request timeout to counter malicious requests (e.g., DDoS attacks).
896
- requestTimeout: 100000
897
- }
898
967
  ```
899
968
 
900
969
  ## Request Context
901
970
 
902
- The request context is an object encapsulating various request data, abstracting differences between HTTP/1.1 and HTTP/2 and handling Node.js version incompatibilities. For HTTP/2, the request object is a `stream`, not `IncomingMessage` and `ServerResponse` as in HTTP/1.1.
971
+ Request context is an object encapsulating various request data. This design handles differences between HTTP/1.1 and HTTP/2 protocols and incompatibilities from Node.js evolution. For design and performance, the HTTP2 module encapsulates the request object as a stream, not the `http` module's `IncomingMessage` and `ServerResponse`.
903
972
 
904
- **Request Context Properties and Descriptions**
973
+ **Request Context Properties and Description**
905
974
 
906
975
  | Property | Description |
907
- |----------|-------------|
908
- | version | Protocol version (`'1.1'` or `'2'`). |
909
- | major | Major protocol version (1, 2, or 3 for HTTP/1.1, HTTP/2, HTTP/3; 3 not yet supported). |
910
- | maxBody | Maximum request body size (bytes), defaults to `maxBody` from initialization, adjustable in middleware. |
911
- | method | Request method (e.g., `GET`, `POST`), uppercase string. |
912
- | host | Hostname from `request.headers.host`. |
913
- | protocol | Protocol string (`http` or `https`, no colon). |
914
- | path | Requested path. |
915
- | routepath | Actual route string executed. |
916
- | query | URL query parameters. |
976
+ | ---- | ---- |
977
+ | version | Protocol version, string type, '1.1' or '2'. |
978
+ | major | Major protocol version number, 1, 2, 3 (HTTP/1.1, HTTP/2, HTTP/3). |
979
+ | maxBody | Max supported request body bytes. Number. Defaults to `maxBody` init option. Can be set automatically in middleware based on request. |
980
+ | method | Request type (GET, POST, etc.). Uppercase string. |
981
+ | host | Service hostname, value of `request.headers.host`. |
982
+ | protocol | Protocol string without colon, 'https', 'http'. |
983
+ | path | Specific request path. |
984
+ | routepath | Actual executed route string. |
985
+ | query | URL passed parameters. |
917
986
  | param | Route parameters. |
918
987
  | files | Uploaded file information. |
919
- | body | Request body data (string, object, or Buffer, depending on `content-type`). |
920
- | port | Client request port. |
921
- | ip | Client IP address (socket address; check `x-real-ip` or `x-forwarded-for` for proxies). |
922
- | headers | Reference to `request.headers`. |
923
- | isUpload() | Checks if the request is a file upload (`multipart/form-data`). |
924
- | name | Route name (default: empty string). |
925
- | group | Route group (default: empty string). |
926
- | reply | HTTP/1.1: `response`; HTTP/2: `stream`. |
927
- | request | HTTP/1.1: `IncomingMessage`; HTTP/2: `stream`. |
928
- | box | Empty object for dynamically passing data to subsequent layers. |
929
- | service | Dependency injection object, points to `app.service`. |
930
- | data | Final response data (set directly or via `ctx.send`). Before v24.x, was `ctx.res.body`. |
931
- | ext | Helper functions (see Wiki). |
932
- | send(data) | Sets `ctx.data`. |
933
- | write(data) | Writes data directly to the client. |
934
- | moveFile(file, target_filepath) | Moves an uploaded file to the specified path. |
935
- | status() | Sets the status code. |
936
- | setHeader(k, v) | Sets a response header. |
937
- | removeHeader(k) | Removes a pending response header. |
938
- | getFile(name) | Retrieves uploaded file information from `files`. |
939
- | sendHeader() | Sends headers (HTTP/2 only; no-op for HTTP/1.1). |
940
- | user | Standard property for user login (default: `null`). |
941
- | json(data) | Sets response data with `content-type: application/json`. |
942
- | text(data) | Sets response data with `content-type: text/plain`. |
943
- | html(data) | Sets response data with `content-type: text/html`. |
944
- | pipe(filepath) | Streams file data (e.g., `await ctx.setHeader('content-type', 'text/html').pipe('./index.html')`). |
945
- | pipeJson(filepath) | Streams file data as JSON. |
946
- | pipeText(filepath) | Streams file data as text. |
947
- | pipeHtml(filepath) | Streams file data as HTML. |
948
-
949
- **Note:** The `send` function only sets `ctx.data`. Its equivalent to direct assignment but helps catch errors faster, as incorrect property assignments create new properties without errors, leading to incorrect responses.
988
+ | body | Body request data. Format depends on content-type (string, object, buffer). |
989
+ | port | Client request port number. |
990
+ | ip | Client request IP address (socket address). Check x-real-ip or x-forwarded-for if using proxy. |
991
+ | headers | Points to `request.headers`. |
992
+ | isUpload() | Is it a file upload request? Checks if content-type header is multipart/form-data. |
993
+ | name | Route name, defaults to empty string. |
994
+ | group | Route group, defaults to empty string. |
995
+ | res | HTTP/1.1 points to response, HTTP/2 points to stream. |
996
+ | req | HTTP/1.1 is `IncomingMessage` object, HTTP/2 points to stream object. |
997
+ | box | Defaults to empty object. Can add any property value to dynamically pass info to next layer components. |
998
+ | service | Object for dependency injection, points to `app.service`. |
999
+ | data | Saves final data to return to client. Assign to data or use `ctx.to`. Pre-v24.x was `ctx.res.body`. |
1000
+ | ext | Provides helper functions. See wiki. |
1001
+ | to(data) | Function to set `ctx.data`. |
1002
+ | write(data) | Directly write data to client. |
1003
+ | moveFile(file:object, target_filepath:string) | Function to move uploaded file to specified path. |
1004
+ | status() | Function to set status code. |
1005
+ | setHeader(k, v) | Function to set header. |
1006
+ | removeHeader(k) | Function to remove header waiting to be sent. |
1007
+ | getFile(name) | Function to get uploaded file info (reads files property). |
1008
+ | sendHeader() | Function to send headers for http2. `setHeader` only caches headers. For http/1.1, this is an empty function for code consistency. |
1009
+ | user | Standard property for user login, defaults to null. |
1010
+ | json(data) | Function, sets return data and marks type as json. |
1011
+ | text(data) | Function, sets return data and marks type as text. |
1012
+ | html(data) | Function, sets return data and marks type as html. |
1013
+ | pipe(filepath) | Function, stream response data. Example: `await ctx.setHeader('content-type', 'text/html').pipe('./index.html')` |
1014
+ | pipeJson(filepath) | Stream file data as json type. |
1015
+ | pipeText(filepath) | Stream file data as text type. |
1016
+ | pipeHtml(filepath) | Stream file data as html type. |
1017
+
1018
+ Note: `to` function only sets `ctx.data` value; data is returned at the end. It's same as direct assignment, but function calls reveal errors faster (wrong assignment adds a property without error but returns incorrect data).
950
1019
 
951
1020
  ## Dependency Injection
952
1021
 
953
- The request context includes a `service` property pointing to `app.service`. After initializing the `app`, you can attach pre-initialized data or instances to `app.service`.
1022
+ The request context has a `service` item pointing to `app.service`. After initializing the app, all data/instances needed can be mounted to `app.service`.
954
1023
 
955
- ```javascript
956
- 'use strict'
1024
+ ``` JavaScript
1025
+
1026
+ 'use strict';
957
1027
 
958
- const titbit = require('titbit')
1028
+ const Topbit = require('topbit');
959
1029
 
960
- const app = new titbit({
1030
+ let app = new Topbit({
961
1031
  debug: true
962
- })
1032
+ });
963
1033
 
964
1034
  // Overwrites if exists, adds if not.
965
- app.addService('name', 'first')
1035
+ app.addService('name', 'first');
966
1036
  app.addService('data', {
967
- id: 123,
968
- ip: '127.0.0.1'
969
- })
1037
+ id : 123,
1038
+ ip : '127.0.0.1'
1039
+ });
970
1040
 
1041
+ /*
1042
+ This might seem useless in a single file where variables are accessible.
1043
+ However, it becomes very important for module separation.
1044
+ */
971
1045
  app.get('/info', async c => {
972
- c.send({
973
- name: c.service.name,
974
- data: c.service.data
1046
+ c.to({
1047
+ name : c.service.name,
1048
+ data : c.service.data
975
1049
  })
976
1050
  })
977
1051
 
978
1052
  app.run(1234)
1053
+
979
1054
  ```
980
1055
 
981
- ## Extending the Request Context
1056
+ ## Extending Request Context
982
1057
 
983
- To extend the request context, use `app.httpServ.context`. This is the constructor for the request context.
1058
+ To add extension support to the request context object, use `app.httpServ.context`. This property is the constructor for the request context.
984
1059
 
985
1060
  **Example:**
986
1061
 
987
1062
  ```javascript
988
1063
  'use strict'
989
-
990
- const titbit = require('titbit')
991
-
992
- const app = new titbit({
993
- debug: true
1064
+ const Topbit = require('topbit')
1065
+ const app = new Topbit({
1066
+ debug: true
994
1067
  })
995
1068
 
996
- // `this` refers to the request context
1069
+ // 'this' represents the request context
997
1070
  app.httpServ.context.prototype.testCtx = function () {
998
- console.log(this.method, this.path)
1071
+ console.log(this.method, this.path)
999
1072
  }
1000
1073
 
1001
1074
  app.get('/test', async ctx => {
1002
- ctx.testCtx()
1075
+ ctx.testCtx()
1003
1076
  })
1004
1077
 
1005
1078
  app.run(1234)
1079
+
1006
1080
  ```
1007
1081
 
1008
- ## `app.isMaster` and `app.isWorker`
1082
+ ## app.isMaster and app.isWorker
1009
1083
 
1010
- Since Node.js v16.x, the `cluster` module recommends `isPrimary` over `isMaster`, though `isMaster` remains available. After initializing the `app`, `app.isMaster` and `app.isWorker` getter properties are provided, mirroring `cluster` properties to:
1084
+ Node.js v16.x recommends `isPrimary` over `isMaster`. However, `isMaster` is still available. After Topbit initialization, `app` has two getters: `isMaster` and `isWorker`. They function identically to `cluster` properties. Purposes:
1011
1085
 
1012
- - Avoid requiring `const cluster = require('cluster')`.
1013
- - Shield against future `cluster` incompatibilities.
1086
+ - Avoid writing `const cluster = require('cluster')` again.
1087
+ - Shield future incompatible changes in `cluster`, enhancing code compatibility.
1014
1088
 
1015
- ## `daemon` and `run`
1089
+ ## daemon and run
1016
1090
 
1017
- The `run` interface accepts `port` and `host` (default: `0.0.0.0`). It also supports a `sockPath` (e.g., `.sock` file), consistent with the HTTP `listen` interface, in which case `host` is ignored.
1091
+ `run` parameters: `port`, `host`. `host` defaults to `0.0.0.0`. Can also be `sockPath` (path to .sock file), supported by http listen interface. Using .sock ignores host.
1018
1092
 
1019
- The `daemon` interface shares the same first two parameters but supports a third parameter specifying the number of subprocesses. If set to 0, it defaults to the number of CPU cores. It maintains subprocess stability by creating new ones if any terminate unexpectedly.
1093
+ `daemon` first two parameters match `run`. Third parameter is a number indicating how many child processes to use. Defaults to 0, which automatically creates child processes based on CPU core count. It maintains stable child process count, creating new ones if any terminate unexpectedly.
1020
1094
 
1021
- **In cluster mode, the maximum number of subprocesses is twice the CPU core count.**
1095
+ **In cluster mode, max child processes will not exceed 2x CPU cores.**
1022
1096
 
1023
- **Examples:**
1097
+ Example:
1024
1098
 
1025
1099
  ```javascript
1026
- // Default host: 0.0.0.0, port: 1234
1100
+
1101
+ // Host defaults to 0.0.0.0, port 1234
1027
1102
  app.run(1234)
1028
1103
 
1029
- // Listen on localhost (local access only)
1104
+ // Listen on localhost, only accessible locally
1030
1105
  app.run(1234, 'localhost')
1031
1106
 
1032
- // Use 2 subprocesses, default host: 0.0.0.0
1107
+ // Use 2 child processes, host defaults to 0.0.0.0
1033
1108
  app.daemon(1234, 2)
1034
1109
 
1035
- // Use 3 subprocesses
1110
+ // Use 3 child processes
1036
1111
  app.daemon(1234, 'localhost', 3)
1112
+
1037
1113
  ```
1038
1114
 
1039
1115
  ## Logging
1040
1116
 
1041
- The framework provides global logging when using `daemon` mode (cluster). Enable it with the `globalLog` option, which supports file output or terminal output (in single-process `run` mode, logs go to the terminal but can be redirected to files).
1117
+ The framework provides global logging. When using cluster mode (`daemon` interface), use initialization option `globalLog` to enable global logs and specify a log file. In single process mode, logs output to terminal (can use output/error redirection to save to file).
1042
1118
 
1043
- **Note: Only `daemon` mode supports saving logs to files. In `run` mode, logs are output to the terminal but can be redirected.**
1119
+ **Note: Only daemon execution (cluster mode) allows saving logs to file natively. `run` (single process) outputs to screen; use IO redirection to save.**
1044
1120
 
1045
- You can use the `logHandle` option to define a custom logging function, which overrides `logFile` and `errorLogFile`.
1121
+ Besides file saving and terminal output, `logHandle` option allows custom log processing functions.
1046
1122
 
1047
- **Example:**
1123
+ **Setting `logHandle` invalidates `logFile` and `errorLogFile`. See code for details.**
1048
1124
 
1049
- ```javascript
1050
- const titbit = require('titbit')
1125
+ Example:
1126
+
1127
+ ``` JavaScript
1051
1128
 
1052
- const app = new titbit({
1129
+ const Topbit = require('topbit')
1130
+
1131
+ const app = new Topbit({
1053
1132
  debug: true,
1133
+ // Enable global log
1054
1134
  globalLog: true,
1055
- logType: 'file', // 'file' for file output, 'stdio' for terminal
1056
- logFile: '/tmp/titbit.log', // Successful requests (2xx, 3xx)
1057
- errorLogFile: '/tmp/titbit-error.log', // Error requests (4xx, 5xx)
1058
- logHandle: (w, msg) => {
1059
- // Custom log handler; overrides logFile and errorLogFile
1060
- // `msg` format: { type: '_log', success: true, log: '@ GET | https://localhost:2021/randst | 200 | 2020-10-31 20:27:7 | 127.0.0.1 | User-Agent' }
1135
+
1136
+ // Output to file. Default is 'stdio' (terminal).
1137
+ logType: 'file'
1138
+
1139
+ // Log file for status codes 2xx or 3xx
1140
+ logFile : '/tmp/topbit.log',
1141
+
1142
+ // Log file for errors (4xx or 5xx)
1143
+ errorLogFile: '/tmp/topbit-error.log',
1144
+
1145
+ // Custom handler function. logFile/errorLogFile become invalid.
1146
+ // Parameters: (worker, message)
1147
+ // worker: see cluster worker docs
1148
+ /*
1149
+ msg is log object, properties:
1150
+ {
1151
+ type : '_log',
1152
+ success : true,
1153
+ log : '@ GET | https://localhost:2021/randst | 200 | 2020-10-31 20:27:7 | 127.0.0.1 | User-Agent'
1154
+ }
1155
+ */
1156
+ logHandle : (w, msg) => {
1061
1157
  console.log(w.id, msg)
1062
1158
  }
1159
+
1063
1160
  })
1064
1161
 
1065
1162
  app.daemon(1234, 3)
1163
+
1066
1164
  ```
1067
1165
 
1068
- Middleware-based logging does not conflict with global logging but cannot capture 404 errors (no route found), as the framework returns early without creating a request context.
1166
+ Using middleware for logging does not conflict with global logging. However, middleware logging cannot capture 404s returned by the framework when no route matches (as context is not created to avoid overhead).
1167
+
1168
+ Furthermore, this method integrates better with cluster mode as it uses master-worker communication internally.
1069
1169
 
1070
1170
  ## Message Event Handling
1071
1171
 
1072
- In `daemon` mode (using `cluster`), the `setMsgEvent` function handles messages sent by subprocesses. Messages must be objects with a required `type` property indicating the event name.
1172
+ Based on the `message` event, in daemon mode (based on cluster module), a `setMsgEvent` function is provided to handle events sent by child processes.
1073
1173
 
1074
- **Example:**
1174
+ This requires the message sent by worker processes to be an object, with a mandatory `type` property indicating the event name. Other fields can be custom.
1075
1175
 
1076
- ```javascript
1077
- const titbit = require('titbit')
1176
+ Usage:
1177
+
1178
+ ``` JavaScript
1179
+
1180
+ const Topbit = require('topbit')
1078
1181
  const cluster = require('cluster')
1079
1182
 
1080
- const app = new titbit({
1183
+ const app = new Topbit({
1081
1184
  debug: true,
1082
1185
  loadInfoFile: '/tmp/loadinfo.log'
1083
1186
  })
1084
1187
 
1085
1188
  if (cluster.isMaster) {
1086
1189
  app.setMsgEvent('test-msg', (worker, msg, handle) => {
1087
- worker.send({
1088
- id: worker.id,
1089
- data: 'ok'
1190
+ // Child process receives message via message event
1191
+ worker.to({
1192
+ id : worker.id,
1193
+ data : 'ok'
1090
1194
  })
1195
+
1091
1196
  console.log(msg)
1092
1197
  })
1093
1198
  } else {
1199
+ // Receive message sent by worker.to
1094
1200
  process.on('message', msg => {
1095
1201
  console.log(msg)
1096
1202
  })
1097
1203
 
1098
- setInterval(() => {
1099
- process.send({
1100
- type: 'test-msg',
1101
- pid: process.pid,
1102
- time: new Date().toLocaleString()
1204
+ setIneterval(() => {
1205
+ process.to({
1206
+ type : 'test-msg',
1207
+ pid : process.pid,
1208
+ time : (new Date()).toLocaleString()
1103
1209
  })
1104
1210
  }, 1000)
1211
+
1105
1212
  }
1213
+
1106
1214
  ```
1107
1215
 
1108
- Since v22.4.0, the `app.send` method simplifies sending messages from workers to the master process.
1216
+ Sending messages from worker processes is complex. Since version 22.4.0, a `send` method is provided for quick message sending. It only sends to master from a worker process, so no extra worker check is needed.
1109
1217
 
1110
- ## `app.send` and `app.workerMsg`
1218
+ ## app.to and app.workerMsg
1111
1219
 
1112
- Rewriting the above example using `app.send` and `app.workerMsg`:
1220
+ Let's rewrite the worker message sending part of the code above:
1113
1221
 
1114
1222
  ```javascript
1115
- const titbit = require('titbit')
1116
1223
 
1117
- const app = new titbit({
1224
+ const Topbit = require('topbit')
1225
+
1226
+ const app = new Topbit({
1118
1227
  debug: true,
1119
1228
  loadInfoFile: '/tmp/loadinfo.log'
1120
1229
  })
1121
1230
 
1231
+ // Master process registers message event type. Worker process ignores this.
1122
1232
  app.setMsgEvent('test-msg', (worker, msg, handle) => {
1123
- worker.send({
1124
- id: worker.id,
1125
- data: 'ok'
1233
+ // Child process receives message via message event
1234
+ worker.to({
1235
+ id : worker.id,
1236
+ data : 'ok'
1126
1237
  })
1238
+
1127
1239
  console.log(msg)
1128
1240
  })
1129
1241
 
1242
+ // Only worker process listens.
1130
1243
  app.workerMsg(msg => {
1131
1244
  console.log(msg)
1132
1245
  })
1133
1246
 
1134
- cluster.isWorker &&
1135
- setInterval(() => {
1136
- app.send('test-msg', {
1137
- pid: process.pid,
1138
- time: new Date().toLocaleString()
1139
- })
1140
- }, 1000)
1247
+ cluster.isWorker
1248
+ &&
1249
+ setInterval(() => {
1250
+ // Only worker executes.
1251
+ app.to('test-msg', {
1252
+ pid: process.pid,
1253
+ time: (new Date).toLocaleString()
1254
+ })
1255
+
1256
+ }, 1000)
1141
1257
 
1142
1258
  app.daemon(1234, 2)
1259
+
1143
1260
  ```
1144
1261
 
1145
- ## Automatic Subprocess Adjustment
1262
+ ## Automatically Adjusting Child Process Quantity
1146
1263
 
1147
- The `daemon` interface sets the base number of subprocesses:
1264
+ The parameter passed to `daemon` sets the base number of child processes, e.g.:
1148
1265
 
1149
- ```javascript
1150
- // Use 2 subprocesses
1266
+ ``` JavaScript
1267
+
1268
+ // Use 2 child processes to handle requests.
1151
1269
  app.daemon(1234, 2)
1270
+
1152
1271
  ```
1153
1272
 
1154
- To automatically adjust subprocesses based on load, use `autoWorker` to set a maximum number of subprocesses (must be greater than the base number):
1273
+ To automatically create child processes based on load and terminate them when idle (maintaining base quantity), use `autoWorker` to set a maximum value. This value must be larger than the base quantity to take effect.
1155
1274
 
1156
1275
  ```javascript
1157
- // Maximum 9 subprocesses
1276
+
1277
+ // Max 9 child processes.
1158
1278
  app.autoWorker(9)
1159
1279
 
1280
+ //...
1281
+
1160
1282
  app.daemon(1234, 2)
1283
+
1161
1284
  ```
1162
1285
 
1163
- When load is high, new subprocesses are created. When idle, subprocesses with zero connections are terminated to revert to the base number.
1286
+ When load is high, child processes are created. After a period of idleness, processes with 0 connections are terminated, restoring the base number.
1287
+
1288
+ **Available in v21.9.6+. Please use the latest version as this feature has been improved for stability and performance in subsequent versions.**
1164
1289
 
1165
- **Available since v21.9.6. Use the latest version for improved stability and performance.**
1290
+ ----
1166
1291
 
1167
1292
  ## Strong Mode
1168
1293
 
1169
- Enable `strong` mode to handle `uncaughtException` and `unhandledRejection` events, ensuring program stability. Simply set `strong: true`.
1294
+ Enable strong mode via the `strong` option. This monitors `uncaughtException` and `unhandledRejection` events to ensure stable operation. Simplest usage: set `strong` to `true`.
1170
1295
 
1171
- **All `strong` mode features can be implemented manually using the `process` module; this just simplifies the process.**
1296
+ **All strong mode features can be implemented via the `process` module; this just simplifies it.**
1172
1297
 
1173
1298
  ```javascript
1174
- 'use strict'
1299
+ 'use strict';
1175
1300
 
1176
- const titbit = require('titbit')
1301
+ const Topbit = require('topbit');
1177
1302
 
1178
1303
  setTimeout(() => {
1179
- throw new Error('test error')
1180
- }, 2000)
1304
+ // Throw exception inside timer loop
1305
+ throw new Error(`test error`)
1306
+ }, 2000);
1181
1307
 
1182
- const app = new titbit({
1183
- debug: true,
1184
- strong: true
1185
- })
1308
+ const app = new Topbit({
1309
+ // Debug mode, output errors.
1310
+ debug: true,
1311
+ // Enable strong mode
1312
+ strong: true
1313
+ });
1314
+
1315
+ app.run(1234);
1186
1316
 
1187
- app.run(1234)
1188
1317
  ```
1189
1318
 
1190
- By default, `strong` mode catches:
1319
+ By default, strong mode captures:
1191
1320
 
1192
1321
  ```
1193
- TypeError, ReferenceError, RangeError, AssertionError, URIError, Error
1322
+ 'TypeError', 'ReferenceError', 'RangeError', 'AssertionError', 'URIError', 'Error'
1194
1323
  ```
1195
1324
 
1196
- Customize handling with an object:
1325
+ You can customize handling by passing an object to `strong`.
1197
1326
 
1198
1327
  ```javascript
1199
- const app = new titbit({
1200
- debug: true,
1201
- strong: {
1202
- quiet: true, // Suppress error output
1203
- errorHandle: (err, errname) => {
1204
- // Custom error handling
1205
- },
1206
- catchErrors: ['TypeError', 'URIError', 'Error', 'RangeError']
1207
- }
1208
- })
1328
+
1329
+ // Core code example
1330
+ const app = new Topbit({
1331
+ // Debug mode, output errors.
1332
+ debug: true,
1333
+ // Enable strong mode
1334
+ strong: {
1335
+ // Silent behavior, no error output.
1336
+ quiet: true,
1337
+ // Custom error handler
1338
+ errorHandle: (err, errname) => {
1339
+ //....
1340
+ },
1341
+
1342
+ // Which errors to catch
1343
+ catchErrors: [
1344
+ 'TypeError', 'URIError', 'Error', 'RangeError'
1345
+ ]
1346
+
1347
+ }
1348
+ });
1349
+
1209
1350
  ```
1210
1351
 
1211
- ## Running HTTP and HTTPS Simultaneously?
1352
+ ## Simultaneous HTTP and HTTPS?
1212
1353
 
1213
- **This is not recommended in production.** If HTTPS is enabled, HTTP is unnecessary, and some front-end features require HTTPS.
1354
+ Note the question mark. You shouldn't do this in production. If HTTPS is enabled, HTTP isn't needed, and some frontend features won't work without HTTPS.
1214
1355
 
1215
- For testing, you can do:
1356
+ If needed (perhaps for testing), do this:
1216
1357
 
1217
1358
  ```javascript
1218
1359
  'use strict'
1219
1360
 
1220
- const Titbit = require('titbit')
1361
+ const Topbit = require('topbit')
1221
1362
  const http = require('node:http')
1222
- const https = require('node:https')
1363
+ const https = require('https')
1223
1364
 
1224
- const app = new Titbit({
1225
- debug: true
1365
+ const app = new Topbit({
1366
+ // Enable debug
1367
+ debug: true,
1226
1368
  })
1227
1369
 
1370
+ // Below are http/1.1 services. For http2, enable http2 and compatibility mode.
1371
+ // Or use topbit-httpc extension.
1372
+
1373
+ // In this case, you must set event listeners manually.
1374
+
1228
1375
  let http_server = http.createServer(app.httpServ.onRequest())
1229
1376
  let https_server = https.createServer(app.httpServ.onRequest())
1230
1377
 
1231
1378
  http_server.listen(2025)
1232
1379
  https_server.listen(2026)
1380
+
1381
+ ```
1382
+
1383
+ **Note: You cannot support http2 in this scenario, but you can use http2 to be compatible with http1.**
1384
+
1385
+ ## Request Limiting
1386
+
1387
+ The framework provides IP-based rate limiting to prevent dense requests from the same IP. For HTTP/2, use `http2limit` module from `topbit-toolkit`.
1388
+
1389
+ ```javascript
1390
+ 'use strict';
1391
+
1392
+ const Topbit = require('topbit')
1393
+
1394
+ const app = new Topbit({
1395
+ debug : true,
1396
+ // Enable request limiting
1397
+ useLimit: true,
1398
+
1399
+ // IPs allowed without frequency limit
1400
+ allow: new Set(['127.0.0.1']),
1401
+
1402
+ // IPs to deny
1403
+ deny: (ip) => {
1404
+ // Example only, supports function and Set
1405
+ if (ip.indexOf('1.') === 0) return false
1406
+ return true
1407
+ },
1408
+
1409
+ // Max requests per IP within unit time
1410
+ maxIPRequest: 6,
1411
+
1412
+ // Unit time, 15 seconds
1413
+ unitTime: 15,
1414
+
1415
+ // Max concurrent connections per worker process
1416
+ maxConn: 2000,
1417
+
1418
+ loadMonitor: true,
1419
+ loadInfoType : 'text',
1420
+ globalLog : true,
1421
+ logType: 'stdio',
1422
+ // Load info in memory
1423
+ loadInfoFile : '--mem'
1424
+ })
1425
+
1426
+
1427
+ app.get('/', async ctx => {
1428
+ ctx.to('ok')
1429
+ })
1430
+
1431
+ // Use 3 worker processes. Each allows 6 requests/unit time.
1432
+ // Total approx 6 requests? Set maxIPRequest to 2.
1433
+ app.daemon(1234, '0.0.0.0', 3)
1434
+
1233
1435
  ```
1234
1436
 
1235
- **Note: This setup does not support HTTP/2. Use HTTP/2 with `allowHTTP1` for compatibility.**
1437
+ ## Other
1438
+
1439
+ - After running, Topbit has a final wrapper middleware. Setting `c.data` returns data. It detects simple text types and sets content-type automatically (text/plain, text/html, application/json) if not set.
1236
1440
 
1237
- ## Miscellaneous
1441
+ - Defaults to limiting max URL length and sets a max memory usage rate based on hardware.
1238
1442
 
1239
- - A final middleware handles responses, automatically setting `content-type` (e.g., `text/plain`, `text/html`, `application/json`) if not set.
1240
- - Default limits on URL length and memory usage are based on hardware.
1241
- - Configurations and middleware allow for extension and overrides.
1242
- - The framework is optimized for speed. For performance comparisons, test with multiple middleware and hundreds of routes.
1243
- - The `sched` function sets cluster scheduling policy (`'rr'` or `'none'`), equivalent to `cluster.schedulingPolicy`.
1443
+ - All of this can be extended/overridden via config options or middleware.
1244
1444
 
1245
- The framework auto-detects memory size and sets limits, adjustable via the `secure` object in `daemon` mode:
1445
+ - It is fast, and we focus on optimization. If comparing, add multiple middleware and hundreds of routes to test.
1446
+
1447
+ - Provides a `sched` function to quickly set cluster scheduling policy ('rr' or 'none'). Sets `cluster.schedulingPolicy`.
1448
+
1449
+ The framework automatically detects memory size and sets limits on initialization. You can change limits via properties in `secure` after init, but this requires using `daemon` (master managing workers).
1246
1450
 
1247
1451
  ```javascript
1248
1452
  'use strict'
1249
1453
 
1250
- const Titbit = require('titbit')
1454
+ const Topbit = require('topbit');
1251
1455
 
1252
- let app = new Titbit()
1456
+ let app = new Topbit();
1253
1457
 
1254
- // Max memory 600MB, restarts only when connections are 0.
1255
- app.secure.maxmem = 600_000_000
1458
+ /*
1459
+ Operations below can be controlled via memFactor option. See config options above.
1460
+ */
1256
1461
 
1257
- // Hard limit 900MB; restarts if exceeded, even with active connections.
1258
- app.secure.diemem = 900_000_000
1462
+ // Max memory 600M, auto restart only when connection count is 0.
1463
+ app.secure.maxmem = 600_000_000;
1259
1464
 
1260
- // Max RSS memory 800MB (excludes Buffer allocations).
1261
- app.secure.maxrss = 800_000_000
1465
+ // Mandatory restart max memory limit 900M. Total memory usage including Buffers.
1466
+ // Must be larger than maxmem. If memory > maxmem AND connections != 0,
1467
+ // AND continues to exceed diemem, process restarts immediately.
1468
+ app.secure.diemem = 900_000_000;
1469
+
1470
+ // Max RSS memory 800M. Program memory usage, excluding Buffer allocations.
1471
+ app.secure.maxrss = 800_000_000;
1262
1472
 
1263
1473
  app.get('/', async c => {
1264
- c.send('ok')
1474
+ c.to('ok');
1265
1475
  })
1266
1476
 
1267
- app.daemon(8008, 2)
1477
+ app.daemon(8008, 2);
1478
+
1268
1479
  ```
1269
1480
 
1270
- **Requires `loadMonitor: true` (default unless set to `false`).**
1481
+ **Note: Requires `loadMonitor` option (enabled by default unless set to false).**
1271
1482
 
1272
- Use default configurations unless specific control is needed.
1483
+ Service initialization automatically sets configuration based on available system memory. Unless necessary, stick to default configurations.