@loopback/rest 4.0.0-alpha.8 → 5.0.1

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 (388) hide show
  1. package/CHANGELOG.md +1822 -0
  2. package/LICENSE +1 -1
  3. package/README.md +30 -58
  4. package/dist/body-parsers/body-parser.d.ts +25 -0
  5. package/dist/body-parsers/body-parser.helpers.d.ts +44 -0
  6. package/dist/body-parsers/body-parser.helpers.js +102 -0
  7. package/dist/body-parsers/body-parser.helpers.js.map +1 -0
  8. package/dist/body-parsers/body-parser.js +159 -0
  9. package/dist/body-parsers/body-parser.js.map +1 -0
  10. package/dist/body-parsers/body-parser.json.d.ts +9 -0
  11. package/dist/body-parsers/body-parser.json.js +43 -0
  12. package/dist/body-parsers/body-parser.json.js.map +1 -0
  13. package/dist/body-parsers/body-parser.raw.d.ts +12 -0
  14. package/dist/body-parsers/body-parser.raw.js +39 -0
  15. package/dist/body-parsers/body-parser.raw.js.map +1 -0
  16. package/dist/body-parsers/body-parser.stream.d.ts +12 -0
  17. package/dist/body-parsers/body-parser.stream.js +28 -0
  18. package/dist/body-parsers/body-parser.stream.js.map +1 -0
  19. package/dist/body-parsers/body-parser.text.d.ts +9 -0
  20. package/dist/body-parsers/body-parser.text.js +38 -0
  21. package/dist/body-parsers/body-parser.text.js.map +1 -0
  22. package/dist/body-parsers/body-parser.urlencoded.d.ts +9 -0
  23. package/dist/body-parsers/body-parser.urlencoded.js +36 -0
  24. package/dist/body-parsers/body-parser.urlencoded.js.map +1 -0
  25. package/dist/body-parsers/index.d.ts +8 -0
  26. package/dist/body-parsers/index.js +16 -0
  27. package/dist/body-parsers/index.js.map +1 -0
  28. package/dist/body-parsers/types.d.ts +51 -0
  29. package/dist/body-parsers/types.js +12 -0
  30. package/dist/body-parsers/types.js.map +1 -0
  31. package/dist/coercion/coerce-parameter.d.ts +9 -0
  32. package/dist/coercion/coerce-parameter.js +166 -0
  33. package/dist/coercion/coerce-parameter.js.map +1 -0
  34. package/dist/coercion/utils.d.ts +43 -0
  35. package/dist/coercion/utils.js +96 -0
  36. package/dist/coercion/utils.js.map +1 -0
  37. package/dist/coercion/validator.d.ts +49 -0
  38. package/dist/coercion/validator.js +85 -0
  39. package/dist/coercion/validator.js.map +1 -0
  40. package/dist/http-handler.d.ts +38 -0
  41. package/dist/http-handler.js +68 -0
  42. package/dist/http-handler.js.map +1 -0
  43. package/dist/index.d.ts +36 -1
  44. package/dist/index.js +40 -6
  45. package/dist/index.js.map +1 -0
  46. package/dist/keys.d.ts +198 -0
  47. package/dist/keys.js +202 -0
  48. package/dist/keys.js.map +1 -0
  49. package/dist/parse-json.d.ts +11 -0
  50. package/dist/parse-json.js +42 -0
  51. package/dist/parse-json.js.map +1 -0
  52. package/dist/parser.d.ts +11 -0
  53. package/dist/parser.js +76 -0
  54. package/dist/parser.js.map +1 -0
  55. package/{dist6/src/providers/find-route.d.ts → dist/providers/find-route.provider.d.ts} +3 -1
  56. package/dist/providers/find-route.provider.js +36 -0
  57. package/dist/providers/find-route.provider.js.map +1 -0
  58. package/dist/providers/index.d.ts +6 -0
  59. package/dist/providers/index.js +14 -0
  60. package/dist/providers/index.js.map +1 -0
  61. package/dist/{src/providers/invoke-method.d.ts → providers/invoke-method.provider.d.ts} +3 -1
  62. package/dist/providers/invoke-method.provider.js +30 -0
  63. package/dist/providers/invoke-method.provider.js.map +1 -0
  64. package/dist/providers/log-error.provider.d.ts +6 -0
  65. package/dist/providers/log-error.provider.js +21 -0
  66. package/dist/providers/log-error.provider.js.map +1 -0
  67. package/dist/providers/parse-params.provider.d.ts +15 -0
  68. package/dist/providers/parse-params.provider.js +41 -0
  69. package/dist/providers/parse-params.provider.js.map +1 -0
  70. package/dist/providers/reject.provider.d.ts +10 -0
  71. package/dist/providers/reject.provider.js +47 -0
  72. package/dist/providers/reject.provider.js.map +1 -0
  73. package/dist/{src/providers/send.d.ts → providers/send.provider.d.ts} +1 -4
  74. package/dist/{src/providers/send.js → providers/send.provider.js} +4 -6
  75. package/dist/providers/send.provider.js.map +1 -0
  76. package/dist/request-context.d.ts +36 -0
  77. package/dist/request-context.js +104 -0
  78. package/dist/request-context.js.map +1 -0
  79. package/dist/rest-http-error.d.ts +37 -0
  80. package/dist/rest-http-error.js +51 -0
  81. package/dist/rest-http-error.js.map +1 -0
  82. package/dist/rest.application.d.ts +232 -0
  83. package/dist/rest.application.js +174 -0
  84. package/dist/rest.application.js.map +1 -0
  85. package/dist/rest.component.d.ts +15 -0
  86. package/dist/rest.component.js +72 -0
  87. package/dist/rest.component.js.map +1 -0
  88. package/dist/rest.server.d.ts +443 -0
  89. package/dist/rest.server.js +748 -0
  90. package/dist/rest.server.js.map +1 -0
  91. package/dist/router/base-route.d.ts +29 -0
  92. package/dist/router/base-route.js +41 -0
  93. package/dist/router/base-route.js.map +1 -0
  94. package/dist/router/controller-route.d.ts +61 -0
  95. package/dist/router/controller-route.js +160 -0
  96. package/dist/router/controller-route.js.map +1 -0
  97. package/dist/router/external-express-routes.d.ts +24 -0
  98. package/dist/router/external-express-routes.js +90 -0
  99. package/dist/router/external-express-routes.js.map +1 -0
  100. package/dist/router/handler-route.d.ts +12 -0
  101. package/dist/router/handler-route.js +30 -0
  102. package/dist/router/handler-route.js.map +1 -0
  103. package/dist/router/index.d.ts +14 -0
  104. package/dist/router/index.js +25 -0
  105. package/dist/router/index.js.map +1 -0
  106. package/dist/router/openapi-path.d.ts +14 -0
  107. package/dist/router/openapi-path.js +64 -0
  108. package/dist/router/openapi-path.js.map +1 -0
  109. package/dist/router/redirect-route.d.ts +23 -0
  110. package/dist/router/redirect-route.js +50 -0
  111. package/dist/router/redirect-route.js.map +1 -0
  112. package/dist/router/regexp-router.d.ts +25 -0
  113. package/dist/router/regexp-router.js +84 -0
  114. package/dist/router/regexp-router.js.map +1 -0
  115. package/dist/router/rest-router.d.ts +35 -0
  116. package/dist/{src/internal-types.js → router/rest-router.js} +2 -2
  117. package/dist/router/rest-router.js.map +1 -0
  118. package/dist/router/route-entry.d.ts +46 -0
  119. package/dist/router/route-entry.js +20 -0
  120. package/dist/router/route-entry.js.map +1 -0
  121. package/dist/router/route-sort.d.ts +7 -0
  122. package/dist/router/route-sort.js +75 -0
  123. package/dist/router/route-sort.js.map +1 -0
  124. package/dist/router/router-base.d.ts +42 -0
  125. package/dist/router/router-base.js +101 -0
  126. package/dist/router/router-base.js.map +1 -0
  127. package/dist/router/router-spec.d.ts +3 -0
  128. package/dist/router/router-spec.js +40 -0
  129. package/dist/router/router-spec.js.map +1 -0
  130. package/dist/router/routing-table.d.ts +32 -0
  131. package/dist/router/routing-table.js +86 -0
  132. package/dist/router/routing-table.js.map +1 -0
  133. package/dist/router/trie-router.d.ts +13 -0
  134. package/dist/router/trie-router.js +55 -0
  135. package/dist/router/trie-router.js.map +1 -0
  136. package/dist/router/trie.d.ts +59 -0
  137. package/dist/router/trie.js +180 -0
  138. package/dist/router/trie.js.map +1 -0
  139. package/{dist6/src → dist}/sequence.d.ts +28 -23
  140. package/dist/sequence.js +112 -0
  141. package/dist/sequence.js.map +1 -0
  142. package/dist/spec-enhancers/consolidate.spec-enhancer.d.ts +68 -0
  143. package/dist/spec-enhancers/consolidate.spec-enhancer.js +145 -0
  144. package/dist/spec-enhancers/consolidate.spec-enhancer.js.map +1 -0
  145. package/dist/spec-enhancers/info.spec-enhancer.d.ts +19 -0
  146. package/dist/spec-enhancers/info.spec-enhancer.js +89 -0
  147. package/dist/spec-enhancers/info.spec-enhancer.js.map +1 -0
  148. package/dist/types.d.ts +178 -0
  149. package/dist/types.js +12 -0
  150. package/dist/types.js.map +1 -0
  151. package/dist/validation/ajv-factory.provider.d.ts +12 -0
  152. package/dist/validation/ajv-factory.provider.js +87 -0
  153. package/dist/validation/ajv-factory.provider.js.map +1 -0
  154. package/dist/validation/request-body.validator.d.ts +14 -0
  155. package/dist/validation/request-body.validator.js +161 -0
  156. package/dist/validation/request-body.validator.js.map +1 -0
  157. package/dist/writer.d.ts +9 -0
  158. package/dist/writer.js +62 -0
  159. package/dist/writer.js.map +1 -0
  160. package/package.json +66 -38
  161. package/src/body-parsers/body-parser.helpers.ts +148 -0
  162. package/src/body-parsers/body-parser.json.ts +46 -0
  163. package/src/body-parsers/body-parser.raw.ts +42 -0
  164. package/src/body-parsers/body-parser.stream.ts +27 -0
  165. package/src/body-parsers/body-parser.text.ts +44 -0
  166. package/src/body-parsers/body-parser.ts +208 -0
  167. package/src/body-parsers/body-parser.urlencoded.ts +42 -0
  168. package/src/body-parsers/index.ts +13 -0
  169. package/src/body-parsers/types.ts +60 -0
  170. package/src/coercion/coerce-parameter.ts +207 -0
  171. package/src/coercion/utils.ts +103 -0
  172. package/src/coercion/validator.ts +98 -0
  173. package/src/http-handler.ts +84 -41
  174. package/src/index.ts +37 -30
  175. package/src/keys.ts +273 -20
  176. package/src/parse-json.ts +42 -0
  177. package/src/parser.ts +89 -104
  178. package/src/providers/{find-route.ts → find-route.provider.ts} +10 -7
  179. package/src/providers/index.ts +7 -9
  180. package/src/providers/{invoke-method.ts → invoke-method.provider.ts} +8 -5
  181. package/src/providers/log-error.provider.ts +27 -0
  182. package/src/providers/parse-params.provider.ts +42 -0
  183. package/src/providers/reject.provider.ts +44 -0
  184. package/src/providers/{send.ts → send.provider.ts} +2 -5
  185. package/src/request-context.ts +123 -0
  186. package/src/rest-http-error.ts +87 -0
  187. package/src/rest.application.ts +390 -0
  188. package/src/rest.component.ts +111 -0
  189. package/src/rest.server.ts +1192 -0
  190. package/src/router/base-route.ts +53 -0
  191. package/src/router/controller-route.ts +241 -0
  192. package/src/router/external-express-routes.ts +139 -0
  193. package/src/router/handler-route.ts +44 -0
  194. package/src/router/index.ts +24 -0
  195. package/src/router/openapi-path.ts +67 -0
  196. package/src/router/redirect-route.ts +64 -0
  197. package/src/router/regexp-router.ts +104 -0
  198. package/src/router/rest-router.ts +48 -0
  199. package/src/router/route-entry.ts +74 -0
  200. package/src/router/route-sort.ts +74 -0
  201. package/src/router/router-base.ts +124 -0
  202. package/src/router/router-spec.ts +36 -0
  203. package/src/router/routing-table.ts +83 -279
  204. package/src/router/trie-router.ts +57 -0
  205. package/src/router/trie.ts +233 -0
  206. package/src/sequence.ts +44 -37
  207. package/src/spec-enhancers/consolidate.spec-enhancer.ts +182 -0
  208. package/src/spec-enhancers/info.spec-enhancer.ts +92 -0
  209. package/src/types.ts +216 -0
  210. package/src/validation/ajv-factory.provider.ts +94 -0
  211. package/src/validation/request-body.validator.ts +208 -0
  212. package/src/writer.ts +41 -68
  213. package/api-docs/.DS_Store +0 -0
  214. package/api-docs/apple-touch-icon-114x114-precomposed.png +0 -0
  215. package/api-docs/apple-touch-icon-144x144-precomposed.png +0 -0
  216. package/api-docs/apple-touch-icon-57x57-precomposed.png +0 -0
  217. package/api-docs/apple-touch-icon-72x72-precomposed.png +0 -0
  218. package/api-docs/apple-touch-icon-precomposed.png +0 -0
  219. package/api-docs/apple-touch-icon.png +0 -0
  220. package/api-docs/css/bootstrap.min.css +0 -9
  221. package/api-docs/css/code-themes/arta.css +0 -158
  222. package/api-docs/css/code-themes/ascetic.css +0 -50
  223. package/api-docs/css/code-themes/brown_paper.css +0 -104
  224. package/api-docs/css/code-themes/brown_papersq.png +0 -0
  225. package/api-docs/css/code-themes/dark.css +0 -103
  226. package/api-docs/css/code-themes/default.css +0 -135
  227. package/api-docs/css/code-themes/far.css +0 -111
  228. package/api-docs/css/code-themes/github.css +0 -127
  229. package/api-docs/css/code-themes/googlecode.css +0 -144
  230. package/api-docs/css/code-themes/idea.css +0 -121
  231. package/api-docs/css/code-themes/ir_black.css +0 -104
  232. package/api-docs/css/code-themes/magula.css +0 -121
  233. package/api-docs/css/code-themes/monokai.css +0 -114
  234. package/api-docs/css/code-themes/pojoaque.css +0 -104
  235. package/api-docs/css/code-themes/pojoaque.jpg +0 -0
  236. package/api-docs/css/code-themes/rainbow.css +0 -114
  237. package/api-docs/css/code-themes/school_book.css +0 -111
  238. package/api-docs/css/code-themes/school_book.png +0 -0
  239. package/api-docs/css/code-themes/sl-theme.css +0 -45
  240. package/api-docs/css/code-themes/solarized_dark.css +0 -88
  241. package/api-docs/css/code-themes/solarized_light.css +0 -88
  242. package/api-docs/css/code-themes/sunburst.css +0 -158
  243. package/api-docs/css/code-themes/tomorrow-night-blue.css +0 -52
  244. package/api-docs/css/code-themes/tomorrow-night-bright.css +0 -51
  245. package/api-docs/css/code-themes/tomorrow-night-eighties.css +0 -51
  246. package/api-docs/css/code-themes/tomorrow-night.css +0 -52
  247. package/api-docs/css/code-themes/tomorrow.css +0 -49
  248. package/api-docs/css/code-themes/vs.css +0 -86
  249. package/api-docs/css/code-themes/xcode.css +0 -154
  250. package/api-docs/css/code-themes/zenburn.css +0 -115
  251. package/api-docs/css/main.css +0 -139
  252. package/api-docs/favicon.ico +0 -0
  253. package/api-docs/fonts/0ihfXUL2emPh0ROJezvraLO3LdcAZYWl9Si6vvxL-qU.woff +0 -0
  254. package/api-docs/fonts/OsJ2DjdpjqFRVUSto6IffLO3LdcAZYWl9Si6vvxL-qU.woff +0 -0
  255. package/api-docs/fonts/_aijTyevf54tkVDLy-dlnLO3LdcAZYWl9Si6vvxL-qU.woff +0 -0
  256. package/api-docs/index.html +0 -7082
  257. package/api-docs/js/main.js +0 -19
  258. package/api-docs/js/vendor/bootstrap.min.js +0 -6
  259. package/api-docs/js/vendor/jquery-1.10.1.min.js +0 -6
  260. package/api-docs/js/vendor/jquery.scrollTo-1.4.3.1.js +0 -218
  261. package/api-docs/js/vendor/modernizr-2.6.2-respond-1.1.0.min.js +0 -11
  262. package/dist/src/http-handler.d.ts +0 -19
  263. package/dist/src/http-handler.js +0 -43
  264. package/dist/src/http-handler.js.map +0 -1
  265. package/dist/src/index.d.ts +0 -14
  266. package/dist/src/index.js +0 -33
  267. package/dist/src/index.js.map +0 -1
  268. package/dist/src/internal-types.d.ts +0 -67
  269. package/dist/src/internal-types.js.map +0 -1
  270. package/dist/src/keys.d.ts +0 -22
  271. package/dist/src/keys.js +0 -35
  272. package/dist/src/keys.js.map +0 -1
  273. package/dist/src/parser.d.ts +0 -11
  274. package/dist/src/parser.js +0 -98
  275. package/dist/src/parser.js.map +0 -1
  276. package/dist/src/providers/bind-element.d.ts +0 -7
  277. package/dist/src/providers/bind-element.js +0 -34
  278. package/dist/src/providers/bind-element.js.map +0 -1
  279. package/dist/src/providers/find-route.d.ts +0 -9
  280. package/dist/src/providers/find-route.js +0 -42
  281. package/dist/src/providers/find-route.js.map +0 -1
  282. package/dist/src/providers/get-from-context.d.ts +0 -7
  283. package/dist/src/providers/get-from-context.js +0 -34
  284. package/dist/src/providers/get-from-context.js.map +0 -1
  285. package/dist/src/providers/index.d.ts +0 -8
  286. package/dist/src/providers/index.js +0 -18
  287. package/dist/src/providers/index.js.map +0 -1
  288. package/dist/src/providers/invoke-method.js +0 -36
  289. package/dist/src/providers/invoke-method.js.map +0 -1
  290. package/dist/src/providers/log-error-provider.d.ts +0 -6
  291. package/dist/src/providers/log-error-provider.js +0 -17
  292. package/dist/src/providers/log-error-provider.js.map +0 -1
  293. package/dist/src/providers/parse-params.d.ts +0 -13
  294. package/dist/src/providers/parse-params.js +0 -22
  295. package/dist/src/providers/parse-params.js.map +0 -1
  296. package/dist/src/providers/reject.d.ts +0 -6
  297. package/dist/src/providers/reject.js +0 -40
  298. package/dist/src/providers/reject.js.map +0 -1
  299. package/dist/src/providers/send.js.map +0 -1
  300. package/dist/src/rest-component.d.ts +0 -12
  301. package/dist/src/rest-component.js +0 -50
  302. package/dist/src/rest-component.js.map +0 -1
  303. package/dist/src/rest-server.d.ts +0 -211
  304. package/dist/src/rest-server.js +0 -426
  305. package/dist/src/rest-server.js.map +0 -1
  306. package/dist/src/router/metadata.d.ts +0 -150
  307. package/dist/src/router/metadata.js +0 -410
  308. package/dist/src/router/metadata.js.map +0 -1
  309. package/dist/src/router/routing-table.d.ts +0 -68
  310. package/dist/src/router/routing-table.js +0 -204
  311. package/dist/src/router/routing-table.js.map +0 -1
  312. package/dist/src/sequence.d.ts +0 -81
  313. package/dist/src/sequence.js +0 -104
  314. package/dist/src/sequence.js.map +0 -1
  315. package/dist/src/writer.d.ts +0 -17
  316. package/dist/src/writer.js +0 -87
  317. package/dist/src/writer.js.map +0 -1
  318. package/dist6/index.d.ts +0 -1
  319. package/dist6/index.js +0 -12
  320. package/dist6/src/http-handler.d.ts +0 -19
  321. package/dist6/src/http-handler.js +0 -53
  322. package/dist6/src/http-handler.js.map +0 -1
  323. package/dist6/src/index.d.ts +0 -14
  324. package/dist6/src/index.js +0 -33
  325. package/dist6/src/index.js.map +0 -1
  326. package/dist6/src/internal-types.d.ts +0 -67
  327. package/dist6/src/internal-types.js +0 -7
  328. package/dist6/src/internal-types.js.map +0 -1
  329. package/dist6/src/keys.d.ts +0 -22
  330. package/dist6/src/keys.js +0 -35
  331. package/dist6/src/keys.js.map +0 -1
  332. package/dist6/src/parser.d.ts +0 -11
  333. package/dist6/src/parser.js +0 -108
  334. package/dist6/src/parser.js.map +0 -1
  335. package/dist6/src/providers/bind-element.d.ts +0 -7
  336. package/dist6/src/providers/bind-element.js +0 -34
  337. package/dist6/src/providers/bind-element.js.map +0 -1
  338. package/dist6/src/providers/find-route.js +0 -42
  339. package/dist6/src/providers/find-route.js.map +0 -1
  340. package/dist6/src/providers/get-from-context.d.ts +0 -7
  341. package/dist6/src/providers/get-from-context.js +0 -34
  342. package/dist6/src/providers/get-from-context.js.map +0 -1
  343. package/dist6/src/providers/index.d.ts +0 -8
  344. package/dist6/src/providers/index.js +0 -18
  345. package/dist6/src/providers/index.js.map +0 -1
  346. package/dist6/src/providers/invoke-method.d.ts +0 -7
  347. package/dist6/src/providers/invoke-method.js +0 -44
  348. package/dist6/src/providers/invoke-method.js.map +0 -1
  349. package/dist6/src/providers/log-error-provider.d.ts +0 -6
  350. package/dist6/src/providers/log-error-provider.js +0 -17
  351. package/dist6/src/providers/log-error-provider.js.map +0 -1
  352. package/dist6/src/providers/parse-params.d.ts +0 -13
  353. package/dist6/src/providers/parse-params.js +0 -22
  354. package/dist6/src/providers/parse-params.js.map +0 -1
  355. package/dist6/src/providers/reject.d.ts +0 -6
  356. package/dist6/src/providers/reject.js +0 -40
  357. package/dist6/src/providers/reject.js.map +0 -1
  358. package/dist6/src/providers/send.d.ts +0 -15
  359. package/dist6/src/providers/send.js +0 -24
  360. package/dist6/src/providers/send.js.map +0 -1
  361. package/dist6/src/rest-component.d.ts +0 -12
  362. package/dist6/src/rest-component.js +0 -50
  363. package/dist6/src/rest-component.js.map +0 -1
  364. package/dist6/src/rest-server.d.ts +0 -211
  365. package/dist6/src/rest-server.js +0 -444
  366. package/dist6/src/rest-server.js.map +0 -1
  367. package/dist6/src/router/metadata.d.ts +0 -150
  368. package/dist6/src/router/metadata.js +0 -410
  369. package/dist6/src/router/metadata.js.map +0 -1
  370. package/dist6/src/router/routing-table.d.ts +0 -68
  371. package/dist6/src/router/routing-table.js +0 -218
  372. package/dist6/src/router/routing-table.js.map +0 -1
  373. package/dist6/src/sequence.js +0 -114
  374. package/dist6/src/sequence.js.map +0 -1
  375. package/dist6/src/writer.d.ts +0 -17
  376. package/dist6/src/writer.js +0 -87
  377. package/dist6/src/writer.js.map +0 -1
  378. package/index.d.ts +0 -6
  379. package/index.js +0 -9
  380. package/src/internal-types.ts +0 -96
  381. package/src/providers/bind-element.ts +0 -15
  382. package/src/providers/get-from-context.ts +0 -16
  383. package/src/providers/log-error-provider.ts +0 -23
  384. package/src/providers/parse-params.ts +0 -20
  385. package/src/providers/reject.ts +0 -27
  386. package/src/rest-component.ts +0 -54
  387. package/src/rest-server.ts +0 -584
  388. package/src/router/metadata.ts +0 -517
package/src/parser.ts CHANGED
@@ -1,135 +1,120 @@
1
- // Copyright IBM Corp. 2017. All Rights Reserved.
1
+ // Copyright IBM Corp. 2017,2020. All Rights Reserved.
2
2
  // Node module: @loopback/rest
3
3
  // This file is licensed under the MIT License.
4
4
  // License text available at https://opensource.org/licenses/MIT
5
5
 
6
- import {ServerRequest} from 'http';
7
- import {HttpErrors} from './';
8
- import {OperationObject, ParameterObject} from '@loopback/openapi-spec';
9
- import {promisify} from '@loopback/core';
6
+ import {
7
+ isReferenceObject,
8
+ OperationObject,
9
+ ParameterObject,
10
+ REQUEST_BODY_INDEX,
11
+ SchemasObject,
12
+ } from '@loopback/openapi-v3';
13
+ import debugFactory from 'debug';
14
+ import {RequestBody, RequestBodyParser} from './body-parsers';
15
+ import {coerceParameter} from './coercion/coerce-parameter';
16
+ import {RestHttpErrors} from './rest-http-error';
17
+ import {ResolvedRoute} from './router';
10
18
  import {
11
19
  OperationArgs,
12
- ParsedRequest,
13
20
  PathParameterValues,
14
- } from './internal-types';
15
- import {ResolvedRoute} from './router/routing-table';
16
- type HttpError = HttpErrors.HttpError;
17
-
18
- type jsonBodyFn = (
19
- req: ServerRequest,
20
- cb: (err?: Error, body?: {}) => void,
21
- ) => void;
22
- const jsonBody: jsonBodyFn = require('body/json');
23
-
24
- // tslint:disable:no-any
25
- type MaybeBody = any | undefined;
26
- // tslint:enable:no-any
27
-
28
- const parseJsonBody: (req: ServerRequest) => Promise<MaybeBody> = promisify(
29
- jsonBody,
30
- );
31
-
32
- /**
33
- * Get the content-type header value from the request
34
- * @param req Http request
35
- */
36
- function getContentType(req: ServerRequest): string | undefined {
37
- const val = req.headers['content-type'];
38
- if (typeof val === 'string') {
39
- return val;
40
- } else if (Array.isArray(val)) {
41
- // Assume only one value is present
42
- return val[0];
43
- }
44
- return undefined;
45
- }
21
+ Request,
22
+ RequestBodyValidationOptions,
23
+ } from './types';
24
+ import {validateRequestBody} from './validation/request-body.validator';
25
+ const debug = debugFactory('loopback:rest:parser');
46
26
 
47
27
  /**
48
28
  * Parses the request to derive arguments to be passed in for the Application
49
29
  * controller method
50
30
  *
51
- * @param request Incoming HTTP request
52
- * @param operationSpec Swagger spec defined in the controller
53
- * @param pathParams Path parameters in incoming HTTP request
31
+ * @param request - Incoming HTTP request
32
+ * @param route - Resolved Route
54
33
  */
55
34
  export async function parseOperationArgs(
56
- request: ParsedRequest,
35
+ request: Request,
57
36
  route: ResolvedRoute,
37
+ requestBodyParser: RequestBodyParser = new RequestBodyParser(),
38
+ options: RequestBodyValidationOptions = {},
58
39
  ): Promise<OperationArgs> {
40
+ debug('Parsing operation arguments for route %s', route.describe());
59
41
  const operationSpec = route.spec;
60
42
  const pathParams = route.pathParams;
61
- const body = await loadRequestBodyIfNeeded(operationSpec, request);
62
- return buildOperationArguments(operationSpec, request, pathParams, body);
43
+ const body = await requestBodyParser.loadRequestBodyIfNeeded(
44
+ operationSpec,
45
+ request,
46
+ );
47
+ return buildOperationArguments(
48
+ operationSpec,
49
+ request,
50
+ pathParams,
51
+ body,
52
+ route.schemas,
53
+ options,
54
+ );
63
55
  }
64
56
 
65
- function loadRequestBodyIfNeeded(
57
+ async function buildOperationArguments(
66
58
  operationSpec: OperationObject,
67
- request: ServerRequest,
68
- ): Promise<MaybeBody> {
69
- if (!hasArgumentsFromBody(operationSpec)) return Promise.resolve();
70
-
71
- const contentType = getContentType(request);
72
- if (contentType && !/json/.test(contentType)) {
73
- const err = new HttpErrors.UnsupportedMediaType(
74
- `Content-type ${contentType} is not supported.`,
75
- );
76
- return Promise.reject(err);
77
- }
78
-
79
- return parseJsonBody(request).catch((err: HttpError) => {
80
- err.statusCode = 400;
81
- return Promise.reject(err);
82
- });
83
- }
84
-
85
- function hasArgumentsFromBody(operationSpec: OperationObject): boolean {
86
- if (!operationSpec.parameters || !operationSpec.parameters.length)
87
- return false;
88
-
89
- for (const paramSpec of operationSpec.parameters) {
90
- if ('$ref' in paramSpec) continue;
91
- const source = (paramSpec as ParameterObject).in;
92
- if (source === 'formData' || source === 'body') return true;
59
+ request: Request,
60
+ pathParams: PathParameterValues,
61
+ body: RequestBody,
62
+ globalSchemas: SchemasObject,
63
+ options: RequestBodyValidationOptions = {},
64
+ ): Promise<OperationArgs> {
65
+ let requestBodyIndex = -1;
66
+ if (operationSpec.requestBody) {
67
+ // the type of `operationSpec.requestBody` could be `RequestBodyObject`
68
+ // or `ReferenceObject`, resolving a `$ref` value is not supported yet.
69
+ if (isReferenceObject(operationSpec.requestBody)) {
70
+ throw new Error('$ref requestBody is not supported yet.');
71
+ }
72
+ const i = operationSpec.requestBody[REQUEST_BODY_INDEX];
73
+ requestBodyIndex = i ? i : 0;
93
74
  }
94
- return false;
95
- }
96
75
 
97
- function buildOperationArguments(
98
- operationSpec: OperationObject,
99
- request: ParsedRequest,
100
- pathParams: PathParameterValues,
101
- body?: MaybeBody,
102
- ): OperationArgs {
103
- const args: OperationArgs = [];
76
+ const paramArgs: OperationArgs = [];
104
77
 
105
- for (const paramSpec of operationSpec.parameters || []) {
106
- if ('$ref' in paramSpec) {
78
+ for (const paramSpec of operationSpec.parameters ?? []) {
79
+ if (isReferenceObject(paramSpec)) {
107
80
  // TODO(bajtos) implement $ref parameters
108
81
  // See https://github.com/strongloop/loopback-next/issues/435
109
82
  throw new Error('$ref parameters are not supported yet.');
110
83
  }
111
84
  const spec = paramSpec as ParameterObject;
112
- switch (spec.in) {
113
- case 'query':
114
- args.push(request.query[spec.name]);
115
- break;
116
- case 'path':
117
- args.push(pathParams[spec.name]);
118
- break;
119
- case 'header':
120
- args.push(request.headers[spec.name.toLowerCase()]);
121
- break;
122
- case 'formData':
123
- args.push(body ? body[spec.name] : undefined);
124
- break;
125
- case 'body':
126
- args.push(body);
127
- break;
128
- default:
129
- throw new HttpErrors.NotImplemented(
130
- 'Parameters with "in: ' + spec.in + '" are not supported yet.',
131
- );
132
- }
85
+ const rawValue = getParamFromRequest(spec, request, pathParams);
86
+ const coercedValue = coerceParameter(rawValue, spec);
87
+ paramArgs.push(coercedValue);
88
+ }
89
+
90
+ debug('Validating request body - value %j', body);
91
+ await validateRequestBody(
92
+ body,
93
+ operationSpec.requestBody,
94
+ globalSchemas,
95
+ options,
96
+ );
97
+
98
+ if (requestBodyIndex > -1) paramArgs.splice(requestBodyIndex, 0, body.value);
99
+ return paramArgs;
100
+ }
101
+
102
+ function getParamFromRequest(
103
+ spec: ParameterObject,
104
+ request: Request,
105
+ pathParams: PathParameterValues,
106
+ ) {
107
+ switch (spec.in) {
108
+ case 'query':
109
+ return request.query[spec.name];
110
+ case 'path':
111
+ return pathParams[spec.name];
112
+ case 'header':
113
+ // @jannyhou TBD: check edge cases
114
+ return request.headers[spec.name.toLowerCase()];
115
+ // TODO(jannyhou) to support `cookie`,
116
+ // see issue https://github.com/strongloop/loopback-next/issues/997
117
+ default:
118
+ throw RestHttpErrors.invalidParamLocation(spec.in);
133
119
  }
134
- return args;
135
120
  }
@@ -1,12 +1,13 @@
1
- // Copyright IBM Corp. 2017. All Rights Reserved.
1
+ // Copyright IBM Corp. 2018,2020. All Rights Reserved.
2
2
  // Node module: @loopback/rest
3
3
  // This file is licensed under the MIT License.
4
4
  // License text available at https://opensource.org/licenses/MIT
5
5
 
6
6
  import {Context, inject, Provider} from '@loopback/context';
7
- import {FindRoute} from '../internal-types';
7
+ import {FindRoute, Request} from '../types';
8
8
  import {HttpHandler} from '../http-handler';
9
9
  import {RestBindings} from '../keys';
10
+ import {ResolvedRoute} from '../router';
10
11
 
11
12
  export class FindRouteProvider implements Provider<FindRoute> {
12
13
  constructor(
@@ -15,10 +16,12 @@ export class FindRouteProvider implements Provider<FindRoute> {
15
16
  ) {}
16
17
 
17
18
  value(): FindRoute {
18
- return request => {
19
- const found = this.handler.findRoute(request);
20
- found.updateBindings(this.context);
21
- return found;
22
- };
19
+ return request => this.action(request);
20
+ }
21
+
22
+ action(request: Request): ResolvedRoute {
23
+ const found = this.handler.findRoute(request);
24
+ found.updateBindings(this.context);
25
+ return found;
23
26
  }
24
27
  }
@@ -1,13 +1,11 @@
1
- // Copyright IBM Corp. 2013,2017. All Rights Reserved.
1
+ // Copyright IBM Corp. 2017,2020. All Rights Reserved.
2
2
  // Node module: @loopback/rest
3
3
  // This file is licensed under the MIT License.
4
4
  // License text available at https://opensource.org/licenses/MIT
5
5
 
6
- export * from './bind-element';
7
- export * from './find-route';
8
- export * from './get-from-context';
9
- export * from './invoke-method';
10
- export * from './log-error-provider';
11
- export * from './reject';
12
- export * from './parse-params';
13
- export * from './send';
6
+ export * from './find-route.provider';
7
+ export * from './invoke-method.provider';
8
+ export * from './log-error.provider';
9
+ export * from './parse-params.provider';
10
+ export * from './reject.provider';
11
+ export * from './send.provider';
@@ -1,18 +1,21 @@
1
- // Copyright IBM Corp. 2017. All Rights Reserved.
1
+ // Copyright IBM Corp. 2018,2020. All Rights Reserved.
2
2
  // Node module: @loopback/rest
3
3
  // This file is licensed under the MIT License.
4
4
  // License text available at https://opensource.org/licenses/MIT
5
5
 
6
6
  import {Context, inject, Provider} from '@loopback/context';
7
- import {InvokeMethod} from '../internal-types';
7
+ import {InvokeMethod, OperationArgs, OperationRetval} from '../types';
8
8
  import {RestBindings} from '../keys';
9
+ import {RouteEntry} from '../router';
9
10
 
10
11
  export class InvokeMethodProvider implements Provider<InvokeMethod> {
11
12
  constructor(@inject(RestBindings.Http.CONTEXT) protected context: Context) {}
12
13
 
13
14
  value(): InvokeMethod {
14
- return async (route, args) => {
15
- return await route.invokeHandler(this.context, args);
16
- };
15
+ return (route, args) => this.action(route, args);
16
+ }
17
+
18
+ action(route: RouteEntry, args: OperationArgs): Promise<OperationRetval> {
19
+ return route.invokeHandler(this.context, args);
17
20
  }
18
21
  }
@@ -0,0 +1,27 @@
1
+ // Copyright IBM Corp. 2018,2020. All Rights Reserved.
2
+ // Node module: @loopback/rest
3
+ // This file is licensed under the MIT License.
4
+ // License text available at https://opensource.org/licenses/MIT
5
+
6
+ import {Provider} from '@loopback/context';
7
+ import {LogError, Request} from '../types';
8
+
9
+ export class LogErrorProvider implements Provider<LogError> {
10
+ value(): LogError {
11
+ return (err, statusCode, req) => this.action(err, statusCode, req);
12
+ }
13
+
14
+ action(err: Error, statusCode: number, req: Request) {
15
+ if (statusCode < 500) {
16
+ return;
17
+ }
18
+
19
+ console.error(
20
+ 'Unhandled error in %s %s: %s %s',
21
+ req.method,
22
+ req.url,
23
+ statusCode,
24
+ err.stack ?? err,
25
+ );
26
+ }
27
+ }
@@ -0,0 +1,42 @@
1
+ // Copyright IBM Corp. 2018,2020. All Rights Reserved.
2
+ // Node module: @loopback/rest
3
+ // This file is licensed under the MIT License.
4
+ // License text available at https://opensource.org/licenses/MIT
5
+
6
+ import {inject, Provider} from '@loopback/context';
7
+ import {RequestBodyParser} from '../body-parsers';
8
+ import {RestBindings} from '../keys';
9
+ import {parseOperationArgs} from '../parser';
10
+ import {ResolvedRoute} from '../router';
11
+ import {
12
+ AjvFactory,
13
+ ParseParams,
14
+ Request,
15
+ RequestBodyValidationOptions,
16
+ } from '../types';
17
+ /**
18
+ * Provides the function for parsing args in requests at runtime.
19
+ *
20
+ * @returns The handler function that will parse request args.
21
+ */
22
+ export class ParseParamsProvider implements Provider<ParseParams> {
23
+ constructor(
24
+ @inject(RestBindings.REQUEST_BODY_PARSER)
25
+ private requestBodyParser: RequestBodyParser,
26
+ @inject(
27
+ RestBindings.REQUEST_BODY_PARSER_OPTIONS.deepProperty('validation'),
28
+ {optional: true},
29
+ )
30
+ private validationOptions: RequestBodyValidationOptions = {},
31
+ @inject(RestBindings.AJV_FACTORY, {optional: true})
32
+ private ajvFactory?: AjvFactory,
33
+ ) {}
34
+
35
+ value(): ParseParams {
36
+ return (request: Request, route: ResolvedRoute) =>
37
+ parseOperationArgs(request, route, this.requestBodyParser, {
38
+ ajvFactory: this.ajvFactory,
39
+ ...this.validationOptions,
40
+ });
41
+ }
42
+ }
@@ -0,0 +1,44 @@
1
+ // Copyright IBM Corp. 2018,2020. All Rights Reserved.
2
+ // Node module: @loopback/rest
3
+ // This file is licensed under the MIT License.
4
+ // License text available at https://opensource.org/licenses/MIT
5
+
6
+ import {LogError, Reject, HandlerContext} from '../types';
7
+ import {inject, Provider} from '@loopback/context';
8
+ import {HttpError} from 'http-errors';
9
+ import {RestBindings} from '../keys';
10
+ import {writeErrorToResponse, ErrorWriterOptions} from 'strong-error-handler';
11
+
12
+ // TODO(bajtos) Make this mapping configurable at RestServer level,
13
+ // allow apps and extensions to contribute additional mappings.
14
+ const codeToStatusCodeMap: {[key: string]: number} = {
15
+ ENTITY_NOT_FOUND: 404,
16
+ };
17
+
18
+ export class RejectProvider implements Provider<Reject> {
19
+ constructor(
20
+ @inject(RestBindings.SequenceActions.LOG_ERROR)
21
+ protected logError: LogError,
22
+ @inject(RestBindings.ERROR_WRITER_OPTIONS, {optional: true})
23
+ protected errorWriterOptions?: ErrorWriterOptions,
24
+ ) {}
25
+
26
+ value(): Reject {
27
+ return (context, error) => this.action(context, error);
28
+ }
29
+
30
+ action({request, response}: HandlerContext, error: Error) {
31
+ const err = <HttpError>error;
32
+
33
+ if (!err.status && !err.statusCode && err.code) {
34
+ const customStatus = codeToStatusCodeMap[err.code];
35
+ if (customStatus) {
36
+ err.statusCode = customStatus;
37
+ }
38
+ }
39
+
40
+ const statusCode = err.statusCode || err.status || 500;
41
+ writeErrorToResponse(err, request, response, this.errorWriterOptions);
42
+ this.logError(error, statusCode, request);
43
+ }
44
+ }
@@ -1,4 +1,4 @@
1
- // Copyright IBM Corp. 2017. All Rights Reserved.
1
+ // Copyright IBM Corp. 2018,2020. All Rights Reserved.
2
2
  // Node module: @loopback/rest
3
3
  // This file is licensed under the MIT License.
4
4
  // License text available at https://opensource.org/licenses/MIT
@@ -9,10 +9,7 @@ import {writeResultToResponse} from '../writer';
9
9
  * Provides the function that populates the response object with
10
10
  * the results of the operation.
11
11
  *
12
- * @export
13
- * @class SendProvider
14
- * @implements {Provider<BoundValue>}
15
- * @returns {BoundValue} The handler function that will populate the
12
+ * @returns The handler function that will populate the
16
13
  * response with operation results.
17
14
  */
18
15
  export class SendProvider implements Provider<BoundValue> {
@@ -0,0 +1,123 @@
1
+ // Copyright IBM Corp. 2018,2020. All Rights Reserved.
2
+ // Node module: @loopback/rest
3
+ // This file is licensed under the MIT License.
4
+ // License text available at https://opensource.org/licenses/MIT
5
+
6
+ import {Context} from '@loopback/context';
7
+ import {
8
+ HandlerContext,
9
+ MiddlewareContext,
10
+ Request,
11
+ Response,
12
+ } from '@loopback/express';
13
+ import {RestBindings} from './keys';
14
+ import {RestServerResolvedConfig} from './rest.server';
15
+
16
+ /**
17
+ * A per-request Context combining an IoC container with handler context
18
+ * (request, response, etc.).
19
+ */
20
+ export class RequestContext extends MiddlewareContext
21
+ implements HandlerContext {
22
+ /**
23
+ * Get the protocol used by the client to make the request.
24
+ * Please note this protocol may be different from what we are observing
25
+ * at HTTP/TCP level, because reverse proxies like nginx or sidecars like
26
+ * Envoy are switching between protocols.
27
+ */
28
+ get requestedProtocol(): string {
29
+ return (
30
+ ((this.request.get('x-forwarded-proto') ?? '').split(',')[0] ||
31
+ this.request.protocol ||
32
+ this.serverConfig.protocol) ??
33
+ 'http'
34
+ );
35
+ }
36
+
37
+ /**
38
+ * Get the effective base path of the incoming request. This base path
39
+ * combines `baseUrl` provided by Express when LB4 handler is mounted on
40
+ * a non-root path, with the `basePath` value configured at LB4 side.
41
+ */
42
+ get basePath(): string {
43
+ const request = this.request;
44
+ let basePath = this.serverConfig.basePath ?? '';
45
+ if (request.baseUrl && request.baseUrl !== '/') {
46
+ if (!basePath || request.baseUrl.endsWith(basePath)) {
47
+ // Express has already applied basePath to baseUrl
48
+ basePath = request.baseUrl;
49
+ } else {
50
+ basePath = request.baseUrl + basePath;
51
+ }
52
+ }
53
+ return basePath;
54
+ }
55
+
56
+ /**
57
+ * Get the base URL used by the client to make the request.
58
+ * This URL contains the protocol, hostname, port and base path.
59
+ * The path of the invoked route and query string is not included.
60
+ *
61
+ * Please note these values may be different from what we are observing
62
+ * at HTTP/TCP level, because reverse proxies like nginx are rewriting them.
63
+ */
64
+ get requestedBaseUrl(): string {
65
+ const request = this.request;
66
+ const config = this.serverConfig;
67
+
68
+ const protocol = this.requestedProtocol;
69
+ // The host can be in one of the forms
70
+ // [::1]:3000
71
+ // [::1]
72
+ // 127.0.0.1:3000
73
+ // 127.0.0.1
74
+ let {host, port} = parseHostAndPort(
75
+ request.get('x-forwarded-host') ?? request.headers.host,
76
+ );
77
+
78
+ const forwardedPort = (request.get('x-forwarded-port') ?? '').split(',')[0];
79
+ port = forwardedPort || port;
80
+
81
+ if (!host) {
82
+ // No host detected from http headers
83
+ // Use the configured values or the local network address
84
+ host = config.host ?? request.socket.localAddress;
85
+ port = (config.port || request.socket.localPort).toString();
86
+ }
87
+
88
+ // clear default ports
89
+ port = protocol === 'https' && port === '443' ? '' : port;
90
+ port = protocol === 'http' && port === '80' ? '' : port;
91
+
92
+ // add port number of present
93
+ host += port !== '' ? ':' + port : '';
94
+
95
+ return protocol + '://' + host + this.basePath;
96
+ }
97
+
98
+ constructor(
99
+ public readonly request: Request,
100
+ public readonly response: Response,
101
+ parent: Context,
102
+ public readonly serverConfig: RestServerResolvedConfig,
103
+ name?: string,
104
+ ) {
105
+ super(request, response, parent, name);
106
+ }
107
+
108
+ protected setupBindings() {
109
+ super.setupBindings();
110
+ this.bind(RestBindings.Http.REQUEST).to(this.request).lock();
111
+ this.bind(RestBindings.Http.RESPONSE).to(this.response).lock();
112
+ this.bind(RestBindings.Http.CONTEXT).to(this).lock();
113
+ }
114
+ }
115
+
116
+ function parseHostAndPort(host: string | undefined) {
117
+ host = host ?? '';
118
+ host = host.split(',')[0];
119
+ const portPattern = /:([0-9]+)$/;
120
+ const port = (host.match(portPattern) ?? [])[1] || '';
121
+ host = host.replace(portPattern, '');
122
+ return {host, port};
123
+ }
@@ -0,0 +1,87 @@
1
+ // Copyright IBM Corp. 2018,2020. All Rights Reserved.
2
+ // Node module: @loopback/rest
3
+ // This file is licensed under the MIT License.
4
+ // License text available at https://opensource.org/licenses/MIT
5
+
6
+ import HttpErrors from 'http-errors';
7
+
8
+ export namespace RestHttpErrors {
9
+ export function invalidData<T, Props extends object = {}>(
10
+ data: T,
11
+ name: string,
12
+ extraProperties?: Props,
13
+ ): HttpErrors.HttpError & Props {
14
+ const msg = `Invalid data ${JSON.stringify(data)} for parameter ${name}!`;
15
+ return Object.assign(
16
+ new HttpErrors.BadRequest(msg),
17
+ {
18
+ code: 'INVALID_PARAMETER_VALUE',
19
+ parameterName: name,
20
+ },
21
+ extraProperties,
22
+ );
23
+ }
24
+
25
+ export function unsupportedMediaType(
26
+ contentType: string,
27
+ allowedTypes: string[] = [],
28
+ ) {
29
+ const msg = allowedTypes?.length
30
+ ? `Content-type ${contentType} does not match [${allowedTypes}].`
31
+ : `Content-type ${contentType} is not supported.`;
32
+ return Object.assign(new HttpErrors.UnsupportedMediaType(msg), {
33
+ code: 'UNSUPPORTED_MEDIA_TYPE',
34
+ contentType: contentType,
35
+ allowedMediaTypes: allowedTypes,
36
+ });
37
+ }
38
+
39
+ export function missingRequired(name: string): HttpErrors.HttpError {
40
+ const msg = `Required parameter ${name} is missing!`;
41
+ return Object.assign(new HttpErrors.BadRequest(msg), {
42
+ code: 'MISSING_REQUIRED_PARAMETER',
43
+ parameterName: name,
44
+ });
45
+ }
46
+
47
+ export function invalidParamLocation(location: string): HttpErrors.HttpError {
48
+ const msg = `Parameters with "in: ${location}" are not supported yet.`;
49
+ return new HttpErrors.NotImplemented(msg);
50
+ }
51
+
52
+ export const INVALID_REQUEST_BODY_MESSAGE =
53
+ 'The request body is invalid. See error object `details` property for more info.';
54
+ export function invalidRequestBody(): HttpErrors.HttpError {
55
+ return Object.assign(
56
+ new HttpErrors.UnprocessableEntity(INVALID_REQUEST_BODY_MESSAGE),
57
+ {
58
+ code: 'VALIDATION_FAILED',
59
+ },
60
+ );
61
+ }
62
+
63
+ /**
64
+ * An invalid request body error contains a `details` property as the machine-readable error.
65
+ * Each entry in `error.details` contains 4 attributes: `path`, `code`, `info` and `message`.
66
+ * `ValidationErrorDetails` defines the type of each entry, which is an object.
67
+ * The type of `error.details` is `ValidationErrorDetails[]`.
68
+ */
69
+ export interface ValidationErrorDetails {
70
+ /**
71
+ * A path to the invalid field.
72
+ */
73
+ path: string;
74
+ /**
75
+ * A single word code represents the error's type.
76
+ */
77
+ code: string;
78
+ /**
79
+ * A human readable description of the error.
80
+ */
81
+ message: string;
82
+ /**
83
+ * Some additional details that the 3 attributes above don't cover.
84
+ */
85
+ info: object;
86
+ }
87
+ }