acrylblends 0.6.2 → 0.6.4

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 (316) hide show
  1. package/README.md +5 -1
  2. package/docs/framework/README.md +3 -2
  3. package/docs/framework/UPSTREAM.md +1 -1
  4. package/docs/framework/cordis/acryl-in-a-cordis-app.md +6 -0
  5. package/docs/framework/getting-started/basic.md +1 -0
  6. package/docs/framework/getting-started/desktop.md +41 -0
  7. package/docs/framework/guides/jsx-dom.md +0 -3
  8. package/docs/framework/guides/jsx.md +24 -3
  9. package/docs/framework/helpers/css.md +0 -3
  10. package/docs/framework/helpers/ssg.md +3 -6
  11. package/docs/framework/middleware/builtin/jsx-renderer.md +0 -3
  12. package/docs/framework/upstream.json +3 -3
  13. package/lib/accepts.d.ts +1 -1
  14. package/lib/acryl-base.d.ts +1 -1
  15. package/lib/adapter.d.ts +1 -1
  16. package/lib/{app-BdrVHBlJ.d.ts → app-D8riatvv.d.ts} +1 -1
  17. package/lib/base-BpQy2Rk7.d.ts +783 -0
  18. package/lib/base-ddiVmiE7.js +721 -0
  19. package/lib/basic-auth.d.ts +1 -1
  20. package/lib/bearer-auth.d.ts +1 -1
  21. package/lib/body-limit.d.ts +1 -1
  22. package/lib/cache.d.ts +1 -1
  23. package/lib/cache.js +4 -4
  24. package/lib/{client-BHTr318g.js → client-DDLXwhBo.js} +2 -125
  25. package/lib/client.d.ts +2 -2
  26. package/lib/client.js +2 -1
  27. package/lib/combine.d.ts +1 -1
  28. package/lib/common-D8Z4JRCw.d.ts +46 -0
  29. package/lib/components-0o0CBYNv.js +268 -0
  30. package/lib/components-DdI7-ns7.js +24 -0
  31. package/lib/compress.d.ts +1 -1
  32. package/lib/conninfo.d.ts +2 -2
  33. package/lib/constants-BR9jM9BC.js +9 -0
  34. package/lib/{context-B5Y1lLha.d.ts → context-DS3fYyG-.d.ts} +2 -2
  35. package/lib/context-DxFdY_un.d.ts +250 -0
  36. package/lib/context-storage.d.ts +1 -1
  37. package/lib/{cookie-D9_nQbsM.d.ts → cookie-CCelEqky.d.ts} +1 -1
  38. package/lib/cookie.d.ts +3 -3
  39. package/lib/cors.d.ts +1 -1
  40. package/lib/csrf.d.ts +1 -1
  41. package/lib/css-D2jb7X8b.js +320 -0
  42. package/lib/css.d.ts +82 -0
  43. package/lib/css.js +134 -0
  44. package/lib/dev.d.ts +2 -2
  45. package/lib/etag.d.ts +1 -1
  46. package/lib/factory.d.ts +2 -2
  47. package/lib/hooks-BMw6BAyP.js +762 -0
  48. package/lib/html-SllAe0YT.d.ts +46 -0
  49. package/lib/html.d.ts +1 -1
  50. package/lib/{index-m12zcmph.d.ts → index-Bb7YJKCq.d.ts} +3 -3
  51. package/lib/{index-Kqou8Fjh.d.ts → index-BvFh1Gfx.d.ts} +2 -2
  52. package/lib/index-C850MeBA.d.ts +94 -0
  53. package/lib/{index-DtEoak1i.d.ts → index-CTy4N7ng.d.ts} +2 -2
  54. package/lib/index.d.ts +4 -4
  55. package/lib/ip-restriction.d.ts +2 -2
  56. package/lib/jsx/dom/client.d.ts +30 -0
  57. package/lib/jsx/dom/client.js +49 -0
  58. package/lib/jsx/dom/css.d.ts +84 -0
  59. package/lib/jsx/dom/css.js +2 -0
  60. package/lib/jsx/dom/jsx-dev-runtime.d.ts +3 -0
  61. package/lib/jsx/dom/jsx-dev-runtime.js +2 -0
  62. package/lib/jsx/dom/jsx-runtime.d.ts +3 -0
  63. package/lib/jsx/dom/jsx-runtime.js +2 -0
  64. package/lib/jsx/dom/server.d.ts +105 -0
  65. package/lib/jsx/dom/server.js +37 -0
  66. package/lib/jsx/dom.d.ts +5 -0
  67. package/lib/jsx/dom.js +74 -0
  68. package/lib/jsx/jsx-dev-runtime.d.ts +6 -0
  69. package/lib/jsx/jsx-dev-runtime.js +14 -0
  70. package/lib/jsx/jsx-runtime.d.ts +10 -0
  71. package/lib/jsx/jsx-runtime.js +30 -0
  72. package/lib/jsx/streaming.d.ts +2 -0
  73. package/lib/jsx/streaming.js +2 -0
  74. package/lib/jsx-dev-runtime-B22O-Npk.d.ts +7 -0
  75. package/lib/jsx-dev-runtime-D18Y35TI.js +354 -0
  76. package/lib/jsx-renderer.d.ts +75 -0
  77. package/lib/jsx-renderer.js +99 -0
  78. package/lib/jsx-runtime-BVVgDSdq.d.ts +1 -0
  79. package/lib/jsx.d.ts +3 -0
  80. package/lib/jsx.js +47 -0
  81. package/lib/jwk.d.ts +4 -4
  82. package/lib/{jwt-BA65QhnJ.d.ts → jwt-BARhotDz.d.ts} +1 -1
  83. package/lib/jwt.d.ts +4 -4
  84. package/lib/language.d.ts +1 -1
  85. package/lib/logger.d.ts +1 -1
  86. package/lib/loopback-only.d.ts +1 -1
  87. package/lib/method-not-allowed.d.ts +2 -2
  88. package/lib/method-override.d.ts +2 -2
  89. package/lib/mount.d.ts +1 -1
  90. package/lib/node-server/conninfo.d.ts +1 -1
  91. package/lib/node-server/serve-static.d.ts +1 -1
  92. package/lib/node-server/serve-static.js +1 -1
  93. package/lib/node-server.d.ts +1 -1
  94. package/lib/node-server.js +1 -1
  95. package/lib/powered-by.d.ts +1 -1
  96. package/lib/pretty-json.d.ts +1 -1
  97. package/lib/proxy.d.ts +1 -1
  98. package/lib/quick.d.ts +1 -1
  99. package/lib/request-id.d.ts +1 -1
  100. package/lib/request.d.ts +1 -1
  101. package/lib/route.d.ts +1 -1
  102. package/lib/schema-validator.d.ts +1 -1
  103. package/lib/secure-headers.d.ts +1 -1
  104. package/lib/{serve-static-Dw_m6KxR.js → serve-static-B14MF1Va.js} +2 -0
  105. package/lib/serve-static.d.ts +1 -1
  106. package/lib/ssg-BwjcA-Ge.js +389 -0
  107. package/lib/ssg.d.ts +156 -0
  108. package/lib/ssg.js +2 -0
  109. package/lib/streaming.d.ts +1 -1
  110. package/lib/testing.d.ts +4 -4
  111. package/lib/testing.js +1 -1
  112. package/lib/timeout.d.ts +1 -1
  113. package/lib/timing.d.ts +1 -1
  114. package/lib/tiny.d.ts +1 -1
  115. package/lib/trailing-slash.d.ts +1 -1
  116. package/lib/{types-ZM9gIiKh.d.ts → types-Dv_nV2cv.d.ts} +1 -1
  117. package/lib/{types-D9DEXMjE.d.ts → types-Lqx5BxQF.d.ts} +8 -1
  118. package/lib/{types-Bd29gh2o.d.ts → types-zNxyEpG2.d.ts} +3 -3
  119. package/lib/types.d.ts +1 -1
  120. package/lib/utils/body.d.ts +1 -1
  121. package/lib/utils/cookie.d.ts +1 -1
  122. package/lib/utils/crypto.d.ts +1 -1
  123. package/lib/utils/html.d.ts +1 -45
  124. package/lib/utils/ipaddr.d.ts +1 -1
  125. package/lib/utils/jwt.d.ts +1 -1
  126. package/lib/utils/types.d.ts +2 -2
  127. package/lib/utils-Cx61GFMC.js +125 -0
  128. package/lib/validator.d.ts +2 -2
  129. package/lib/ws.d.ts +1 -1
  130. package/package.json +53 -1
  131. package/plugins/acryl-inspect/upstream.json +1 -1
  132. package/plugins/acryl-node-server/upstream.json +1 -1
  133. package/plugins/acryl-router/src/core/helper/css/common.ts +337 -0
  134. package/plugins/acryl-router/src/core/helper/css/index.ts +262 -0
  135. package/plugins/acryl-router/src/core/helper/ssg/index.ts +14 -0
  136. package/plugins/acryl-router/src/core/helper/ssg/middleware.ts +83 -0
  137. package/plugins/acryl-router/src/core/helper/ssg/plugins.ts +72 -0
  138. package/plugins/acryl-router/src/core/helper/ssg/ssg.ts +471 -0
  139. package/plugins/acryl-router/src/core/helper/ssg/utils.ts +118 -0
  140. package/plugins/acryl-router/src/core/jsx/base.ts +490 -0
  141. package/plugins/acryl-router/src/core/jsx/children.ts +20 -0
  142. package/plugins/acryl-router/src/core/jsx/components.ts +257 -0
  143. package/plugins/acryl-router/src/core/jsx/constants.ts +6 -0
  144. package/plugins/acryl-router/src/core/jsx/context.ts +266 -0
  145. package/plugins/acryl-router/src/core/jsx/dom/client.ts +89 -0
  146. package/plugins/acryl-router/src/core/jsx/dom/components.ts +39 -0
  147. package/plugins/acryl-router/src/core/jsx/dom/context.ts +52 -0
  148. package/plugins/acryl-router/src/core/jsx/dom/css.ts +263 -0
  149. package/plugins/acryl-router/src/core/jsx/dom/hooks/index.ts +91 -0
  150. package/plugins/acryl-router/src/core/jsx/dom/index.ts +170 -0
  151. package/plugins/acryl-router/src/core/jsx/dom/intrinsic-element/components.ts +438 -0
  152. package/plugins/acryl-router/src/core/jsx/dom/jsx-dev-runtime.ts +23 -0
  153. package/plugins/acryl-router/src/core/jsx/dom/jsx-runtime.ts +8 -0
  154. package/plugins/acryl-router/src/core/jsx/dom/render.ts +879 -0
  155. package/plugins/acryl-router/src/core/jsx/dom/server.ts +76 -0
  156. package/plugins/acryl-router/src/core/jsx/dom/utils.ts +7 -0
  157. package/plugins/acryl-router/src/core/jsx/hooks/index.ts +437 -0
  158. package/plugins/acryl-router/src/core/jsx/index.ts +115 -0
  159. package/plugins/acryl-router/src/core/jsx/intrinsic-element/common.ts +23 -0
  160. package/plugins/acryl-router/src/core/jsx/intrinsic-element/components.ts +210 -0
  161. package/plugins/acryl-router/src/core/jsx/intrinsic-elements.ts +938 -0
  162. package/plugins/acryl-router/src/core/jsx/jsx-dev-runtime.ts +26 -0
  163. package/plugins/acryl-router/src/core/jsx/jsx-runtime.ts +51 -0
  164. package/plugins/acryl-router/src/core/jsx/streaming.ts +231 -0
  165. package/plugins/acryl-router/src/core/jsx/types.ts +41 -0
  166. package/plugins/acryl-router/src/core/jsx/utils.ts +202 -0
  167. package/plugins/acryl-router/src/core/middleware/cache/index.ts +4 -4
  168. package/plugins/acryl-router/src/core/middleware/jsx-renderer/index.ts +162 -0
  169. package/plugins/acryl-router/src/core/utils/types.ts +6 -0
  170. package/plugins/acryl-router/src/helpers/index.ts +2 -0
  171. package/plugins/acryl-router/src/middleware/index.ts +1 -0
  172. package/plugins/acryl-router/upstream.json +3 -3
  173. package/plugins/acryl-router-http/upstream.json +1 -1
  174. package/plugins/acryl-state/upstream.json +1 -1
  175. package/plugins/desktop/upstream.json +1 -1
  176. package/plugins/desktop-web/upstream.json +1 -1
  177. package/plugins/extensions/resolve.ts +4 -0
  178. package/plugins/save/plugin.json +1 -1
  179. package/shared/AGENTS.core.md +4 -0
  180. package/templates/agent/tsconfig.json +7 -1
  181. package/templates/todo/upstream.json +1 -1
  182. package/plugins/acryl-inspect/tests/report.spec.ts +0 -69
  183. package/plugins/acryl-inspect/tests/service.spec.ts +0 -121
  184. package/plugins/acryl-node-server/tests/adapter/app.ts +0 -76
  185. package/plugins/acryl-node-server/tests/adapter/assets/.static/plain.txt +0 -1
  186. package/plugins/acryl-node-server/tests/adapter/assets/favicon.ico +0 -0
  187. package/plugins/acryl-node-server/tests/adapter/assets/secret.txt +0 -1
  188. package/plugins/acryl-node-server/tests/adapter/assets/static/acryl.html +0 -1
  189. package/plugins/acryl-node-server/tests/adapter/assets/static/admin/secret.txt +0 -1
  190. package/plugins/acryl-node-server/tests/adapter/assets/static/data.json +0 -5
  191. package/plugins/acryl-node-server/tests/adapter/assets/static/extensionless +0 -1
  192. package/plugins/acryl-node-server/tests/adapter/assets/static/foo..bar.txt +0 -0
  193. package/plugins/acryl-node-server/tests/adapter/assets/static/index.html +0 -1
  194. package/plugins/acryl-node-server/tests/adapter/assets/static/plain.txt +0 -1
  195. package/plugins/acryl-node-server/tests/adapter/assets/static-with-context-path/plain.txt +0 -1
  196. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin +0 -1
  197. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin.br +0 -1
  198. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt +0 -1
  199. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.br +0 -1
  200. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.zst +0 -1
  201. package/plugins/acryl-node-server/tests/adapter/conninfo.test.ts +0 -34
  202. package/plugins/acryl-node-server/tests/adapter/content-length.test.ts +0 -248
  203. package/plugins/acryl-node-server/tests/adapter/early-hints.test.ts +0 -457
  204. package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-cert.pem +0 -23
  205. package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-key.pem +0 -27
  206. package/plugins/acryl-node-server/tests/adapter/headers.test.ts +0 -252
  207. package/plugins/acryl-node-server/tests/adapter/helpers/request.ts +0 -190
  208. package/plugins/acryl-node-server/tests/adapter/listener.test.ts +0 -727
  209. package/plugins/acryl-node-server/tests/adapter/request-client-disconnect.test.ts +0 -189
  210. package/plugins/acryl-node-server/tests/adapter/request.test.ts +0 -1230
  211. package/plugins/acryl-node-server/tests/adapter/response.test.ts +0 -285
  212. package/plugins/acryl-node-server/tests/adapter/serve-static.test.ts +0 -849
  213. package/plugins/acryl-node-server/tests/adapter/server-socket.test.ts +0 -790
  214. package/plugins/acryl-node-server/tests/adapter/server.test.ts +0 -1312
  215. package/plugins/acryl-node-server/tests/adapter/setup.ts +0 -16
  216. package/plugins/acryl-node-server/tests/adapter/url.test.ts +0 -76
  217. package/plugins/acryl-node-server/tests/adapter/utils/response.test.ts +0 -23
  218. package/plugins/acryl-node-server/tests/adapter/utils/stream.test.ts +0 -75
  219. package/plugins/acryl-node-server/tests/adapter/utils.test.ts +0 -235
  220. package/plugins/acryl-node-server/tests/adapter/websocket.test.ts +0 -260
  221. package/plugins/acryl-node-server/tests/server.spec.ts +0 -141
  222. package/plugins/acryl-router/src/core/app.test.ts +0 -3833
  223. package/plugins/acryl-router/src/core/client/client.test.ts +0 -2313
  224. package/plugins/acryl-router/src/core/client/types.test.ts +0 -149
  225. package/plugins/acryl-router/src/core/client/utils.test.ts +0 -405
  226. package/plugins/acryl-router/src/core/compose.test.ts +0 -751
  227. package/plugins/acryl-router/src/core/context.test.ts +0 -629
  228. package/plugins/acryl-router/src/core/helper/accepts/accepts.test.ts +0 -344
  229. package/plugins/acryl-router/src/core/helper/adapter/index.test.ts +0 -55
  230. package/plugins/acryl-router/src/core/helper/cookie/index.test.ts +0 -510
  231. package/plugins/acryl-router/src/core/helper/dev/index.test.ts +0 -181
  232. package/plugins/acryl-router/src/core/helper/factory/index.test.ts +0 -434
  233. package/plugins/acryl-router/src/core/helper/html/index.test.ts +0 -78
  234. package/plugins/acryl-router/src/core/helper/proxy/index.test.ts +0 -356
  235. package/plugins/acryl-router/src/core/helper/route/index.test.ts +0 -259
  236. package/plugins/acryl-router/src/core/helper/streaming/stream.test.ts +0 -128
  237. package/plugins/acryl-router/src/core/helper/streaming/text.test.ts +0 -34
  238. package/plugins/acryl-router/src/core/helper/testing/index.test.ts +0 -42
  239. package/plugins/acryl-router/src/core/helper/websocket/index.test.ts +0 -124
  240. package/plugins/acryl-router/src/core/http-exception.test.ts +0 -45
  241. package/plugins/acryl-router/src/core/middleware/basic-auth/index.test.ts +0 -434
  242. package/plugins/acryl-router/src/core/middleware/bearer-auth/index.test.ts +0 -1018
  243. package/plugins/acryl-router/src/core/middleware/body-limit/index.test.ts +0 -273
  244. package/plugins/acryl-router/src/core/middleware/cache/index.test.ts +0 -1211
  245. package/plugins/acryl-router/src/core/middleware/combine/index.test.ts +0 -355
  246. package/plugins/acryl-router/src/core/middleware/compress/index.test.ts +0 -535
  247. package/plugins/acryl-router/src/core/middleware/context-storage/index.test.ts +0 -43
  248. package/plugins/acryl-router/src/core/middleware/cors/index.test.ts +0 -532
  249. package/plugins/acryl-router/src/core/middleware/csrf/index.test.ts +0 -534
  250. package/plugins/acryl-router/src/core/middleware/etag/digest.test.ts +0 -21
  251. package/plugins/acryl-router/src/core/middleware/etag/index.test.ts +0 -503
  252. package/plugins/acryl-router/src/core/middleware/ip-restriction/index.test.ts +0 -217
  253. package/plugins/acryl-router/src/core/middleware/jwk/index.test.ts +0 -1135
  254. package/plugins/acryl-router/src/core/middleware/jwk/keys.test.json +0 -48
  255. package/plugins/acryl-router/src/core/middleware/jwt/index.test.ts +0 -802
  256. package/plugins/acryl-router/src/core/middleware/language/index.test.ts +0 -552
  257. package/plugins/acryl-router/src/core/middleware/logger/index.test.ts +0 -204
  258. package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.test.ts +0 -292
  259. package/plugins/acryl-router/src/core/middleware/method-override/index.test.ts +0 -221
  260. package/plugins/acryl-router/src/core/middleware/mount/index.test.ts +0 -334
  261. package/plugins/acryl-router/src/core/middleware/powered-by/index.test.ts +0 -37
  262. package/plugins/acryl-router/src/core/middleware/pretty-json/index.test.ts +0 -218
  263. package/plugins/acryl-router/src/core/middleware/request-id/index.test.ts +0 -168
  264. package/plugins/acryl-router/src/core/middleware/secure-headers/index.test.ts +0 -671
  265. package/plugins/acryl-router/src/core/middleware/serve-static/index.test.ts +0 -450
  266. package/plugins/acryl-router/src/core/middleware/serve-static/path.test.ts +0 -50
  267. package/plugins/acryl-router/src/core/middleware/timeout/index.test.ts +0 -70
  268. package/plugins/acryl-router/src/core/middleware/timing/index.test.ts +0 -310
  269. package/plugins/acryl-router/src/core/middleware/trailing-slash/index.test.ts +0 -381
  270. package/plugins/acryl-router/src/core/preset/quick.test.ts +0 -42
  271. package/plugins/acryl-router/src/core/preset/tiny.test.ts +0 -42
  272. package/plugins/acryl-router/src/core/request.test.ts +0 -751
  273. package/plugins/acryl-router/src/core/router/common.case.test.ts +0 -1132
  274. package/plugins/acryl-router/src/core/router/linear-router/router.test.ts +0 -80
  275. package/plugins/acryl-router/src/core/router/pattern-router/router.test.ts +0 -46
  276. package/plugins/acryl-router/src/core/router/reg-exp-router/prepared-router.test.ts +0 -154
  277. package/plugins/acryl-router/src/core/router/reg-exp-router/router.test.ts +0 -265
  278. package/plugins/acryl-router/src/core/router/smart-router/router.test.ts +0 -13
  279. package/plugins/acryl-router/src/core/router/trie-router/node.test.ts +0 -896
  280. package/plugins/acryl-router/src/core/router/trie-router/router.test.ts +0 -8
  281. package/plugins/acryl-router/src/core/types.test.ts +0 -4134
  282. package/plugins/acryl-router/src/core/utils/accept.test.ts +0 -299
  283. package/plugins/acryl-router/src/core/utils/basic-auth.test.ts +0 -56
  284. package/plugins/acryl-router/src/core/utils/body.test.ts +0 -496
  285. package/plugins/acryl-router/src/core/utils/buffer.test.ts +0 -147
  286. package/plugins/acryl-router/src/core/utils/color.test.ts +0 -23
  287. package/plugins/acryl-router/src/core/utils/concurrent.test.ts +0 -69
  288. package/plugins/acryl-router/src/core/utils/cookie.test.ts +0 -460
  289. package/plugins/acryl-router/src/core/utils/crypto.test.ts +0 -43
  290. package/plugins/acryl-router/src/core/utils/encode.test.ts +0 -61
  291. package/plugins/acryl-router/src/core/utils/filepath.test.ts +0 -78
  292. package/plugins/acryl-router/src/core/utils/html.test.ts +0 -16
  293. package/plugins/acryl-router/src/core/utils/ipaddr.test.ts +0 -149
  294. package/plugins/acryl-router/src/core/utils/jwt/jwa.test.ts +0 -86
  295. package/plugins/acryl-router/src/core/utils/jwt/jwt.test.ts +0 -1788
  296. package/plugins/acryl-router/src/core/utils/mime.test.ts +0 -64
  297. package/plugins/acryl-router/src/core/utils/stream.test.ts +0 -212
  298. package/plugins/acryl-router/src/core/utils/types.test.ts +0 -295
  299. package/plugins/acryl-router/src/core/utils/url.test.ts +0 -384
  300. package/plugins/acryl-router/src/core/validator/utils.test.ts +0 -82
  301. package/plugins/acryl-router/src/core/validator/validator.test.ts +0 -1488
  302. package/plugins/acryl-router/tests/docs-examples.spec.ts +0 -152
  303. package/plugins/acryl-router/tests/live-install.spec.ts +0 -64
  304. package/plugins/acryl-router/tests/loader.spec.ts +0 -72
  305. package/plugins/acryl-router/tests/loopback-only.spec.ts +0 -80
  306. package/plugins/acryl-router/tests/no-dom/consumer.ts +0 -11
  307. package/plugins/acryl-router/tests/no-dom/tsconfig.json +0 -13
  308. package/plugins/acryl-router/tests/service.spec.ts +0 -426
  309. package/plugins/acryl-router/tests/shadow-state.spec.ts +0 -79
  310. package/plugins/acryl-router-http/tests/adapter.spec.ts +0 -174
  311. package/plugins/acryl-router-http/tests/stream.spec.ts +0 -133
  312. package/plugins/acryl-router-http/tests/surfaces.spec.ts +0 -101
  313. package/plugins/acryl-state/src/react.ts +0 -11
  314. package/plugins/acryl-state/tests/cell.spec.ts +0 -71
  315. package/plugins/acryl-state/tests/react.spec.ts +0 -39
  316. package/plugins/acryl-state/tests/service.spec.ts +0 -132
@@ -1,1488 +0,0 @@
1
- /* eslint-disable @typescript-eslint/no-unused-vars */
2
- import * as z from 'zod'
3
- import type { Context } from '../context.ts'
4
- import { Acryl } from '../app.ts'
5
- import { HTTPException } from '../http-exception.ts'
6
- import { cloneRawRequest } from '../request.ts'
7
- import type {
8
- ErrorHandler,
9
- ExtractSchema,
10
- ExtractSchemaForStatusCode,
11
- FormValue,
12
- MiddlewareHandler,
13
- TypedResponse,
14
- ValidationTargets,
15
- } from '../types.ts'
16
- import type { ContentfulStatusCode, StatusCode } from '../utils/http-status.ts'
17
- import type { Equal, Expect } from '../utils/types.ts'
18
- import type { ValidationFunction } from './validator.ts'
19
- import { validator } from './validator.ts'
20
-
21
- // Helper type to extract the response type from the validation function
22
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
23
- type InferValidatorResponse<VF> = VF extends (value: any, c: any) => infer R
24
- ? R extends Promise<infer PR>
25
- ? // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
- PR extends Response | TypedResponse<any, any, any>
27
- ? PR
28
- : never
29
- : // eslint-disable-next-line @typescript-eslint/no-explicit-any
30
- R extends Response | TypedResponse<any, any, any>
31
- ? R
32
- : never
33
- : never
34
-
35
- // Reference implementation for only testing
36
- const zodValidator = <
37
- T extends z.ZodSchema,
38
- E extends {},
39
- P extends string,
40
- Target extends keyof ValidationTargets,
41
- >(
42
- target: Target,
43
- schema: T
44
- ) => {
45
- const validationFunc = (value: unknown, c: Context<E, P>) => {
46
- const result = schema.safeParse(value)
47
- if (!result.success) {
48
- return c.text('Invalid!', 400)
49
- }
50
- return result.data as z.output<T>
51
- }
52
-
53
- type ResponseType = InferValidatorResponse<typeof validationFunc>
54
-
55
- return validator(target, validationFunc) as MiddlewareHandler<
56
- E,
57
- P,
58
- { in: { [K in Target]: z.input<T> }; out: { [K in Target]: z.output<T> } },
59
- ResponseType
60
- >
61
- }
62
-
63
- describe('Basic', () => {
64
- const app = new Acryl()
65
-
66
- const route = app.get(
67
- '/search',
68
- async (_c, next) => {
69
- await next()
70
- },
71
- validator('query', (value, c) => {
72
- type verify = Expect<Equal<Record<string, string | string[]>, typeof value>>
73
- if (!value.q) {
74
- return c.text('Invalid!', 400)
75
- }
76
- }),
77
- (c) => {
78
- return c.text('Valid!', 200)
79
- }
80
- )
81
-
82
- type Expected = {
83
- '/search': {
84
- $get:
85
- | {
86
- input: {
87
- query: {}
88
- }
89
- output: 'Invalid!'
90
- outputFormat: 'text'
91
- status: 400
92
- }
93
- | {
94
- input: {
95
- query: {}
96
- }
97
- output: 'Valid!'
98
- outputFormat: 'text'
99
- status: 200
100
- }
101
- }
102
- }
103
-
104
- type Actual = ExtractSchema<typeof route>
105
-
106
- type verify = Expect<Equal<Expected, Actual>>
107
-
108
- it('Should return 200 response', async () => {
109
- const res = await app.request('http://localhost/search?q=foo')
110
- expect(res.status).toBe(200)
111
- })
112
-
113
- it('Should return 400 response', async () => {
114
- const res = await app.request('http://localhost/search')
115
- expect(res.status).toBe(400)
116
- })
117
- })
118
-
119
- const onErrorHandler: ErrorHandler = (e, c) => {
120
- if (e instanceof HTTPException) {
121
- return c.json({ message: e.message, success: false }, e.status)
122
- }
123
- return c.json({ message: e.message }, 500)
124
- }
125
-
126
- describe('JSON', () => {
127
- const app = new Acryl()
128
- app.post(
129
- '/post',
130
- validator('json', (value) => value),
131
- (c) => {
132
- return c.json(c.req.valid('json'))
133
- }
134
- )
135
-
136
- it('Should return 200 response with a valid JSON data', async () => {
137
- const res = await app.request('http://localhost/post', {
138
- method: 'POST',
139
- body: JSON.stringify({ foo: 'bar' }),
140
- headers: {
141
- 'Content-Type': 'application/json',
142
- },
143
- })
144
- expect(res.status).toBe(200)
145
- const data = await res.json()
146
- expect(data).toEqual({ foo: 'bar' })
147
- })
148
-
149
- it('Should validate if Content-Type media type is mixed-case', async () => {
150
- const res = await app.request('http://localhost/post', {
151
- method: 'POST',
152
- body: JSON.stringify({ foo: 'bar' }),
153
- headers: {
154
- 'Content-Type': 'Application/JSON',
155
- },
156
- })
157
- expect(res.status).toBe(200)
158
- expect(await res.json()).toEqual({ foo: 'bar' })
159
- })
160
-
161
- it('Should not validate if Content-Type is not set', async () => {
162
- const res = await app.request('http://localhost/post', {
163
- method: 'POST',
164
- body: JSON.stringify({ foo: 'bar' }),
165
- })
166
- expect(res.status).toBe(200)
167
- const data = await res.json()
168
- expect(data.foo).toBeUndefined()
169
- })
170
-
171
- it('Should not validate if Content-Type is wrong', async () => {
172
- const res = await app.request('http://localhost/post', {
173
- method: 'POST',
174
- headers: {
175
- 'Content-Type': 'text/plain;charset=utf-8',
176
- },
177
- body: JSON.stringify({ foo: 'bar' }),
178
- })
179
- expect(res.status).toBe(200)
180
- })
181
-
182
- it('Should validate if Content-Type is a application/json with a charset', async () => {
183
- const res = await app.request('http://localhost/post', {
184
- method: 'POST',
185
- headers: {
186
- 'Content-Type': 'application/json',
187
- },
188
- body: JSON.stringify({ foo: 'bar' }),
189
- })
190
- expect(res.status).toBe(200)
191
- expect(await res.json()).toEqual({ foo: 'bar' })
192
- })
193
-
194
- it('Should validate if Content-Type is a subtype like application/merge-patch+json', async () => {
195
- const res = await app.request('http://localhost/post', {
196
- method: 'POST',
197
- headers: {
198
- 'Content-Type': 'application/merge-patch+json',
199
- },
200
- body: JSON.stringify({ foo: 'bar' }),
201
- })
202
- expect(res.status).toBe(200)
203
- expect(await res.json()).toEqual({ foo: 'bar' })
204
- })
205
-
206
- it('Should validate if Content-Type is application/vnd.api+json', async () => {
207
- const res = await app.request('http://localhost/post', {
208
- method: 'POST',
209
- headers: {
210
- 'Content-Type': 'application/vnd.api+json',
211
- },
212
- body: JSON.stringify({ foo: 'bar' }),
213
- })
214
- expect(res.status).toBe(200)
215
- expect(await res.json()).toEqual({ foo: 'bar' })
216
- })
217
-
218
- it('Should not validate if Content-Type does not start with application/json', async () => {
219
- const res = await app.request('http://localhost/post', {
220
- method: 'POST',
221
- headers: {
222
- 'Content-Type': 'Xapplication/json',
223
- },
224
- body: JSON.stringify({ foo: 'bar' }),
225
- })
226
- expect(res.status).toBe(200)
227
- const data = await res.json()
228
- expect(data.foo).toBeUndefined()
229
- })
230
- })
231
-
232
- describe('Malformed JSON', () => {
233
- const app = new Acryl()
234
- app.post(
235
- '/post',
236
- validator('json', (value) => value),
237
- (c) => {
238
- return c.json(c.req.valid('json'))
239
- }
240
- )
241
-
242
- it('Should return 400 response if the body data is not a valid JSON', async () => {
243
- const formData = new FormData()
244
- formData.append('foo', 'bar')
245
- const res = await app.request('http://localhost/post', {
246
- method: 'POST',
247
- headers: {
248
- 'Content-Type': 'application/json',
249
- },
250
- body: formData,
251
- })
252
- expect(res.status).toBe(400)
253
- })
254
- })
255
-
256
- describe('FormData', () => {
257
- const app = new Acryl()
258
- app.post(
259
- '/post',
260
- validator('form', (value) => value),
261
- (c) => {
262
- return c.json(c.req.valid('form'))
263
- }
264
- )
265
-
266
- it('Should return 200 response with a valid form data', async () => {
267
- const formData = new FormData()
268
- formData.append('message', 'hi')
269
- const res = await app.request('http://localhost/post', {
270
- method: 'POST',
271
- body: formData,
272
- })
273
- expect(res.status).toBe(200)
274
- expect(await res.json()).toEqual({ message: 'hi' })
275
- })
276
-
277
- it('Should validate a URL Encoded Data', async () => {
278
- const params = new URLSearchParams()
279
- params.append('foo', 'bar')
280
- const res = await app.request('/post', {
281
- method: 'POST',
282
- body: params,
283
- headers: {
284
- 'content-type': 'application/x-www-form-urlencoded',
285
- },
286
- })
287
- expect(res.status).toBe(200)
288
- expect(await res.json()).toEqual({
289
- foo: 'bar',
290
- })
291
- })
292
-
293
- it('Should validate mixed-case URL Encoded Content-Type', async () => {
294
- const params = new URLSearchParams()
295
- params.append('foo', 'bar')
296
- const res = await app.request('/post', {
297
- method: 'POST',
298
- body: params,
299
- headers: {
300
- 'content-type': 'Application/X-WWW-Form-Urlencoded; charset=UTF-8',
301
- },
302
- })
303
- expect(res.status).toBe(200)
304
- expect(await res.json()).toEqual({
305
- foo: 'bar',
306
- })
307
- })
308
-
309
- it('Should validate mixed-case multipart/form-data Content-Type', async () => {
310
- const formData = new FormData()
311
- formData.append('foo', 'bar')
312
- const source = new Request('http://localhost/post', { method: 'POST', body: formData })
313
- const contentType = source.headers
314
- .get('Content-Type')!
315
- .replace('multipart/form-data', 'Multipart/Form-Data')
316
- const res = await app.request('/post', {
317
- method: 'POST',
318
- body: await source.arrayBuffer(),
319
- headers: {
320
- 'content-type': contentType,
321
- },
322
- })
323
- expect(res.status).toBe(200)
324
- expect(await res.json()).toEqual({
325
- foo: 'bar',
326
- })
327
- })
328
-
329
- it('Should validate if Content-Type is a application/x-www-form-urlencoded with a charset', async () => {
330
- const params = new URLSearchParams()
331
- params.append('foo', 'bar')
332
- const res = await app.request('/post', {
333
- method: 'POST',
334
- body: params,
335
- headers: {
336
- 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
337
- },
338
- })
339
- expect(res.status).toBe(200)
340
- expect(await res.json()).toEqual({
341
- foo: 'bar',
342
- })
343
- })
344
-
345
- it('Should return `foo[]` as an array', async () => {
346
- const form = new FormData()
347
- form.append('foo[]', 'bar1')
348
- form.append('foo[]', 'bar2')
349
- const res = await app.request('/post', {
350
- method: 'POST',
351
- body: form,
352
- })
353
- expect(res.status).toBe(200)
354
- expect(await res.json()).toEqual({
355
- 'foo[]': ['bar1', 'bar2'],
356
- })
357
- })
358
-
359
- it('Should return `foo` as an array if multiple values are appended', async () => {
360
- const form = new FormData()
361
- form.append('foo', 'bar1')
362
- form.append('foo', 'bar2')
363
- form.append('foo', 'bar3')
364
- const res = await app.request('/post', {
365
- method: 'POST',
366
- body: form,
367
- })
368
- expect(res.status).toBe(200)
369
- expect(await res.json()).toEqual({
370
- foo: ['bar1', 'bar2', 'bar3'],
371
- })
372
- })
373
- })
374
-
375
- describe('Malformed FormData request', () => {
376
- const app = new Acryl()
377
- app.post(
378
- '/post',
379
- validator('form', (value) => value),
380
- (c) => {
381
- return c.json(c.req.valid('form'))
382
- }
383
- )
384
- app.onError(onErrorHandler)
385
-
386
- it('Should return 400 response, for malformed content type header', async () => {
387
- const res = await app.request('http://localhost/post', {
388
- method: 'POST',
389
- body: 'hi',
390
- headers: {
391
- 'content-type': 'multipart/form-data',
392
- },
393
- })
394
- expect(res.status).toBe(400)
395
- const data = await res.json()
396
- expect(data['success']).toBe(false)
397
- expect(data['message']).toMatch(/^Malformed FormData request./)
398
- })
399
- })
400
-
401
- describe('JSON and FormData', () => {
402
- const app = new Acryl()
403
- app.post(
404
- '/',
405
- validator('json', (value) => value),
406
- validator('form', (value) => value),
407
- async (c) => {
408
- const jsonData = c.req.valid('json')
409
- const formData = c.req.valid('form')
410
- return c.json({
411
- json: jsonData,
412
- form: formData,
413
- })
414
- }
415
- )
416
-
417
- it('Should validate a JSON request', async () => {
418
- const res = await app.request('/', {
419
- method: 'POST',
420
- body: JSON.stringify({ foo: 'bar' }),
421
- headers: {
422
- 'Content-Type': 'application/json',
423
- },
424
- })
425
- expect(res.status).toBe(200)
426
- const data = await res.json()
427
- expect(data.json).toEqual({ foo: 'bar' })
428
- })
429
-
430
- it('Should validate a FormData request', async () => {
431
- const form = new FormData()
432
- form.append('foo', 'bar')
433
- const res = await app.request('/', {
434
- method: 'POST',
435
- body: form,
436
- })
437
- expect(res.status).toBe(200)
438
- const data = await res.json()
439
- expect(data.form).toEqual({ foo: 'bar' })
440
- })
441
- })
442
-
443
- describe('Cached contents', () => {
444
- describe('json', () => {
445
- const app = new Acryl()
446
- const bodyTypes = ['json', 'text', 'arrayBuffer', 'blob']
447
-
448
- for (const type of bodyTypes) {
449
- app.post(
450
- `/${type}`,
451
- async (c, next) => {
452
- // @ts-expect-error not type safe
453
- await c.req[type]()
454
- await next()
455
- },
456
- validator('json', (value) => {
457
- if (value instanceof Promise) {
458
- throw new Error('Value is Promise')
459
- }
460
- return value
461
- }),
462
- async (c) => {
463
- const data = await c.req.json()
464
- return c.json(data, 200)
465
- }
466
- )
467
- }
468
-
469
- for (const type of bodyTypes) {
470
- const endpoint = `/${type}`
471
- it(`Should return the cached JSON content - ${endpoint}`, async () => {
472
- const res = await app.request(endpoint, {
473
- method: 'POST',
474
- body: JSON.stringify({ message: 'Hello' }),
475
- headers: {
476
- 'Content-Type': 'application/json',
477
- },
478
- })
479
- expect(res.status).toBe(200)
480
- expect(await res.json()).toEqual({ message: 'Hello' })
481
- })
482
- }
483
- })
484
-
485
- describe('Cached content', () => {
486
- const app = new Acryl()
487
- const bodyTypes = ['formData', 'text', 'arrayBuffer', 'blob']
488
-
489
- for (const type of bodyTypes) {
490
- app.post(
491
- `/${type}`,
492
- async (c, next) => {
493
- // @ts-expect-error not type safe
494
- await c.req[type]()
495
- await next()
496
- },
497
- validator('form', (value) => {
498
- if (value instanceof Promise) {
499
- throw new Error('Value is Promise')
500
- }
501
- return value
502
- }),
503
- async (c) => {
504
- return c.json({ message: 'OK' }, 200)
505
- }
506
- )
507
- }
508
-
509
- for (const type of bodyTypes) {
510
- const endpoint = `/${type}`
511
- it(`Should return the cached FormData content - ${endpoint}`, async () => {
512
- const form = new FormData()
513
- form.append('message', 'Hello')
514
- const res = await app.request(endpoint, {
515
- method: 'POST',
516
- body: form,
517
- })
518
- expect(res.status).toBe(200)
519
- expect(await res.json()).toEqual({ message: 'OK' })
520
- })
521
- }
522
- })
523
- })
524
-
525
- describe('Validator middleware with a custom validation function', () => {
526
- const app = new Acryl()
527
-
528
- const validationFunction: ValidationFunction<{ id: string }, { id: number }> = (v) => {
529
- return {
530
- id: Number(v.id),
531
- }
532
- }
533
-
534
- const route = app.post('/post', validator('json', validationFunction), (c) => {
535
- const post = c.req.valid('json')
536
- type Expected = {
537
- id: number
538
- }
539
- type verify = Expect<Equal<Expected, typeof post>>
540
- return c.json({
541
- post,
542
- })
543
- })
544
-
545
- type Expected = {
546
- '/post': {
547
- $post:
548
- | {
549
- input: {
550
- json: {
551
- id: number
552
- }
553
- }
554
- output: {}
555
- outputFormat: string
556
- status: StatusCode
557
- }
558
- | {
559
- input: {
560
- json: {
561
- id: number
562
- }
563
- }
564
- output: {
565
- post: {
566
- id: number
567
- }
568
- }
569
- outputFormat: 'json'
570
- status: ContentfulStatusCode
571
- }
572
- }
573
- }
574
-
575
- type Actual = ExtractSchema<typeof route>
576
- type verify2 = Expect<Equal<Expected, Actual>>
577
-
578
- it('Should validate JSON with transformation and return 200 response', async () => {
579
- const res = await app.request('http://localhost/post', {
580
- method: 'POST',
581
- headers: {
582
- 'Content-Type': 'application/json',
583
- },
584
- body: JSON.stringify({
585
- id: '123',
586
- }),
587
- })
588
- expect(res.status).toBe(200)
589
- expect(await res.json()).toEqual({
590
- post: {
591
- id: 123,
592
- },
593
- })
594
- })
595
- })
596
-
597
- describe('Validator middleware with Zod validates JSON', () => {
598
- const app = new Acryl()
599
-
600
- const schema = z.object({
601
- id: z.number(),
602
- title: z.string(),
603
- })
604
-
605
- const route = app.post('/post', zodValidator('json', schema), (c) => {
606
- const post = c.req.valid('json')
607
- type Expected = {
608
- id: number
609
- title: string
610
- }
611
- type verify = Expect<Equal<Expected, typeof post>>
612
- return c.json({
613
- post: post,
614
- })
615
- })
616
-
617
- type Expected = {
618
- '/post': {
619
- $post:
620
- | {
621
- input: {
622
- json: {
623
- id: number
624
- title: string
625
- }
626
- }
627
- output: 'Invalid!'
628
- outputFormat: 'text'
629
- status: 400
630
- }
631
- | {
632
- input: {
633
- json: {
634
- id: number
635
- title: string
636
- }
637
- }
638
- output: {
639
- post: {
640
- id: number
641
- title: string
642
- }
643
- }
644
- outputFormat: 'json'
645
- status: ContentfulStatusCode
646
- }
647
- }
648
- }
649
-
650
- type Actual = ExtractSchema<typeof route>
651
-
652
- type verify2 = Expect<Equal<Expected, Actual>>
653
-
654
- it('Should validate JSON and return 200 response', async () => {
655
- const res = await app.request('http://localhost/post', {
656
- method: 'POST',
657
- headers: {
658
- 'Content-Type': 'application/json',
659
- },
660
- body: JSON.stringify({
661
- id: 123,
662
- title: 'Hello',
663
- }),
664
- })
665
- expect(res.status).toBe(200)
666
- expect(await res.json()).toEqual({
667
- post: {
668
- id: 123,
669
- title: 'Hello',
670
- },
671
- })
672
- })
673
-
674
- it('Should validate JSON and return 400 response', async () => {
675
- const res = await app.request('http://localhost/post', {
676
- method: 'POST',
677
- headers: {
678
- 'Content-Type': 'application/json',
679
- },
680
- body: JSON.stringify({
681
- id: '123',
682
- title: 'Hello',
683
- }),
684
- })
685
- expect(res.status).toBe(400)
686
- expect(await res.text()).toBe('Invalid!')
687
- })
688
- })
689
-
690
- describe('Validator middleware with Zod validates Form data', () => {
691
- const app = new Acryl()
692
-
693
- const schema = z.object({
694
- id: z.string(),
695
- title: z.string(),
696
- })
697
- app.post('/post', zodValidator('form', schema), (c) => {
698
- const post = c.req.valid('form')
699
- return c.json({
700
- post: post,
701
- })
702
- })
703
-
704
- it('Should validate Form data and return 200 response', async () => {
705
- const form = new FormData()
706
- form.append('id', '123')
707
- form.append('title', 'Hello')
708
- const res = await app.request('http://localhost/post', {
709
- method: 'POST',
710
- body: form,
711
- })
712
- expect(res.status).toBe(200)
713
- expect(await res.json()).toEqual({
714
- post: {
715
- id: '123',
716
- title: 'Hello',
717
- },
718
- })
719
- })
720
-
721
- it('Should validate Form data and return 400 response', async () => {
722
- const res = await app.request('http://localhost/post', {
723
- method: 'POST',
724
- })
725
- expect(res.status).toBe(400)
726
- expect(await res.text()).toBe('Invalid!')
727
- })
728
- })
729
-
730
- describe('Validator middleware with Zod validates query params', () => {
731
- const app = new Acryl()
732
-
733
- const schema = z.object({
734
- page: z
735
- .string()
736
- .refine((v) => {
737
- return !isNaN(Number(v))
738
- })
739
- .transform((v) => {
740
- return Number(v)
741
- }),
742
- tag: z.array(z.string()),
743
- })
744
-
745
- app.get('/search', zodValidator('query', schema), (c) => {
746
- const res = c.req.valid('query')
747
- return c.json({
748
- page: res.page,
749
- tags: res.tag,
750
- })
751
- })
752
-
753
- it('Should validate query params and return 200 response', async () => {
754
- const res = await app.request('http://localhost/search?page=123&tag=a&tag=b')
755
- expect(res.status).toBe(200)
756
- expect(await res.json()).toEqual({
757
- page: 123,
758
- tags: ['a', 'b'],
759
- })
760
- })
761
-
762
- it('Should validate query params and return 400 response', async () => {
763
- const res = await app.request('http://localhost/search?page=onetwothree')
764
- expect(res.status).toBe(400)
765
- expect(await res.text()).toBe('Invalid!')
766
- })
767
- })
768
-
769
- describe('Validator middleware with Zod validates param', () => {
770
- const app = new Acryl()
771
-
772
- const schema = z.object({
773
- id: z
774
- .string()
775
- .regex(/^[0-9]+$/)
776
- .transform((v) => {
777
- return Number(v)
778
- }),
779
- title: z.string(),
780
- })
781
- app.get('/users/:id/books/:title', zodValidator('param', schema), (c) => {
782
- const param = c.req.valid('param')
783
- return c.json({
784
- param: param,
785
- })
786
- })
787
-
788
- it('Should validate Form data and return 200 response', async () => {
789
- const res = await app.request('http://localhost/users/123/books/Hello')
790
- expect(res.status).toBe(200)
791
- expect(await res.json()).toEqual({
792
- param: {
793
- id: 123,
794
- title: 'Hello',
795
- },
796
- })
797
- })
798
-
799
- it('Should validate Form data and return 400 response', async () => {
800
- const res = await app.request('http://localhost/users/0.123/books/Hello')
801
- expect(res.status).toBe(400)
802
- expect(await res.text()).toBe('Invalid!')
803
- })
804
- })
805
-
806
- describe('Validator middleware with Zod validates header values', () => {
807
- const app = new Acryl()
808
-
809
- const schema = z.object({
810
- 'x-request-id': z.string().uuid(),
811
- })
812
-
813
- app.get('/ping', zodValidator('header', schema), (c) => {
814
- const data = c.req.valid('header')
815
- const xRequestId = data['x-request-id']
816
- return c.json({
817
- xRequestId,
818
- })
819
- })
820
-
821
- it('Should validate header values and return 200 response', async () => {
822
- const res = await app.request('/ping', {
823
- headers: {
824
- 'x-request-id': '6ec0bd7f-11c0-43da-975e-2a8ad9ebae0b',
825
- },
826
- })
827
- expect(res.status).toBe(200)
828
- expect(await res.json()).toEqual({
829
- xRequestId: '6ec0bd7f-11c0-43da-975e-2a8ad9ebae0b',
830
- })
831
- })
832
-
833
- it('Should validate header values and return 400 response', async () => {
834
- const res = await app.request('http://localhost/ping', {
835
- headers: {
836
- 'x-request-id': 'invalid-key',
837
- },
838
- })
839
- expect(res.status).toBe(400)
840
- expect(await res.text()).toBe('Invalid!')
841
- })
842
- })
843
-
844
- describe('Validator middleware with Zod validates cookies', () => {
845
- const app = new Acryl()
846
-
847
- const schema = z.object({
848
- debug: z.enum(['0', '1']),
849
- })
850
-
851
- app.get('/api/user', zodValidator('cookie', schema), (c) => {
852
- const { debug } = c.req.valid('cookie')
853
- return c.json({
854
- debug,
855
- })
856
- })
857
-
858
- it('Should validate cookies and return 200 response', async () => {
859
- const res = await app.request('/api/user', {
860
- headers: {
861
- Cookie: 'debug=0',
862
- },
863
- })
864
- expect(res.status).toBe(200)
865
- expect(await res.json()).toEqual({
866
- debug: '0',
867
- })
868
- })
869
-
870
- it('Should validate cookies and return 400 response', async () => {
871
- const res = await app.request('/api/user', {
872
- headers: {
873
- Cookie: 'debug=true',
874
- },
875
- })
876
- expect(res.status).toBe(400)
877
- expect(await res.text()).toBe('Invalid!')
878
- })
879
- })
880
-
881
- describe('Validator middleware with Zod multiple validators', () => {
882
- const app = new Acryl<{ Variables: { id: number } }>()
883
- const route = app.post(
884
- '/posts',
885
- zodValidator(
886
- 'query',
887
- z.object({
888
- page: z
889
- .string()
890
- .refine((v) => {
891
- return !isNaN(Number(v))
892
- })
893
- .transform((v) => {
894
- return Number(v)
895
- }),
896
- })
897
- ),
898
- zodValidator(
899
- 'form',
900
- z.object({
901
- title: z.string(),
902
- })
903
- ),
904
- (c) => {
905
- const id = c.get('id')
906
- type verify = Expect<Equal<number, typeof id>>
907
- const formValidatedData = c.req.valid('form')
908
- type verify2 = Expect<Equal<{ title: string }, typeof formValidatedData>>
909
- const { page } = c.req.valid('query')
910
- const { title } = c.req.valid('form')
911
- return c.json({ page, title })
912
- }
913
- )
914
-
915
- type Actual = ExtractSchema<typeof route>
916
-
917
- type Expected = {
918
- '/posts': {
919
- $post:
920
- | {
921
- input: {
922
- query: {
923
- page: string
924
- }
925
- } & {
926
- form: {
927
- title: string
928
- }
929
- }
930
- output: 'Invalid!'
931
- outputFormat: 'text'
932
- status: 400
933
- }
934
- | {
935
- input: {
936
- query: {
937
- page: string
938
- }
939
- } & {
940
- form: {
941
- title: string
942
- }
943
- }
944
- output: {
945
- page: number
946
- title: string
947
- }
948
- outputFormat: 'json'
949
- status: ContentfulStatusCode
950
- }
951
- }
952
- }
953
-
954
- type verify = Expect<Equal<Expected, Actual>>
955
-
956
- it('Should validate both query param and form data and return 200 response', async () => {
957
- const form = new FormData()
958
- form.append('title', 'Hello')
959
- const res = await app.request('http://localhost/posts?page=2', {
960
- method: 'POST',
961
- body: form,
962
- })
963
- expect(res.status).toBe(200)
964
- expect(await res.json()).toEqual({
965
- page: 2,
966
- title: 'Hello',
967
- })
968
- })
969
-
970
- it('Should validate both query param and form data and return 400 response', async () => {
971
- const res = await app.request('http://localhost/posts?page=2', {
972
- method: 'POST',
973
- })
974
- expect(res.status).toBe(400)
975
- expect(await res.text()).toBe('Invalid!')
976
- })
977
- })
978
-
979
- it('With path parameters', () => {
980
- const app = new Acryl()
981
-
982
- const route = app.put(
983
- '/posts/:id',
984
- validator('param', () => {
985
- return {
986
- id: '123',
987
- }
988
- }),
989
- validator('form', () => {
990
- return {
991
- title: 'Foo',
992
- }
993
- }),
994
- (c) => {
995
- return c.text('Valid!')
996
- }
997
- )
998
-
999
- type Expected = {
1000
- '/posts/:id': {
1001
- $put: {
1002
- input: {
1003
- form: {
1004
- title: FormValue | FormValue[]
1005
- }
1006
- } & {
1007
- param: {
1008
- id: string
1009
- }
1010
- }
1011
- output: 'Valid!'
1012
- outputFormat: 'text'
1013
- status: ContentfulStatusCode
1014
- }
1015
- }
1016
- }
1017
-
1018
- type Actual = ExtractSchema<typeof route>
1019
- type verify = Expect<Equal<Expected, Actual>>
1020
- })
1021
-
1022
- it('`on`', () => {
1023
- const app = new Acryl()
1024
-
1025
- const route = app.on(
1026
- 'PURGE',
1027
- '/purge',
1028
- validator('form', () => {
1029
- return {
1030
- tag: 'foo',
1031
- }
1032
- }),
1033
- validator('query', () => {
1034
- return {
1035
- q: 'bar',
1036
- }
1037
- }),
1038
- (c) => {
1039
- return c.json({
1040
- success: true,
1041
- })
1042
- }
1043
- )
1044
-
1045
- type Expected = {
1046
- '/purge': {
1047
- $purge: {
1048
- input: {
1049
- form: {
1050
- tag: FormValue | FormValue[]
1051
- }
1052
- } & {
1053
- query: {
1054
- q: string | string[]
1055
- }
1056
- }
1057
- output: {
1058
- success: true
1059
- }
1060
- outputFormat: 'json'
1061
- status: ContentfulStatusCode
1062
- }
1063
- }
1064
- }
1065
-
1066
- type Actual = ExtractSchema<typeof route>
1067
- type verify = Expect<Equal<Expected, Actual>>
1068
- })
1069
-
1070
- it('`app.on`', () => {
1071
- const app = new Acryl()
1072
-
1073
- const route = app
1074
- .get(
1075
- '/posts',
1076
- validator('query', () => {
1077
- return {
1078
- page: '2',
1079
- }
1080
- }),
1081
- (c) => {
1082
- return c.json({
1083
- posts: [
1084
- {
1085
- title: 'foo',
1086
- },
1087
- ],
1088
- })
1089
- }
1090
- )
1091
- .post(
1092
- validator('json', () => {
1093
- return {
1094
- title: 'Hello',
1095
- }
1096
- }),
1097
- validator('query', () => {
1098
- return {
1099
- title: 'Hello',
1100
- }
1101
- }),
1102
- (c) => {
1103
- return c.json({
1104
- success: true,
1105
- })
1106
- }
1107
- )
1108
-
1109
- type Actual = ExtractSchema<typeof route>
1110
- //type verify = Expect<Equal<Expected, Actual>>
1111
- })
1112
-
1113
- describe('Clone Request object', () => {
1114
- describe('json', () => {
1115
- const app = new Acryl()
1116
- app.post(
1117
- '/',
1118
- validator('json', () => {
1119
- return {
1120
- foo: 'bar',
1121
- }
1122
- }),
1123
- async (c) => {
1124
- // `c.req.json()` should not throw the error
1125
- await c.req.json()
1126
- // `c.req.text()` should not throw the error
1127
- await c.req.text()
1128
- // `c.req.arrayBuffer()` should not throw the error
1129
- await c.req.arrayBuffer()
1130
- // `c.req.blob()` should not throw the error
1131
- await c.req.blob()
1132
- return c.text('foo')
1133
- }
1134
- )
1135
-
1136
- it('Should not throw the error with c.req.json()', async () => {
1137
- const req = new Request('http://localhost', {
1138
- method: 'POST',
1139
- headers: {
1140
- 'Content-Type': 'application/json',
1141
- },
1142
- body: JSON.stringify({ foo: 'bar' }),
1143
- })
1144
- const res = await app.request(req)
1145
- expect(res.status).toBe(200)
1146
- })
1147
- })
1148
-
1149
- describe('form', () => {
1150
- const app = new Acryl()
1151
- app.post(
1152
- '/',
1153
- validator('form', () => {
1154
- return {
1155
- foo: 'bar',
1156
- }
1157
- }),
1158
- async (c) => {
1159
- // `c.req.parseBody()` should not throw the error
1160
- await c.req.parseBody()
1161
- // `c.req.text()` should not throw the error
1162
- await c.req.text()
1163
- // `c.req.arrayBuffer()` should not throw the error
1164
- await c.req.arrayBuffer()
1165
- // `c.req.blob()` should not throw the error
1166
- await c.req.blob()
1167
- return c.text('foo')
1168
- }
1169
- )
1170
-
1171
- app.post(
1172
- '/cached',
1173
- async (c, next) => {
1174
- await c.req.parseBody()
1175
- await next()
1176
- },
1177
- validator('form', (value) => {
1178
- if (value instanceof FormData) {
1179
- throw new Error('The value should not be a FormData')
1180
- }
1181
- return value
1182
- }),
1183
- (c) => {
1184
- const v = c.req.valid('form')
1185
- return c.json(v)
1186
- }
1187
- )
1188
-
1189
- it('Should not throw the error with c.req.parseBody()', async () => {
1190
- const body = new FormData()
1191
- body.append('foo', 'bar')
1192
- const req = new Request('http://localhost', {
1193
- method: 'POST',
1194
- body: body,
1195
- })
1196
- const res = await app.request(req)
1197
- expect(res.status).toBe(200)
1198
- })
1199
-
1200
- it('Should not be an instance of FormData if the formData is cached', async () => {
1201
- const body = new FormData()
1202
- body.append('foo', 'bar')
1203
- const req = new Request('http://localhost/cached', {
1204
- method: 'POST',
1205
- body: body,
1206
- })
1207
- const res = await app.request(req)
1208
- expect(res.status).toBe(200)
1209
- expect(await res.json()).toEqual({ foo: 'bar' })
1210
- })
1211
- })
1212
- })
1213
-
1214
- describe('Async validator function', () => {
1215
- const app = new Acryl()
1216
-
1217
- app.get(
1218
- '/posts',
1219
- validator('query', async () => {
1220
- return {
1221
- page: '1',
1222
- }
1223
- }),
1224
- (c) => {
1225
- const { page } = c.req.valid('query')
1226
- return c.json({ page })
1227
- }
1228
- )
1229
-
1230
- it('Should get the values from the async function', async () => {
1231
- const res = await app.request('/posts')
1232
- expect(res.status).toBe(200)
1233
- expect(await res.json()).toEqual({
1234
- page: '1',
1235
- })
1236
- })
1237
- })
1238
-
1239
- describe('Validator with using Zod directly', () => {
1240
- it('Should exclude Response & TypedResponse type', () => {
1241
- const testSchema = z.object({
1242
- name: z.string(),
1243
- age: z.number(),
1244
- type: z.enum(['a']),
1245
- })
1246
- const app = new Acryl()
1247
-
1248
- const route = app.post(
1249
- '/posts',
1250
- validator('json', (value, c) => {
1251
- const parsed = testSchema.safeParse(value)
1252
- if (!parsed.success) {
1253
- return c.json({ foo: 'bar' }, 401)
1254
- }
1255
- return parsed.data
1256
- }),
1257
- (c) => {
1258
- const data = c.req.valid('json')
1259
- expectTypeOf(data.name).toEqualTypeOf<string>()
1260
- return c.json(
1261
- {
1262
- message: 'Created!',
1263
- },
1264
- 201
1265
- )
1266
- }
1267
- )
1268
-
1269
- expectTypeOf<ExtractSchemaForStatusCode<typeof route, 201>>().toEqualTypeOf<{
1270
- '/posts': {
1271
- $post: {
1272
- input: {
1273
- json: {
1274
- type: 'a'
1275
- name: string
1276
- age: number
1277
- }
1278
- }
1279
- output: {
1280
- message: string
1281
- }
1282
- outputFormat: 'json'
1283
- status: 201
1284
- }
1285
- }
1286
- }>()
1287
-
1288
- expectTypeOf<ExtractSchemaForStatusCode<typeof route, 401>>().toEqualTypeOf<{
1289
- '/posts': {
1290
- $post: {
1291
- input: {
1292
- json: {
1293
- type: 'a'
1294
- name: string
1295
- age: number
1296
- }
1297
- }
1298
- output: {
1299
- foo: string
1300
- }
1301
- outputFormat: 'json'
1302
- status: 401
1303
- }
1304
- }
1305
- }>()
1306
- })
1307
- })
1308
-
1309
- describe('Transform', () => {
1310
- it('Should be number when the type is transformed', () => {
1311
- const route = new Acryl().get(
1312
- '/',
1313
- validator('query', async () => {
1314
- return {
1315
- page: 1,
1316
- }
1317
- }),
1318
- (c) => {
1319
- const { page } = c.req.valid('query')
1320
- expectTypeOf(page).toEqualTypeOf<number>()
1321
- return c.json({ page })
1322
- }
1323
- )
1324
-
1325
- type Expected = {
1326
- '/': {
1327
- $get: {
1328
- input: {
1329
- query: {
1330
- page: string | string[]
1331
- }
1332
- }
1333
- output: {
1334
- page: number
1335
- }
1336
- outputFormat: 'json'
1337
- status: ContentfulStatusCode
1338
- }
1339
- }
1340
- }
1341
-
1342
- type Actual = ExtractSchema<typeof route>
1343
- type verify = Expect<Equal<Expected, Actual>>
1344
-
1345
- // Temporary: let's see what the actual type is
1346
- type TestActual = Actual
1347
- })
1348
-
1349
- it('Should be number and union when the type is transformed', () => {
1350
- const app = new Acryl()
1351
- const route = app.get(
1352
- '/',
1353
- validator('query', () => {
1354
- return {
1355
- page: 1,
1356
- orderBy: 'asc',
1357
- } as {
1358
- page: number
1359
- orderBy: 'asc' | 'desc'
1360
- orderByWithdefault?: 'asc' | 'desc' | undefined
1361
- }
1362
- }),
1363
- (c) => {
1364
- const { page, orderBy, orderByWithdefault } = c.req.valid('query')
1365
- expectTypeOf(page).toEqualTypeOf<number>()
1366
- expectTypeOf(orderBy).toEqualTypeOf<'asc' | 'desc'>()
1367
- expectTypeOf(orderByWithdefault).toEqualTypeOf<'asc' | 'desc' | undefined>()
1368
- return c.json({ page, orderBy, orderByWithdefault })
1369
- }
1370
- )
1371
-
1372
- type Expected = {
1373
- '/': {
1374
- $get: {
1375
- input: {
1376
- query: {
1377
- page: string | string[]
1378
- orderBy: 'asc' | 'desc'
1379
- orderByWithdefault?: 'asc' | 'desc' | undefined
1380
- }
1381
- }
1382
- output: {
1383
- page: number
1384
- orderBy: 'asc' | 'desc'
1385
- orderByWithdefault: 'asc' | 'desc' | undefined
1386
- }
1387
- outputFormat: 'json'
1388
- status: ContentfulStatusCode
1389
- }
1390
- }
1391
- }
1392
-
1393
- type Actual = ExtractSchema<typeof route>
1394
- type verify = Expect<Equal<Expected, Actual>>
1395
-
1396
- // Temporary: let's see what the actual type is
1397
- type TestActual = Actual
1398
- })
1399
- })
1400
-
1401
- describe('Raw Request cloning after validation', () => {
1402
- it('Should allow the `cloneRawRequest` util to clone the request object after validation', async () => {
1403
- const app = new Acryl()
1404
-
1405
- app.post(
1406
- '/json-validation',
1407
- validator('json', (data) => data),
1408
- async (c) => {
1409
- const clonedReq = await cloneRawRequest(c.req)
1410
- const clonedJSON = await clonedReq.json()
1411
-
1412
- return c.json({
1413
- originalMethod: c.req.raw.method,
1414
- clonedMethod: clonedReq.method,
1415
- clonedUrl: clonedReq.url,
1416
- clonedHeaders: {
1417
- contentType: clonedReq.headers.get('Content-Type'),
1418
- customHeader: clonedReq.headers.get('X-Custom-Header'),
1419
- },
1420
- originalCache: c.req.raw.cache,
1421
- clonedCache: clonedReq.cache,
1422
- originalCredentials: c.req.raw.credentials,
1423
- clonedCredentials: clonedReq.credentials,
1424
- originalMode: c.req.raw.mode,
1425
- clonedMode: clonedReq.mode,
1426
- originalRedirect: c.req.raw.redirect,
1427
- clonedRedirect: clonedReq.redirect,
1428
- originalReferrerPolicy: c.req.raw.referrerPolicy,
1429
- clonedReferrerPolicy: clonedReq.referrerPolicy,
1430
- cloned: JSON.stringify(clonedJSON) === JSON.stringify(await c.req.json()),
1431
- payload: clonedJSON,
1432
- })
1433
- }
1434
- )
1435
-
1436
- const testData = { message: 'test', userId: 123 }
1437
- const res = await app.request('/json-validation', {
1438
- method: 'POST',
1439
- headers: {
1440
- 'Content-Type': 'application/json',
1441
- 'X-Custom-Header': 'test-value',
1442
- },
1443
- body: JSON.stringify(testData),
1444
- cache: 'no-cache',
1445
- credentials: 'include',
1446
- mode: 'cors',
1447
- redirect: 'follow',
1448
- referrerPolicy: 'origin',
1449
- })
1450
-
1451
- expect(res.status).toBe(200)
1452
-
1453
- const result = await res.json()
1454
-
1455
- expect(result.originalMethod).toBe('POST')
1456
- expect(result.clonedMethod).toBe('POST')
1457
- expect(result.clonedUrl).toBe('http://localhost/json-validation')
1458
- expect(result.clonedHeaders.contentType).toBe('application/json')
1459
- expect(result.clonedHeaders.customHeader).toBe('test-value')
1460
- expect(result.clonedCache).toBe(result.originalCache)
1461
- expect(result.clonedCredentials).toBe(result.originalCredentials)
1462
- expect(result.clonedMode).toBe(result.originalMode)
1463
- expect(result.clonedRedirect).toBe(result.originalRedirect)
1464
- expect(result.clonedReferrerPolicy).toBe(result.originalReferrerPolicy)
1465
- expect(result.cloned).toBe(true)
1466
- expect(result.payload).toMatchObject(testData)
1467
- })
1468
- })
1469
-
1470
- describe('Form validator prototype pollution prevention', () => {
1471
- it('should store __proto__ as data and not misdetect inherited keys', async () => {
1472
- const app = new Acryl()
1473
- app.post(
1474
- '/form',
1475
- validator('form', (value) => value),
1476
- (c) => c.json(c.req.valid('form'))
1477
- )
1478
-
1479
- const form = new FormData()
1480
- form.append('__proto__', 'evil')
1481
- form.append('toString', 'hello')
1482
-
1483
- const res = await app.request('/form', { method: 'POST', body: form })
1484
- const result = await res.json()
1485
- expect(result['__proto__']).toBe('evil')
1486
- expect(result['toString']).toBe('hello')
1487
- })
1488
- })