wok-server 0.5.0 → 0.6.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 (372) hide show
  1. package/README.en.md +61 -0
  2. package/README.md +44 -29
  3. package/dist/cache/cache.js +98 -98
  4. package/dist/cache/config.js +19 -19
  5. package/dist/cache/index.js +27 -27
  6. package/dist/cache/purge-task.js +46 -46
  7. package/dist/cache/stat.js +47 -47
  8. package/dist/config/convert.js +36 -36
  9. package/dist/config/exception.js +14 -14
  10. package/dist/config/index.js +81 -81
  11. package/dist/http-client/index.js +136 -136
  12. package/dist/i18n/ar.js +17 -17
  13. package/dist/i18n/de.js +17 -17
  14. package/dist/i18n/en-us.js +17 -17
  15. package/dist/i18n/es.js +17 -17
  16. package/dist/i18n/fr.js +17 -17
  17. package/dist/i18n/i18n.js +231 -231
  18. package/dist/i18n/index.js +52 -52
  19. package/dist/i18n/ja.js +17 -17
  20. package/dist/i18n/ko.js +17 -17
  21. package/dist/i18n/msg.js +2 -2
  22. package/dist/i18n/pt.js +17 -17
  23. package/dist/i18n/ru.js +17 -17
  24. package/dist/i18n/tag.js +18 -18
  25. package/dist/i18n/zh-HK.js +17 -17
  26. package/dist/i18n/zh-TW.js +17 -17
  27. package/dist/i18n/zh-cn.js +17 -17
  28. package/dist/index.js +14 -14
  29. package/dist/lock/index.js +114 -114
  30. package/dist/log/config.js +35 -35
  31. package/dist/log/date.js +21 -21
  32. package/dist/log/file.js +198 -198
  33. package/dist/log/index.js +135 -135
  34. package/dist/log/level.js +33 -33
  35. package/dist/log/log.js +56 -56
  36. package/dist/log/store.js +19 -19
  37. package/dist/mongodb/collection.js +2 -2
  38. package/dist/mongodb/config.js +34 -34
  39. package/dist/mongodb/doc.js +2 -2
  40. package/dist/mongodb/exception.js +14 -14
  41. package/dist/mongodb/index.js +58 -58
  42. package/dist/mongodb/manager/base.js +563 -563
  43. package/dist/mongodb/manager/index.js +63 -63
  44. package/dist/mongodb/manager/tx-strict.js +84 -84
  45. package/dist/mongodb/manager/tx.js +30 -30
  46. package/dist/mongodb/migration.js +52 -52
  47. package/dist/mvc/access-log.js +33 -33
  48. package/dist/mvc/config.js +27 -27
  49. package/dist/mvc/exchange.js +113 -113
  50. package/dist/mvc/handler/index.js +7 -6
  51. package/dist/mvc/handler/json.js +65 -65
  52. package/dist/mvc/handler/restful.js +35 -35
  53. package/dist/mvc/handler/sse.js +65 -0
  54. package/dist/mvc/handler/upload.js +31 -31
  55. package/dist/mvc/index.js +50 -50
  56. package/dist/mvc/interceptor.js +2 -2
  57. package/dist/mvc/query.js +43 -43
  58. package/dist/mvc/render/file.js +132 -132
  59. package/dist/mvc/render/html/html.js +90 -90
  60. package/dist/mvc/render/html/index.js +18 -18
  61. package/dist/mvc/render/html/style.js +2 -2
  62. package/dist/mvc/render/index.js +7 -7
  63. package/dist/mvc/render/json.js +26 -26
  64. package/dist/mvc/render/text.js +16 -16
  65. package/dist/mvc/router.js +2 -2
  66. package/dist/mvc/server.js +272 -272
  67. package/dist/mvc/static/header.js +67 -67
  68. package/dist/mvc/static/index.js +6 -6
  69. package/dist/mvc/static/mime-type.js +84 -84
  70. package/dist/mvc/static/server-cache-config.js +66 -66
  71. package/dist/mvc/static/server-cache.js +133 -133
  72. package/dist/mvc/static/static-handler.js +372 -372
  73. package/dist/mysql/config.js +51 -51
  74. package/dist/mysql/exception.js +14 -14
  75. package/dist/mysql/index.js +87 -87
  76. package/dist/mysql/manager/base.js +239 -239
  77. package/dist/mysql/manager/index.js +107 -107
  78. package/dist/mysql/manager/ops/count.js +20 -20
  79. package/dist/mysql/manager/ops/criteria.js +356 -356
  80. package/dist/mysql/manager/ops/delete.js +65 -65
  81. package/dist/mysql/manager/ops/exist.js +26 -26
  82. package/dist/mysql/manager/ops/find.js +169 -169
  83. package/dist/mysql/manager/ops/index.js +14 -14
  84. package/dist/mysql/manager/ops/insert.js +106 -106
  85. package/dist/mysql/manager/ops/modify.js +10 -10
  86. package/dist/mysql/manager/ops/paginate.js +23 -23
  87. package/dist/mysql/manager/ops/query.js +9 -9
  88. package/dist/mysql/manager/ops/update.js +216 -216
  89. package/dist/mysql/manager/ops/utils.js +24 -24
  90. package/dist/mysql/manager/tx-strict.js +103 -103
  91. package/dist/mysql/manager/tx.js +30 -30
  92. package/dist/mysql/manager/utils.js +56 -56
  93. package/dist/mysql/migration.js +136 -136
  94. package/dist/mysql/table-info.js +8 -8
  95. package/dist/task/daily.js +59 -59
  96. package/dist/task/fixed-delay.js +38 -38
  97. package/dist/task/fixed-rate.js +42 -42
  98. package/dist/task/index.js +9 -9
  99. package/dist/task/task.js +56 -56
  100. package/dist/validation/exception.js +36 -36
  101. package/dist/validation/index.js +40 -40
  102. package/dist/validation/validator/array.js +34 -34
  103. package/dist/validation/validator/enum.js +28 -28
  104. package/dist/validation/validator/index.js +14 -14
  105. package/dist/validation/validator/length.js +40 -40
  106. package/dist/validation/validator/max-length.js +35 -35
  107. package/dist/validation/validator/max.js +29 -29
  108. package/dist/validation/validator/min-length.js +33 -33
  109. package/dist/validation/validator/min.js +29 -29
  110. package/dist/validation/validator/not-blank.js +33 -33
  111. package/dist/validation/validator/not-null.js +21 -21
  112. package/dist/validation/validator/plain-obj.js +32 -32
  113. package/dist/validation/validator/regexp.js +34 -34
  114. package/documentation/en/cache.md +56 -0
  115. package/documentation/en/config.md +96 -0
  116. package/documentation/en/engineering.md +256 -0
  117. package/documentation/en/http-client.md +32 -0
  118. package/documentation/en/i18n.md +143 -0
  119. package/documentation/en/index.md +24 -0
  120. package/documentation/en/lock.md +51 -0
  121. package/documentation/en/log.md +109 -0
  122. package/documentation/en/mongodb.md +256 -0
  123. package/documentation/en/mvc.md +688 -0
  124. package/documentation/en/mysql.md +552 -0
  125. package/documentation/en/task.md +45 -0
  126. package/documentation/en/test.md +56 -0
  127. package/documentation/en/validate.md +130 -0
  128. package/documentation/zh-cn/mvc.md +66 -24
  129. package/package.json +3 -1
  130. package/skills/wok-server-api-rules/SKILL.md +350 -0
  131. package/skills/wok-server-cache/SKILL.md +216 -0
  132. package/skills/wok-server-config/SKILL.md +200 -0
  133. package/skills/wok-server-getting-started/SKILL.md +123 -0
  134. package/skills/wok-server-getting-started/references/engineering.md +169 -0
  135. package/skills/wok-server-http-client/SKILL.md +164 -0
  136. package/skills/wok-server-i18n/SKILL.md +214 -0
  137. package/skills/wok-server-lock/SKILL.md +144 -0
  138. package/skills/wok-server-log/SKILL.md +218 -0
  139. package/skills/wok-server-mongodb/SKILL.md +235 -0
  140. package/skills/wok-server-mvc/SKILL.md +251 -0
  141. package/skills/wok-server-mvc/references/respond-html.md +157 -0
  142. package/skills/wok-server-mvc/references/sse.md +121 -0
  143. package/skills/wok-server-mvc/references/static-files.md +47 -0
  144. package/skills/wok-server-mvc/references/upload.md +62 -0
  145. package/skills/wok-server-mvc/references/websocket.md +30 -0
  146. package/skills/wok-server-mysql/SKILL.md +315 -0
  147. package/skills/wok-server-mysql/references/multi-datasource.md +76 -0
  148. package/skills/wok-server-mysql/references/version-control.md +22 -0
  149. package/skills/wok-server-task/SKILL.md +158 -0
  150. package/skills/wok-server-validate/SKILL.md +167 -0
  151. package/src/cache/cache.ts +118 -0
  152. package/src/cache/config.ts +53 -0
  153. package/src/cache/index.ts +27 -0
  154. package/src/cache/purge-task.ts +53 -0
  155. package/src/cache/stat.ts +47 -0
  156. package/src/config/convert.ts +27 -0
  157. package/src/config/exception.ts +8 -0
  158. package/src/config/index.ts +92 -0
  159. package/src/http-client/index.ts +202 -0
  160. package/src/i18n/ar.ts +16 -0
  161. package/src/i18n/de.ts +16 -0
  162. package/src/i18n/en-us.ts +16 -0
  163. package/src/i18n/es.ts +16 -0
  164. package/src/i18n/fr.ts +16 -0
  165. package/src/i18n/i18n.ts +230 -0
  166. package/src/i18n/index.ts +50 -0
  167. package/src/i18n/ja.ts +16 -0
  168. package/src/i18n/ko.ts +16 -0
  169. package/src/i18n/msg.ts +50 -0
  170. package/src/i18n/pt.ts +16 -0
  171. package/src/i18n/ru.ts +16 -0
  172. package/src/i18n/tag.ts +18 -0
  173. package/src/i18n/zh-HK.ts +16 -0
  174. package/src/i18n/zh-TW.ts +16 -0
  175. package/src/i18n/zh-cn.ts +16 -0
  176. package/src/index.ts +11 -0
  177. package/src/lock/index.ts +164 -0
  178. package/src/log/config.ts +71 -0
  179. package/src/log/date.ts +19 -0
  180. package/src/log/file.ts +215 -0
  181. package/src/log/index.ts +136 -0
  182. package/src/log/level.ts +29 -0
  183. package/src/log/log.ts +77 -0
  184. package/src/log/store.ts +31 -0
  185. package/src/mongodb/collection.ts +25 -0
  186. package/src/mongodb/config.ts +69 -0
  187. package/src/mongodb/doc.ts +12 -0
  188. package/src/mongodb/exception.ts +8 -0
  189. package/src/mongodb/index.ts +71 -0
  190. package/src/mongodb/manager/base.ts +674 -0
  191. package/src/mongodb/manager/index.ts +80 -0
  192. package/src/mongodb/manager/tx-strict.ts +153 -0
  193. package/src/mongodb/manager/tx.ts +34 -0
  194. package/src/mongodb/migration.ts +66 -0
  195. package/src/mvc/access-log.ts +33 -0
  196. package/src/mvc/config.ts +70 -0
  197. package/src/mvc/exchange.ts +126 -0
  198. package/src/mvc/handler/index.ts +4 -0
  199. package/src/mvc/handler/json.ts +96 -0
  200. package/src/mvc/handler/restful.ts +39 -0
  201. package/src/mvc/handler/sse.ts +90 -0
  202. package/src/mvc/handler/upload.ts +54 -0
  203. package/src/mvc/index.ts +48 -0
  204. package/src/mvc/interceptor.ts +12 -0
  205. package/src/mvc/query.ts +36 -0
  206. package/src/mvc/render/file.ts +148 -0
  207. package/src/mvc/render/html/html.ts +187 -0
  208. package/src/mvc/render/html/index.ts +16 -0
  209. package/src/mvc/render/html/style.ts +1201 -0
  210. package/src/mvc/render/index.ts +4 -0
  211. package/src/mvc/render/json.ts +24 -0
  212. package/src/mvc/render/text.ts +14 -0
  213. package/src/mvc/router.ts +13 -0
  214. package/src/mvc/server.ts +315 -0
  215. package/src/mvc/static/header.ts +86 -0
  216. package/src/mvc/static/index.ts +3 -0
  217. package/src/mvc/static/mime-type.ts +81 -0
  218. package/src/mvc/static/server-cache-config.ts +92 -0
  219. package/src/mvc/static/server-cache.ts +171 -0
  220. package/src/mvc/static/static-handler.ts +445 -0
  221. package/src/mysql/config.ts +130 -0
  222. package/src/mysql/exception.ts +8 -0
  223. package/src/mysql/index.ts +88 -0
  224. package/src/mysql/manager/base.ts +285 -0
  225. package/src/mysql/manager/index.ts +112 -0
  226. package/src/mysql/manager/ops/count.ts +30 -0
  227. package/src/mysql/manager/ops/criteria.ts +412 -0
  228. package/src/mysql/manager/ops/delete.ts +96 -0
  229. package/src/mysql/manager/ops/exist.ts +41 -0
  230. package/src/mysql/manager/ops/find.ts +226 -0
  231. package/src/mysql/manager/ops/index.ts +11 -0
  232. package/src/mysql/manager/ops/insert.ts +120 -0
  233. package/src/mysql/manager/ops/modify.ts +14 -0
  234. package/src/mysql/manager/ops/paginate.ts +60 -0
  235. package/src/mysql/manager/ops/query.ts +13 -0
  236. package/src/mysql/manager/ops/update.ts +294 -0
  237. package/src/mysql/manager/ops/utils.ts +20 -0
  238. package/src/mysql/manager/tx-strict.ts +138 -0
  239. package/src/mysql/manager/tx.ts +31 -0
  240. package/src/mysql/manager/utils.ts +75 -0
  241. package/src/mysql/migration.ts +149 -0
  242. package/src/mysql/table-info.ts +41 -0
  243. package/src/task/daily.ts +70 -0
  244. package/src/task/fixed-delay.ts +45 -0
  245. package/src/task/fixed-rate.ts +49 -0
  246. package/src/task/index.ts +4 -0
  247. package/src/task/task.ts +70 -0
  248. package/src/validation/exception.ts +27 -0
  249. package/src/validation/index.ts +61 -0
  250. package/src/validation/validator/array.ts +32 -0
  251. package/src/validation/validator/enum.ts +25 -0
  252. package/src/validation/validator/index.ts +11 -0
  253. package/src/validation/validator/length.ts +42 -0
  254. package/src/validation/validator/max-length.ts +33 -0
  255. package/src/validation/validator/max.ts +26 -0
  256. package/src/validation/validator/min-length.ts +31 -0
  257. package/src/validation/validator/min.ts +26 -0
  258. package/src/validation/validator/not-blank.ts +31 -0
  259. package/src/validation/validator/not-null.ts +19 -0
  260. package/src/validation/validator/plain-obj.ts +30 -0
  261. package/src/validation/validator/regexp.ts +32 -0
  262. package/types/cache/cache.d.ts +52 -52
  263. package/types/cache/config.d.ts +32 -32
  264. package/types/cache/index.d.ts +2 -2
  265. package/types/cache/purge-task.d.ts +11 -11
  266. package/types/cache/stat.d.ts +26 -26
  267. package/types/config/convert.d.ts +6 -6
  268. package/types/config/exception.d.ts +7 -7
  269. package/types/config/index.d.ts +25 -25
  270. package/types/http-client/index.d.ts +71 -71
  271. package/types/i18n/ar.d.ts +2 -2
  272. package/types/i18n/de.d.ts +2 -2
  273. package/types/i18n/en-us.d.ts +2 -2
  274. package/types/i18n/es.d.ts +2 -2
  275. package/types/i18n/fr.d.ts +2 -2
  276. package/types/i18n/i18n.d.ts +102 -102
  277. package/types/i18n/index.d.ts +9 -9
  278. package/types/i18n/ja.d.ts +2 -2
  279. package/types/i18n/ko.d.ts +2 -2
  280. package/types/i18n/msg.d.ts +50 -50
  281. package/types/i18n/pt.d.ts +2 -2
  282. package/types/i18n/ru.d.ts +2 -2
  283. package/types/i18n/tag.d.ts +11 -11
  284. package/types/i18n/zh-HK.d.ts +2 -2
  285. package/types/i18n/zh-TW.d.ts +2 -2
  286. package/types/i18n/zh-cn.d.ts +2 -2
  287. package/types/index.d.ts +11 -11
  288. package/types/lock/index.d.ts +64 -64
  289. package/types/log/config.d.ts +35 -35
  290. package/types/log/date.d.ts +2 -2
  291. package/types/log/file.d.ts +13 -13
  292. package/types/log/index.d.ts +53 -53
  293. package/types/log/level.d.ts +14 -14
  294. package/types/log/log.d.ts +40 -40
  295. package/types/log/store.d.ts +19 -19
  296. package/types/mongodb/collection.d.ts +25 -25
  297. package/types/mongodb/config.d.ts +45 -45
  298. package/types/mongodb/doc.d.ts +11 -11
  299. package/types/mongodb/exception.d.ts +7 -7
  300. package/types/mongodb/index.d.ts +29 -29
  301. package/types/mongodb/manager/base.d.ts +188 -188
  302. package/types/mongodb/manager/index.d.ts +38 -38
  303. package/types/mongodb/manager/tx-strict.d.ts +41 -41
  304. package/types/mongodb/manager/tx.d.ts +21 -21
  305. package/types/mongodb/migration.d.ts +12 -12
  306. package/types/mvc/access-log.d.ts +7 -7
  307. package/types/mvc/config.d.ts +42 -42
  308. package/types/mvc/exchange.d.ts +72 -72
  309. package/types/mvc/handler/index.d.ts +4 -3
  310. package/types/mvc/handler/json.d.ts +44 -44
  311. package/types/mvc/handler/restful.d.ts +11 -11
  312. package/types/mvc/handler/sse.d.ts +34 -0
  313. package/types/mvc/handler/upload.d.ts +36 -36
  314. package/types/mvc/index.d.ts +22 -22
  315. package/types/mvc/interceptor.d.ts +11 -11
  316. package/types/mvc/query.d.ts +13 -13
  317. package/types/mvc/render/file.d.ts +10 -10
  318. package/types/mvc/render/html/html.d.ts +98 -98
  319. package/types/mvc/render/html/index.d.ts +11 -11
  320. package/types/mvc/render/html/style.d.ts +1201 -1201
  321. package/types/mvc/render/index.d.ts +4 -4
  322. package/types/mvc/render/json.d.ts +17 -17
  323. package/types/mvc/render/text.d.ts +10 -10
  324. package/types/mvc/router.d.ts +11 -11
  325. package/types/mvc/server.d.ts +90 -90
  326. package/types/mvc/static/header.d.ts +27 -27
  327. package/types/mvc/static/index.d.ts +3 -3
  328. package/types/mvc/static/mime-type.d.ts +2 -2
  329. package/types/mvc/static/server-cache-config.d.ts +30 -30
  330. package/types/mvc/static/server-cache.d.ts +76 -76
  331. package/types/mvc/static/static-handler.d.ts +77 -77
  332. package/types/mysql/config.d.ts +90 -90
  333. package/types/mysql/exception.d.ts +7 -7
  334. package/types/mysql/index.d.ts +16 -16
  335. package/types/mysql/manager/base.d.ts +165 -165
  336. package/types/mysql/manager/index.d.ts +36 -36
  337. package/types/mysql/manager/ops/count.d.ts +13 -13
  338. package/types/mysql/manager/ops/criteria.d.ts +134 -134
  339. package/types/mysql/manager/ops/delete.d.ts +46 -46
  340. package/types/mysql/manager/ops/exist.d.ts +6 -6
  341. package/types/mysql/manager/ops/find.d.ts +86 -86
  342. package/types/mysql/manager/ops/index.d.ts +10 -10
  343. package/types/mysql/manager/ops/insert.d.ts +18 -18
  344. package/types/mysql/manager/ops/modify.d.ts +3 -3
  345. package/types/mysql/manager/ops/paginate.d.ts +36 -36
  346. package/types/mysql/manager/ops/query.d.ts +3 -3
  347. package/types/mysql/manager/ops/update.d.ts +76 -76
  348. package/types/mysql/manager/ops/utils.d.ts +5 -5
  349. package/types/mysql/manager/tx-strict.d.ts +36 -36
  350. package/types/mysql/manager/tx.d.ts +15 -15
  351. package/types/mysql/manager/utils.d.ts +17 -17
  352. package/types/mysql/migration.d.ts +8 -8
  353. package/types/mysql/table-info.d.ts +36 -36
  354. package/types/task/daily.d.ts +16 -16
  355. package/types/task/fixed-delay.d.ts +9 -9
  356. package/types/task/fixed-rate.d.ts +9 -9
  357. package/types/task/index.d.ts +4 -4
  358. package/types/task/task.d.ts +34 -34
  359. package/types/validation/exception.d.ts +38 -38
  360. package/types/validation/index.d.ts +32 -32
  361. package/types/validation/validator/array.d.ts +5 -5
  362. package/types/validation/validator/enum.d.ts +8 -8
  363. package/types/validation/validator/index.d.ts +11 -11
  364. package/types/validation/validator/length.d.ts +10 -10
  365. package/types/validation/validator/max-length.d.ts +8 -8
  366. package/types/validation/validator/max.d.ts +7 -7
  367. package/types/validation/validator/min-length.d.ts +6 -6
  368. package/types/validation/validator/min.d.ts +7 -7
  369. package/types/validation/validator/not-blank.d.ts +7 -7
  370. package/types/validation/validator/not-null.d.ts +6 -6
  371. package/types/validation/validator/plain-obj.d.ts +7 -7
  372. package/types/validation/validator/regexp.d.ts +8 -8
@@ -0,0 +1,688 @@
1
+ # MVC
2
+
3
+ The MVC component is built on Node.js's built-in http module, making it easier to build HTTP servers and handle requests.
4
+
5
+ ### Environment Variables
6
+
7
+ | Environment Variable | Description |
8
+ | :--------------------------- | :-------------------------------------------------------------------------- |
9
+ | SERVER_PORT | Port number |
10
+ | SERVER_TIMEOUT | Timeout in milliseconds, default 30000 |
11
+ | SERVER_ACCESS_LOG | Whether to enable access log, disabled by default. Set to true to enable |
12
+ | SERVER_CORS_ALLOW_ORIGIN | Allowed CORS origin, default * |
13
+ | SERVER_CORS_ALLOW_HEADERS | Allowed CORS headers, default * |
14
+ | SERVER_CORS_ALLOW_METHODS | Allowed CORS methods, default * |
15
+
16
+ ### Getting Started
17
+
18
+ ```ts
19
+ // Start server
20
+ await startWebServer({
21
+ // Routes. In actual development with many pages, each route handler can be written in a separate file
22
+ routers: {
23
+ '/': async exchange => exchange.respondText('Hello world!')
24
+ },
25
+ // Interceptors for authorization validation, exception handling, etc.
26
+ interceptors: [
27
+ async (exchange, next) => {
28
+ // Example: HTTP Basic Authentication
29
+ if (!validateAuth(exchange.request.headers.authorization)) {
30
+ // Respond with authentication required
31
+ exchange.respond({
32
+ statusCode: 401,
33
+ headers: { 'www-Authenticate': 'Basic realm="family"' }
34
+ })
35
+ return
36
+ }
37
+ // Call next to continue
38
+ await next()
39
+ }
40
+ ]
41
+ })
42
+ ```
43
+
44
+ Starting the server mainly requires two parameters: routes and interceptors. Routes handle specific requests, and interceptors execute before routes to intercept requests. These are detailed below.
45
+
46
+ The server can be stopped when needed.
47
+
48
+ ```ts
49
+ // Stop server
50
+ await stopWebServer()
51
+ ```
52
+
53
+ ### Routes
54
+
55
+ Route configuration is key-value pairs where the key is the path and the value is an async handler function that receives an exchange parameter of type ServerExchange, providing methods to read request information and respond with common formats.
56
+
57
+ ```ts
58
+ await startWebServer({
59
+ // Routes
60
+ routers: {
61
+ '/': async exchange => {
62
+ // Get request information via exchange
63
+ const url = exchange.request.url
64
+ const method = exchange.request.method
65
+ const referer = exchange.request.headers.referer
66
+ // Read body
67
+ const body = await exchange.bodyJson()
68
+ // Respond
69
+ exchange.respondJson({ ok: true })
70
+ },
71
+ '/users': async exchange => {
72
+ const list = await listUser()
73
+ exchange.respondJson(list)
74
+ },
75
+ '*': async exchange => {
76
+ exchange.respondText(`404 Not Found`, 404)
77
+ }
78
+ }
79
+ })
80
+ ```
81
+
82
+ `*` is a special path for custom 404 pages, handling all requests that fail to match any path. If no 404 page is set, a JSON error message is returned by default.
83
+
84
+ **Currently, route paths only support exact addresses, not fuzzy paths or variable binding**, such as `/users/:id` where user id is part of the path. This is not supported due to performance considerations, but may be considered in future versions.
85
+
86
+ ### Interceptors
87
+
88
+ Interceptor configuration is a list where each interceptor is an async function receiving two parameters: exchange and next. exchange is of type ServerExchange, same as in routes. next is a parameterless async function that calls the next interceptor or route.
89
+
90
+ ```ts
91
+ // Start server
92
+ await startWebServer({
93
+ // Routes
94
+ routers: {
95
+ // Route configuration omitted
96
+ },
97
+ // Interceptors for authorization validation, exception handling, etc.
98
+ interceptors: [
99
+ async (exchange, next) => {
100
+ // Get native request and response objects
101
+ const { request, response } = exchange
102
+ const url = request.url
103
+ const userAgent = request.headers['user-agent']
104
+ const ip = request.socket.remoteAddr
105
+ try {
106
+ // Call next to continue
107
+ await next()
108
+ } catch (e) {
109
+ // Example: Handle validation exceptions uniformly in interceptor
110
+ if (e instanceof ValidationException) {
111
+ // Use utility method on exchange for quick response
112
+ exchange.respondErrMsg(e.errMsg, 400)
113
+ return
114
+ }
115
+ // Throw unhandled exceptions directly
116
+ // Framework responds with 500 status code and logs error for troubleshooting
117
+ throw e
118
+ }
119
+ }
120
+ ]
121
+ })
122
+ ```
123
+
124
+ ### Exception Handling
125
+
126
+ For business processing failures, the recommended approach is to throw exceptions to abort processing, then handle them uniformly by interceptors and respond. However, this is not mandatory. The framework does not provide any preset types or functions; you need to extend them according to actual needs. Here is an example:
127
+
128
+ ```ts
129
+ /**
130
+ * Custom business exception.
131
+ */
132
+ export class BusinessException {
133
+ constructor(
134
+ /**
135
+ * Message.
136
+ */
137
+ readonly message: string,
138
+ /**
139
+ * Custom status code, default 400
140
+ */
141
+ readonly status?: number
142
+ ) {}
143
+ }
144
+
145
+ /**
146
+ * Global exception interceptor that handles specific exceptions and provides appropriate response messages.
147
+ * @param exchange
148
+ * @param next
149
+ */
150
+ export async function globalErrorInterceptor(
151
+ exchange: ServerExchange,
152
+ next: () => Promise<void>
153
+ ): Promise<void> {
154
+ try {
155
+ await next()
156
+ } catch (e) {
157
+ // Handle custom business exceptions
158
+ if (e instanceof BusinessException) {
159
+ const status = typeof e.status === 'number' ? e.status : 400
160
+ const message = e.message || ''
161
+ exchange.respondErrMsg(message, status)
162
+ return
163
+ }
164
+ // Handle validation exceptions
165
+ if (e instanceof ValidationException) {
166
+ exchange.respondErrMsg(e.errMsg, 400)
167
+ return
168
+ }
169
+ // Other exceptions are thrown directly, framework responds with 500 status code by default
170
+ throw e
171
+ }
172
+ }
173
+ ```
174
+
175
+ In actual development, multiple business exceptions can be set according to needs, responding with different JSON formats. The respondErrMsg method responds with a preset JSON format. Custom formats can be responded via respondJson method.
176
+
177
+ Set the error handling interceptor as the first interceptor, and use custom exceptions during request processing.
178
+
179
+ ```ts
180
+ // Start server
181
+ await startWebServer({
182
+ interceptors: [
183
+ // Set error handling interceptor first
184
+ globalErrorInterceptor,
185
+ // Other interceptors follow
186
+ authInterceptor
187
+ ],
188
+ // Routes
189
+ routers: {
190
+ '/order/cancel': createJsonHandler<{ id: string }>({
191
+ validation: { id: [notBlank()] },
192
+ async handle(body, exchange) {
193
+ const order = await findOrderById(body.id)
194
+ if (!order) {
195
+ // Use custom business exception to interrupt processing
196
+ throw new BusinessException('Order not found')
197
+ }
198
+ // Continue processing...
199
+ }
200
+ })
201
+ // Route configuration omitted
202
+ }
203
+ })
204
+ ```
205
+
206
+ ### Handle Different Methods
207
+
208
+ The component provides a restful function to dispatch by request method when configuring routes.
209
+
210
+ ```ts
211
+ // Start server
212
+ await startWebServer({
213
+ // Routes
214
+ routers: {
215
+ '/users': restful({
216
+ get: async exchange => {
217
+ // Handle GET request
218
+ },
219
+ post: async exchange => {
220
+ // Handle POST request
221
+ },
222
+ delete: async exchange => {
223
+ // Handle DELETE request
224
+ }
225
+ })
226
+ }
227
+ })
228
+ ```
229
+
230
+ Note: Routes do not support RESTful dynamic paths, as explained earlier.
231
+
232
+ ### JSON Request Handling
233
+
234
+ The framework provides createJsonHandler function to easily create a RouterHandler for handling JSON requests. Both request and response are JSON format, and can be empty. Use type {} for empty request and void for empty response.
235
+
236
+ ```ts
237
+ // JSON request body definition
238
+ interface Form {
239
+ name: string
240
+ age: number
241
+ }
242
+ // JSON response definition
243
+ interface Resp {
244
+ id: string
245
+ }
246
+
247
+ export const userCreateHandler = createJsonHandler<Form, Resp>({
248
+ /**
249
+ * Request body mapping object validation rules, optional.
250
+ */
251
+ validation: {
252
+ name: [notBlank('Name cannot be empty'), length({ min: 2, max: 16, message: 'Name must be 2-16 characters' })],
253
+ age: [notNull('Age required'), min(2), max(16)]
254
+ },
255
+ async handle(body, exchange) {
256
+ // Get authorization
257
+ const { authorization } = exchange.headers
258
+ const currentUser = await findUserByAuth(authorization)
259
+ console.log(`Name: ${body.name}`)
260
+ console.log(`Age: ${body.age}`)
261
+ const newUser = await createUser(body)
262
+ return { id: newUser.id }
263
+ }
264
+ })
265
+ ```
266
+
267
+ Validation automatically switches the validator's language based on the `accept-language` header. For validations without custom error messages, the switched language's default message is used.
268
+
269
+ Starting from version 0.3.0, createJsonHandler supports caching response results through the cache option. When the cache can be reused, it avoids executing the entire handle method flow, improving performance.
270
+
271
+ ```ts
272
+ export const userGetHandler = createJsonHandler<Form, User>({
273
+ // Set cache, same parameters as handle method
274
+ // Only caches valid responses, no caching if no response body
275
+ async cache(body, exchange) {
276
+ // Build cache key using id parameter
277
+ const key = `get-user-${body.id}`
278
+ // Return cache key and optional expiration time
279
+ return { key, expiresInSeconds: 60 }
280
+ },
281
+ async handle(body, exchange) {
282
+ // Handle flow omitted...
283
+ }
284
+ })
285
+ ```
286
+
287
+ Since caching is based on the cache component, you can also clear the cache through the cache component.
288
+
289
+ ```ts
290
+ export const userUpdateHandler = createJsonHandler<Form>({
291
+ async handle(body, exchange) {
292
+ // Handle flow omitted...
293
+ // Clear user detail cache
294
+ getCache().remove(`get-user-${body.id}`)
295
+ }
296
+ })
297
+ ```
298
+
299
+ However, do not use the cache component to retrieve cached content, as the cached content is stored as Buffer for performance reasons, avoiding re-serialization when using the cache. It is not the object returned by the handle method.
300
+
301
+ ### Binary Upload
302
+
303
+ Binary upload means sending files as binary data in the request body. The request body contains only the file content without additional information. The Content-Type is application/octet-stream.
304
+
305
+ The advantage is simplicity and better performance, as the server receives the file directly without parsing like multipart/form-data. However, since the request body only contains the file, other business parameters must be passed through query strings.
306
+
307
+ The framework provides createUploadHandler function to create route handlers for binary format requests, responding with JSON format. Here is an example:
308
+
309
+ ```ts
310
+ interface Resp {
311
+ /**
312
+ * New avatar URL
313
+ */
314
+ url: string
315
+ }
316
+
317
+ /**
318
+ * Example: Upload avatar
319
+ */
320
+ export const uploadAvatar = createUploadHandler<Resp>({
321
+ async handle(body, exchange) {
322
+ // Get userId parameter from Query String
323
+ const userId = exchange.query.getStr('userId') as string
324
+ // Validate parameters
325
+ validate(
326
+ { userId },
327
+ {
328
+ userId: [notBlank(), maxLength(64)]
329
+ }
330
+ )
331
+ // Check file size
332
+ if (body.byteLength > 2 * 1024 * 1024) {
333
+ // BusinessException is a custom business exception, handled by interceptor
334
+ throw new BusinessException('File size cannot exceed 2MB')
335
+ }
336
+ const user = await getMysqlManager().findById(tableUser, userId)
337
+ if (!user) {
338
+ throw new BusinessException('User not found')
339
+ }
340
+ // Simulate uploading to object storage, oss is object storage service SDK
341
+ const key = `users/${userId}/avatar`
342
+ await oss.putObject(key, body)
343
+ await getMysqlManager().partialUpdate(tableUser, { id: userId, avatar_key: key })
344
+ return { url: oss.getUrl(key) }
345
+ }
346
+ })
347
+ ```
348
+
349
+ For higher flexibility, such as not returning JSON format, define a regular route handler and use the bodyBuffer method on exchange to get the request body.
350
+
351
+ ```ts
352
+ export const uploadAvatar: RouterHandler = async exchange => {
353
+ // Read file
354
+ const file = await exchange.bodyBuffer()
355
+ // Get parameters from Query String
356
+ const query = exchange.parseQueryString()
357
+ const userId = query.getStr('userId')
358
+
359
+ // Parameter validation and file storage omitted...
360
+ }
361
+ ```
362
+
363
+ ### multipart/form-data File Upload
364
+
365
+ The component does not currently handle multipart/form-data requests. Third-party libraries can be used to parse file upload requests.
366
+
367
+ Here is an example using formidable:
368
+
369
+ ```ts
370
+ // Start server
371
+ import formidable from 'formidable'
372
+
373
+ await startWebServer({
374
+ // Routes
375
+ routers: {
376
+ '/cover': async exchange => {
377
+ const form = formidable({})
378
+ // Parse request
379
+ const [fields, files] = await form.parse(exchange.request)
380
+ // TODO continue business processing
381
+ }
382
+ }
383
+ })
384
+ ```
385
+
386
+ If other libraries are used to read the request content, the bodyXxx methods in exchange cannot be used, otherwise an exception is thrown. Similarly, after calling bodyXxx methods, other libraries cannot read the content, otherwise incomplete content will be read or exceptions will occur. However, bodyXxx methods can be called repeatedly without errors. Calling bodyText then bodyJson is allowed.
387
+
388
+ ### Respond HTML
389
+
390
+ The ServerExchange type provides a respondHtml method for rendering HTML, easily organizing tag hierarchies and dynamically building structures.
391
+
392
+ ```ts
393
+ // Start server
394
+ await startWebServer({
395
+ // Routes
396
+ routers: {
397
+ '/profile': async exchange => {
398
+ // Get user info
399
+ const user = await getUser(exchange.headers.authorization)
400
+ // HTML structure logic may be long, complex business can be extracted into separate functions
401
+ exchange.respondHtml({
402
+ lang: 'zh',
403
+ head: [
404
+ // Add tags to head, here add a title
405
+ // Strings in children represent TextNode
406
+ { tag: 'title', children: ['Profile'] },
407
+ { tag: 'script', attrs: { type: 'module', src: 'main.js' } }
408
+ ],
409
+ body: {
410
+ // Attributes with type inference for HTML global attributes like style, id, class
411
+ attrs: {
412
+ // Style has type inference
413
+ style: { 'background-color': 'white' }
414
+ },
415
+ // children can also accept a function whose parameter is a function to add child elements
416
+ // This allows dynamic rendering with loops and conditionals
417
+ children: add => {
418
+ add({ tag: 'h1', children: ['Profile'] })
419
+ // Render different elements based on user presence
420
+ if (user) {
421
+ add({ tag: 'p', children: [`Username: ${user.account}`] })
422
+ } else {
423
+ add({
424
+ tag: 'p',
425
+ children: [
426
+ 'Please login to view',
427
+ { tag: 'a', attrs: { href: '/login' }, children: ['Click to login'] }
428
+ ]
429
+ })
430
+ }
431
+ // Common combinations can be encapsulated into functions returning HtmlTag type
432
+ // footer is such an example
433
+ add(footer())
434
+ }
435
+ }
436
+ })
437
+ }
438
+ }
439
+ })
440
+ ```
441
+
442
+ footer function example:
443
+
444
+ ```ts
445
+ function footer(): HtmlTag {
446
+ return {
447
+ tag: 'div',
448
+ attrs: { class: 'footer' },
449
+ children: [
450
+ { tag: 'a', attrs: { href: '/about' }, children: ['About Us'] },
451
+ { tag: 'a', attrs: { href: '/call' }, children: ['Contact Us'] },
452
+ { tag: 'a', attrs: { href: '/privacy' }, children: ['Privacy Policy'] }
453
+ ]
454
+ }
455
+ }
456
+ ```
457
+
458
+ If you don't like the framework's built-in HTML rendering, you can use third-party template rendering components.
459
+
460
+ Here is an example using handlebars:
461
+
462
+ ```ts
463
+ import { compile } from 'handlebars'
464
+
465
+ await startWebServer({
466
+ // Routes
467
+ routers: {
468
+ '/html': async exchange => {
469
+ const source =
470
+ '<!DOCTYPE html>' +
471
+ '<html>' +
472
+ '<head>' +
473
+ '<title>Handlebars Example</title>' +
474
+ '</head>' +
475
+ '<body>' +
476
+ '<p>Hello, my name is {{name}}. I am from {{hometown}}. I have ' +
477
+ '{{kids.length}} kids:</p>' +
478
+ '<ul>{{#kids}}<li>{{name}} is {{age}}</li>{{/kids}}</ul>' +
479
+ '</body>' +
480
+ '</html>'
481
+ // Compile template
482
+ const template = compile(source)
483
+ // Data
484
+ const data = {
485
+ name: 'Alan',
486
+ hometown: 'Somewhere, TX',
487
+ kids: [
488
+ { name: 'Jimmy', age: '12' },
489
+ { name: 'Sally', age: '4' }
490
+ ]
491
+ }
492
+ // Build content
493
+ const result = template(data)
494
+ // Render HTML string
495
+ exchange.respondHtml(result)
496
+ }
497
+ }
498
+ })
499
+ ```
500
+
501
+ Here is an example using Vue 3.x SSR to render HTML:
502
+
503
+ ```ts
504
+ import { createSSRApp } from 'vue'
505
+ import { renderToString } from 'vue/server-renderer'
506
+
507
+ await startWebServer({
508
+ // Routes
509
+ routers: {
510
+ '/html': async exchange => {
511
+ const app = createSSRApp({
512
+ data: () => ({ count: 1 }),
513
+ template: `<button>{{ count }}</button>`
514
+ })
515
+ const html = await renderToString(app)
516
+ // Render page with generated HTML
517
+ exchange.respondHtml(`<!DOCTYPE html>
518
+ <html>
519
+ <head>
520
+ <title>Vue SSR Example</title>
521
+ </head>
522
+ <body>
523
+ <div id="app">${html}</div>
524
+ </body>
525
+ </html>`)
526
+ }
527
+ }
528
+ })
529
+ ```
530
+
531
+ ### Static Files
532
+
533
+ Static file directory mapping can be set via the static parameter, mapping a directory to a request path.
534
+
535
+ ```ts
536
+ await startWebServer({
537
+ static: {
538
+ '/a': { dir: 'D:\\Download', cacheAge: 300 },
539
+ '/a/b': { dir: 'E:\\Dowload', cacheAge: 150 },
540
+ '/b': { dir: 'static', cacheAge: 0 }
541
+ },
542
+ routers: {}
543
+ })
544
+ ```
545
+
546
+ The dir parameter is the mapped directory path, which can be absolute or relative. Relative paths are resolved from the current working directory. The cacheAge parameter is the cache time. If set to a value greater than 0, a Cache-Control header is generated accordingly.
547
+
548
+ Request paths only support prefix matching, not wildcards. For example, /a/demo.html matches /a path and responds with the demo.html file from the configured directory. Path configurations have priority. Accessing /a/b/music.mp3 matches the /a/b configuration, not /a, because /a/b is more specific and has higher priority. If the file is not found in the /a/b directory, the /a configuration is not tried.
549
+
550
+ Static files also support automatic index page mapping. For example, accessing /a/b/c matches the /a/b configuration, then looks for file c in the configured directory. If not found, it tries to find index.html in directory c.
551
+
552
+ #### Static File Server Cache
553
+
554
+ Starting from version 0.3.0, server-side caching of static files is supported. To enable static file caching, configure the following environment variables:
555
+
556
+ | Environment Variable | Description |
557
+ | :--------------------------------- | :-------------------------------------------------------------------------- |
558
+ | SERVER_STATIC_CACHE_ENABLE | Whether to enable server cache, default false |
559
+ | SERVER_STATIC_CACHE_MAX_AGE | Server cache time in seconds, default 600 |
560
+ | SERVER_STATIC_CACHE_MAX_FILE_SIZE | Maximum cacheable file size, supports semantic format like 10m and 100k, default 10m |
561
+ | SERVER_STATIC_CACHE_MAX_SIZE | Maximum cache size, triggers cleanup when exceeded. Same semantic format support, default 100m |
562
+
563
+ Version 0.3.2 adds the removeServerStaticCache function to actively delete specified server-side static cache.
564
+
565
+ ```ts
566
+ import { removeServerStaticCache } from 'wok-server'
567
+
568
+ removeServerStaticCache('/assets/index.js')
569
+ ```
570
+
571
+ ### Request Logs
572
+
573
+ Set the environment variable SERVER_ACCESS_LOG to true to enable request logging. When enabled, request and response information is output to the log after each response, disabled by default.
574
+
575
+ ```
576
+ [2023/10/25 17:09:47.872][INFO][access-log]{"method":"GET","url":"/html?tab=%E6%9C%8D%E8%A3%85","ip":"::ffff:127.0.0.1","start":"2023/10/25 17:09:47.871","rt":1,"status":200}
577
+ ```
578
+
579
+ The component only provides simple JSON format output and does not support format configuration. For request statistics, consider custom interceptors to store request information in message queues or databases for calculation, or analyze logs to extract request information.
580
+
581
+ ### WebSocket
582
+
583
+ The component does not provide WebSocket functionality directly, but the startWebServer function provides a preHandler parameter for pre-processing the server, allowing additional operations before server startup to integrate other libraries that support the native http module.
584
+
585
+ WebSocket support can be implemented by integrating socket.io.
586
+
587
+ ```ts
588
+ import { Server } from 'socket.io'
589
+ await startWebServer({
590
+ routers: {
591
+ // Route configuration omitted...
592
+ },
593
+ // Pre-handler to connect socket.io
594
+ preHandler: async server => {
595
+ const io = new Server(server)
596
+ // Adapt /chat path. If /chat also exists in routes, there's no conflict
597
+ // HTTP requests from clients are handled by routes
598
+ // Of course, this is not recommended
599
+ io.of('/chat').on('connection', socket => {
600
+ // Manage sessions and handle business in callback after connection
601
+ socket.on('message', data => {
602
+ /* Handle custom events */
603
+ })
604
+ socket.on('disconnect', () => {
605
+ /* Handle disconnection */
606
+ })
607
+ })
608
+ }
609
+ })
610
+ ```
611
+
612
+ ### Server-Sent Events
613
+
614
+ Starting from version 0.4.0, the MVC component includes `createSseHandler`, which encapsulates SSE protocol details and greatly simplifies server push implementation.
615
+
616
+ ```ts
617
+ import { createSseHandler } from 'wok-server'
618
+
619
+ await startWebServer({
620
+ routers: {
621
+ '/sse': createSseHandler({
622
+ async handle(ctx) {
623
+ let counter = 0
624
+ for (let i = 0; i < 10; i++) {
625
+ await new Promise<void>(resolve => setTimeout(resolve, 1000))
626
+ counter++
627
+ ctx.send({ message: 'Real-time update', count: counter })
628
+ if (counter >= 10) {
629
+ break
630
+ }
631
+ }
632
+ ctx.close()
633
+ }
634
+ })
635
+ }
636
+ })
637
+ ```
638
+
639
+ The `handle` function receives an `SseContext` object with the following capabilities:
640
+
641
+ | Method/Property | Description |
642
+ | :--------------------------------------- | :----------------------------------------------------------- |
643
+ | `ctx.send(data, event?, id?)` | Sends an SSE event. `data` is JSON serialized. `event` specifies the event name (use `addEventListener` on frontend), `id` sets the event ID (for `Last-Event-ID` on reconnection) |
644
+ | `ctx.close()` | Explicitly ends the SSE connection. The connection also closes automatically when `handle` returns or throws |
645
+ | `ctx.request` | The raw `IncomingMessage`, for reading request headers etc. |
646
+ | `ctx.response` | The raw `ServerResponse`, for advanced scenarios |
647
+
648
+ #### Named Events
649
+
650
+ Send named events via the `event` parameter, allowing the frontend to handle different event types separately:
651
+
652
+ ```ts
653
+ createSseHandler({
654
+ async handle(ctx) {
655
+ ctx.send({ title: 'New message' }, 'notification')
656
+ ctx.send({ progress: 50 }, 'progress')
657
+ }
658
+ })
659
+ ```
660
+
661
+ Frontend:
662
+
663
+ ```ts
664
+ const es = new EventSource('/sse')
665
+ es.addEventListener('notification', e => {
666
+ const data = JSON.parse(e.data)
667
+ console.log('Notification:', data.title)
668
+ })
669
+ ```
670
+
671
+ #### Reconnection
672
+
673
+ Set event IDs via the `id` parameter. On reconnection, the browser automatically sends the `Last-Event-ID` header:
674
+
675
+ ```ts
676
+ createSseHandler({
677
+ async handle(ctx) {
678
+ const lastId = ctx.request.headers['last-event-id']
679
+ // Determine where to resume based on lastId
680
+ }
681
+ })
682
+ ```
683
+
684
+ #### Connection Lifecycle
685
+
686
+ - SSE headers are sent and the connection is established when `handle` begins execution
687
+ - `ctx.send()` becomes a no-op when the client disconnects (automatically ignored)
688
+ - The connection closes automatically when `handle` returns or throws