acrylblends 0.3.0 → 0.4.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 (315) hide show
  1. package/dist/index.d.ts +6 -1
  2. package/dist/index.js +28 -3
  3. package/dist/index.js.map +1 -1
  4. package/package.json +1 -1
  5. package/plugins/acryl-inspect/plugin.json +2 -1
  6. package/plugins/acryl-inspect/src/report.ts +16 -13
  7. package/plugins/acryl-inspect/src/service.ts +9 -7
  8. package/plugins/acryl-inspect/tests/report.spec.ts +7 -8
  9. package/plugins/acryl-inspect/tests/service.spec.ts +11 -10
  10. package/plugins/acryl-inspect/upstream.json +2 -2
  11. package/plugins/acryl-node-server/LICENSE +26 -0
  12. package/plugins/acryl-node-server/plugin.json +2 -1
  13. package/plugins/acryl-node-server/src/adapter/conninfo.ts +23 -0
  14. package/plugins/acryl-node-server/src/adapter/early-hints.ts +55 -0
  15. package/plugins/acryl-node-server/src/adapter/error.ts +11 -0
  16. package/plugins/acryl-node-server/src/adapter/headers.ts +249 -0
  17. package/plugins/acryl-node-server/src/adapter/index.ts +6 -0
  18. package/plugins/acryl-node-server/src/adapter/listener.ts +540 -0
  19. package/plugins/acryl-node-server/src/adapter/request.ts +797 -0
  20. package/plugins/acryl-node-server/src/adapter/response.ts +186 -0
  21. package/plugins/acryl-node-server/src/adapter/serve-static.ts +238 -0
  22. package/plugins/acryl-node-server/src/adapter/server.ts +36 -0
  23. package/plugins/acryl-node-server/src/adapter/types.ts +82 -0
  24. package/plugins/acryl-node-server/src/adapter/url.ts +42 -0
  25. package/plugins/acryl-node-server/src/adapter/utils/response/constants.ts +1 -0
  26. package/plugins/acryl-node-server/src/adapter/utils/response.ts +4 -0
  27. package/plugins/acryl-node-server/src/adapter/utils/stream.ts +88 -0
  28. package/plugins/acryl-node-server/src/adapter/utils.ts +96 -0
  29. package/plugins/acryl-node-server/src/adapter/websocket-types.ts +45 -0
  30. package/plugins/acryl-node-server/src/adapter/websocket.ts +375 -0
  31. package/plugins/acryl-node-server/src/index.ts +19 -6
  32. package/plugins/acryl-node-server/tests/adapter/app.ts +76 -0
  33. package/plugins/acryl-node-server/tests/adapter/assets/.static/plain.txt +1 -0
  34. package/plugins/acryl-node-server/tests/adapter/assets/favicon.ico +0 -0
  35. package/plugins/acryl-node-server/tests/adapter/assets/secret.txt +1 -0
  36. package/plugins/acryl-node-server/tests/adapter/assets/static/acryl.html +1 -0
  37. package/plugins/acryl-node-server/tests/adapter/assets/static/admin/secret.txt +1 -0
  38. package/plugins/acryl-node-server/tests/adapter/assets/static/data.json +5 -0
  39. package/plugins/acryl-node-server/tests/adapter/assets/static/extensionless +1 -0
  40. package/plugins/acryl-node-server/tests/adapter/assets/static/foo..bar.txt +0 -0
  41. package/plugins/acryl-node-server/tests/adapter/assets/static/index.html +1 -0
  42. package/plugins/acryl-node-server/tests/adapter/assets/static/plain.txt +1 -0
  43. package/plugins/acryl-node-server/tests/adapter/assets/static-with-context-path/plain.txt +1 -0
  44. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin +1 -0
  45. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.bin.br +1 -0
  46. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt +1 -0
  47. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.br +1 -0
  48. package/plugins/acryl-node-server/tests/adapter/assets/static-with-precompressed/hello.txt.zst +1 -0
  49. package/plugins/acryl-node-server/tests/adapter/conninfo.test.ts +34 -0
  50. package/plugins/acryl-node-server/tests/adapter/content-length.test.ts +248 -0
  51. package/plugins/acryl-node-server/tests/adapter/early-hints.test.ts +457 -0
  52. package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-cert.pem +23 -0
  53. package/plugins/acryl-node-server/tests/adapter/fixtures/keys/agent1-key.pem +27 -0
  54. package/plugins/acryl-node-server/tests/adapter/headers.test.ts +252 -0
  55. package/plugins/acryl-node-server/tests/adapter/helpers/request.ts +190 -0
  56. package/plugins/acryl-node-server/tests/adapter/listener.test.ts +727 -0
  57. package/plugins/acryl-node-server/tests/adapter/request-client-disconnect.test.ts +189 -0
  58. package/plugins/acryl-node-server/tests/adapter/request.test.ts +1230 -0
  59. package/plugins/acryl-node-server/tests/adapter/response.test.ts +285 -0
  60. package/plugins/acryl-node-server/tests/adapter/serve-static.test.ts +849 -0
  61. package/plugins/acryl-node-server/tests/adapter/server-socket.test.ts +790 -0
  62. package/plugins/acryl-node-server/tests/adapter/server.test.ts +1302 -0
  63. package/plugins/acryl-node-server/tests/adapter/setup.ts +16 -0
  64. package/plugins/acryl-node-server/tests/adapter/url.test.ts +76 -0
  65. package/plugins/acryl-node-server/tests/adapter/utils/response.test.ts +23 -0
  66. package/plugins/acryl-node-server/tests/adapter/utils/stream.test.ts +75 -0
  67. package/plugins/acryl-node-server/tests/adapter/utils.test.ts +235 -0
  68. package/plugins/acryl-node-server/tests/adapter/websocket.test.ts +260 -0
  69. package/plugins/acryl-node-server/tests/server.spec.ts +44 -8
  70. package/plugins/acryl-node-server/upstream.json +3 -3
  71. package/plugins/acryl-router/LICENSE +26 -0
  72. package/plugins/acryl-router/plugin.json +2 -1
  73. package/plugins/acryl-router/src/client.ts +2 -0
  74. package/plugins/acryl-router/src/connection.ts +42 -0
  75. package/plugins/acryl-router/src/core/app-base.ts +544 -0
  76. package/plugins/acryl-router/src/core/app.test.ts +3833 -0
  77. package/plugins/acryl-router/src/core/app.ts +34 -0
  78. package/plugins/acryl-router/src/core/client/client.test.ts +2313 -0
  79. package/plugins/acryl-router/src/core/client/client.ts +249 -0
  80. package/plugins/acryl-router/src/core/client/fetch-result-please.ts +95 -0
  81. package/plugins/acryl-router/src/core/client/index.ts +17 -0
  82. package/plugins/acryl-router/src/core/client/types.test.ts +149 -0
  83. package/plugins/acryl-router/src/core/client/types.ts +393 -0
  84. package/plugins/acryl-router/src/core/client/utils.test.ts +405 -0
  85. package/plugins/acryl-router/src/core/client/utils.ts +117 -0
  86. package/plugins/acryl-router/src/core/compose.test.ts +751 -0
  87. package/plugins/acryl-router/src/core/compose.ts +73 -0
  88. package/plugins/acryl-router/src/core/context.test.ts +629 -0
  89. package/plugins/acryl-router/src/core/context.ts +781 -0
  90. package/plugins/acryl-router/src/core/helper/accepts/accepts.test.ts +344 -0
  91. package/plugins/acryl-router/src/core/helper/accepts/accepts.ts +96 -0
  92. package/plugins/acryl-router/src/core/helper/accepts/index.ts +6 -0
  93. package/plugins/acryl-router/src/core/helper/adapter/index.test.ts +55 -0
  94. package/plugins/acryl-router/src/core/helper/adapter/index.ts +90 -0
  95. package/plugins/acryl-router/src/core/helper/conninfo/index.ts +6 -0
  96. package/plugins/acryl-router/src/core/helper/conninfo/types.ts +45 -0
  97. package/plugins/acryl-router/src/core/helper/cookie/index.test.ts +510 -0
  98. package/plugins/acryl-router/src/core/helper/cookie/index.ts +146 -0
  99. package/plugins/acryl-router/src/core/helper/factory/index.test.ts +434 -0
  100. package/plugins/acryl-router/src/core/helper/factory/index.ts +375 -0
  101. package/plugins/acryl-router/src/core/helper/html/index.test.ts +78 -0
  102. package/plugins/acryl-router/src/core/helper/html/index.ts +57 -0
  103. package/plugins/acryl-router/src/core/helper/route/index.test.ts +259 -0
  104. package/plugins/acryl-router/src/core/helper/route/index.ts +144 -0
  105. package/plugins/acryl-router/src/core/helper/streaming/index.ts +9 -0
  106. package/plugins/acryl-router/src/core/helper/streaming/sse.ts +101 -0
  107. package/plugins/acryl-router/src/core/helper/streaming/stream.test.ts +128 -0
  108. package/plugins/acryl-router/src/core/helper/streaming/stream.ts +45 -0
  109. package/plugins/acryl-router/src/core/helper/streaming/text.test.ts +34 -0
  110. package/plugins/acryl-router/src/core/helper/streaming/text.ts +15 -0
  111. package/plugins/acryl-router/src/core/helper/streaming/utils.ts +11 -0
  112. package/plugins/acryl-router/src/core/helper/testing/index.test.ts +42 -0
  113. package/plugins/acryl-router/src/core/helper/testing/index.ts +28 -0
  114. package/plugins/acryl-router/src/core/helper/websocket/index.test.ts +124 -0
  115. package/plugins/acryl-router/src/core/helper/websocket/index.ts +140 -0
  116. package/plugins/acryl-router/src/core/http-exception.test.ts +45 -0
  117. package/plugins/acryl-router/src/core/http-exception.ts +77 -0
  118. package/plugins/acryl-router/src/core/index.ts +53 -0
  119. package/plugins/acryl-router/src/core/middleware/basic-auth/index.test.ts +434 -0
  120. package/plugins/acryl-router/src/core/middleware/basic-auth/index.ts +152 -0
  121. package/plugins/acryl-router/src/core/middleware/bearer-auth/index.test.ts +1018 -0
  122. package/plugins/acryl-router/src/core/middleware/bearer-auth/index.ts +220 -0
  123. package/plugins/acryl-router/src/core/middleware/body-limit/index.test.ts +273 -0
  124. package/plugins/acryl-router/src/core/middleware/body-limit/index.ts +110 -0
  125. package/plugins/acryl-router/src/core/middleware/combine/index.test.ts +355 -0
  126. package/plugins/acryl-router/src/core/middleware/combine/index.ts +171 -0
  127. package/plugins/acryl-router/src/core/middleware/compress/index.test.ts +535 -0
  128. package/plugins/acryl-router/src/core/middleware/compress/index.ts +142 -0
  129. package/plugins/acryl-router/src/core/middleware/context-storage/index.test.ts +43 -0
  130. package/plugins/acryl-router/src/core/middleware/context-storage/index.ts +58 -0
  131. package/plugins/acryl-router/src/core/middleware/cors/index.test.ts +532 -0
  132. package/plugins/acryl-router/src/core/middleware/cors/index.ts +163 -0
  133. package/plugins/acryl-router/src/core/middleware/csrf/index.test.ts +534 -0
  134. package/plugins/acryl-router/src/core/middleware/csrf/index.ts +150 -0
  135. package/plugins/acryl-router/src/core/middleware/etag/digest.test.ts +21 -0
  136. package/plugins/acryl-router/src/core/middleware/etag/digest.ts +95 -0
  137. package/plugins/acryl-router/src/core/middleware/etag/index.test.ts +503 -0
  138. package/plugins/acryl-router/src/core/middleware/etag/index.ts +135 -0
  139. package/plugins/acryl-router/src/core/middleware/ip-restriction/index.test.ts +217 -0
  140. package/plugins/acryl-router/src/core/middleware/ip-restriction/index.ts +278 -0
  141. package/plugins/acryl-router/src/core/middleware/jwk/index.test.ts +1135 -0
  142. package/plugins/acryl-router/src/core/middleware/jwk/index.ts +1 -0
  143. package/plugins/acryl-router/src/core/middleware/jwk/jwk.ts +195 -0
  144. package/plugins/acryl-router/src/core/middleware/jwk/keys.test.json +48 -0
  145. package/plugins/acryl-router/src/core/middleware/jwt/index.test.ts +802 -0
  146. package/plugins/acryl-router/src/core/middleware/jwt/index.ts +9 -0
  147. package/plugins/acryl-router/src/core/middleware/jwt/jwt.ts +189 -0
  148. package/plugins/acryl-router/src/core/middleware/language/index.test.ts +552 -0
  149. package/plugins/acryl-router/src/core/middleware/language/index.ts +12 -0
  150. package/plugins/acryl-router/src/core/middleware/language/language.ts +314 -0
  151. package/plugins/acryl-router/src/core/middleware/logger/index.test.ts +204 -0
  152. package/plugins/acryl-router/src/core/middleware/logger/index.ts +95 -0
  153. package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.test.ts +292 -0
  154. package/plugins/acryl-router/src/core/middleware/method-not-allowed/index.ts +141 -0
  155. package/plugins/acryl-router/src/core/middleware/method-override/index.test.ts +221 -0
  156. package/plugins/acryl-router/src/core/middleware/method-override/index.ts +147 -0
  157. package/plugins/acryl-router/src/core/middleware/powered-by/index.test.ts +37 -0
  158. package/plugins/acryl-router/src/core/middleware/powered-by/index.ts +35 -0
  159. package/plugins/acryl-router/src/core/middleware/pretty-json/index.test.ts +218 -0
  160. package/plugins/acryl-router/src/core/middleware/pretty-json/index.ts +67 -0
  161. package/plugins/acryl-router/src/core/middleware/request-id/index.test.ts +168 -0
  162. package/plugins/acryl-router/src/core/middleware/request-id/index.ts +7 -0
  163. package/plugins/acryl-router/src/core/middleware/request-id/request-id.ts +59 -0
  164. package/plugins/acryl-router/src/core/middleware/secure-headers/index.test.ts +671 -0
  165. package/plugins/acryl-router/src/core/middleware/secure-headers/index.ts +8 -0
  166. package/plugins/acryl-router/src/core/middleware/secure-headers/permissions-policy.ts +101 -0
  167. package/plugins/acryl-router/src/core/middleware/secure-headers/secure-headers.ts +340 -0
  168. package/plugins/acryl-router/src/core/middleware/serve-static/index.test.ts +450 -0
  169. package/plugins/acryl-router/src/core/middleware/serve-static/index.ts +135 -0
  170. package/plugins/acryl-router/src/core/middleware/serve-static/path.test.ts +50 -0
  171. package/plugins/acryl-router/src/core/middleware/serve-static/path.ts +25 -0
  172. package/plugins/acryl-router/src/core/middleware/timeout/index.test.ts +70 -0
  173. package/plugins/acryl-router/src/core/middleware/timeout/index.ts +58 -0
  174. package/plugins/acryl-router/src/core/middleware/timing/index.test.ts +310 -0
  175. package/plugins/acryl-router/src/core/middleware/timing/index.ts +8 -0
  176. package/plugins/acryl-router/src/core/middleware/timing/timing.ts +256 -0
  177. package/plugins/acryl-router/src/core/middleware/trailing-slash/index.test.ts +381 -0
  178. package/plugins/acryl-router/src/core/middleware/trailing-slash/index.ts +156 -0
  179. package/plugins/acryl-router/src/core/request/constants.ts +1 -0
  180. package/plugins/acryl-router/src/core/request.test.ts +751 -0
  181. package/plugins/acryl-router/src/core/request.ts +502 -0
  182. package/plugins/acryl-router/src/core/router/common.case.test.ts +1132 -0
  183. package/plugins/acryl-router/src/core/router/linear-router/index.ts +6 -0
  184. package/plugins/acryl-router/src/core/router/linear-router/router.test.ts +80 -0
  185. package/plugins/acryl-router/src/core/router/linear-router/router.ts +152 -0
  186. package/plugins/acryl-router/src/core/router/pattern-router/index.ts +6 -0
  187. package/plugins/acryl-router/src/core/router/pattern-router/router.test.ts +46 -0
  188. package/plugins/acryl-router/src/core/router/pattern-router/router.ts +54 -0
  189. package/plugins/acryl-router/src/core/router/reg-exp-router/index.ts +7 -0
  190. package/plugins/acryl-router/src/core/router/reg-exp-router/matcher.ts +33 -0
  191. package/plugins/acryl-router/src/core/router/reg-exp-router/node.ts +170 -0
  192. package/plugins/acryl-router/src/core/router/reg-exp-router/prepared-router.test.ts +154 -0
  193. package/plugins/acryl-router/src/core/router/reg-exp-router/prepared-router.ts +165 -0
  194. package/plugins/acryl-router/src/core/router/reg-exp-router/router.test.ts +265 -0
  195. package/plugins/acryl-router/src/core/router/reg-exp-router/router.ts +174 -0
  196. package/plugins/acryl-router/src/core/router/reg-exp-router/trie.ts +84 -0
  197. package/plugins/acryl-router/src/core/router/smart-router/index.ts +6 -0
  198. package/plugins/acryl-router/src/core/router/smart-router/router.test.ts +13 -0
  199. package/plugins/acryl-router/src/core/router/smart-router/router.ts +70 -0
  200. package/plugins/acryl-router/src/core/router/trie-router/index.ts +6 -0
  201. package/plugins/acryl-router/src/core/router/trie-router/node.test.ts +896 -0
  202. package/plugins/acryl-router/src/core/router/trie-router/node.ts +221 -0
  203. package/plugins/acryl-router/src/core/router/trie-router/router.test.ts +8 -0
  204. package/plugins/acryl-router/src/core/router/trie-router/router.ts +18 -0
  205. package/plugins/acryl-router/src/core/router/utils.ts +1 -0
  206. package/plugins/acryl-router/src/core/router.ts +103 -0
  207. package/plugins/acryl-router/src/core/types.test.ts +4134 -0
  208. package/plugins/acryl-router/src/core/types.ts +2782 -0
  209. package/plugins/acryl-router/src/core/utils/accept.test.ts +299 -0
  210. package/plugins/acryl-router/src/core/utils/accept.ts +268 -0
  211. package/plugins/acryl-router/src/core/utils/basic-auth.test.ts +56 -0
  212. package/plugins/acryl-router/src/core/utils/basic-auth.ts +26 -0
  213. package/plugins/acryl-router/src/core/utils/body.test.ts +496 -0
  214. package/plugins/acryl-router/src/core/utils/body.ts +263 -0
  215. package/plugins/acryl-router/src/core/utils/buffer.test.ts +147 -0
  216. package/plugins/acryl-router/src/core/utils/buffer.ts +117 -0
  217. package/plugins/acryl-router/src/core/utils/color.test.ts +23 -0
  218. package/plugins/acryl-router/src/core/utils/color.ts +56 -0
  219. package/plugins/acryl-router/src/core/utils/compress.ts +10 -0
  220. package/plugins/acryl-router/src/core/utils/concurrent.test.ts +69 -0
  221. package/plugins/acryl-router/src/core/utils/concurrent.ts +55 -0
  222. package/plugins/acryl-router/src/core/utils/constants.ts +4 -0
  223. package/plugins/acryl-router/src/core/utils/cookie.test.ts +460 -0
  224. package/plugins/acryl-router/src/core/utils/cookie.ts +281 -0
  225. package/plugins/acryl-router/src/core/utils/crypto.test.ts +43 -0
  226. package/plugins/acryl-router/src/core/utils/crypto.ts +58 -0
  227. package/plugins/acryl-router/src/core/utils/encode.test.ts +61 -0
  228. package/plugins/acryl-router/src/core/utils/encode.ts +34 -0
  229. package/plugins/acryl-router/src/core/utils/filepath.test.ts +78 -0
  230. package/plugins/acryl-router/src/core/utils/filepath.ts +60 -0
  231. package/plugins/acryl-router/src/core/utils/handler.ts +15 -0
  232. package/plugins/acryl-router/src/core/utils/headers.ts +355 -0
  233. package/plugins/acryl-router/src/core/utils/html.test.ts +16 -0
  234. package/plugins/acryl-router/src/core/utils/html.ts +182 -0
  235. package/plugins/acryl-router/src/core/utils/http-status.ts +72 -0
  236. package/plugins/acryl-router/src/core/utils/ipaddr.test.ts +149 -0
  237. package/plugins/acryl-router/src/core/utils/ipaddr.ts +374 -0
  238. package/plugins/acryl-router/src/core/utils/jwt/index.ts +7 -0
  239. package/plugins/acryl-router/src/core/utils/jwt/jwa.test.ts +86 -0
  240. package/plugins/acryl-router/src/core/utils/jwt/jwa.ts +38 -0
  241. package/plugins/acryl-router/src/core/utils/jwt/jws.ts +235 -0
  242. package/plugins/acryl-router/src/core/utils/jwt/jwt.test.ts +1788 -0
  243. package/plugins/acryl-router/src/core/utils/jwt/jwt.ts +292 -0
  244. package/plugins/acryl-router/src/core/utils/jwt/types.ts +157 -0
  245. package/plugins/acryl-router/src/core/utils/jwt/utf8.ts +7 -0
  246. package/plugins/acryl-router/src/core/utils/mime.test.ts +64 -0
  247. package/plugins/acryl-router/src/core/utils/mime.ts +94 -0
  248. package/plugins/acryl-router/src/core/utils/stream.test.ts +212 -0
  249. package/plugins/acryl-router/src/core/utils/stream.ts +107 -0
  250. package/plugins/acryl-router/src/core/utils/types.test.ts +295 -0
  251. package/plugins/acryl-router/src/core/utils/types.ts +123 -0
  252. package/plugins/acryl-router/src/core/utils/url.test.ts +384 -0
  253. package/plugins/acryl-router/src/core/utils/url.ts +337 -0
  254. package/plugins/acryl-router/src/core/validator/index.ts +8 -0
  255. package/plugins/acryl-router/src/core/validator/utils.test.ts +82 -0
  256. package/plugins/acryl-router/src/core/validator/utils.ts +69 -0
  257. package/plugins/acryl-router/src/core/validator/validator.test.ts +1488 -0
  258. package/plugins/acryl-router/src/core/validator/validator.ts +185 -0
  259. package/plugins/acryl-router/src/helpers/index.ts +13 -3
  260. package/plugins/acryl-router/src/helpers/schema-validator.ts +35 -0
  261. package/plugins/acryl-router/src/index.ts +6 -6
  262. package/plugins/acryl-router/src/middleware/index.ts +25 -6
  263. package/plugins/acryl-router/src/middleware/loopback-only.ts +19 -13
  264. package/plugins/acryl-router/src/service.ts +256 -64
  265. package/plugins/acryl-router/tests/docs-examples.spec.ts +152 -0
  266. package/plugins/acryl-router/tests/live-install.spec.ts +2 -2
  267. package/plugins/acryl-router/tests/loader.spec.ts +3 -2
  268. package/plugins/acryl-router/tests/service.spec.ts +264 -66
  269. package/plugins/acryl-router/tests/shadow-state.spec.ts +79 -0
  270. package/plugins/acryl-router/upstream.json +2 -2
  271. package/plugins/acryl-router-http/plugin.json +2 -1
  272. package/plugins/acryl-router-http/src/bridge.ts +3 -1
  273. package/plugins/acryl-router-http/tests/adapter.spec.ts +16 -12
  274. package/plugins/acryl-router-http/tests/surfaces.spec.ts +12 -10
  275. package/plugins/acryl-router-http/upstream.json +3 -3
  276. package/plugins/acryl-state/NOTICE +30 -0
  277. package/plugins/acryl-state/plugin.json +1 -1
  278. package/plugins/acryl-state/src/cell.ts +1 -1
  279. package/plugins/acryl-state/src/service.ts +33 -15
  280. package/plugins/acryl-state/upstream.json +6 -4
  281. package/plugins/builder/script.ts +4 -7
  282. package/plugins/extensions/index.ts +2 -2
  283. package/plugins/extensions/resolve.ts +28 -3
  284. package/plugins/openai/prompt.ts +9 -7
  285. package/plugins/tsconfig.json +5 -4
  286. package/templates/agent/src/routes.ts +37 -27
  287. package/templates/agent/test/model.test.ts +2 -1
  288. package/templates/agent/tsconfig.json +8 -5
  289. package/templates/blank/src/routes.ts +6 -4
  290. package/templates/blank/tsconfig.json +2 -4
  291. package/templates/todo/src/page.ts +1 -1
  292. package/templates/todo/src/todo.ts +56 -29
  293. package/templates/todo/tsconfig.json +2 -4
  294. package/templates/todo/upstream.json +2 -2
  295. package/plugins/acryl-router/src/compose.ts +0 -21
  296. package/plugins/acryl-router/src/context.ts +0 -59
  297. package/plugins/acryl-router/src/helpers/read-body.ts +0 -59
  298. package/plugins/acryl-router/src/helpers/sse.ts +0 -83
  299. package/plugins/acryl-router/src/helpers/validate.ts +0 -44
  300. package/plugins/acryl-router/src/http-error.ts +0 -21
  301. package/plugins/acryl-router/src/middleware/body-limit.ts +0 -25
  302. package/plugins/acryl-router/src/middleware/cors.ts +0 -46
  303. package/plugins/acryl-router/src/middleware/logger.ts +0 -21
  304. package/plugins/acryl-router/src/middleware/request-id.ts +0 -24
  305. package/plugins/acryl-router/src/middleware/secure-headers.ts +0 -23
  306. package/plugins/acryl-router/src/pattern.ts +0 -89
  307. package/plugins/acryl-router/src/registrar.ts +0 -90
  308. package/plugins/acryl-router/src/table.ts +0 -156
  309. package/plugins/acryl-router/src/types.ts +0 -73
  310. package/plugins/acryl-router/src/util.ts +0 -23
  311. package/plugins/acryl-router/tests/api.spec.ts +0 -223
  312. package/plugins/acryl-router/tests/conformance.ts +0 -170
  313. package/plugins/acryl-router/tests/helpers.spec.ts +0 -172
  314. package/plugins/acryl-router/tests/middleware.spec.ts +0 -183
  315. package/plugins/acryl-router/tests/table.spec.ts +0 -47
@@ -0,0 +1,1132 @@
1
+ import type { RunnerTestSuite } from 'vitest'
2
+ import type { ParamIndexMap, Params, Router } from '../router.ts'
3
+
4
+ const getSuiteHierarchy = (suite?: RunnerTestSuite) => {
5
+ const res: RunnerTestSuite[] = []
6
+ let s: RunnerTestSuite | undefined = suite
7
+ while (s) {
8
+ res.unshift(s)
9
+ s = s.suite
10
+ }
11
+ return res
12
+ }
13
+
14
+ export const runTest = ({
15
+ skip = [],
16
+ newRouter,
17
+ }: {
18
+ skip?: {
19
+ reason: string
20
+ tests: string[]
21
+ }[]
22
+ newRouter: <T>() => Router<T>
23
+ }) => {
24
+ describe('Common', () => {
25
+ type Match = (method: string, path: string) => { handler: string; params: Params }[]
26
+ let router: Router<string>
27
+ let match: Match
28
+
29
+ beforeEach(({ task, skip: skipTask }) => {
30
+ const suites = getSuiteHierarchy(task.suite)
31
+ const name = [...suites.slice(2).map((s) => s.name), task.name].join(' > ')
32
+ const isSkip = skip.find((s) => s.tests.includes(name))
33
+ if (isSkip) {
34
+ console.log(`Skip: ${isSkip.reason}`)
35
+ skipTask()
36
+ return
37
+ }
38
+
39
+ router = newRouter()
40
+ match = (method: string, path: string) => {
41
+ const [matchRes, stash] = router.match(method, path)
42
+ const res = matchRes.map((r) =>
43
+ stash
44
+ ? {
45
+ handler: r[0],
46
+ params: Object.keys(r[1]).reduce(
47
+ (acc, key) => {
48
+ acc[key] = stash[(r[1] as ParamIndexMap)[key]]
49
+ return acc
50
+ },
51
+ Object.create(null) as Params
52
+ ),
53
+ }
54
+ : { handler: r[0], params: r[1] as Params }
55
+ )
56
+ return res
57
+ }
58
+ })
59
+
60
+ describe('Basic Usage', () => {
61
+ beforeEach(() => {
62
+ router.add('GET', '/hello', 'get hello')
63
+ router.add('POST', '/hello', 'post hello')
64
+ router.add('PURGE', '/hello', 'purge hello')
65
+ })
66
+
67
+ it('GET, post hello', async () => {
68
+ let res = match('GET', '/hello')
69
+ expect(res.length).toBe(1)
70
+ expect(res[0].handler).toEqual('get hello')
71
+ res = match('POST', '/hello')
72
+ expect(res.length).toBe(1)
73
+ expect(res[0].handler).toEqual('post hello')
74
+ res = match('PURGE', '/hello')
75
+ expect(res.length).toBe(1)
76
+ expect(res[0].handler).toEqual('purge hello')
77
+ res = match('PUT', '/hello')
78
+ expect(res.length).toBe(0)
79
+ res = match('GET', '/')
80
+ expect(res.length).toBe(0)
81
+ })
82
+ })
83
+
84
+ describe('Reserved words', () => {
85
+ it('Reserved words and named parameter', async () => {
86
+ router.add('GET', '/entry/:constructor', 'get entry')
87
+ const res = match('GET', '/entry/123')
88
+ expect(res.length).toBe(1)
89
+ expect(res[0].handler).toEqual('get entry')
90
+ expect(res[0].params['constructor']).toBe('123')
91
+ })
92
+
93
+ it('Reserved words and wildcard', async () => {
94
+ router.add('GET', '/wild/*/card', 'get wildcard')
95
+ const res = match('GET', '/wild/constructor/card')
96
+ expect(res.length).toBe(1)
97
+ expect(res[0].handler).toEqual('get wildcard')
98
+ })
99
+
100
+ it('Reserved words and optional named parameter', async () => {
101
+ router.add('GET', '/api/animals/:constructor?', 'animals')
102
+ const res = match('GET', '/api/animals')
103
+ expect(res.length).toBe(1)
104
+ expect(res[0].handler).toEqual('animals')
105
+ expect(res[0].params['constructor']).toBeUndefined()
106
+ })
107
+ })
108
+
109
+ describe('Complex', () => {
110
+ it('Named Param', async () => {
111
+ router.add('GET', '/entry/:id', 'get entry')
112
+ let res = match('GET', '/entry/123')
113
+
114
+ expect(res.length).toBe(1)
115
+ expect(res[0].handler).toEqual('get entry')
116
+ expect(res[0].params['id']).toBe('123')
117
+
118
+ res = match('GET', '/entry-123')
119
+ expect(res.length).toBe(0)
120
+ })
121
+
122
+ it('Wildcard', async () => {
123
+ router.add('GET', '/wild/*/card', 'get wildcard')
124
+ const res = match('GET', '/wild/xxx/card')
125
+ expect(res.length).toBe(1)
126
+ expect(res[0].handler).toEqual('get wildcard')
127
+ })
128
+
129
+ it('Default', async () => {
130
+ router.add('GET', '/api/abc', 'get api')
131
+ router.add('GET', '/api/*', 'fallback')
132
+ let res = match('GET', '/api/abc')
133
+ expect(res.length).toBe(2)
134
+ expect(res[0].handler).toEqual('get api')
135
+ expect(res[1].handler).toEqual('fallback')
136
+ res = match('GET', '/api/def')
137
+ expect(res.length).toBe(1)
138
+ expect(res[0].handler).toEqual('fallback')
139
+ })
140
+
141
+ it('Regexp', async () => {
142
+ router.add('GET', '/post/:date{[0-9]+}/:title{[a-z]+}', 'get post')
143
+ let res = match('GET', '/post/20210101/hello')
144
+ expect(res.length).toBe(1)
145
+ expect(res[0].handler).toEqual('get post')
146
+ expect(res[0].params['date']).toBe('20210101')
147
+ expect(res[0].params['title']).toBe('hello')
148
+ res = match('GET', '/post/onetwothree')
149
+ expect(res.length).toBe(0)
150
+ res = match('GET', '/post/123/123')
151
+ expect(res.length).toBe(0)
152
+ })
153
+
154
+ it('Parameter with {.*} regexp', () => {
155
+ router.add('GET', '/files/:name{.*}', 'file')
156
+ let res = match('GET', '/files')
157
+ expect(res.length).toBe(0)
158
+
159
+ res = match('GET', '/files/a')
160
+ expect(res.length).toBe(1)
161
+ expect(res[0].handler).toEqual('file')
162
+ expect(res[0].params['name']).toEqual('a')
163
+
164
+ res = match('GET', '/files/a/b')
165
+ expect(res.length).toBe(1)
166
+ expect(res[0].handler).toEqual('file')
167
+ expect(res[0].params['name']).toEqual('a/b')
168
+
169
+ res = match('GET', '/files/')
170
+ expect(res.length).toBe(1)
171
+ expect(res[0].handler).toEqual('file')
172
+ })
173
+
174
+ it('/*', async () => {
175
+ router.add('GET', '/api/*', 'auth middleware')
176
+ router.add('GET', '/api', 'top')
177
+ router.add('GET', '/api/posts', 'posts')
178
+ router.add('GET', '/api/*', 'fallback')
179
+
180
+ let res = match('GET', '/api')
181
+ expect(res.length).toBe(3)
182
+ expect(res[0].handler).toEqual('auth middleware')
183
+ expect(res[1].handler).toEqual('top')
184
+ expect(res[2].handler).toEqual('fallback')
185
+ res = match('GET', '/api/posts')
186
+ expect(res.length).toBe(3)
187
+ expect(res[0].handler).toEqual('auth middleware')
188
+ expect(res[1].handler).toEqual('posts')
189
+ expect(res[2].handler).toEqual('fallback')
190
+ })
191
+ })
192
+
193
+ describe('Registration order', () => {
194
+ it('middleware -> handler', async () => {
195
+ router.add('GET', '*', 'bar')
196
+ router.add('GET', '/:type/:action', 'foo')
197
+ const res = match('GET', '/posts/123')
198
+ expect(res.length).toBe(2)
199
+ expect(res[0].handler).toEqual('bar')
200
+ expect(res[1].handler).toEqual('foo')
201
+ })
202
+
203
+ it('handler -> fallback', async () => {
204
+ router.add('GET', '/:type/:action', 'foo')
205
+ router.add('GET', '*', 'fallback')
206
+ const res = match('GET', '/posts/123')
207
+ expect(res.length).toBe(2)
208
+ expect(res[0].handler).toEqual('foo')
209
+ expect(res[1].handler).toEqual('fallback')
210
+ })
211
+ })
212
+
213
+ describe('Multi match', () => {
214
+ describe('Blog', () => {
215
+ beforeEach(() => {
216
+ router.add('ALL', '*', 'middleware a')
217
+ router.add('GET', '*', 'middleware b')
218
+ router.add('GET', '/entry', 'get entries')
219
+ router.add('POST', '/entry/*', 'middleware c')
220
+ router.add('POST', '/entry', 'post entry')
221
+ router.add('GET', '/entry/:id', 'get entry')
222
+ router.add('GET', '/entry/:id/comment/:comment_id', 'get comment')
223
+ })
224
+
225
+ it('GET /', async () => {
226
+ const res = match('GET', '/')
227
+ expect(res.length).toBe(2)
228
+ expect(res[0].handler).toEqual('middleware a')
229
+ expect(res[1].handler).toEqual('middleware b')
230
+ })
231
+ it('GET /entry/123', async () => {
232
+ const res = match('GET', '/entry/123')
233
+ expect(res.length).toBe(3)
234
+ expect(res[0].handler).toEqual('middleware a')
235
+ expect(res[0].params['id']).toBe(undefined)
236
+ expect(res[1].handler).toEqual('middleware b')
237
+ expect(res[1].params['id']).toBe(undefined)
238
+ expect(res[2].handler).toEqual('get entry')
239
+ expect(res[2].params['id']).toBe('123')
240
+ })
241
+ it('GET /entry/123/comment/456', async () => {
242
+ const res = match('GET', '/entry/123/comment/456')
243
+ expect(res.length).toBe(3)
244
+ expect(res[0].handler).toEqual('middleware a')
245
+ expect(res[0].params['id']).toBe(undefined)
246
+ expect(res[0].params['comment_id']).toBe(undefined)
247
+ expect(res[1].handler).toEqual('middleware b')
248
+ expect(res[1].params['id']).toBe(undefined)
249
+ expect(res[1].params['comment_id']).toBe(undefined)
250
+ expect(res[2].handler).toEqual('get comment')
251
+ expect(res[2].params['id']).toBe('123')
252
+ expect(res[2].params['comment_id']).toBe('456')
253
+ })
254
+ it('POST /entry', async () => {
255
+ const res = match('POST', '/entry')
256
+ expect(res.length).toBe(3)
257
+ expect(res[0].handler).toEqual('middleware a')
258
+ expect(res[1].handler).toEqual('middleware c')
259
+ expect(res[2].handler).toEqual('post entry')
260
+ })
261
+ it('DELETE /entry', async () => {
262
+ const res = match('DELETE', '/entry')
263
+ expect(res.length).toBe(1)
264
+ expect(res[0].handler).toEqual('middleware a')
265
+ })
266
+ })
267
+
268
+ describe('`params` per a handler', () => {
269
+ beforeEach(() => {
270
+ router.add('ALL', '*', 'middleware a')
271
+ router.add('GET', '/entry/:id/*', 'middleware b')
272
+ router.add('GET', '/entry/:id/:action', 'action')
273
+ })
274
+
275
+ it('GET /entry/123/show', async () => {
276
+ const res = match('GET', '/entry/123/show')
277
+ expect(res.length).toBe(3)
278
+ expect(res[0].handler).toEqual('middleware a')
279
+ expect(res[0].params['id']).toBe(undefined)
280
+ expect(res[0].params['action']).toBe(undefined)
281
+ expect(res[1].handler).toEqual('middleware b')
282
+ expect(res[1].params['id']).toBe('123')
283
+ expect(res[1].params['comment_id']).toBe(undefined)
284
+ expect(res[2].handler).toEqual('action')
285
+ expect(res[2].params['id']).toBe('123')
286
+ expect(res[2].params['action']).toBe('show')
287
+ })
288
+ })
289
+
290
+ it('hierarchy', () => {
291
+ router.add('GET', '/posts/:id/comments/:comment_id', 'foo')
292
+ router.add('GET', '/posts/:id', 'bar')
293
+ expect(() => {
294
+ router.match('GET', '/')
295
+ }).not.toThrow()
296
+ })
297
+ })
298
+
299
+ describe('Duplicate param name', () => {
300
+ it('self', () => {
301
+ router.add('GET', '/:id/:id', 'foo')
302
+ const res = match('GET', '/123/456')
303
+ expect(res.length).toBe(1)
304
+ expect(res[0].handler).toEqual('foo')
305
+ expect(res[0].params['id']).toBe('123')
306
+ })
307
+
308
+ it('self with trailing static', () => {
309
+ router.add('GET', '/:id/:id/x', 'foo')
310
+ const res = match('GET', '/123/456/x')
311
+ expect(res.length).toBe(1)
312
+ expect(res[0].handler).toEqual('foo')
313
+ expect(res[0].params['id']).toBe('123')
314
+ })
315
+
316
+ it('self three times', () => {
317
+ router.add('GET', '/:id/:id/:id', 'foo')
318
+ const res = match('GET', '/1/2/3')
319
+ expect(res.length).toBe(1)
320
+ expect(res[0].handler).toEqual('foo')
321
+ expect(res[0].params['id']).toBe('1')
322
+ })
323
+
324
+ it('parent', () => {
325
+ router.add('GET', '/:id/:action', 'foo')
326
+ router.add('GET', '/posts/:id', 'bar')
327
+ const res = match('GET', '/posts/get')
328
+ expect(res.length).toBe(2)
329
+ expect(res[0].handler).toEqual('foo')
330
+ expect(res[0].params['id']).toBe('posts')
331
+ expect(res[0].params['action']).toBe('get')
332
+ expect(res[1].handler).toEqual('bar')
333
+ expect(res[1].params['id']).toBe('get')
334
+ })
335
+
336
+ it('child', () => {
337
+ router.add('GET', '/posts/:id', 'foo')
338
+ router.add('GET', '/:id/:action', 'bar')
339
+ const res = match('GET', '/posts/get')
340
+ expect(res.length).toBe(2)
341
+ expect(res[0].handler).toEqual('foo')
342
+ expect(res[0].params['id']).toBe('get')
343
+ expect(res[1].handler).toEqual('bar')
344
+ expect(res[1].params['id']).toBe('posts')
345
+ expect(res[1].params['action']).toBe('get')
346
+ })
347
+ })
348
+
349
+ describe('page', () => {
350
+ it('GET /page', async () => {
351
+ router.add('GET', '/page', 'page')
352
+ router.add('ALL', '*', 'fallback') // or '*'
353
+
354
+ const res = match('GET', '/page')
355
+ expect(res.length).toBe(2)
356
+ expect(res[0].handler).toEqual('page')
357
+ expect(res[1].handler).toEqual('fallback')
358
+ })
359
+ })
360
+
361
+ describe('star', () => {
362
+ beforeEach(() => {
363
+ router.add('GET', '/', '/')
364
+ router.add('GET', '/*', '/*')
365
+ router.add('GET', '*', '*')
366
+
367
+ router.add('GET', '/x', '/x')
368
+ router.add('GET', '/x/*', '/x/*')
369
+ })
370
+
371
+ it('top', async () => {
372
+ const res = match('GET', '/')
373
+ expect(res.length).toBe(3)
374
+ expect(res[0].handler).toEqual('/')
375
+ expect(res[1].handler).toEqual('/*')
376
+ expect(res[2].handler).toEqual('*')
377
+ })
378
+
379
+ it('Under a certain path', async () => {
380
+ const res = match('GET', '/x')
381
+ expect(res.length).toBe(4)
382
+ expect(res[0].handler).toEqual('/*')
383
+ expect(res[1].handler).toEqual('*')
384
+ expect(res[2].handler).toEqual('/x')
385
+ expect(res[3].handler).toEqual('/x/*')
386
+ })
387
+ })
388
+
389
+ describe('Suffix wildcard', () => {
390
+ beforeEach(() => {
391
+ router.add('GET', '/assets*', 'assets')
392
+ })
393
+
394
+ it('GET /assets', async () => {
395
+ const res = match('GET', '/assets')
396
+ expect(res.length).toBe(1)
397
+ expect(res[0].handler).toEqual('assets')
398
+ })
399
+
400
+ it('GET /assets-v2', async () => {
401
+ const res = match('GET', '/assets-v2')
402
+ expect(res.length).toBe(1)
403
+ expect(res[0].handler).toEqual('assets')
404
+ })
405
+
406
+ it('GET /assets/app.js', async () => {
407
+ const res = match('GET', '/assets/app.js')
408
+ expect(res.length).toBe(1)
409
+ expect(res[0].handler).toEqual('assets')
410
+ })
411
+
412
+ it('GET /asset', async () => {
413
+ const res = match('GET', '/asset')
414
+ expect(res.length).toBe(0)
415
+ })
416
+
417
+ it.each([
418
+ ['/assets*', '/assets/app.js'],
419
+ ['/assets/app.js', '/assets*'],
420
+ ])('associates %s before %s', async (first, second) => {
421
+ router.add('POST', first, first)
422
+ router.add('POST', second, second)
423
+ const res = match('POST', '/assets/app.js')
424
+ expect(res.map(({ handler }) => handler)).toEqual([first, second])
425
+ })
426
+ })
427
+
428
+ describe('Trailing wildcard', () => {
429
+ beforeEach(() => {
430
+ router.add('GET', '/path/*', 'path')
431
+ })
432
+
433
+ it('GET /path', async () => {
434
+ const res = match('GET', '/path')
435
+ expect(res.length).toBe(1)
436
+ expect(res[0].handler).toEqual('path')
437
+ })
438
+
439
+ it('GET /path/to/file', async () => {
440
+ const res = match('GET', '/path/to/file')
441
+ expect(res.length).toBe(1)
442
+ expect(res[0].handler).toEqual('path')
443
+ })
444
+
445
+ it('GET /pathfoo', async () => {
446
+ const res = match('GET', '/pathfoo')
447
+ expect(res.length).toBe(0)
448
+ })
449
+ })
450
+
451
+ describe('Trailing wildcard after a label', () => {
452
+ beforeEach(() => {
453
+ router.add('GET', '/:name/*', 'middleware')
454
+ router.add('GET', '/:id', 'handler')
455
+ })
456
+
457
+ it('GET /abc', async () => {
458
+ const res = match('GET', '/abc')
459
+ expect(res.length).toBe(2)
460
+ expect(res[0]).toEqual({ handler: 'middleware', params: { name: 'abc' } })
461
+ expect(res[1]).toEqual({ handler: 'handler', params: { id: 'abc' } })
462
+ })
463
+
464
+ it('GET /abc/sub', async () => {
465
+ const res = match('GET', '/abc/sub')
466
+ expect(res).toEqual([{ handler: 'middleware', params: { name: 'abc' } }])
467
+ })
468
+ })
469
+
470
+ describe('Trailing wildcard after a pattern label', () => {
471
+ beforeEach(() => {
472
+ router.add('GET', '/:name{[a-z]+}/*', 'middleware')
473
+ router.add('GET', '/:id{[a-z]+}', 'handler')
474
+ })
475
+
476
+ it('GET /abc', async () => {
477
+ const res = match('GET', '/abc')
478
+ expect(res.length).toBe(2)
479
+ expect(res[0]).toEqual({ handler: 'middleware', params: { name: 'abc' } })
480
+ expect(res[1]).toEqual({ handler: 'handler', params: { id: 'abc' } })
481
+ })
482
+
483
+ it('GET /reverse/abc in reverse registration order', async () => {
484
+ router = newRouter()
485
+ router.add('GET', '/reverse/:id{[a-z]+}', 'handler')
486
+ router.add('GET', '/reverse/:name{[a-z]+}/*', 'middleware')
487
+ const res = match('GET', '/reverse/abc')
488
+ expect(res.map(({ handler }) => handler)).toEqual(['handler', 'middleware'])
489
+ })
490
+
491
+ it('POST /all/abc with ALL middleware', async () => {
492
+ router = newRouter()
493
+ router.add('ALL', '/all/:name{[a-z]+}/*', 'middleware')
494
+ router.add('POST', '/all/:id{[a-z]+}', 'handler')
495
+ const res = match('POST', '/all/abc')
496
+ expect(res.map(({ handler }) => handler)).toEqual(['middleware', 'handler'])
497
+ })
498
+
499
+ it('GET /posts/2024/comments with nested braces', async () => {
500
+ router = newRouter()
501
+ router.add('GET', '/posts/:year{[0-9]{4}}/*', 'middleware')
502
+ router.add('GET', '/posts/:yr{[0-9]{4}}/comments', 'handler')
503
+ const res = match('GET', '/posts/2024/comments')
504
+ expect(res).toEqual([
505
+ { handler: 'middleware', params: { year: '2024' } },
506
+ { handler: 'handler', params: { yr: '2024' } },
507
+ ])
508
+ })
509
+
510
+ it('GET /files/foo/detail with regexp meta characters', async () => {
511
+ router = newRouter()
512
+ router.add('GET', '/files/:kind{(?:foo|bar)}/*', 'middleware')
513
+ router.add('GET', '/files/:type{(?:foo|bar)}/detail', 'handler')
514
+ const res = match('GET', '/files/foo/detail')
515
+ expect(res).toEqual([
516
+ { handler: 'middleware', params: { kind: 'foo' } },
517
+ { handler: 'handler', params: { type: 'foo' } },
518
+ ])
519
+ })
520
+
521
+ it('GET /user/123/profile with the default pattern', async () => {
522
+ router = newRouter()
523
+ router.add('GET', '/user/:userId{[^/]+}/*', 'middleware')
524
+ router.add('GET', '/user/:id/profile', 'handler')
525
+ const res = match('GET', '/user/123/profile')
526
+ expect(res).toEqual([
527
+ { handler: 'middleware', params: { userId: '123' } },
528
+ { handler: 'handler', params: { id: '123' } },
529
+ ])
530
+ })
531
+
532
+ it('GET /user/123/profile with the default pattern in reverse registration order', async () => {
533
+ router = newRouter()
534
+ router.add('GET', '/user/:id/profile', 'handler')
535
+ router.add('GET', '/user/:userId{[^/]+}/*', 'middleware')
536
+ const res = match('GET', '/user/123/profile')
537
+ expect(res).toEqual([
538
+ { handler: 'handler', params: { id: '123' } },
539
+ { handler: 'middleware', params: { userId: '123' } },
540
+ ])
541
+ })
542
+ })
543
+
544
+ describe('Trailing wildcard after a middle wildcard', () => {
545
+ beforeEach(() => {
546
+ router.add('GET', '/a/*/b/*', 'b')
547
+ })
548
+
549
+ it('GET /a/x/b', async () => {
550
+ const res = match('GET', '/a/x/b')
551
+ expect(res.length).toBe(1)
552
+ expect(res[0].handler).toEqual('b')
553
+ })
554
+
555
+ it('GET /a/x/b/c', async () => {
556
+ const res = match('GET', '/a/x/b/c')
557
+ expect(res.length).toBe(1)
558
+ expect(res[0].handler).toEqual('b')
559
+ })
560
+
561
+ it('GET /a/x/bz', async () => {
562
+ const res = match('GET', '/a/x/bz')
563
+ expect(res.length).toBe(0)
564
+ })
565
+ })
566
+
567
+ describe('Optional route', () => {
568
+ beforeEach(() => {
569
+ router.add('GET', '/api/animals/:type?', 'animals')
570
+ router.add('GET', '/v1/:version?/:platform?', 'result')
571
+ })
572
+
573
+ it('GET /api/animals/dog', async () => {
574
+ const res = match('GET', '/api/animals/dog')
575
+ expect(res.length).toBe(1)
576
+ expect(res[0].handler).toEqual('animals')
577
+ expect(res[0].params['type']).toBe('dog')
578
+ })
579
+ it('GET /api/animals', async () => {
580
+ const res = match('GET', '/api/animals')
581
+ expect(res.length).toBe(1)
582
+ expect(res[0].handler).toEqual('animals')
583
+ expect(res[0].params['type']).toBeUndefined()
584
+ })
585
+ it('GET /v1/123/abc', () => {
586
+ const res = match('GET', '/v1/123/abc')
587
+ expect(res.length).toBe(1)
588
+ expect(res[0].handler).toEqual('result')
589
+ expect(res[0].params['version']).toBe('123')
590
+ expect(res[0].params['platform']).toBe('abc')
591
+ })
592
+ it('GET /v1/123', () => {
593
+ const res = match('GET', '/v1/123')
594
+ expect(res.length).toBe(1)
595
+ expect(res[0].handler).toEqual('result')
596
+ expect(res[0].params['version']).toBe('123')
597
+ expect(res[0].params['platform']).toBeUndefined()
598
+ })
599
+ it('GET /v1', () => {
600
+ const res = match('GET', '/v1')
601
+ expect(res.length).toBe(1)
602
+ expect(res[0].handler).toEqual('result')
603
+ expect(res[0].params['version']).toBeUndefined()
604
+ expect(res[0].params['platform']).toBeUndefined()
605
+ })
606
+ })
607
+
608
+ describe('All', () => {
609
+ beforeEach(() => {
610
+ router.add('GET', '/hello', 'get hello')
611
+ router.add('ALL', '/all', 'get all')
612
+ })
613
+
614
+ it('GET, all hello', async () => {
615
+ const res = match('GET', '/all')
616
+ expect(res.length).toBe(1)
617
+ })
618
+ })
619
+
620
+ describe('long prefix, then star', () => {
621
+ describe('GET only', () => {
622
+ beforeEach(() => {
623
+ router.add('GET', '/long/prefix/*', 'long-prefix')
624
+ router.add('GET', '/long/*', 'long')
625
+ router.add('GET', '*', 'star1')
626
+ router.add('GET', '*', 'star2')
627
+ })
628
+
629
+ it('GET /', () => {
630
+ const res = match('GET', '/')
631
+ expect(res.length).toBe(2)
632
+ expect(res[0].handler).toEqual('star1')
633
+ expect(res[1].handler).toEqual('star2')
634
+ })
635
+
636
+ it('GET /long/prefix', () => {
637
+ const res = match('GET', '/long/prefix')
638
+ expect(res.length).toBe(4)
639
+ expect(res[0].handler).toEqual('long-prefix')
640
+ expect(res[1].handler).toEqual('long')
641
+ expect(res[2].handler).toEqual('star1')
642
+ expect(res[3].handler).toEqual('star2')
643
+ })
644
+
645
+ it('GET /long/prefix/test', () => {
646
+ const res = match('GET', '/long/prefix/test')
647
+ expect(res.length).toBe(4)
648
+ expect(res[0].handler).toEqual('long-prefix')
649
+ expect(res[1].handler).toEqual('long')
650
+ expect(res[2].handler).toEqual('star1')
651
+ expect(res[3].handler).toEqual('star2')
652
+ })
653
+ })
654
+
655
+ describe('ALL and GET', () => {
656
+ beforeEach(() => {
657
+ router.add('ALL', '/long/prefix/*', 'long-prefix')
658
+ router.add('ALL', '/long/*', 'long')
659
+ router.add('GET', '*', 'star1')
660
+ router.add('GET', '*', 'star2')
661
+ })
662
+
663
+ it('GET /', () => {
664
+ const res = match('GET', '/')
665
+ expect(res.length).toBe(2)
666
+ expect(res[0].handler).toEqual('star1')
667
+ expect(res[1].handler).toEqual('star2')
668
+ })
669
+
670
+ it('GET /long/prefix', () => {
671
+ const res = match('GET', '/long/prefix')
672
+ expect(res.length).toBe(4)
673
+ expect(res[0].handler).toEqual('long-prefix')
674
+ expect(res[1].handler).toEqual('long')
675
+ expect(res[2].handler).toEqual('star1')
676
+ expect(res[3].handler).toEqual('star2')
677
+ })
678
+
679
+ it('GET /long/prefix/test', () => {
680
+ const res = match('GET', '/long/prefix/test')
681
+ expect(res.length).toBe(4)
682
+ expect(res[0].handler).toEqual('long-prefix')
683
+ expect(res[1].handler).toEqual('long')
684
+ expect(res[2].handler).toEqual('star1')
685
+ expect(res[3].handler).toEqual('star2')
686
+ })
687
+ })
688
+ })
689
+
690
+ describe('Including slashes', () => {
691
+ beforeEach(() => {
692
+ router.add('GET', '/js/:filename{[a-z0-9/]+.js}', 'any file')
693
+ })
694
+
695
+ it('GET /js/main.js', () => {
696
+ router.add('GET', '/js/main.js', 'main.js')
697
+
698
+ const res = match('GET', '/js/main.js')
699
+ expect(res.length).toBe(2)
700
+ expect(res[0].handler).toEqual('any file')
701
+ expect(res[0].params['filename']).toEqual('main.js')
702
+ expect(res[1].handler).toEqual('main.js')
703
+ expect(res[1].params['filename']).toEqual(undefined)
704
+ })
705
+
706
+ it('GET /js/chunk/123.js', () => {
707
+ const res = match('GET', '/js/chunk/123.js')
708
+ expect(res.length).toBe(1)
709
+ expect(res[0].handler).toEqual('any file')
710
+ expect(res[0].params['filename']).toEqual('chunk/123.js')
711
+ })
712
+
713
+ it('GET /js/chunk/nest/123.js', () => {
714
+ const res = match('GET', '/js/chunk/nest/123.js')
715
+ expect(res.length).toBe(1)
716
+ expect(res[0].handler).toEqual('any file')
717
+ expect(res[0].params['filename']).toEqual('chunk/nest/123.js')
718
+ })
719
+ })
720
+
721
+ describe('Capture simple multiple directories', () => {
722
+ beforeEach(() => {
723
+ router.add('GET', '/:dirs{.+}/file.html', 'file.html')
724
+ })
725
+
726
+ it('GET /foo/bar/file.html', () => {
727
+ const res = match('GET', '/foo/bar/file.html')
728
+ expect(res.length).toBe(1)
729
+ expect(res[0].handler).toEqual('file.html')
730
+ expect(res[0].params['dirs']).toEqual('foo/bar')
731
+ })
732
+
733
+ it('GET /foo/bar/baz/file.html', () => {
734
+ const res = match('GET', '/foo/bar/baz/file.html')
735
+ expect(res.length).toBe(1)
736
+ expect(res[0].handler).toEqual('file.html')
737
+ expect(res[0].params['dirs']).toEqual('foo/bar/baz')
738
+ })
739
+
740
+ it('GET /a/b/c/d/file.html', () => {
741
+ const res = match('GET', '/a/b/c/d/file.html')
742
+ expect(res.length).toBe(1)
743
+ expect(res[0].handler).toEqual('file.html')
744
+ expect(res[0].params['dirs']).toEqual('a/b/c/d')
745
+ })
746
+ })
747
+
748
+ describe('Capture regex pattern has trailing wildcard', () => {
749
+ beforeEach(() => {
750
+ router.add('GET', '/:dir{[a-z]+}/*/file.html', 'file.html')
751
+ })
752
+
753
+ it('GET /foo/bar/file.html', () => {
754
+ const res = match('GET', '/foo/bar/file.html')
755
+ expect(res.length).toBe(1)
756
+ expect(res[0].handler).toEqual('file.html')
757
+ expect(res[0].params['dir']).toEqual('foo')
758
+ })
759
+ })
760
+
761
+ describe('Capture regex param with trailing wildcard on empty remainder', () => {
762
+ beforeEach(() => {
763
+ router.add('GET', '/:id{[0-9]+}/*', 'regexp')
764
+ })
765
+
766
+ it('GET /123', () => {
767
+ const res = match('GET', '/123')
768
+ expect(res.length).toBe(1)
769
+ expect(res[0].handler).toEqual('regexp')
770
+ expect(res[0].params['id']).toBe('123')
771
+ })
772
+ })
773
+
774
+ describe('Capture regex param with trailing wildcard and sibling route', () => {
775
+ beforeEach(() => {
776
+ router.add('GET', '/regex-abc/:id{[0-9]+}/*', 'middleware')
777
+ router.add('GET', '/regex-abc/:id{[0-9]+}/def', 'regexp')
778
+ })
779
+
780
+ it('GET /regex-abc/123/ghi', () => {
781
+ const res = match('GET', '/regex-abc/123/ghi')
782
+ expect(res.length).toBe(1)
783
+ expect(res[0].handler).toEqual('middleware')
784
+ expect(res[0].params['id']).toBe('123')
785
+ })
786
+ })
787
+
788
+ describe('Capture complex multiple directories', () => {
789
+ beforeEach(() => {
790
+ router.add('GET', '/:first{.+}/middle-a/:reference?', '1')
791
+ router.add('GET', '/:first{.+}/middle-b/end-c/:uuid', '2')
792
+ router.add('GET', '/:first{.+}/middle-b/:digest', '3')
793
+ })
794
+
795
+ it('GET /part1/middle-b/latest', () => {
796
+ const res = match('GET', '/part1/middle-b/latest')
797
+ expect(res.length).toBe(1)
798
+ expect(res[0].handler).toEqual('3')
799
+ expect(res[0].params['first']).toEqual('part1')
800
+ expect(res[0].params['digest']).toEqual('latest')
801
+ })
802
+
803
+ it('GET /part1/middle-b/end-c/latest', () => {
804
+ const res = match('GET', '/part1/middle-b/end-c/latest')
805
+ expect(res.length).toBe(1)
806
+ expect(res[0].handler).toEqual('2')
807
+ expect(res[0].params['first']).toEqual('part1')
808
+ expect(res[0].params['uuid']).toEqual('latest')
809
+ })
810
+ })
811
+
812
+ describe('Capture multiple directories and optional', () => {
813
+ beforeEach(() => {
814
+ router.add('GET', '/:prefix{.+}/contents/:id?', 'contents')
815
+ })
816
+
817
+ it('GET /foo/bar/contents', () => {
818
+ const res = match('GET', '/foo/bar/contents')
819
+ expect(res.length).toBe(1)
820
+ expect(res[0].handler).toEqual('contents')
821
+ expect(res[0].params['prefix']).toEqual('foo/bar')
822
+ expect(res[0].params['id']).toEqual(undefined)
823
+ })
824
+
825
+ it('GET /foo/bar/contents/123', () => {
826
+ const res = match('GET', '/foo/bar/contents/123')
827
+ expect(res.length).toBe(1)
828
+ expect(res[0].handler).toEqual('contents')
829
+ expect(res[0].params['prefix']).toEqual('foo/bar')
830
+ expect(res[0].params['id']).toEqual('123')
831
+ })
832
+ })
833
+
834
+ describe('non ascii characters', () => {
835
+ beforeEach(() => {
836
+ router.add('ALL', '/$/*', 'middleware $')
837
+ router.add('GET', '/$/:name', 'get $ name')
838
+ router.add('ALL', '/()/*', 'middleware ()')
839
+ router.add('GET', '/()/:name', 'get () name')
840
+ })
841
+
842
+ it('GET /$/acryl', () => {
843
+ const res = match('GET', '/$/acryl')
844
+ expect(res.length).toBe(2)
845
+ expect(res[0].handler).toEqual('middleware $')
846
+ expect(res[0].params).toEqual({})
847
+ expect(res[1].handler).toEqual('get $ name')
848
+ expect(res[1].params['name']).toEqual('acryl')
849
+ })
850
+
851
+ it('GET /()/acryl', () => {
852
+ const res = match('GET', '/()/acryl')
853
+ expect(res.length).toBe(2)
854
+ expect(res[0].handler).toEqual('middleware ()')
855
+ expect(res[0].params).toEqual({})
856
+ expect(res[1].handler).toEqual('get () name')
857
+ expect(res[1].params['name']).toEqual('acryl')
858
+ })
859
+ })
860
+
861
+ describe('REST API', () => {
862
+ beforeEach(() => {
863
+ router.add('GET', '/users/:username{[a-z]+}', 'profile')
864
+ router.add('GET', '/users/:username{[a-z]+}/posts', 'posts')
865
+ })
866
+
867
+ it('GET /users/acryl', () => {
868
+ const res = match('GET', '/users/acryl')
869
+ expect(res.length).toBe(1)
870
+ expect(res[0].handler).toEqual('profile')
871
+ })
872
+
873
+ it('GET /users/acryl/posts', () => {
874
+ const res = match('GET', '/users/acryl/posts')
875
+ expect(res.length).toBe(1)
876
+ expect(res[0].handler).toEqual('posts')
877
+ })
878
+ })
879
+
880
+ describe('Trailing slash', () => {
881
+ beforeEach(() => {
882
+ router.add('GET', '/book', 'GET /book')
883
+ router.add('GET', '/book/:id', 'GET /book/:id')
884
+ })
885
+
886
+ it('GET /book', () => {
887
+ const res = match('GET', '/book')
888
+ expect(res.length).toBe(1)
889
+ expect(res[0].handler).toEqual('GET /book')
890
+ })
891
+ it('GET /book/', () => {
892
+ const res = match('GET', '/book/')
893
+ expect(res.length).toBe(0)
894
+ })
895
+ })
896
+
897
+ describe('Empty path segment', () => {
898
+ beforeEach(() => {
899
+ router.add('GET', '/u/:id/posts', 'posts')
900
+ })
901
+
902
+ it('GET /u/42/posts', () => {
903
+ const res = match('GET', '/u/42/posts')
904
+ expect(res.length).toBe(1)
905
+ expect(res[0].params['id']).toBe('42')
906
+ })
907
+
908
+ it('GET /u//posts does not match with an empty param', () => {
909
+ const res = match('GET', '/u//posts')
910
+ expect(res.length).toBe(0)
911
+ })
912
+ })
913
+
914
+ describe('Same path', () => {
915
+ beforeEach(() => {
916
+ router.add('GET', '/hey', 'Middleware A')
917
+ router.add('GET', '/hey', 'Middleware B')
918
+ })
919
+
920
+ it('GET /hey', () => {
921
+ const res = match('GET', '/hey')
922
+ expect(res.length).toBe(2)
923
+ expect(res[0].handler).toEqual('Middleware A')
924
+ expect(res[1].handler).toEqual('Middleware B')
925
+ })
926
+ })
927
+
928
+ describe('Routing with a hostname', () => {
929
+ beforeEach(() => {
930
+ router.add('GET', 'www1.example.com/hello', 'www1')
931
+ router.add('GET', 'www2.example.com/hello', 'www2')
932
+ })
933
+ it('GET www1.example.com/hello', () => {
934
+ const res = match('GET', 'www1.example.com/hello')
935
+ expect(res.length).toBe(1)
936
+ expect(res[0].handler).toEqual('www1')
937
+ })
938
+ it('GET www2.example.com/hello', () => {
939
+ const res = match('GET', 'www2.example.com/hello')
940
+ expect(res.length).toBe(1)
941
+ expect(res[0].handler).toEqual('www2')
942
+ })
943
+ it('GET /hello', () => {
944
+ const res = match('GET', '/hello')
945
+ expect(res.length).toBe(0)
946
+ })
947
+ })
948
+
949
+ describe('static routes of ALL and GET', () => {
950
+ beforeEach(() => {
951
+ router.add('ALL', '/foo', 'foo')
952
+ router.add('GET', '/bar', 'bar')
953
+ })
954
+
955
+ it('get /foo', () => {
956
+ const res = match('GET', '/foo')
957
+ expect(res[0].handler).toEqual('foo')
958
+ })
959
+ })
960
+
961
+ describe('ALL and Star', () => {
962
+ beforeEach(() => {
963
+ router.add('ALL', '/x', '/x')
964
+ router.add('GET', '*', 'star')
965
+ })
966
+
967
+ it('Should return /x and star', async () => {
968
+ const res = match('GET', '/x')
969
+ expect(res.length).toBe(2)
970
+ expect(res[0].handler).toEqual('/x')
971
+ expect(res[1].handler).toEqual('star')
972
+ })
973
+ })
974
+
975
+ describe('GET star, ALL static, GET star...', () => {
976
+ beforeEach(() => {
977
+ router.add('GET', '*', 'star1')
978
+ router.add('ALL', '/x', '/x')
979
+ router.add('GET', '*', 'star2')
980
+ router.add('GET', '*', 'star3')
981
+ })
982
+
983
+ it('Should return /x and star', async () => {
984
+ const res = match('GET', '/x')
985
+ expect(res.length).toBe(4)
986
+ expect(res[0].handler).toEqual('star1')
987
+ expect(res[1].handler).toEqual('/x')
988
+ expect(res[2].handler).toEqual('star2')
989
+ expect(res[3].handler).toEqual('star3')
990
+ })
991
+ })
992
+ describe('GET star, GET static, ALL star...', () => {
993
+ beforeEach(() => {
994
+ router.add('GET', '/y/*', 'star1')
995
+ router.add('GET', '/y/a', 'a')
996
+ router.add('ALL', '/y/b/*', 'star2')
997
+ router.add('GET', '/y/b/bar', 'bar')
998
+ })
999
+
1000
+ it('Should return star1, star2, and bar', async () => {
1001
+ const res = match('GET', '/y/b/bar')
1002
+ expect(res.length).toBe(3)
1003
+ expect(res[0].handler).toEqual('star1')
1004
+ expect(res[1].handler).toEqual('star2')
1005
+ expect(res[2].handler).toEqual('bar')
1006
+ })
1007
+ })
1008
+
1009
+ describe('ALL star, ALL star, GET static, ALL star...', () => {
1010
+ beforeEach(() => {
1011
+ router.add('ALL', '*', 'wildcard')
1012
+ router.add('ALL', '/a/*', 'star1')
1013
+ router.add('GET', '/a/foo', 'foo')
1014
+ router.add('ALL', '/b/*', 'star2')
1015
+ router.add('GET', '/b/bar', 'bar')
1016
+ })
1017
+
1018
+ it('Should return wildcard, star2 and bar', async () => {
1019
+ const res = match('GET', '/b/bar')
1020
+ expect(res.length).toBe(3)
1021
+ expect(res[0].handler).toEqual('wildcard')
1022
+ expect(res[1].handler).toEqual('star2')
1023
+ expect(res[2].handler).toEqual('bar')
1024
+ })
1025
+ })
1026
+
1027
+ describe('Capture Group', () => {
1028
+ describe('Simple capturing group', () => {
1029
+ beforeEach(() => {
1030
+ router.add('get', '/foo/:capture{(bar|baz)}', 'ok')
1031
+ })
1032
+
1033
+ it('GET /foo/bar', () => {
1034
+ const res = match('get', '/foo/bar')
1035
+ expect(res.length).toBe(1)
1036
+ expect(res[0].handler).toBe('ok')
1037
+ expect(res[0].params['capture']).toBe('bar')
1038
+ })
1039
+
1040
+ it('GET /foo/baz', () => {
1041
+ const res = match('get', '/foo/baz')
1042
+ expect(res.length).toBe(1)
1043
+ expect(res[0].handler).toBe('ok')
1044
+ expect(res[0].params['capture']).toBe('baz')
1045
+ })
1046
+
1047
+ it('GET /foo/qux', () => {
1048
+ const res = match('get', '/foo/qux')
1049
+ expect(res.length).toBe(0)
1050
+ })
1051
+ })
1052
+
1053
+ describe('Non-capturing group', () => {
1054
+ beforeEach(() => {
1055
+ router.add('get', '/foo/:capture{(?:bar|baz)}', 'ok')
1056
+ })
1057
+
1058
+ it('GET /foo/bar', () => {
1059
+ const res = match('get', '/foo/bar')
1060
+ expect(res.length).toBe(1)
1061
+ expect(res[0].handler).toBe('ok')
1062
+ expect(res[0].params['capture']).toBe('bar')
1063
+ })
1064
+
1065
+ it('GET /foo/baz', () => {
1066
+ const res = match('get', '/foo/baz')
1067
+ expect(res.length).toBe(1)
1068
+ expect(res[0].handler).toBe('ok')
1069
+ expect(res[0].params['capture']).toBe('baz')
1070
+ })
1071
+
1072
+ it('GET /foo/qux', () => {
1073
+ const res = match('get', '/foo/qux')
1074
+ expect(res.length).toBe(0)
1075
+ })
1076
+ })
1077
+
1078
+ describe('Non-capturing group with prefix', () => {
1079
+ beforeEach(() => {
1080
+ router.add('get', '/foo/:capture{ba(?:r|z)}', 'ok')
1081
+ })
1082
+
1083
+ it('GET /foo/bar', () => {
1084
+ const res = match('get', '/foo/bar')
1085
+ expect(res.length).toBe(1)
1086
+ expect(res[0].handler).toBe('ok')
1087
+ expect(res[0].params['capture']).toBe('bar')
1088
+ })
1089
+
1090
+ it('GET /foo/baz', () => {
1091
+ const res = match('get', '/foo/baz')
1092
+ expect(res.length).toBe(1)
1093
+ expect(res[0].handler).toBe('ok')
1094
+ expect(res[0].params['capture']).toBe('baz')
1095
+ })
1096
+
1097
+ it('GET /foo/qux', () => {
1098
+ const res = match('get', '/foo/qux')
1099
+ expect(res.length).toBe(0)
1100
+ })
1101
+ })
1102
+
1103
+ describe('Complex capturing group', () => {
1104
+ it('GET request', () => {
1105
+ router.add('get', '/foo/:capture{ba(r|z)}', 'ok')
1106
+
1107
+ const res = match('get', '/foo/bar')
1108
+ expect(res.length).toBe(1)
1109
+ expect(res[0].handler).toBe('ok')
1110
+ })
1111
+ })
1112
+ })
1113
+
1114
+ describe('Unknown method', () => {
1115
+ beforeEach(() => {
1116
+ router.add('GET', '/', 'index')
1117
+ router.add('ALL', '/all', 'all')
1118
+ })
1119
+
1120
+ it('UNKNOWN_METHOD /', () => {
1121
+ const res = match('UNKNOWN_METHOD', '/')
1122
+ expect(res.length).toBe(0)
1123
+ })
1124
+
1125
+ it('UNKNOWN_METHOD /all', () => {
1126
+ const res = match('UNKNOWN_METHOD', '/all')
1127
+ expect(res.length).toBe(1)
1128
+ expect(res[0].handler).toBe('all')
1129
+ })
1130
+ })
1131
+ })
1132
+ }