@toa.io/extensions.exposition 1.0.0-alpha.5 → 1.0.0-alpha.51

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 (443) hide show
  1. package/components/context.toa.yaml +2 -2
  2. package/components/identity.bans/manifest.toa.yaml +15 -7
  3. package/components/identity.bans/operations/transit.d.ts +14 -0
  4. package/components/identity.bans/operations/transit.js +11 -0
  5. package/components/identity.bans/operations/transit.js.map +1 -0
  6. package/components/identity.bans/operations/tsconfig.tsbuildinfo +1 -0
  7. package/components/identity.bans/source/transit.ts +21 -0
  8. package/components/identity.bans/tsconfig.json +9 -0
  9. package/components/identity.basic/manifest.toa.yaml +22 -9
  10. package/components/identity.basic/operations/authenticate.d.ts +5 -1
  11. package/components/identity.basic/operations/authenticate.js +5 -2
  12. package/components/identity.basic/operations/authenticate.js.map +1 -1
  13. package/components/identity.basic/operations/incept.d.ts +12 -0
  14. package/components/identity.basic/operations/incept.js +26 -0
  15. package/components/identity.basic/operations/incept.js.map +1 -0
  16. package/components/identity.basic/operations/transit.d.ts +4 -4
  17. package/components/identity.basic/operations/transit.js +5 -3
  18. package/components/identity.basic/operations/transit.js.map +1 -1
  19. package/components/identity.basic/operations/tsconfig.tsbuildinfo +1 -1
  20. package/components/identity.basic/operations/types.d.ts +8 -4
  21. package/components/identity.basic/source/authenticate.ts +16 -5
  22. package/components/identity.basic/source/incept.ts +38 -0
  23. package/components/identity.basic/source/transit.ts +8 -6
  24. package/components/identity.basic/source/types.ts +8 -4
  25. package/components/identity.federation/manifest.toa.yaml +32 -22
  26. package/components/identity.federation/operations/authenticate.d.ts +2 -2
  27. package/components/identity.federation/operations/authenticate.js +4 -11
  28. package/components/identity.federation/operations/authenticate.js.map +1 -1
  29. package/components/identity.federation/operations/incept.d.ts +11 -0
  30. package/components/identity.federation/operations/{create.js → incept.js} +6 -7
  31. package/components/identity.federation/operations/incept.js.map +1 -0
  32. package/components/identity.federation/operations/lib/jwt.d.ts +4 -5
  33. package/components/identity.federation/operations/lib/jwt.js +12 -7
  34. package/components/identity.federation/operations/lib/jwt.js.map +1 -1
  35. package/components/identity.federation/operations/tsconfig.tsbuildinfo +1 -1
  36. package/components/identity.federation/operations/types/configuration.d.ts +14 -0
  37. package/components/identity.federation/operations/types/configuration.js +3 -0
  38. package/components/identity.federation/operations/types/configuration.js.map +1 -0
  39. package/components/identity.federation/operations/{types.d.ts → types/context.d.ts} +15 -7
  40. package/components/identity.federation/operations/types/context.js +3 -0
  41. package/components/identity.federation/operations/types/context.js.map +1 -0
  42. package/components/identity.federation/operations/types/entity.d.ts +6 -0
  43. package/components/identity.federation/operations/types/entity.js +3 -0
  44. package/components/identity.federation/operations/types/entity.js.map +1 -0
  45. package/components/identity.federation/operations/types/index.d.ts +3 -0
  46. package/components/identity.federation/operations/types/index.js +20 -0
  47. package/components/identity.federation/operations/types/index.js.map +1 -0
  48. package/components/identity.federation/source/authenticate.ts +6 -19
  49. package/components/identity.federation/source/{create.ts → incept.ts} +10 -9
  50. package/components/identity.federation/source/lib/jwt.test.ts +50 -4
  51. package/components/identity.federation/source/lib/jwt.ts +20 -12
  52. package/components/identity.federation/source/types/configuration.ts +15 -0
  53. package/components/identity.federation/source/{types.ts → types/context.ts} +17 -6
  54. package/components/identity.federation/source/types/entity.ts +6 -0
  55. package/components/identity.federation/source/types/index.ts +3 -0
  56. package/components/identity.federation/tsconfig.json +2 -2
  57. package/components/identity.roles/manifest.toa.yaml +18 -6
  58. package/components/identity.roles/operations/grant.d.ts +10 -0
  59. package/components/identity.roles/operations/grant.js +21 -0
  60. package/components/identity.roles/operations/grant.js.map +1 -0
  61. package/components/identity.roles/operations/lib/Entity.d.ts +5 -0
  62. package/components/identity.roles/operations/lib/Entity.js +3 -0
  63. package/components/identity.roles/operations/lib/Entity.js.map +1 -0
  64. package/components/identity.roles/operations/list.d.ts +1 -4
  65. package/components/identity.roles/operations/list.js.map +1 -1
  66. package/components/identity.roles/operations/principal.d.ts +4 -6
  67. package/components/identity.roles/operations/principal.js +6 -1
  68. package/components/identity.roles/operations/principal.js.map +1 -1
  69. package/components/identity.roles/operations/tsconfig.tsbuildinfo +1 -1
  70. package/components/identity.roles/source/grant.ts +32 -0
  71. package/components/identity.roles/source/lib/Entity.ts +5 -0
  72. package/components/identity.roles/source/list.ts +2 -4
  73. package/components/identity.roles/source/principal.ts +10 -8
  74. package/components/identity.tokens/manifest.toa.yaml +19 -5
  75. package/components/identity.tokens/operations/authenticate.d.ts +2 -2
  76. package/components/identity.tokens/operations/authenticate.js +12 -5
  77. package/components/identity.tokens/operations/authenticate.js.map +1 -1
  78. package/components/identity.tokens/operations/decrypt.js +1 -0
  79. package/components/identity.tokens/operations/decrypt.js.map +1 -1
  80. package/components/identity.tokens/operations/encrypt.js +5 -1
  81. package/components/identity.tokens/operations/encrypt.js.map +1 -1
  82. package/components/identity.tokens/operations/tsconfig.tsbuildinfo +1 -1
  83. package/components/identity.tokens/operations/types.d.ts +8 -2
  84. package/components/identity.tokens/receivers/identity.bans.created.js +3 -0
  85. package/components/identity.tokens/source/authenticate.test.ts +11 -4
  86. package/components/identity.tokens/source/authenticate.ts +14 -6
  87. package/components/identity.tokens/source/decrypt.test.ts +5 -3
  88. package/components/identity.tokens/source/decrypt.ts +9 -8
  89. package/components/identity.tokens/source/encrypt.test.ts +26 -2
  90. package/components/identity.tokens/source/encrypt.ts +5 -1
  91. package/components/identity.tokens/source/types.ts +9 -2
  92. package/components/octets.storage/manifest.toa.yaml +0 -7
  93. package/documentation/access.md +75 -38
  94. package/documentation/authorities.md +49 -0
  95. package/documentation/cache.md +8 -1
  96. package/documentation/components.md +47 -22
  97. package/documentation/flow.md +31 -0
  98. package/documentation/identity.md +17 -22
  99. package/documentation/introspection.md +68 -0
  100. package/documentation/io.md +56 -0
  101. package/documentation/octets.md +34 -23
  102. package/documentation/protocol.md +3 -0
  103. package/documentation/query.md +17 -11
  104. package/documentation/require.md +15 -0
  105. package/documentation/tree.md +35 -4
  106. package/documentation/vary.md +14 -14
  107. package/features/access.feature +89 -47
  108. package/features/annotation.feature +2 -0
  109. package/features/auth.claim.feature +170 -0
  110. package/features/authorities.basic.feature +141 -0
  111. package/features/authorities.feature +32 -0
  112. package/features/authorities.federation.feature +100 -0
  113. package/features/authorities.tokens.feature +117 -0
  114. package/features/body.feature +4 -0
  115. package/features/cache.feature +112 -5
  116. package/features/cors.feature +5 -0
  117. package/features/debug.feature +34 -0
  118. package/features/directives.feature +5 -0
  119. package/features/dynamic.feature +18 -7
  120. package/features/errors.feature +18 -4
  121. package/features/etag.feature +7 -0
  122. package/features/flow.feature +45 -0
  123. package/features/identity.bans.feature +137 -0
  124. package/features/identity.basic.feature +142 -19
  125. package/features/identity.feature +7 -2
  126. package/features/identity.federation.feature +68 -15
  127. package/features/identity.roles.feature +251 -7
  128. package/features/identity.tokens.feature +57 -4
  129. package/features/introspection.feature +76 -0
  130. package/features/io.feature +205 -0
  131. package/features/octets.entries.feature +10 -1
  132. package/features/octets.feature +60 -64
  133. package/features/octets.meta.feature +7 -3
  134. package/features/octets.workflows.feature +240 -19
  135. package/features/probes.feature +14 -0
  136. package/features/{queries.feature → query.feature} +50 -3
  137. package/features/require.feature +67 -0
  138. package/features/response.feature +12 -3
  139. package/features/routes.feature +110 -12
  140. package/features/steps/Database.ts +17 -10
  141. package/features/steps/Gateway.ts +23 -6
  142. package/features/steps/IdP.ts +30 -25
  143. package/features/steps/components/echo/manifest.toa.yaml +12 -1
  144. package/features/steps/components/echo/operations/identity.js +7 -0
  145. package/features/steps/components/echo/operations/parameters.js +7 -0
  146. package/features/steps/components/echo.beacon/manifest.toa.yaml +2 -0
  147. package/features/steps/components/echo.beacon/operations/hello.js +5 -0
  148. package/features/steps/components/octets.tester/manifest.toa.yaml +22 -1
  149. package/features/steps/components/octets.tester/operations/authority.js +7 -0
  150. package/features/steps/components/octets.tester/operations/baz.js +1 -2
  151. package/features/steps/components/octets.tester/operations/diversify.js +3 -1
  152. package/features/steps/components/octets.tester/operations/foo.js +2 -2
  153. package/features/steps/components/octets.tester/operations/redirect.js +12 -0
  154. package/features/steps/components/octets.tester/operations/yex.js +16 -0
  155. package/features/steps/components/octets.tester/operations/yield.js +13 -0
  156. package/features/steps/components/pots/manifest.toa.yaml +16 -3
  157. package/features/steps/components/users.properties/manifest.toa.yaml +2 -1
  158. package/features/streams.feature +1 -0
  159. package/features/timing.feature +27 -1
  160. package/features/vary.feature +104 -3
  161. package/package.json +12 -11
  162. package/readme.md +19 -14
  163. package/schemas/annotation.cos.yaml +1 -1
  164. package/schemas/io/input.cos.yaml +3 -0
  165. package/schemas/io/message.cos.yaml +5 -0
  166. package/schemas/io/output.cos.yaml +5 -0
  167. package/schemas/node.cos.yaml +1 -0
  168. package/source/Annotation.ts +3 -3
  169. package/source/Composition.ts +2 -2
  170. package/source/Directive.ts +4 -5
  171. package/source/Endpoint.ts +18 -12
  172. package/source/Factory.ts +10 -11
  173. package/source/Gateway.ts +55 -20
  174. package/source/HTTP/Context.ts +24 -2
  175. package/source/HTTP/Server.ts +51 -43
  176. package/source/HTTP/exceptions.ts +7 -1
  177. package/source/HTTP/messages.test.ts +39 -2
  178. package/source/HTTP/messages.ts +7 -3
  179. package/source/Mapping.ts +6 -1
  180. package/source/Query.test.ts +1 -1
  181. package/source/Query.ts +35 -24
  182. package/source/RTD/Endpoint.ts +3 -0
  183. package/source/RTD/Method.ts +4 -0
  184. package/source/RTD/Node.ts +10 -2
  185. package/source/RTD/Route.ts +5 -4
  186. package/source/RTD/factory.ts +5 -2
  187. package/source/RTD/syntax/parse.ts +37 -24
  188. package/source/RTD/syntax/types.ts +2 -1
  189. package/source/Remotes.ts +4 -4
  190. package/source/Tenant.ts +0 -8
  191. package/source/deployment.ts +32 -22
  192. package/source/directives/auth/Authorization.ts +40 -17
  193. package/source/directives/auth/Delegate.ts +42 -0
  194. package/source/directives/auth/Federation.ts +84 -0
  195. package/source/directives/auth/Incept.ts +4 -3
  196. package/source/directives/auth/Role.test.ts +53 -6
  197. package/source/directives/auth/Role.ts +22 -14
  198. package/source/directives/auth/types.ts +1 -1
  199. package/source/directives/cache/Cache.ts +13 -6
  200. package/source/directives/cache/Control.ts +42 -16
  201. package/source/directives/dev/Development.ts +1 -1
  202. package/source/directives/flow/Fetch.ts +88 -0
  203. package/source/directives/flow/Flow.ts +34 -0
  204. package/source/directives/flow/index.ts +3 -0
  205. package/source/directives/flow/types.ts +6 -0
  206. package/source/directives/index.ts +6 -3
  207. package/source/directives/io/Directive.ts +11 -0
  208. package/source/directives/io/IO.ts +43 -0
  209. package/source/directives/io/Input.ts +50 -0
  210. package/source/directives/io/Message.ts +1 -0
  211. package/source/directives/io/Output.ts +69 -0
  212. package/source/directives/io/index.ts +3 -0
  213. package/source/directives/io/schemas.ts +12 -0
  214. package/source/directives/octets/Context.ts +4 -3
  215. package/source/directives/octets/Delete.ts +4 -2
  216. package/source/directives/octets/Directive.ts +10 -0
  217. package/source/directives/octets/Fetch.ts +14 -12
  218. package/source/directives/octets/List.ts +9 -7
  219. package/source/directives/octets/Octets.ts +4 -5
  220. package/source/directives/octets/Store.ts +4 -2
  221. package/source/directives/octets/Workflow.ts +10 -3
  222. package/source/directives/octets/schemas.ts +4 -4
  223. package/source/directives/octets/types.ts +0 -7
  224. package/source/directives/octets/workflows/Execution.ts +59 -8
  225. package/source/directives/octets/workflows/Workflow.ts +2 -1
  226. package/source/directives/require/Directive.ts +5 -0
  227. package/source/directives/require/Headers.ts +20 -0
  228. package/source/directives/require/Require.ts +28 -0
  229. package/source/directives/require/index.ts +3 -0
  230. package/source/directives/vary/Directive.ts +2 -1
  231. package/source/directives/vary/Embed.ts +14 -8
  232. package/source/directives/vary/Vary.ts +6 -4
  233. package/source/directives/vary/embeddings/Authority.ts +8 -0
  234. package/source/directives/vary/embeddings/Embedding.ts +2 -1
  235. package/source/directives/vary/embeddings/Header.ts +8 -6
  236. package/source/directives/vary/embeddings/Parameter.ts +14 -0
  237. package/source/directives/vary/embeddings/index.ts +6 -4
  238. package/source/exceptions.ts +22 -11
  239. package/source/manifest.ts +8 -9
  240. package/source/root.ts +5 -0
  241. package/source/schemas.ts +1 -1
  242. package/transpiled/Annotation.d.ts +3 -3
  243. package/transpiled/Composition.js +2 -2
  244. package/transpiled/Composition.js.map +1 -1
  245. package/transpiled/Directive.js +4 -4
  246. package/transpiled/Directive.js.map +1 -1
  247. package/transpiled/Endpoint.d.ts +5 -4
  248. package/transpiled/Endpoint.js +8 -4
  249. package/transpiled/Endpoint.js.map +1 -1
  250. package/transpiled/Factory.d.ts +1 -1
  251. package/transpiled/Factory.js +9 -8
  252. package/transpiled/Factory.js.map +1 -1
  253. package/transpiled/Gateway.d.ts +2 -0
  254. package/transpiled/Gateway.js +39 -12
  255. package/transpiled/Gateway.js.map +1 -1
  256. package/transpiled/HTTP/Context.d.ts +8 -1
  257. package/transpiled/HTTP/Context.js +15 -2
  258. package/transpiled/HTTP/Context.js.map +1 -1
  259. package/transpiled/HTTP/Server.d.ts +13 -2
  260. package/transpiled/HTTP/Server.js +38 -35
  261. package/transpiled/HTTP/Server.js.map +1 -1
  262. package/transpiled/HTTP/exceptions.d.ts +4 -1
  263. package/transpiled/HTTP/exceptions.js +7 -1
  264. package/transpiled/HTTP/exceptions.js.map +1 -1
  265. package/transpiled/HTTP/messages.d.ts +1 -0
  266. package/transpiled/HTTP/messages.js +9 -3
  267. package/transpiled/HTTP/messages.js.map +1 -1
  268. package/transpiled/Mapping.js +4 -1
  269. package/transpiled/Mapping.js.map +1 -1
  270. package/transpiled/Query.d.ts +1 -0
  271. package/transpiled/Query.js +21 -20
  272. package/transpiled/Query.js.map +1 -1
  273. package/transpiled/RTD/Endpoint.d.ts +1 -0
  274. package/transpiled/RTD/Method.d.ts +1 -0
  275. package/transpiled/RTD/Method.js +3 -0
  276. package/transpiled/RTD/Method.js.map +1 -1
  277. package/transpiled/RTD/Node.d.ts +2 -0
  278. package/transpiled/RTD/Node.js +9 -2
  279. package/transpiled/RTD/Node.js.map +1 -1
  280. package/transpiled/RTD/Route.d.ts +1 -1
  281. package/transpiled/RTD/Route.js +0 -1
  282. package/transpiled/RTD/Route.js.map +1 -1
  283. package/transpiled/RTD/factory.js +5 -2
  284. package/transpiled/RTD/factory.js.map +1 -1
  285. package/transpiled/RTD/syntax/parse.js +34 -22
  286. package/transpiled/RTD/syntax/parse.js.map +1 -1
  287. package/transpiled/RTD/syntax/types.d.ts +1 -0
  288. package/transpiled/RTD/syntax/types.js +1 -1
  289. package/transpiled/RTD/syntax/types.js.map +1 -1
  290. package/transpiled/Remotes.d.ts +2 -2
  291. package/transpiled/Remotes.js.map +1 -1
  292. package/transpiled/Tenant.d.ts +0 -1
  293. package/transpiled/Tenant.js +0 -6
  294. package/transpiled/Tenant.js.map +1 -1
  295. package/transpiled/deployment.d.ts +1 -1
  296. package/transpiled/deployment.js +28 -20
  297. package/transpiled/deployment.js.map +1 -1
  298. package/transpiled/directives/auth/Authorization.js +28 -12
  299. package/transpiled/directives/auth/Authorization.js.map +1 -1
  300. package/transpiled/directives/auth/Delegate.d.ts +10 -0
  301. package/transpiled/directives/auth/Delegate.js +34 -0
  302. package/transpiled/directives/auth/Delegate.js.map +1 -0
  303. package/transpiled/directives/auth/Federation.d.ts +16 -0
  304. package/transpiled/directives/auth/Federation.js +57 -0
  305. package/transpiled/directives/auth/Federation.js.map +1 -0
  306. package/transpiled/directives/auth/Incept.js +4 -3
  307. package/transpiled/directives/auth/Incept.js.map +1 -1
  308. package/transpiled/directives/auth/Role.d.ts +4 -1
  309. package/transpiled/directives/auth/Role.js +20 -14
  310. package/transpiled/directives/auth/Role.js.map +1 -1
  311. package/transpiled/directives/cache/Cache.d.ts +3 -3
  312. package/transpiled/directives/cache/Cache.js +10 -4
  313. package/transpiled/directives/cache/Cache.js.map +1 -1
  314. package/transpiled/directives/cache/Control.d.ts +2 -1
  315. package/transpiled/directives/cache/Control.js +29 -12
  316. package/transpiled/directives/cache/Control.js.map +1 -1
  317. package/transpiled/directives/dev/Development.js +1 -1
  318. package/transpiled/directives/dev/Development.js.map +1 -1
  319. package/transpiled/directives/flow/Fetch.d.ts +13 -0
  320. package/transpiled/directives/flow/Fetch.js +59 -0
  321. package/transpiled/directives/flow/Fetch.js.map +1 -0
  322. package/transpiled/directives/flow/Flow.d.ts +10 -0
  323. package/transpiled/directives/flow/Flow.js +27 -0
  324. package/transpiled/directives/flow/Flow.js.map +1 -0
  325. package/transpiled/directives/flow/index.d.ts +2 -0
  326. package/transpiled/directives/flow/index.js +6 -0
  327. package/transpiled/directives/flow/index.js.map +1 -0
  328. package/transpiled/directives/flow/types.d.ts +5 -0
  329. package/transpiled/directives/flow/types.js.map +1 -0
  330. package/transpiled/directives/index.js +6 -3
  331. package/transpiled/directives/index.js.map +1 -1
  332. package/transpiled/directives/io/Directive.d.ts +8 -0
  333. package/transpiled/directives/io/Directive.js +3 -0
  334. package/transpiled/directives/io/Directive.js.map +1 -0
  335. package/transpiled/directives/io/IO.d.ts +9 -0
  336. package/transpiled/directives/io/IO.js +33 -0
  337. package/transpiled/directives/io/IO.js.map +1 -0
  338. package/transpiled/directives/io/Input.d.ts +11 -0
  339. package/transpiled/directives/{octets/Permute.js → io/Input.js} +33 -26
  340. package/transpiled/directives/io/Input.js.map +1 -0
  341. package/transpiled/directives/io/Message.d.ts +1 -0
  342. package/transpiled/directives/io/Message.js +3 -0
  343. package/transpiled/directives/io/Message.js.map +1 -0
  344. package/transpiled/directives/io/Output.d.ts +13 -0
  345. package/transpiled/directives/io/Output.js +76 -0
  346. package/transpiled/directives/io/Output.js.map +1 -0
  347. package/transpiled/directives/io/index.d.ts +2 -0
  348. package/transpiled/directives/io/index.js +6 -0
  349. package/transpiled/directives/io/index.js.map +1 -0
  350. package/transpiled/directives/io/schemas.d.ts +7 -0
  351. package/transpiled/directives/io/schemas.js +14 -0
  352. package/transpiled/directives/io/schemas.js.map +1 -0
  353. package/transpiled/directives/octets/Context.d.ts +3 -3
  354. package/transpiled/directives/octets/Context.js +4 -2
  355. package/transpiled/directives/octets/Context.js.map +1 -1
  356. package/transpiled/directives/octets/Delete.d.ts +3 -2
  357. package/transpiled/directives/octets/Delete.js +3 -1
  358. package/transpiled/directives/octets/Delete.js.map +1 -1
  359. package/transpiled/directives/octets/Directive.d.ts +8 -0
  360. package/transpiled/directives/octets/Directive.js +8 -0
  361. package/transpiled/directives/octets/Directive.js.map +1 -0
  362. package/transpiled/directives/octets/Fetch.d.ts +6 -5
  363. package/transpiled/directives/octets/Fetch.js +10 -8
  364. package/transpiled/directives/octets/Fetch.js.map +1 -1
  365. package/transpiled/directives/octets/List.d.ts +6 -5
  366. package/transpiled/directives/octets/List.js +6 -4
  367. package/transpiled/directives/octets/List.js.map +1 -1
  368. package/transpiled/directives/octets/Octets.d.ts +2 -1
  369. package/transpiled/directives/octets/Octets.js +2 -4
  370. package/transpiled/directives/octets/Octets.js.map +1 -1
  371. package/transpiled/directives/octets/Store.d.ts +3 -2
  372. package/transpiled/directives/octets/Store.js +3 -1
  373. package/transpiled/directives/octets/Store.js.map +1 -1
  374. package/transpiled/directives/octets/Workflow.d.ts +3 -2
  375. package/transpiled/directives/octets/Workflow.js +9 -2
  376. package/transpiled/directives/octets/Workflow.js.map +1 -1
  377. package/transpiled/directives/octets/schemas.d.ts +4 -4
  378. package/transpiled/directives/octets/schemas.js.map +1 -1
  379. package/transpiled/directives/octets/types.d.ts +0 -5
  380. package/transpiled/directives/octets/workflows/Execution.d.ts +5 -1
  381. package/transpiled/directives/octets/workflows/Execution.js +43 -9
  382. package/transpiled/directives/octets/workflows/Execution.js.map +1 -1
  383. package/transpiled/directives/octets/workflows/Workflow.js +2 -1
  384. package/transpiled/directives/octets/workflows/Workflow.js.map +1 -1
  385. package/transpiled/directives/require/Directive.d.ts +4 -0
  386. package/transpiled/directives/require/Directive.js +3 -0
  387. package/transpiled/directives/require/Directive.js.map +1 -0
  388. package/transpiled/directives/require/Headers.d.ts +7 -0
  389. package/transpiled/directives/require/Headers.js +19 -0
  390. package/transpiled/directives/require/Headers.js.map +1 -0
  391. package/transpiled/directives/require/Require.d.ts +9 -0
  392. package/transpiled/directives/require/Require.js +27 -0
  393. package/transpiled/directives/require/Require.js.map +1 -0
  394. package/transpiled/directives/require/index.d.ts +2 -0
  395. package/transpiled/directives/require/index.js +6 -0
  396. package/transpiled/directives/require/index.js.map +1 -0
  397. package/transpiled/directives/vary/Directive.d.ts +2 -1
  398. package/transpiled/directives/vary/Embed.d.ts +2 -1
  399. package/transpiled/directives/vary/Embed.js +8 -6
  400. package/transpiled/directives/vary/Embed.js.map +1 -1
  401. package/transpiled/directives/vary/Vary.d.ts +2 -2
  402. package/transpiled/directives/vary/Vary.js +3 -3
  403. package/transpiled/directives/vary/Vary.js.map +1 -1
  404. package/transpiled/directives/vary/embeddings/Authority.d.ts +5 -0
  405. package/transpiled/directives/vary/embeddings/Authority.js +10 -0
  406. package/transpiled/directives/vary/embeddings/Authority.js.map +1 -0
  407. package/transpiled/directives/vary/embeddings/Embedding.d.ts +2 -1
  408. package/transpiled/directives/vary/embeddings/Header.js +8 -6
  409. package/transpiled/directives/vary/embeddings/Header.js.map +1 -1
  410. package/transpiled/directives/vary/embeddings/Parameter.d.ts +7 -0
  411. package/transpiled/directives/vary/embeddings/Parameter.js +14 -0
  412. package/transpiled/directives/vary/embeddings/Parameter.js.map +1 -0
  413. package/transpiled/directives/vary/embeddings/index.d.ts +2 -2
  414. package/transpiled/directives/vary/embeddings/index.js +8 -4
  415. package/transpiled/directives/vary/embeddings/index.js.map +1 -1
  416. package/transpiled/exceptions.d.ts +3 -2
  417. package/transpiled/exceptions.js +13 -7
  418. package/transpiled/exceptions.js.map +1 -1
  419. package/transpiled/manifest.js +8 -9
  420. package/transpiled/manifest.js.map +1 -1
  421. package/transpiled/root.js +5 -0
  422. package/transpiled/root.js.map +1 -1
  423. package/transpiled/schemas.d.ts +1 -1
  424. package/transpiled/schemas.js +2 -2
  425. package/transpiled/schemas.js.map +1 -1
  426. package/transpiled/tsconfig.tsbuildinfo +1 -1
  427. package/components/identity.basic/operations/create.d.ts +0 -10
  428. package/components/identity.basic/operations/create.js +0 -10
  429. package/components/identity.basic/operations/create.js.map +0 -1
  430. package/components/identity.basic/source/create.ts +0 -18
  431. package/components/identity.federation/operations/create.d.ts +0 -10
  432. package/components/identity.federation/operations/create.js.map +0 -1
  433. package/components/identity.federation/operations/schemas.d.ts +0 -59
  434. package/components/identity.federation/operations/schemas.js +0 -9
  435. package/components/identity.federation/operations/schemas.js.map +0 -1
  436. package/components/identity.federation/operations/types.js.map +0 -1
  437. package/components/identity.federation/source/schemas.ts +0 -61
  438. package/components/octets.storage/operations/permute.js +0 -7
  439. package/source/HTTP/Server.test.ts +0 -126
  440. package/source/directives/octets/Permute.ts +0 -43
  441. package/transpiled/directives/octets/Permute.d.ts +0 -10
  442. package/transpiled/directives/octets/Permute.js.map +0 -1
  443. /package/{components/identity.federation/operations → transpiled/directives/flow}/types.js +0 -0
package/source/Gateway.ts CHANGED
@@ -1,8 +1,9 @@
1
+ import assert from 'node:assert'
1
2
  import { type bindings, Connector } from '@toa.io/core'
2
3
  import * as http from './HTTP'
3
4
  import { rethrow } from './exceptions'
4
5
  import type { Interception } from './Interception'
5
- import type { Method, Parameter, Tree } from './RTD'
6
+ import type { Node, Method, Parameter, Tree, Match } from './RTD'
6
7
  import type { Label } from './discovery'
7
8
  import type { Branch } from './Branch'
8
9
 
@@ -22,34 +23,30 @@ export class Gateway extends Connector {
22
23
  }
23
24
 
24
25
  public async process (context: http.Context): Promise<http.OutgoingMessage> {
25
- const interception = await context.timing.capture('gate:intercept',
26
+ const interception = await context.timing.capture('intercept',
26
27
  this.interceptor.intercept(context))
27
28
 
28
29
  if (interception !== null)
29
30
  return interception
30
31
 
31
- const match = this.tree.match(context.url.pathname)
32
-
33
- if (match === null)
34
- throw new http.NotFound()
32
+ const { node, parameters } = this.match(context)
35
33
 
36
- const {
37
- node,
38
- parameters
39
- } = match
34
+ if (context.request.method === 'OPTIONS')
35
+ return await this.explain(node)
40
36
 
41
37
  if (!(context.request.method in node.methods))
42
38
  throw new http.MethodNotAllowed()
43
39
 
44
40
  const method = node.methods[context.request.method]
45
41
 
46
- const interruption = await context.timing.capture('gate:preflight',
47
- method.directives.preflight(context, parameters))
42
+ const interruption = await context.timing.capture('preflight',
43
+ method.directives.preflight(context, parameters)).catch(rethrow)
48
44
 
49
45
  const response = interruption ??
50
- await context.timing.capture('gate:call', this.call(method, context, parameters))
46
+ await context.timing.capture('call', this.call(method, context, parameters))
51
47
 
52
- await context.timing.capture('gate:settle', method.directives.settle(context, response))
48
+ await context.timing.capture('settle',
49
+ method.directives.settle(context, response)).catch(rethrow)
53
50
 
54
51
  return response
55
52
  }
@@ -57,17 +54,41 @@ export class Gateway extends Connector {
57
54
  protected override async open (): Promise<void> {
58
55
  await this.discover()
59
56
 
60
- console.info('Gateway has started and is awaiting resource branches.')
57
+ console.info('Gateway has started and is awaiting resource branches')
61
58
  }
62
59
 
63
60
  protected override dispose (): void {
64
- console.info('Gateway is closed.')
61
+ console.info('Gateway is closed')
65
62
  }
66
63
 
67
- private async call (method: Method, context: http.Context, parameters: Parameter[]):
68
- Promise<http.OutgoingMessage> {
64
+ private match (context: http.Context): Match {
65
+ const match = this.tree.match(context.url.pathname)
66
+
67
+ if (match === null)
68
+ throw new http.NotFound('Route not found')
69
+
70
+ if (match.node.forward === null)
71
+ return match
72
+
73
+ const destination = match.node.forward.replace(/\/:([^/]+)/g,
74
+ (_, name) => {
75
+ const value = match.parameters.find((parameter) => parameter.name === name)?.value
76
+
77
+ assert.ok(value !== undefined, `Forwarded parameter '${name}' not found`)
78
+
79
+ return `/${value}`
80
+ })
81
+
82
+ const forward = this.tree.match(destination)
83
+
84
+ assert.ok(forward !== null, 'Forwarded route not found')
85
+
86
+ return forward
87
+ }
88
+
89
+ private async call (method: Method, context: http.Context, parameters: Parameter[]): Promise<http.OutgoingMessage> {
69
90
  if (context.url.pathname[context.url.pathname.length - 1] !== '/')
70
- throw new http.NotFound('Trailing slash is required.')
91
+ throw new http.NotFound('Trailing slash is required')
71
92
 
72
93
  if (context.encoder === null)
73
94
  throw new http.NotAcceptable()
@@ -80,6 +101,20 @@ export class Gateway extends Connector {
80
101
  .catch(rethrow) as http.OutgoingMessage
81
102
  }
82
103
 
104
+ private async explain (node: Node): Promise<http.OutgoingMessage> {
105
+ const methods: Record<string, unknown> = {}
106
+
107
+ const explaining = Object.entries(node.methods)
108
+ .map(async ([verb, method]) => (methods[verb] = await method.explain()))
109
+
110
+ await Promise.all(explaining)
111
+
112
+ const allow = [...Object.keys(node.methods), 'OPTIONS'].join(', ')
113
+ const headers = new Headers({ allow })
114
+
115
+ return { body: methods, headers }
116
+ }
117
+
83
118
  private async discover (): Promise<void> {
84
119
  await this.broadcast.receive<Branch>('expose', this.merge.bind(this))
85
120
  await this.broadcast.transmit<null>('ping', null)
@@ -90,7 +125,7 @@ export class Gateway extends Connector {
90
125
  this.tree.merge(branch.node, branch)
91
126
 
92
127
  console.info('Resource branch of ' +
93
- `'${branch.namespace}.${branch.component}' has been merged.`)
128
+ `'${branch.namespace}.${branch.component}' has been merged`)
94
129
  } catch (exception) {
95
130
  console.error(exception)
96
131
  }
@@ -6,22 +6,29 @@ import type { OutgoingMessage } from './messages'
6
6
  import type * as http from 'node:http'
7
7
 
8
8
  export class Context {
9
+ public readonly authority: string
9
10
  public readonly request: IncomingMessage
10
11
  public readonly url: URL
11
12
  public readonly subtype: string | null = null
12
13
  public readonly encoder: Format | null = null
13
14
  public readonly timing: Timing
15
+ public readonly debug: boolean
14
16
 
15
17
  public readonly pipelines: Pipelines = {
16
18
  body: [],
17
19
  response: []
18
20
  }
19
21
 
20
- public constructor (request: IncomingMessage, trace = false) {
22
+ public constructor (authority: string, request: IncomingMessage, properties: Properties) {
23
+ this.authority = authority
21
24
  this.request = request
22
25
 
23
26
  this.url = new URL(request.url, `https://${request.headers.host}`)
24
- this.timing = new Timing(trace)
27
+ this.timing = new Timing(properties.trace)
28
+ this.debug = properties.debug
29
+
30
+ if (this.debug)
31
+ this.log(request)
25
32
 
26
33
  if (this.request.headers.accept !== undefined) {
27
34
  const match = SUBTYPE.exec(this.request.headers.accept)
@@ -52,6 +59,16 @@ export class Context {
52
59
  ? value
53
60
  : this.pipelines.body.reduce((value, transform) => transform(value), value)
54
61
  }
62
+
63
+ private log (request: IncomingMessage): void {
64
+ const message = `${request.method} ${request.url}`
65
+ const headers = { ...request.headers }
66
+
67
+ if (headers.authorization !== undefined)
68
+ headers.authorization = headers.authorization.slice(0, headers.authorization.indexOf(' '))
69
+
70
+ console.debug(message, headers)
71
+ }
55
72
  }
56
73
 
57
74
  export interface IncomingMessage extends http.IncomingMessage {
@@ -64,4 +81,9 @@ interface Pipelines {
64
81
  response: Array<(output: OutgoingMessage) => void>
65
82
  }
66
83
 
84
+ interface Properties {
85
+ debug: boolean
86
+ trace: boolean
87
+ }
88
+
67
89
  const SUBTYPE = /^(?<type>\w{1,32})\/(vnd\.toa\.(?<subtype>\S{1,32})\+)(?<suffix>\S{1,32})$/
@@ -1,10 +1,9 @@
1
1
  import fs from 'node:fs'
2
2
  import os from 'node:os'
3
3
  import * as http from 'node:http'
4
- import assert from 'node:assert'
5
4
  import { once } from 'node:events'
5
+ import { setTimeout } from 'node:timers/promises'
6
6
  import { Connector } from '@toa.io/core'
7
- import { promex } from '@toa.io/generic'
8
7
  import { type OutgoingMessage, write } from './messages'
9
8
  import { ClientError, Exception } from './exceptions'
10
9
  import { Context } from './Context'
@@ -13,32 +12,22 @@ import type { IncomingMessage } from './Context'
13
12
  export class Server extends Connector {
14
13
  private readonly server: http.Server = http.createServer()
15
14
  private readonly properties: Properties
15
+ private readonly authorities: Record<string, string>
16
16
  private process?: Processing
17
+ private ready: boolean = false
18
+ private startedAt: number = 0
17
19
 
18
20
  private constructor (properties: Properties) {
19
21
  super()
20
22
 
21
23
  this.properties = properties
24
+ this.authorities = Object.fromEntries(Object.entries(properties.authorities).map(([key, value]) => [value, key]))
22
25
 
23
26
  this.server.on('request', (req, res) => this.listener(req, res))
24
27
  }
25
28
 
26
- public get port (): number {
27
- if (this.properties.port !== 0)
28
- return this.properties.port
29
-
30
- const address = this.server.address()
31
-
32
- if (address === null || typeof address === 'string')
33
- throw new Error('Server is not listening on a port.')
34
-
35
- return address.port
36
- }
37
-
38
- public static create (options?: Partial<Properties>): Server {
39
- const properties = options === undefined
40
- ? DEFAULTS
41
- : { ...DEFAULTS, ...options }
29
+ public static create (options: Options): Server {
30
+ const properties: Properties = Object.assign({}, DEFAULTS, options)
42
31
 
43
32
  return new Server(properties)
44
33
  }
@@ -48,21 +37,29 @@ export class Server extends Connector {
48
37
  }
49
38
 
50
39
  protected override async open (): Promise<void> {
40
+ this.startedAt = Date.now()
51
41
  this.server.listen(this.properties.port)
52
42
 
53
43
  await once(this.server, 'listening')
54
44
 
55
- console.info('HTTP Server is listening.')
45
+ console.info('HTTP Server is listening')
46
+
47
+ await setTimeout(this.properties.delay)
48
+
49
+ this.ready = true
50
+
51
+ console.info('Ready')
56
52
  }
57
53
 
58
54
  protected override async close (): Promise<void> {
59
55
  this.server.close()
56
+ this.ready = false
60
57
 
61
- console.info('HTTP Server stopped accepting new connections.')
58
+ console.info('HTTP Server stopped accepting new connections')
62
59
 
63
60
  await once(this.server, 'close')
64
61
 
65
- console.info('HTTP Server has been stopped.')
62
+ console.info('HTTP Server has been stopped')
66
63
  }
67
64
 
68
65
  private listener (request: http.IncomingMessage, response: http.ServerResponse): void {
@@ -72,18 +69,23 @@ export class Server extends Connector {
72
69
  return
73
70
  }
74
71
 
75
- if (request.url === undefined) {
76
- response.writeHead(400).end()
72
+ if (request.url === '/.ready') {
73
+ if (this.ready)
74
+ response.writeHead(200, { 'cache-control': 'no-store' }).end()
75
+ else {
76
+ const remaining = (Math.ceil((Date.now() - this.startedAt) / 1000)).toString()
77
+
78
+ response.writeHead(503, { 'retry-after': remaining }).end()
79
+ }
77
80
 
78
81
  return
79
82
  }
80
83
 
81
- assert.ok(this.process !== undefined,
82
- 'No processing function has been attached to the server.')
83
-
84
- const context = new Context(request as IncomingMessage, this.properties.trace)
84
+ const host = request.headers.host!
85
+ const authority = this.authorities[host] ?? host
86
+ const context = new Context(authority, request as IncomingMessage, this.properties)
85
87
 
86
- this.process(context)
88
+ this.process!(context)
87
89
  .then(this.success(context, response))
88
90
  .catch(this.fail(context, response))
89
91
  }
@@ -102,7 +104,7 @@ export class Server extends Connector {
102
104
  else
103
105
  status = 200
104
106
 
105
- response.statusCode = status
107
+ response.statusCode = message.status = status
106
108
  write(context, response, message)
107
109
  }
108
110
  }
@@ -112,17 +114,16 @@ export class Server extends Connector {
112
114
  if (!context.request.complete)
113
115
  await adam(context.request)
114
116
 
115
- response.statusCode = exception instanceof Exception
116
- ? exception.status
117
- : 500
117
+ response.statusCode = exception instanceof Exception ? exception.status : 500
118
118
 
119
- const message: OutgoingMessage = {}
119
+ const message: OutgoingMessage = { status: response.statusCode }
120
120
  const verbose = exception instanceof ClientError || this.properties.debug
121
121
 
122
122
  if (verbose)
123
- message.body = exception instanceof Exception
124
- ? exception.body
125
- : (exception.stack ?? exception.message)
123
+ message.body =
124
+ exception instanceof Exception
125
+ ? exception.body
126
+ : exception.stack ?? exception.message
126
127
 
127
128
  write(context, response, message)
128
129
  }
@@ -131,28 +132,35 @@ export class Server extends Connector {
131
132
 
132
133
  // https://github.com/whatwg/fetch/issues/1254
133
134
  async function adam (request: http.IncomingMessage): Promise<void> {
134
- const completed = promex()
135
135
  const devnull = fs.createWriteStream(os.devNull)
136
136
 
137
- request
138
- .on('end', completed.callback)
139
- .pipe(devnull)
137
+ request.pipe(devnull)
140
138
 
141
- return completed
139
+ await once(request, 'end')
142
140
  }
143
141
 
144
- const DEFAULTS: Properties = {
142
+ export const PORT = 8000
143
+ export const DELAY = 3 // seconds
144
+
145
+ const DEFAULTS: Omit<Properties, 'authorities'> = {
145
146
  methods: new Set<string>(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']),
146
147
  debug: false,
147
148
  trace: false,
148
- port: 8000
149
+ port: PORT,
150
+ delay: DELAY * 1000
149
151
  }
150
152
 
151
153
  interface Properties {
154
+ authorities: Record<string, string>
152
155
  methods: Set<string>
153
156
  debug: boolean
154
157
  trace: boolean
155
158
  port: number
159
+ delay: number
160
+ }
161
+
162
+ export type Options = { authorities: Properties['authorities'] } & {
163
+ [K in Exclude<keyof Properties, 'authorities'>]?: Properties[K]
156
164
  }
157
165
 
158
166
  export type Processing = (input: Context) => Promise<OutgoingMessage>
@@ -37,11 +37,17 @@ export class NotFound extends ClientError {
37
37
  }
38
38
 
39
39
  export class Conflict extends ClientError {
40
- public constructor (body: any) {
40
+ public constructor (body?: any) {
41
41
  super(409, body)
42
42
  }
43
43
  }
44
44
 
45
+ export class UnprocessableEntity extends ClientError {
46
+ public constructor (body?: any) {
47
+ super(422, body)
48
+ }
49
+ }
50
+
45
51
  export class MethodNotAllowed extends ClientError {
46
52
  public constructor () {
47
53
  super(405)
@@ -1,9 +1,13 @@
1
- import { Readable } from 'node:stream'
1
+ import { PassThrough, Readable } from 'node:stream'
2
+ import * as streamConsumers from 'node:stream/consumers'
3
+ import { once } from 'node:events'
2
4
  import { generate } from 'randomstring'
3
5
  import * as msgpack from 'msgpackr'
4
- import { read } from './messages'
6
+ import { multipart, read, type OutgoingMessage } from './messages'
5
7
  import { BadRequest, UnsupportedMediaType } from './exceptions'
8
+ import { formats } from './formats'
6
9
  import { Timing } from './Timing'
10
+ import type * as http from 'node:http'
7
11
  import type { Context } from './Context'
8
12
 
9
13
  beforeEach(() => {
@@ -69,6 +73,39 @@ describe('read', () => {
69
73
 
70
74
  await expect(read(request)).rejects.toThrow(BadRequest)
71
75
  })
76
+
77
+ it('should output correct mulitpart format', async () => {
78
+ const response = new class extends PassThrough {
79
+ public readonly headers = new Headers()
80
+
81
+ public setHeader (key: string, value: string): this {
82
+ this.headers.set(key, value)
83
+
84
+ return this
85
+ }
86
+ }()
87
+
88
+ const context = { encoder: formats['text/plain'] } as unknown as Context
89
+ const message = { body: Readable.from(['Hello', 'New', 'World']) } as unknown as OutgoingMessage
90
+
91
+ multipart(message, context, response as unknown as http.ServerResponse)
92
+
93
+ await once(message.body, 'end')
94
+
95
+ const result = await streamConsumers.text(response)
96
+
97
+ expect(result).toBe([
98
+ '--cut',
99
+ '',
100
+ 'Hello',
101
+ '--cut',
102
+ '',
103
+ 'New',
104
+ '--cut',
105
+ '',
106
+ 'World',
107
+ '--cut--'].join('\r\n'))
108
+ })
72
109
  })
73
110
 
74
111
  export function createContext
@@ -29,7 +29,7 @@ export async function read (context: Context): Promise<any> {
29
29
  throw new UnsupportedMediaType()
30
30
 
31
31
  const format = formats[type]
32
- const buf = await context.timing.capture('req:buffer', buffer(context.request))
32
+ const buf = await context.timing.capture('buffer', buffer(context.request))
33
33
 
34
34
  try {
35
35
  return format.decode(buf)
@@ -72,7 +72,7 @@ function stream
72
72
  })
73
73
  }
74
74
 
75
- function multipart
75
+ export function multipart
76
76
  (message: OutgoingMessage, context: Context, response: http.ServerResponse): void {
77
77
  if (context.encoder === null)
78
78
  throw new NotAcceptable()
@@ -82,7 +82,11 @@ function multipart
82
82
  response.setHeader('content-type', `${encoder.multipart}; boundary=${BOUNDARY}`)
83
83
 
84
84
  message.body
85
- .map((part: unknown) => Buffer.concat([CUT, encoder.encode(part), CRLF]))
85
+ .map((part: unknown) => Buffer.concat([
86
+ CUT,
87
+ CRLF /* indicates no boundary headers */,
88
+ encoder.encode(part),
89
+ CRLF]))
86
90
  .on('end', () => response.end(FINALCUT))
87
91
  .pipe(response)
88
92
  }
package/source/Mapping.ts CHANGED
@@ -38,6 +38,8 @@ class QueryableMapping extends Mapping {
38
38
 
39
39
  class InputMapping extends Mapping {
40
40
  public fit (input: any, _: unknown, parameters: Parameter[]): core.Request {
41
+ const request: core.Request = {}
42
+
41
43
  if (input === undefined && parameters.length > 0)
42
44
  input = {}
43
45
 
@@ -45,6 +47,9 @@ class InputMapping extends Mapping {
45
47
  for (const parameter of parameters)
46
48
  input[parameter.name] = parameter.value
47
49
 
48
- return { input }
50
+ if (input !== undefined)
51
+ request.input = input
52
+
53
+ return request
49
54
  }
50
55
  }
@@ -16,7 +16,7 @@ it('should combine request criteria', async () => {
16
16
  const instance = new Query(query)
17
17
  const result = instance.fit({ criteria: 'qux==4' }, parameters)
18
18
 
19
- expect(result.criteria).toStrictEqual('(foo==1);(bar==2;baz==3);(qux==4)')
19
+ expect(result.criteria).toStrictEqual('(bar==2;baz==3);(foo==1);(qux==4)')
20
20
  })
21
21
 
22
22
  it('should set id parameter as query.id', async () => {
package/source/Query.ts CHANGED
@@ -7,13 +7,20 @@ import type * as core from '@toa.io/core'
7
7
  export class Query {
8
8
  private readonly query: syntax.Query
9
9
  private readonly closed: boolean = false
10
+ private readonly prepend: ',' | ';' = ';'
10
11
 
11
12
  public constructor (query: syntax.Query) {
12
13
  if (query.criteria !== undefined) {
13
- const open = query.criteria[query.criteria.length - 1] === ';'
14
+ if (query.criteria.endsWith(';'))
15
+ query.criteria = query.criteria.slice(0, -1)
16
+ else
17
+ this.closed = true
14
18
 
15
- if (open) query.criteria = query.criteria.slice(0, -1)
16
- else this.closed = true
19
+ if (query.criteria.startsWith(',') || query.criteria.startsWith(';')) {
20
+ this.prepend = query.criteria[0] as ',' | ';'
21
+
22
+ query.criteria = query.criteria.slice(1)
23
+ }
17
24
  }
18
25
 
19
26
  this.query = query
@@ -33,11 +40,7 @@ export class Query {
33
40
  }
34
41
 
35
42
  private fitCriteria (query: http.Query, parameters: Parameter[]): void {
36
- const criteria: string[] = []
37
-
38
- if (this.query.criteria !== undefined)
39
- criteria.push(this.query.criteria)
40
-
43
+ const groups: CriteriaGroup[] = []
41
44
  const idx = parameters.findIndex((parameter) => parameter.name === 'id')
42
45
 
43
46
  if (idx !== -1) {
@@ -47,27 +50,28 @@ export class Query {
47
50
  }
48
51
 
49
52
  if (parameters.length > 0) {
50
- const chunks = parameters
53
+ const criteria = parameters
51
54
  .map(({ name, value }) => `${name}==${value}`)
52
55
  .join(';')
53
56
 
54
- criteria.push(chunks)
57
+ groups.push({ criteria, operator: this.prepend })
55
58
  }
56
59
 
60
+ if (this.query.criteria !== undefined)
61
+ groups.push({ criteria: this.query.criteria, operator: ';' })
62
+
57
63
  if (query.criteria !== undefined)
58
- if (this.closed) throw new http.BadRequest('Query criteria is closed.')
59
- else criteria.push(query.criteria)
60
-
61
- switch (criteria.length) {
62
- case 0:
63
- break
64
- case 1:
65
- query.criteria = criteria[0]
66
- break
67
- default:
68
- query.criteria = '(' + criteria.join(');(') + ')'
69
- break
70
- }
64
+ if (this.closed)
65
+ throw new http.BadRequest('Query criteria is closed')
66
+ else
67
+ groups.push({ criteria: query.criteria, operator: WHATEVER })
68
+
69
+ if (groups.length > 0)
70
+ query.criteria = groups.reduce((acc, { criteria, operator }, i) => {
71
+ return i === groups.length - 1
72
+ ? `${acc}(${criteria})`
73
+ : `${acc}(${criteria})${operator}`
74
+ }, '')
71
75
  }
72
76
 
73
77
  private fitRanges (qs: http.Query): void {
@@ -99,7 +103,14 @@ function fit (string: string, range: [number, number], name: string): number {
99
103
 
100
104
  if (number < range[0] || number > range[1])
101
105
  throw new http.BadRequest(`Query ${name} must be between ` +
102
- `${range[0]} and ${range[1]} inclusive.`)
106
+ `${range[0]} and ${range[1]} inclusive`)
103
107
 
104
108
  return number
105
109
  }
110
+
111
+ const WHATEVER = ';'
112
+
113
+ interface CriteriaGroup {
114
+ criteria: string
115
+ operator: ',' | ';'
116
+ }
@@ -5,6 +5,9 @@ import type * as RTD from './index'
5
5
 
6
6
  export interface Endpoint {
7
7
  call: (context: http.Context, parameters: RTD.Parameter[]) => Promise<http.OutgoingMessage>
8
+
9
+ explain: () => unknown
10
+
8
11
  close: () => Promise<void>
9
12
  }
10
13
 
@@ -10,6 +10,10 @@ export class Method {
10
10
  this.directives = directives
11
11
  }
12
12
 
13
+ public async explain (): Promise<unknown> {
14
+ return (await this.endpoint?.explain()) ?? null
15
+ }
16
+
13
17
  public async close (): Promise<void> {
14
18
  await this.endpoint?.close()
15
19
  }
@@ -4,6 +4,7 @@ import { type Match, type Parameter } from './Match'
4
4
 
5
5
  export class Node {
6
6
  public intermediate: boolean
7
+ public forward: string | null
7
8
  public methods: Methods
8
9
  private readonly protected: boolean
9
10
  private routes: Route[]
@@ -13,6 +14,7 @@ export class Node {
13
14
  this.routes = routes
14
15
  this.methods = methods
15
16
  this.protected = properties.protected
17
+ this.forward = properties.forward ?? null
16
18
  this.intermediate = this.routes.findIndex((route) => route.root) !== -1
17
19
 
18
20
  this.sort()
@@ -20,7 +22,8 @@ export class Node {
20
22
 
21
23
  public match (fragments: string[], parameters: Parameter[] = []): Match | null {
22
24
  for (const route of this.routes) {
23
- const match = route.match(fragments, parameters)
25
+ const params = parameters.slice()
26
+ const match = route.match(fragments, params)
24
27
 
25
28
  if (match !== null)
26
29
  return match
@@ -75,10 +78,15 @@ export class Node {
75
78
  }
76
79
 
77
80
  private sort (): void {
78
- this.routes.sort((a, b) => a.variables - b.variables)
81
+ this.routes.sort((a, b) => {
82
+ return a.variables === b.variables
83
+ ? b.segments.length - a.segments.length // routes with more segments should be matched first
84
+ : a.variables - b.variables // routes with more variables should be matched last
85
+ })
79
86
  }
80
87
  }
81
88
 
82
89
  export interface Properties {
83
90
  protected: boolean
91
+ forward?: string
84
92
  }