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,39 @@
1
+ import { RouterHandler } from '../router'
2
+
3
+ /**
4
+ * 构建 restful 风格路由
5
+ */
6
+ export function restful(opts: {
7
+ get?: RouterHandler
8
+ post?: RouterHandler
9
+ put?: RouterHandler
10
+ patch?: RouterHandler
11
+ delete?: RouterHandler
12
+ }): RouterHandler {
13
+ return async exchange => {
14
+ const method = (exchange.request.method || '').toLowerCase()
15
+ let handler: RouterHandler | undefined
16
+ switch (method) {
17
+ case 'get':
18
+ handler = opts.get
19
+ break
20
+ case 'post':
21
+ handler = opts.post
22
+ break
23
+ case 'put':
24
+ handler = opts.put
25
+ break
26
+ case 'patch':
27
+ handler = opts.patch
28
+ break
29
+ case 'delete':
30
+ handler = opts.delete
31
+ break
32
+ }
33
+ if (!handler) {
34
+ exchange.respondErrMsg(`${method} ${exchange.request.url} not found`, 404)
35
+ return
36
+ }
37
+ await handler(exchange)
38
+ }
39
+ }
@@ -0,0 +1,90 @@
1
+ import { IncomingMessage, ServerResponse } from 'http'
2
+ import { ServerExchange } from '../exchange'
3
+ import { RouterHandler } from '../router'
4
+
5
+ export interface SseContext {
6
+ /**
7
+ * 请求信息
8
+ */
9
+ readonly request: IncomingMessage
10
+ /**
11
+ * 响应信息
12
+ */
13
+ readonly response: ServerResponse
14
+ /**
15
+ * 发送 SSE 事件
16
+ * @param data 事件数据,会被 JSON 序列化
17
+ * @param event 事件名称,可选。前端可通过 addEventListener 监听
18
+ * @param id 事件 ID,可选。用于断线重连时的 Last-Event-ID
19
+ */
20
+ send(data: any, event?: string, id?: string): void
21
+ /**
22
+ * 结束 SSE 连接
23
+ */
24
+ close(): void
25
+ }
26
+
27
+ /**
28
+ * 创建 SSE (Server-Sent Events) 处理器.
29
+ * 用于服务端向客户端单向推送实时数据。
30
+ *
31
+ * @param opts
32
+ * @returns
33
+ */
34
+ export function createSseHandler(opts: {
35
+ handle: (ctx: SseContext) => Promise<void>
36
+ }): RouterHandler {
37
+ return async function (exchange: ServerExchange) {
38
+ const { request, response } = exchange
39
+
40
+ response.statusCode = 200
41
+ response.setHeader('Content-Type', 'text/event-stream')
42
+ response.setHeader('Cache-Control', 'no-cache')
43
+ response.setHeader('Connection', 'keep-alive')
44
+ response.setHeader('X-Accel-Buffering', 'no')
45
+ response.flushHeaders()
46
+
47
+ let isEnded = false
48
+
49
+ const ctx: SseContext = {
50
+ request,
51
+ response,
52
+ send(data: any, event?: string, id?: string) {
53
+ if (isEnded) return
54
+ if (id !== undefined) {
55
+ response.write(`id: ${id}\n`)
56
+ }
57
+ if (event) {
58
+ response.write(`event: ${event}\n`)
59
+ }
60
+ if (Array.isArray(data)) {
61
+ for (const item of data) {
62
+ response.write(`data: ${JSON.stringify(item)}\n`)
63
+ }
64
+ } else {
65
+ response.write(`data: ${JSON.stringify(data)}\n`)
66
+ }
67
+ response.write('\n')
68
+ },
69
+ close() {
70
+ if (isEnded) return
71
+ isEnded = true
72
+ response.end()
73
+ }
74
+ }
75
+
76
+ function safeEnd() {
77
+ if (isEnded) return
78
+ isEnded = true
79
+ response.end()
80
+ }
81
+
82
+ request.on('close', safeEnd)
83
+
84
+ try {
85
+ await opts.handle(ctx)
86
+ } finally {
87
+ safeEnd()
88
+ }
89
+ }
90
+ }
@@ -0,0 +1,54 @@
1
+ import { IncomingMessage } from 'http'
2
+ import { ServerExchange } from '../exchange'
3
+ import { QueryString } from '../query'
4
+ import { RouterHandler } from '../router'
5
+
6
+ /**
7
+ * 上传请求传输对象.
8
+ */
9
+ export interface UploadHandlerExchange {
10
+ /**
11
+ * 请求信息
12
+ */
13
+ request: IncomingMessage
14
+ /**
15
+ * querystring 解析结果
16
+ */
17
+ query: QueryString
18
+ }
19
+
20
+ /**
21
+ * 创建上传处理器. 上传要求一次只能传输一个文件,并且请求正文就是文件二进制内容。
22
+ * 必须是 post 请求,额外的参数只能通过 queryString 或 header 来传递。
23
+ * 和 postman 中的 binary 模式是一致的,这样上传文件性能会好一些,但是局限性也很大。
24
+ *
25
+ * @param opts
26
+ * @param <RES> 响应json数据类型
27
+ * @returns
28
+ */
29
+ export function createUploadHandler<RES = void>(opts: {
30
+ /**
31
+ * 处理请求.
32
+ * @param body 正文内容
33
+ * @param exchange 请求传输对象,用于获取请求的基本信息
34
+ * @returns
35
+ */
36
+ handle: (body: Buffer, exchange: UploadHandlerExchange) => Promise<RES>
37
+ }): RouterHandler {
38
+ return async function (exchange: ServerExchange) {
39
+ if (!exchange.request.method || exchange.request.method.toUpperCase() !== 'POST') {
40
+ exchange.respondErrMsg('Method Not Allowed', 405)
41
+ return
42
+ }
43
+ const body = await exchange.bodyBuffer()
44
+ const res = await opts.handle(body, {
45
+ request: exchange.request,
46
+ query: exchange.parseQueryString()
47
+ })
48
+ if (!res) {
49
+ exchange.respond({ statusCode: 200 })
50
+ return
51
+ }
52
+ exchange.respondJson(res)
53
+ }
54
+ }
@@ -0,0 +1,48 @@
1
+ import { WokServer, WokServerOpts } from './server'
2
+
3
+ /**
4
+ * 服务实例.
5
+ */
6
+ let SERVER: WokServer | undefined
7
+
8
+ /**
9
+ * 删除服务器静态缓存
10
+ * @param path 路径,必须以斜杠开头,如:/assets/index.js
11
+ */
12
+ export async function removeServerStaticCache(path: string) {
13
+ if (SERVER) {
14
+ SERVER.removeServerStaticCache(path)
15
+ }
16
+ }
17
+
18
+ /**
19
+ * 启动 web服务
20
+ * @param opts
21
+ */
22
+ export async function startWebServer(opts: WokServerOpts) {
23
+ if (SERVER) {
24
+ throw new Error('The server has already been started!')
25
+ }
26
+ SERVER = new WokServer(opts)
27
+ await SERVER.start()
28
+ }
29
+ /**
30
+ * 停止 web 服务
31
+ * @returns
32
+ */
33
+ export async function stopWebServer() {
34
+ if (!SERVER) {
35
+ return
36
+ }
37
+ SERVER.stop()
38
+ SERVER = undefined
39
+ }
40
+
41
+ process.on('beforeExit', stopWebServer)
42
+
43
+ export * from './exchange'
44
+ export * from './handler'
45
+ export * from './interceptor'
46
+ export * from './render'
47
+ export * from './router'
48
+ export * from './static'
@@ -0,0 +1,12 @@
1
+ import { ServerExchange } from './exchange'
2
+
3
+ /**
4
+ * 拦截器.
5
+ */
6
+ export interface Interceptor {
7
+ /**
8
+ * @param exchange 传输对象,提供获取请求信息和通用的响应数据功能
9
+ * @param next 执行后面的流程,可能是下一个拦截器,也可能是路由
10
+ */
11
+ (exchange: ServerExchange, next: () => Promise<void>): Promise<void>
12
+ }
@@ -0,0 +1,36 @@
1
+ import { ParsedUrlQuery, parse } from 'querystring'
2
+
3
+ export class QueryString {
4
+ private qs: ParsedUrlQuery
5
+ constructor(url: string) {
6
+ const idx = url.indexOf('?')
7
+ this.qs = idx !== -1 ? parse(url.substring(idx + 1)) : {}
8
+ }
9
+ /**
10
+ * 获取单个字符串值
11
+ * @param name
12
+ */
13
+ getStr(name: string) {
14
+ const res = this.qs[name]
15
+ if (!res) {
16
+ return undefined
17
+ } else if (typeof res === 'string') {
18
+ return res
19
+ } else {
20
+ return res[0]
21
+ }
22
+ }
23
+ /**
24
+ * 获取参数的所有值
25
+ */
26
+ getStrVals(name: string) {
27
+ const res = this.qs[name]
28
+ if (!res) {
29
+ return undefined
30
+ } else if (typeof res === 'string') {
31
+ return [res]
32
+ } else {
33
+ return res
34
+ }
35
+ }
36
+ }
@@ -0,0 +1,148 @@
1
+ import { createReadStream, existsSync } from 'fs'
2
+ import { stat } from 'fs/promises'
3
+ import { IncomingMessage, ServerResponse } from 'http'
4
+ import { basename } from 'path'
5
+ import { createGzip } from 'zlib'
6
+ import { decideContentType } from '../static'
7
+ import { renderError } from './json'
8
+
9
+
10
+ /**
11
+ * 响应一个文件.
12
+ * @param request 请求信息
13
+ * @param response 响应信息
14
+ * @param filePath 文件路径,如果文件不存在,则会响应 404
15
+ * @param download 是否下载
16
+ */
17
+ export async function renderFile(
18
+ request: IncomingMessage,
19
+ response: ServerResponse,
20
+ filePath: string,
21
+ download = false
22
+ ): Promise<void> {
23
+ if (!existsSync(filePath)) {
24
+ renderError(response, 'Cannot find file.', 404)
25
+ return
26
+ }
27
+ const fileName = basename(filePath)
28
+
29
+ let isDownload = false
30
+ let contentType: string | undefined
31
+ if (download) {
32
+ isDownload = true
33
+ } else {
34
+ contentType = decideContentType(fileName)
35
+ if (!contentType) {
36
+ isDownload = true
37
+ }
38
+ }
39
+
40
+ if (isDownload) {
41
+ response.setHeader('Content-Type', 'application/octet-stream')
42
+ response.setHeader(
43
+ 'Content-Disposition',
44
+ `attachment; filename="${encodeURIComponent(fileName)}"`
45
+ )
46
+ } else if (contentType) {
47
+ response.setHeader('Content-Type', contentType)
48
+ }
49
+ const statRes = await stat(filePath)
50
+ // 支持 If-Modified-Since
51
+ // 由于只是简单的文件映射,没有 etag,不能支持 If-None-Match
52
+ // 缓存校验只能支持时间的比对,修改时间是文件系统本来就有的
53
+ if (request.headers['if-modified-since']) {
54
+ const modifiedSince = new Date(request.headers['if-modified-since'])
55
+ // 判定日期是否有效
56
+ if (modifiedSince instanceof Date && !isNaN(modifiedSince.getTime())) {
57
+ // 比较更改日期,只精确到秒, UTC 格式只精确到秒,但是 mtime 是包含毫秒的
58
+ const { mtime } = statRes
59
+ mtime.setMilliseconds(0)
60
+ if (modifiedSince >= mtime) {
61
+ response.statusCode = 304
62
+ response.setHeader('Last-Modified', statRes.mtime.toUTCString())
63
+ response.end()
64
+ return
65
+ }
66
+ }
67
+ }
68
+
69
+ // 支持 Range
70
+ // https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Range
71
+ const rangeHeader = request.headers['range']
72
+ if (!rangeHeader) {
73
+ response.setHeader('Last-Modified', statRes.mtime.toUTCString())
74
+ return streamFile(filePath, request, response)
75
+ }
76
+ // 解析,range 示例:bytes=200-1000, 2000-6576, 19000-
77
+ // 多段的情况,暂时不做支持,非常麻烦,段数多还可能会有效率问题
78
+ // 如果不是字节范围不是以字节为单位,暂时也不做支持
79
+ const ranges = rangeHeader.split(',')
80
+ let range = ranges.length ? ranges[0] : undefined
81
+ if (!range) {
82
+ return streamFile(filePath, request, response)
83
+ }
84
+ range = range.trim()
85
+ if (!range.startsWith('bytes=')) {
86
+ return streamFile(filePath, request, response)
87
+ }
88
+ range = range.substring(6)
89
+ const strs = range.split('-')
90
+ let start = strs[0] ? parseInt(strs[0], 10) : NaN
91
+ let end = strs[1] ? parseInt(strs[1], 10) : NaN
92
+ // 解析文件
93
+ if (isNaN(start) || start < 0) {
94
+ // 范围不合法,返回 416
95
+ renderError(response, `Range not satisfiable,start is ${start}`, 416)
96
+ return
97
+ }
98
+ if (isNaN(end)) {
99
+ end = statRes.size - 1
100
+ } else if (end > statRes.size - 1) {
101
+ // 范围不合法,返回 416
102
+ renderError(
103
+ response,
104
+ `Range not satisfiable,end must not be greater than ${statRes.size - 1}`,
105
+ 416
106
+ )
107
+ return
108
+ }
109
+ // 注:Range 和 Content-Range 还有 createReadStream 中的字节范围,都是前后全包含的
110
+ // Content-Range: bytes 42-1233/1234
111
+ response.setHeader('Content-Range', `bytes ${start}-${end}/${statRes.size}`)
112
+ return streamFile(filePath, request, response, { start, end })
113
+ }
114
+
115
+ function streamFile(
116
+ filePath: string,
117
+ request: IncomingMessage,
118
+ response: ServerResponse,
119
+ opts?: { start?: number; end?: number }
120
+ ): Promise<void> {
121
+ return new Promise<void>((res, rej) => {
122
+ if (opts && typeof opts.start === 'number') {
123
+ // 部分返回 206
124
+ response.statusCode = 206
125
+ } else {
126
+ // 全部返回 200
127
+ response.statusCode = 200
128
+ }
129
+ // 支持 gzip
130
+ const acceptEncoding = request.headers['accept-encoding'] as string
131
+ if (acceptEncoding) {
132
+ // Accept-Encoding: br;q=1.0, gzip;q=0.8, *;q=0.1
133
+ const acceptEncodings = acceptEncoding
134
+ .trim()
135
+ .split(',')
136
+ .map(item => item.trim())
137
+ .map(item => item.split(';')[0])
138
+ if (acceptEncodings.includes('gzip') || acceptEncodings.includes('*')) {
139
+ response.setHeader('Content-Encoding', 'gzip')
140
+ createReadStream(filePath, opts).pipe(createGzip()).pipe(response)
141
+ response.once('finish', res).once('error', rej)
142
+ return
143
+ }
144
+ }
145
+ createReadStream(filePath, opts).pipe(response)
146
+ response.once('finish', res).once('error', rej)
147
+ })
148
+ }
@@ -0,0 +1,187 @@
1
+ import { CSSStyleDeclaration } from './style'
2
+
3
+ /**
4
+ * 全局属性.
5
+ * https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes
6
+ */
7
+ export interface GlobalAttrs {
8
+ accesskey?: string
9
+ id?: string
10
+ autocapitalize?: 'off' | 'none' | 'on' | 'sentences' | 'words' | 'characters'
11
+ autofocus?: boolean
12
+ class?: string
13
+ contenteditable?: boolean | 'plaintext-only'
14
+ dir?: 'ltr' | 'rtl' | 'auto'
15
+ draggable?: boolean
16
+ enterkeyhint?: string
17
+ hidden?: string
18
+ inert?: boolean
19
+ inputmode?: 'none' | 'text' | 'decimal' | 'numeric' | 'tel' | 'search' | 'email' | 'url'
20
+ is?: string
21
+ itemid?: string
22
+ itemprop?: string
23
+ itemref?: string
24
+ itemscope?: string
25
+ itemtype?: string
26
+ lang?: string
27
+ nonce?: string
28
+ part?: string
29
+ popover?: string
30
+ role?: string
31
+ slot?: string
32
+ spellcheck?: string
33
+ style?: CSSStyleDeclaration | string
34
+ tabindex?: string
35
+ title?: string
36
+ translate?: 'yes' | 'no'
37
+ virtualkeyboardpolicy?: 'auto' | 'manual'
38
+ }
39
+ /**
40
+ * 子元素选项,子元素可以是一个标签也可以是字符串(表示文本,对应 TextNode)。子元素列表
41
+ * 可以传一个数组,也可以传一个函数,函数的参数是一个添加函数,通过这个添加函数来
42
+ * 动态添加子元素.
43
+ */
44
+ export type SubElementsOpt =
45
+ | Array<HtmlTag | string>
46
+ | ((add: (...child: Array<HtmlTag | string>) => void) => void)
47
+
48
+ export interface HtmlAttrs extends GlobalAttrs {
49
+ [key: string]: any
50
+ }
51
+ /**
52
+ * 标签定义
53
+ */
54
+ export interface HtmlTag {
55
+ /**
56
+ * 标签名称
57
+ */
58
+ tag: string
59
+ /**
60
+ * 是否自闭合标签,自闭合符合是不渲染子元素内容的,如 <br/>
61
+ */
62
+ selfClosing?: boolean
63
+ /**
64
+ * 属性
65
+ */
66
+ attrs?: HtmlAttrs
67
+ /**
68
+ * 子元素.
69
+ */
70
+ children?: SubElementsOpt
71
+ }
72
+
73
+ function encodeAttrs(attrs: HtmlAttrs) {
74
+ return Object.entries(attrs)
75
+ .map(entry => {
76
+ const [name, value] = entry
77
+ return { name, value }
78
+ })
79
+ .filter(attr => attr.value !== undefined)
80
+ .map(attr => {
81
+ const { name } = attr
82
+ const value = attr.value as any
83
+ if (name === 'style') {
84
+ if (typeof value === 'string') {
85
+ return value
86
+ }
87
+ const style = value as CSSStyleDeclaration
88
+ return Object.entries(style)
89
+ .map<string>(entry => `${entry[0]}:${entry[1]}`)
90
+ .join(';')
91
+ }
92
+ if (typeof value === 'boolean' && value) {
93
+ return `${name}`
94
+ }
95
+ if (typeof value === 'string') {
96
+ return `${name}="${value.replace(/"/g, '&quot;')}"`
97
+ }
98
+ return `${name}="${value}"`
99
+ })
100
+ .join(' ')
101
+ }
102
+ /**
103
+ * 将标签编码
104
+ */
105
+ function encodeTag(tag: HtmlTag) {
106
+ if (tag.selfClosing) {
107
+ if (tag.attrs) {
108
+ return `<${tag.tag} ${encodeAttrs(tag.attrs)}/>`
109
+ }
110
+ return `<${tag.tag}/>`
111
+ }
112
+ let html = ''
113
+ html += `<${tag.tag}`
114
+ if (tag.attrs) {
115
+ html += ` ${encodeAttrs(tag.attrs)}>`
116
+ } else {
117
+ html += '>'
118
+ }
119
+ if (tag.children) {
120
+ if (Array.isArray(tag.children)) {
121
+ html += tag.children
122
+ .map(subTag => (typeof subTag === 'string' ? subTag : encodeTag(subTag)))
123
+ .join('')
124
+ } else {
125
+ const children: Array<HtmlTag | string> = []
126
+ tag.children((...child) => {
127
+ children.push(...child)
128
+ })
129
+ html += children
130
+ .map(subTag => (typeof subTag === 'string' ? subTag : encodeTag(subTag)))
131
+ .join('')
132
+ }
133
+ }
134
+ html += `</${tag.tag}>`
135
+ return html
136
+ }
137
+
138
+ /**
139
+ * html 结构
140
+ */
141
+ export interface HtmlStuct {
142
+ /**
143
+ * 语言,根元素上的属性 lang ,示例:<html lang="zh">
144
+ */
145
+ lang?: string
146
+ /**
147
+ * head 标签内容
148
+ */
149
+ head: SubElementsOpt
150
+ /**
151
+ * body 标签内容
152
+ */
153
+ body:
154
+ | {
155
+ /**
156
+ * 属性
157
+ */
158
+ attrs?: HtmlAttrs
159
+ /**
160
+ * 子元素.
161
+ */
162
+ children: SubElementsOpt
163
+ }
164
+ | SubElementsOpt
165
+ }
166
+ /**
167
+ * 生成 html 代码
168
+ * @param html
169
+ */
170
+ export function generateHtmlCode(html: HtmlStuct): string {
171
+ return encodeTag({
172
+ tag: 'html',
173
+ attrs: { lang: html.lang },
174
+ children: [
175
+ { tag: 'head', children: html.head },
176
+ {
177
+ tag: 'body',
178
+ attrs:
179
+ Array.isArray(html.body) || typeof html.body === 'function' ? undefined : html.body.attrs,
180
+ children:
181
+ Array.isArray(html.body) || typeof html.body === 'function'
182
+ ? html.body
183
+ : html.body.children
184
+ }
185
+ ]
186
+ })
187
+ }
@@ -0,0 +1,16 @@
1
+ import { ServerResponse } from 'http'
2
+ import { HtmlStuct, generateHtmlCode } from './html'
3
+
4
+ /**
5
+ * 渲染 html
6
+ * @param response
7
+ * @param html
8
+ * @param status
9
+ */
10
+ export function renderHtml(response: ServerResponse, html: HtmlStuct | string, status = 200) {
11
+ response.statusCode = status
12
+ response.setHeader('content-type', 'text/html; charset=utf-8')
13
+ response.end(typeof html === 'string' ? html : generateHtmlCode(html))
14
+ }
15
+
16
+ export * from './html'