@toa.io/extensions.exposition 0.9.0-canary.2 → 0.20.0-alpha.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 (363) hide show
  1. package/components/context.toa.yaml +15 -0
  2. package/components/identity.bans/manifest.toa.yaml +18 -0
  3. package/components/identity.basic/events/principal.js +9 -0
  4. package/components/identity.basic/manifest.toa.yaml +50 -0
  5. package/components/identity.basic/source/authenticate.ts +29 -0
  6. package/components/identity.basic/source/create.ts +19 -0
  7. package/components/identity.basic/source/transit.ts +64 -0
  8. package/components/identity.basic/source/types.ts +42 -0
  9. package/components/identity.basic/tsconfig.json +9 -0
  10. package/components/identity.roles/manifest.toa.yaml +31 -0
  11. package/components/identity.roles/source/list.ts +7 -0
  12. package/components/identity.roles/source/principal.ts +20 -0
  13. package/components/identity.roles/tsconfig.json +9 -0
  14. package/components/identity.tokens/manifest.toa.yaml +39 -0
  15. package/components/identity.tokens/receivers/identity.bans.updated.js +3 -0
  16. package/components/identity.tokens/source/authenticate.test.ts +56 -0
  17. package/components/identity.tokens/source/authenticate.ts +38 -0
  18. package/components/identity.tokens/source/decrypt.test.ts +59 -0
  19. package/components/identity.tokens/source/decrypt.ts +25 -0
  20. package/components/identity.tokens/source/encrypt.test.ts +35 -0
  21. package/components/identity.tokens/source/encrypt.ts +25 -0
  22. package/components/identity.tokens/source/revoke.ts +5 -0
  23. package/components/identity.tokens/source/types.ts +48 -0
  24. package/components/identity.tokens/tsconfig.json +9 -0
  25. package/cucumber.js +9 -0
  26. package/documentation/.assets/ia3-dark.jpg +0 -0
  27. package/documentation/.assets/ia3-light.jpg +0 -0
  28. package/documentation/.assets/overview-dark.jpg +0 -0
  29. package/documentation/.assets/overview-light.jpg +0 -0
  30. package/documentation/.assets/role-scopes-dark.jpg +0 -0
  31. package/documentation/.assets/role-scopes-light.jpg +0 -0
  32. package/documentation/.assets/rtd-dark.jpg +0 -0
  33. package/documentation/.assets/rtd-light.jpg +0 -0
  34. package/documentation/access.md +256 -0
  35. package/documentation/components.md +276 -0
  36. package/documentation/identity.md +156 -0
  37. package/documentation/protocol.md +15 -0
  38. package/documentation/query.md +226 -0
  39. package/documentation/tree.md +169 -0
  40. package/features/access.feature +442 -0
  41. package/features/annotation.feature +28 -0
  42. package/features/body.feature +21 -0
  43. package/features/directives.feature +56 -0
  44. package/features/dynamic.feature +89 -0
  45. package/features/errors.feature +208 -0
  46. package/features/identity.basic.feature +235 -0
  47. package/features/identity.feature +61 -0
  48. package/features/identity.roles.feature +51 -0
  49. package/features/identity.tokens.feature +116 -0
  50. package/features/queries.feature +214 -0
  51. package/features/routes.feature +49 -0
  52. package/features/steps/Common.ts +10 -0
  53. package/features/steps/Components.ts +43 -0
  54. package/features/steps/Database.ts +58 -0
  55. package/features/steps/Gateway.ts +105 -0
  56. package/features/steps/HTTP.ts +71 -0
  57. package/features/steps/Parameters.ts +12 -0
  58. package/features/steps/Workspace.ts +40 -0
  59. package/features/steps/components/greeter/manifest.toa.yaml +9 -0
  60. package/features/steps/components/greeter/operations/greet.js +7 -0
  61. package/features/steps/components/pots/manifest.toa.yaml +20 -0
  62. package/features/steps/components/users/manifest.toa.yaml +11 -0
  63. package/features/steps/tsconfig.json +9 -0
  64. package/package.json +31 -17
  65. package/readme.md +181 -0
  66. package/schemas/annotation.cos.yaml +4 -0
  67. package/schemas/directive.cos.yaml +3 -0
  68. package/schemas/method.cos.yaml +9 -0
  69. package/schemas/node.cos.yaml +5 -0
  70. package/schemas/query.cos.yaml +16 -0
  71. package/schemas/querystring.cos.yaml +5 -0
  72. package/schemas/range.cos.yaml +2 -0
  73. package/schemas/route.cos.yaml +2 -0
  74. package/source/Annotation.ts +6 -0
  75. package/source/Branch.ts +8 -0
  76. package/source/Composition.ts +58 -0
  77. package/source/Context.ts +6 -0
  78. package/source/Directive.test.ts +91 -0
  79. package/source/Directive.ts +120 -0
  80. package/source/Endpoint.ts +57 -0
  81. package/source/Factory.ts +53 -0
  82. package/source/Gateway.ts +93 -0
  83. package/source/HTTP/Server.fixtures.ts +45 -0
  84. package/source/HTTP/Server.test.ts +221 -0
  85. package/source/HTTP/Server.ts +135 -0
  86. package/source/HTTP/exceptions.ts +77 -0
  87. package/source/HTTP/formats/index.ts +19 -0
  88. package/source/HTTP/formats/json.ts +13 -0
  89. package/source/HTTP/formats/msgpack.ts +10 -0
  90. package/source/HTTP/formats/text.ts +9 -0
  91. package/source/HTTP/formats/yaml.ts +14 -0
  92. package/source/HTTP/index.ts +3 -0
  93. package/source/HTTP/messages.test.ts +116 -0
  94. package/source/HTTP/messages.ts +77 -0
  95. package/source/Mapping.ts +48 -0
  96. package/source/Query.test.ts +37 -0
  97. package/source/Query.ts +105 -0
  98. package/source/RTD/Context.ts +16 -0
  99. package/source/RTD/Directives.ts +9 -0
  100. package/source/RTD/Endpoint.ts +11 -0
  101. package/source/RTD/Match.ts +16 -0
  102. package/source/RTD/Method.ts +24 -0
  103. package/source/RTD/Node.ts +85 -0
  104. package/source/RTD/Route.ts +58 -0
  105. package/source/RTD/Tree.ts +57 -0
  106. package/source/RTD/factory.ts +47 -0
  107. package/source/RTD/index.ts +8 -0
  108. package/source/RTD/segment.test.ts +32 -0
  109. package/source/RTD/segment.ts +25 -0
  110. package/source/RTD/syntax/index.ts +2 -0
  111. package/source/RTD/syntax/parse.test.ts +188 -0
  112. package/source/RTD/syntax/parse.ts +153 -0
  113. package/source/RTD/syntax/types.ts +48 -0
  114. package/source/Remotes.test.ts +41 -0
  115. package/source/Remotes.ts +20 -0
  116. package/source/Tenant.ts +38 -0
  117. package/source/deployment.ts +42 -0
  118. package/source/directives/auth/Anonymous.ts +14 -0
  119. package/source/directives/auth/Echo.ts +12 -0
  120. package/source/directives/auth/Family.ts +145 -0
  121. package/source/directives/auth/Id.ts +19 -0
  122. package/source/directives/auth/Incept.ts +42 -0
  123. package/source/directives/auth/Role.test.ts +62 -0
  124. package/source/directives/auth/Role.ts +56 -0
  125. package/source/directives/auth/Rule.ts +28 -0
  126. package/source/directives/auth/Scheme.ts +26 -0
  127. package/source/directives/auth/index.ts +3 -0
  128. package/source/directives/auth/schemes.ts +8 -0
  129. package/source/directives/auth/split.ts +15 -0
  130. package/source/directives/auth/types.ts +37 -0
  131. package/source/directives/dev/Family.ts +34 -0
  132. package/source/directives/dev/Stub.ts +14 -0
  133. package/source/directives/dev/index.ts +3 -0
  134. package/source/directives/dev/types.ts +5 -0
  135. package/source/directives/index.ts +5 -0
  136. package/source/discovery.ts +1 -0
  137. package/source/exceptions.ts +17 -0
  138. package/source/index.test.ts +9 -0
  139. package/source/index.ts +6 -0
  140. package/source/manifest.test.ts +57 -0
  141. package/source/manifest.ts +35 -0
  142. package/source/root.ts +38 -0
  143. package/source/schemas.ts +9 -0
  144. package/transpiled/Annotation.d.ts +6 -0
  145. package/transpiled/Annotation.js +3 -0
  146. package/transpiled/Annotation.js.map +1 -0
  147. package/transpiled/Branch.d.ts +7 -0
  148. package/transpiled/Branch.js +3 -0
  149. package/transpiled/Branch.js.map +1 -0
  150. package/transpiled/Composition.d.ts +14 -0
  151. package/transpiled/Composition.js +43 -0
  152. package/transpiled/Composition.js.map +1 -0
  153. package/transpiled/Context.d.ts +5 -0
  154. package/transpiled/Context.js +3 -0
  155. package/transpiled/Context.js.map +1 -0
  156. package/transpiled/Directive.d.ts +32 -0
  157. package/transpiled/Directive.js +76 -0
  158. package/transpiled/Directive.js.map +1 -0
  159. package/transpiled/Endpoint.d.ts +20 -0
  160. package/transpiled/Endpoint.js +44 -0
  161. package/transpiled/Endpoint.js.map +1 -0
  162. package/transpiled/Factory.d.ts +11 -0
  163. package/transpiled/Factory.js +67 -0
  164. package/transpiled/Factory.js.map +1 -0
  165. package/transpiled/Gateway.d.ts +19 -0
  166. package/transpiled/Gateway.js +90 -0
  167. package/transpiled/Gateway.js.map +1 -0
  168. package/transpiled/HTTP/Server.d.ts +22 -0
  169. package/transpiled/HTTP/Server.fixtures.d.ts +12 -0
  170. package/transpiled/HTTP/Server.fixtures.js +36 -0
  171. package/transpiled/HTTP/Server.fixtures.js.map +1 -0
  172. package/transpiled/HTTP/Server.js +111 -0
  173. package/transpiled/HTTP/Server.js.map +1 -0
  174. package/transpiled/HTTP/exceptions.d.ts +39 -0
  175. package/transpiled/HTTP/exceptions.js +78 -0
  176. package/transpiled/HTTP/exceptions.js.map +1 -0
  177. package/transpiled/HTTP/formats/index.d.ts +8 -0
  178. package/transpiled/HTTP/formats/index.js +38 -0
  179. package/transpiled/HTTP/formats/index.js.map +1 -0
  180. package/transpiled/HTTP/formats/json.d.ts +4 -0
  181. package/transpiled/HTTP/formats/json.js +15 -0
  182. package/transpiled/HTTP/formats/json.js.map +1 -0
  183. package/transpiled/HTTP/formats/msgpack.d.ts +4 -0
  184. package/transpiled/HTTP/formats/msgpack.js +36 -0
  185. package/transpiled/HTTP/formats/msgpack.js.map +1 -0
  186. package/transpiled/HTTP/formats/text.d.ts +4 -0
  187. package/transpiled/HTTP/formats/text.js +13 -0
  188. package/transpiled/HTTP/formats/text.js.map +1 -0
  189. package/transpiled/HTTP/formats/yaml.d.ts +4 -0
  190. package/transpiled/HTTP/formats/yaml.js +39 -0
  191. package/transpiled/HTTP/formats/yaml.js.map +1 -0
  192. package/transpiled/HTTP/index.d.ts +3 -0
  193. package/transpiled/HTTP/index.js +20 -0
  194. package/transpiled/HTTP/index.js.map +1 -0
  195. package/transpiled/HTTP/messages.d.ts +27 -0
  196. package/transpiled/HTTP/messages.js +49 -0
  197. package/transpiled/HTTP/messages.js.map +1 -0
  198. package/transpiled/Mapping.d.ts +8 -0
  199. package/transpiled/Mapping.js +35 -0
  200. package/transpiled/Mapping.js.map +1 -0
  201. package/transpiled/Query.d.ts +13 -0
  202. package/transpiled/Query.js +107 -0
  203. package/transpiled/Query.js.map +1 -0
  204. package/transpiled/RTD/Context.d.ts +11 -0
  205. package/transpiled/RTD/Context.js +3 -0
  206. package/transpiled/RTD/Context.js.map +1 -0
  207. package/transpiled/RTD/Directives.d.ts +7 -0
  208. package/transpiled/RTD/Directives.js +3 -0
  209. package/transpiled/RTD/Directives.js.map +1 -0
  210. package/transpiled/RTD/Endpoint.d.ts +9 -0
  211. package/transpiled/RTD/Endpoint.js +3 -0
  212. package/transpiled/RTD/Endpoint.js.map +1 -0
  213. package/transpiled/RTD/Match.d.ts +11 -0
  214. package/transpiled/RTD/Match.js +3 -0
  215. package/transpiled/RTD/Match.js.map +1 -0
  216. package/transpiled/RTD/Method.d.ts +9 -0
  217. package/transpiled/RTD/Method.js +16 -0
  218. package/transpiled/RTD/Method.js.map +1 -0
  219. package/transpiled/RTD/Node.d.ts +21 -0
  220. package/transpiled/RTD/Node.js +61 -0
  221. package/transpiled/RTD/Node.js.map +1 -0
  222. package/transpiled/RTD/Route.d.ts +14 -0
  223. package/transpiled/RTD/Route.js +48 -0
  224. package/transpiled/RTD/Route.js.map +1 -0
  225. package/transpiled/RTD/Tree.d.ts +14 -0
  226. package/transpiled/RTD/Tree.js +45 -0
  227. package/transpiled/RTD/Tree.js.map +1 -0
  228. package/transpiled/RTD/factory.d.ts +6 -0
  229. package/transpiled/RTD/factory.js +36 -0
  230. package/transpiled/RTD/factory.js.map +1 -0
  231. package/transpiled/RTD/index.d.ts +8 -0
  232. package/transpiled/RTD/index.js +38 -0
  233. package/transpiled/RTD/index.js.map +1 -0
  234. package/transpiled/RTD/segment.d.ts +8 -0
  235. package/transpiled/RTD/segment.js +23 -0
  236. package/transpiled/RTD/segment.js.map +1 -0
  237. package/transpiled/RTD/syntax/index.d.ts +2 -0
  238. package/transpiled/RTD/syntax/index.js +19 -0
  239. package/transpiled/RTD/syntax/index.js.map +1 -0
  240. package/transpiled/RTD/syntax/parse.d.ts +4 -0
  241. package/transpiled/RTD/syntax/parse.js +128 -0
  242. package/transpiled/RTD/syntax/parse.js.map +1 -0
  243. package/transpiled/RTD/syntax/types.d.ts +41 -0
  244. package/transpiled/RTD/syntax/types.js +5 -0
  245. package/transpiled/RTD/syntax/types.js.map +1 -0
  246. package/transpiled/Remotes.d.ts +7 -0
  247. package/transpiled/Remotes.js +19 -0
  248. package/transpiled/Remotes.js.map +1 -0
  249. package/transpiled/Tenant.d.ts +12 -0
  250. package/transpiled/Tenant.js +30 -0
  251. package/transpiled/Tenant.js.map +1 -0
  252. package/transpiled/deployment.d.ts +3 -0
  253. package/transpiled/deployment.js +61 -0
  254. package/transpiled/deployment.js.map +1 -0
  255. package/transpiled/directives/auth/Anonymous.d.ts +6 -0
  256. package/transpiled/directives/auth/Anonymous.js +17 -0
  257. package/transpiled/directives/auth/Anonymous.js.map +1 -0
  258. package/transpiled/directives/auth/Echo.d.ts +6 -0
  259. package/transpiled/directives/auth/Echo.js +13 -0
  260. package/transpiled/directives/auth/Echo.js.map +1 -0
  261. package/transpiled/directives/auth/Family.d.ts +20 -0
  262. package/transpiled/directives/auth/Family.js +125 -0
  263. package/transpiled/directives/auth/Family.js.map +1 -0
  264. package/transpiled/directives/auth/Id.d.ts +7 -0
  265. package/transpiled/directives/auth/Id.js +17 -0
  266. package/transpiled/directives/auth/Id.js.map +1 -0
  267. package/transpiled/directives/auth/Incept.d.ts +10 -0
  268. package/transpiled/directives/auth/Incept.js +59 -0
  269. package/transpiled/directives/auth/Incept.js.map +1 -0
  270. package/transpiled/directives/auth/Role.d.ts +11 -0
  271. package/transpiled/directives/auth/Role.js +44 -0
  272. package/transpiled/directives/auth/Role.js.map +1 -0
  273. package/transpiled/directives/auth/Rule.d.ts +9 -0
  274. package/transpiled/directives/auth/Rule.js +22 -0
  275. package/transpiled/directives/auth/Rule.js.map +1 -0
  276. package/transpiled/directives/auth/Scheme.d.ts +7 -0
  277. package/transpiled/directives/auth/Scheme.js +47 -0
  278. package/transpiled/directives/auth/Scheme.js.map +1 -0
  279. package/transpiled/directives/auth/index.d.ts +2 -0
  280. package/transpiled/directives/auth/index.js +7 -0
  281. package/transpiled/directives/auth/index.js.map +1 -0
  282. package/transpiled/directives/auth/schemes.d.ts +3 -0
  283. package/transpiled/directives/auth/schemes.js +9 -0
  284. package/transpiled/directives/auth/schemes.js.map +1 -0
  285. package/transpiled/directives/auth/split.d.ts +2 -0
  286. package/transpiled/directives/auth/split.js +38 -0
  287. package/transpiled/directives/auth/split.js.map +1 -0
  288. package/transpiled/directives/auth/types.d.ts +31 -0
  289. package/transpiled/directives/auth/types.js +3 -0
  290. package/transpiled/directives/auth/types.js.map +1 -0
  291. package/transpiled/directives/dev/Family.d.ts +10 -0
  292. package/transpiled/directives/dev/Family.js +25 -0
  293. package/transpiled/directives/dev/Family.js.map +1 -0
  294. package/transpiled/directives/dev/Stub.d.ts +7 -0
  295. package/transpiled/directives/dev/Stub.js +14 -0
  296. package/transpiled/directives/dev/Stub.js.map +1 -0
  297. package/transpiled/directives/dev/index.d.ts +2 -0
  298. package/transpiled/directives/dev/index.js +7 -0
  299. package/transpiled/directives/dev/index.js.map +1 -0
  300. package/transpiled/directives/dev/types.d.ts +4 -0
  301. package/transpiled/directives/dev/types.js +3 -0
  302. package/transpiled/directives/dev/types.js.map +1 -0
  303. package/transpiled/directives/index.d.ts +2 -0
  304. package/transpiled/directives/index.js +10 -0
  305. package/transpiled/directives/index.js.map +1 -0
  306. package/transpiled/discovery.d.ts +1 -0
  307. package/transpiled/discovery.js +3 -0
  308. package/transpiled/discovery.js.map +1 -0
  309. package/transpiled/exceptions.d.ts +2 -0
  310. package/transpiled/exceptions.js +39 -0
  311. package/transpiled/exceptions.js.map +1 -0
  312. package/transpiled/index.d.ts +5 -0
  313. package/transpiled/index.js +12 -0
  314. package/transpiled/index.js.map +1 -0
  315. package/transpiled/manifest.d.ts +3 -0
  316. package/transpiled/manifest.js +30 -0
  317. package/transpiled/manifest.js.map +1 -0
  318. package/transpiled/root.d.ts +2 -0
  319. package/transpiled/root.js +39 -0
  320. package/transpiled/root.js.map +1 -0
  321. package/transpiled/schemas.d.ts +3 -0
  322. package/transpiled/schemas.js +14 -0
  323. package/transpiled/schemas.js.map +1 -0
  324. package/transpiled/tsconfig.tsbuildinfo +1 -0
  325. package/tsconfig.json +12 -0
  326. package/src/.manifest/index.js +0 -7
  327. package/src/.manifest/normalize.js +0 -58
  328. package/src/.manifest/schema.yaml +0 -71
  329. package/src/.manifest/validate.js +0 -17
  330. package/src/constants.js +0 -3
  331. package/src/deployment.js +0 -23
  332. package/src/exposition.js +0 -68
  333. package/src/factory.js +0 -76
  334. package/src/index.js +0 -9
  335. package/src/manifest.js +0 -12
  336. package/src/query/criteria.js +0 -55
  337. package/src/query/enum.js +0 -35
  338. package/src/query/index.js +0 -17
  339. package/src/query/query.js +0 -60
  340. package/src/query/range.js +0 -28
  341. package/src/query/sort.js +0 -19
  342. package/src/remote.js +0 -88
  343. package/src/server.js +0 -83
  344. package/src/tenant.js +0 -29
  345. package/src/translate/etag.js +0 -14
  346. package/src/translate/index.js +0 -7
  347. package/src/translate/request.js +0 -68
  348. package/src/translate/response.js +0 -62
  349. package/src/tree.js +0 -109
  350. package/test/manifest.normalize.fixtures.js +0 -37
  351. package/test/manifest.normalize.test.js +0 -37
  352. package/test/manifest.validate.test.js +0 -40
  353. package/test/query.range.test.js +0 -18
  354. package/test/tree.fixtures.js +0 -21
  355. package/test/tree.test.js +0 -44
  356. package/types/annotations.d.ts +0 -10
  357. package/types/declarations.d.ts +0 -31
  358. package/types/exposition.d.ts +0 -13
  359. package/types/http.d.ts +0 -13
  360. package/types/query.d.ts +0 -16
  361. package/types/remote.d.ts +0 -19
  362. package/types/server.d.ts +0 -13
  363. package/types/tree.d.ts +0 -33
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@toa.io/extensions.exposition",
3
- "version": "0.9.0-canary.2",
4
- "description": "Toa Resources Exposition",
3
+ "version": "0.20.0-alpha.1",
4
+ "description": "Toa Exposition",
5
5
  "author": "temich <tema.gurtovoy@gmail.com>",
6
6
  "homepage": "https://github.com/toa-io/toa#readme",
7
- "main": "src/index.js",
7
+ "main": "transpiled/index.js",
8
+ "types": "transpiled/index.d.ts",
8
9
  "repository": {
9
10
  "type": "git",
10
11
  "url": "git+https://github.com/toa-io/toa.git"
@@ -15,22 +16,35 @@
15
16
  "publishConfig": {
16
17
  "access": "public"
17
18
  },
18
- "scripts": {
19
- "test": "echo \"Error: run tests from root\" && exit 1"
20
- },
21
- "devDependencies": {
22
- "@types/express": "4.17.13"
19
+ "peerDependencies": {
20
+ "nopeable": "*"
23
21
  },
24
22
  "dependencies": {
25
- "@toa.io/bindings.amqp": "0.8.0-canary.2",
26
- "@toa.io/console": "0.6.0",
27
- "@toa.io/core": "1.1.0-canary.2",
28
- "@toa.io/generic": "0.11.0-canary.2",
29
- "@toa.io/schema": "0.8.0-canary.2",
30
- "@toa.io/yaml": "0.8.0-canary.2",
23
+ "@toa.io/core": "0.20.0-alpha.1",
24
+ "@toa.io/generic": "0.20.0-alpha.1",
25
+ "@toa.io/http": "0.20.0-alpha.1",
26
+ "@toa.io/schemas": "0.20.0-alpha.1",
27
+ "bcryptjs": "2.4.3",
31
28
  "cors": "2.8.5",
32
- "express": "4.18.1",
33
- "path-to-regexp": "6.2.0"
29
+ "express": "4.18.2",
30
+ "js-yaml": "4.1.0",
31
+ "msgpackr": "1.9.5",
32
+ "negotiator": "0.6.3",
33
+ "paseto": "3.1.4"
34
+ },
35
+ "jest": {
36
+ "preset": "ts-jest",
37
+ "testEnvironment": "node"
38
+ },
39
+ "scripts": {
40
+ "transpile": "npx tsc && npx tsc -p ./components/identity.basic && npx tsc -p ./components/identity.tokens",
41
+ "features": "npx cucumber-js"
42
+ },
43
+ "devDependencies": {
44
+ "@types/bcryptjs": "2.4.3",
45
+ "@types/cors": "2.8.13",
46
+ "@types/express": "4.17.17",
47
+ "@types/negotiator": "0.6.1"
34
48
  },
35
- "gitHead": "a80d81d4d9da9fec07606154c959dd6512de4c76"
49
+ "gitHead": "d1a5369c0ee4ee423f792b83e9753fd2fd943cce"
36
50
  }
package/readme.md ADDED
@@ -0,0 +1,181 @@
1
+ # Toa Exposition
2
+
3
+ ## TL;DR
4
+
5
+ Exposition is a converter from [ROA](https://en.wikipedia.org/wiki/Resource-oriented_architecture)
6
+ to [SOA](https://en.wikipedia.org/wiki/Service-oriented_architecture).
7
+
8
+ ```yaml
9
+ # manifest.toa.yaml
10
+
11
+ name: dummy
12
+ namespace: dummies
13
+
14
+ exposition:
15
+ /:
16
+ GET: observe
17
+ ```
18
+
19
+ ```yaml
20
+ # context.toa.yaml
21
+
22
+ exposition:
23
+ host: api.example.com
24
+ ```
25
+
26
+ ```http
27
+ GET /dummies/dummy/
28
+ Host: api.example.com
29
+ ```
30
+
31
+ See [features](features) for more examples.
32
+
33
+ ## Overview
34
+
35
+ <a href="https://miro.com/app/board/uXjVOoy0ImU=/?moveToWidget=3458764555658883997&cot=14">
36
+ <picture>
37
+ <source media="(prefers-color-scheme: dark)" srcset="./documentation/.assets/overview-dark.jpg">
38
+ <img alt="Exposition" width="800" height="427" src="./documentation/.assets/overview-light.jpg">
39
+ </picture>
40
+ </a>
41
+
42
+ The Exposition extension includes a Service which is an HTTP server with ingress and a Tenant. The
43
+ Service communicates
44
+ with Tenants to discover their resource declarations and exposes them as HTTP resources. An instance
45
+ of the Tenant is
46
+ running within each Composition that has at least one Component with a resource declaration.
47
+
48
+ ## Resource tree discovery
49
+
50
+ During the startup of the Tenant instance, it broadcasts an `expose` message
51
+ containing [Resource branches](#resource-branch)
52
+ of the Components within the Composition. Upon receiving the `expose` message, instances of the
53
+ Service (re-)configures
54
+ corresponding routes for its HTTP server.
55
+
56
+ During the startup of the Service instance, it broadcasts a `ping` message. Once an instance of the
57
+ Tenant receives
58
+ a `ping` message, it broadcasts an `expose` message.
59
+
60
+ ## Resource branch
61
+
62
+ <a href="">
63
+ <picture>
64
+ <source media="(prefers-color-scheme: dark)" srcset="documentation/.assets/rtd-dark.jpg">
65
+ <img alt="IA3" width="600" height="293" src="documentation/.assets/rtd-light.jpg">
66
+ </picture>
67
+ </a>
68
+
69
+ A Component can specify how to expose its Operations as HTTP resources by declaring a Resource
70
+ branch using the manifest extension.
71
+
72
+ ```yaml
73
+ # manifest.toa.yaml
74
+
75
+ extensions:
76
+ '@toa.io/extensions.exposition': ...
77
+ ```
78
+
79
+ Alternatively, a shortcut `exposition` is available:
80
+
81
+ ```yaml
82
+ # manifest.toa.yaml
83
+
84
+ exposition: ...
85
+ ```
86
+
87
+ Resource branches are attached to a Tree with a prefix `/{namespace}/{name}` or `/{name}` for
88
+ components within the default namespace.
89
+
90
+ ```yaml
91
+ # manifest.toa.yaml
92
+
93
+ name: rooms
94
+ namespace: messaging
95
+
96
+ exposition:
97
+ /: ...
98
+ /:user-id: ...
99
+ /:user-id/:room-id: ...
100
+ ```
101
+
102
+ The declaration above will result in exposing the following resources:
103
+
104
+ ```
105
+ /messaging/rooms/
106
+ /messaging/rooms/:user-id/
107
+ /messaging/rooms/:user-id/:room-id/
108
+ ```
109
+
110
+ > Trailing slash is required.
111
+
112
+ ## Context annotation
113
+
114
+ The Exposition annotation declares options for its deployment.
115
+
116
+ ```yaml
117
+ annotations:
118
+ '@toa.io/extensions.exposition':
119
+ host: the.exmaple.com
120
+ ```
121
+
122
+ A shortcut is also available.
123
+
124
+ ```yaml
125
+ exposition:
126
+ host: the.example.com
127
+ ```
128
+
129
+ | Option | Type | Description |
130
+ |---------------|-----------|------------------------------------------------------------------|
131
+ | `host` | `string` | Domain name to be used for the corresponding Kubernetes Ingress. |
132
+ | `class` | `string` | Ingress class |
133
+ | `annotations` | `object` | Ingress annotations |
134
+ | `debug` | `boolean` | Output server errors. Default `false`. |
135
+
136
+ ### Context resources
137
+
138
+ Exposition annotaion can contain [resource declaration](documentation/tree.md) under the `/` key.
139
+
140
+ ```yaml
141
+ # context.toa.yaml
142
+
143
+ exposition:
144
+ host: the.example.com
145
+ /:
146
+ /code:
147
+ GET:
148
+ endpoint: development.code.checkout
149
+ type: observation
150
+ ```
151
+
152
+ In the example above, a request `GET /code` will be mapped to the `development.code.checkout`
153
+ operation call.
154
+ Unlike a component resource branch declaration, properties `namespace`, `component`, and `type` are
155
+ required.
156
+
157
+ If component resource branch conflicts with an annotation, the annotation takes precedence.
158
+
159
+ ### Example
160
+
161
+ ```yaml
162
+ exposition:
163
+ host: the.example.com
164
+ host@staging: the.example.dev
165
+ class: alb
166
+ annotations:
167
+ alb.ingress.kubernetes.io/scheme: internet-facing
168
+ alb.ingress.kubernetes.io/target-type: ip
169
+ alb.ingress.kubernetes.io/listen-ports: '[{"HTTPS": 443}]'
170
+ /:
171
+ /foo:
172
+ GET: foo.bar.observe
173
+ ```
174
+
175
+ ## See Also
176
+
177
+ - [Protocol support](documentation/protocol.md)
178
+ - [Resource Tree Definition](documentation/tree.md)
179
+ - [Identity authentication](documentation/identity.md)
180
+ - [Access authorization](documentation/access.md)
181
+ - [Features](features)
@@ -0,0 +1,4 @@
1
+ host?: string
2
+ class?: string
3
+ annotations?: <string>
4
+ /?: ref:node
@@ -0,0 +1,3 @@
1
+ family: string
2
+ name: string
3
+ value: ~
@@ -0,0 +1,9 @@
1
+ verb: /^(GET|POST|PUT|PATCH|DELETE)$/
2
+ mapping:
3
+ namespace: string
4
+ component: string
5
+ endpoint: string
6
+ query:
7
+ $ref: query
8
+ default: { }
9
+ directives: [ref:directive]
@@ -0,0 +1,5 @@
1
+ protected: boolean
2
+ isolated: boolean
3
+ routes: [ref:route]
4
+ methods: [ref:method]
5
+ directives: [ref:directive]
@@ -0,0 +1,16 @@
1
+ id?: string
2
+ criteria?: string
3
+ sort?: string
4
+ omit:
5
+ $ref: range
6
+ default:
7
+ value: 0
8
+ range: [0, 1000]
9
+ limit:
10
+ $ref: range
11
+ default:
12
+ value: 10
13
+ range: [1, 1000]
14
+ required: [value]
15
+ selectors?: [string]
16
+ projection?: [string]
@@ -0,0 +1,5 @@
1
+ id: string
2
+ criteria: string
3
+ sort: string
4
+ omit: number
5
+ limit: number
@@ -0,0 +1,2 @@
1
+ value?: number
2
+ range: [number]
@@ -0,0 +1,2 @@
1
+ path: string
2
+ node: ref:node
@@ -0,0 +1,6 @@
1
+ export interface Annotation {
2
+ host?: string
3
+ class?: string
4
+ annotations?: Record<string, string>
5
+ '/'?: object // parsed and validated by RTD.syntax.parse
6
+ }
@@ -0,0 +1,8 @@
1
+ import type * as RTD from './RTD/syntax'
2
+
3
+ export interface Branch {
4
+ namespace: string
5
+ component: string
6
+ isolated: boolean
7
+ node: RTD.Node
8
+ }
@@ -0,0 +1,58 @@
1
+ import { readdirSync } from 'node:fs'
2
+ import { resolve } from 'node:path'
3
+ import { type Dirent } from 'fs'
4
+ import { Connector } from '@toa.io/core'
5
+ import { type Bootloader } from './Factory'
6
+
7
+ export class Composition extends Connector {
8
+ private readonly boot: Bootloader
9
+
10
+ public constructor (boot: Bootloader) {
11
+ super()
12
+ this.boot = boot
13
+ }
14
+
15
+ protected override async open (): Promise<void> {
16
+ const paths = find()
17
+ const composition = await this.boot.composition(paths)
18
+
19
+ await composition.connect()
20
+
21
+ this.depends(composition)
22
+
23
+ console.info('Composition complete.')
24
+ }
25
+
26
+ protected override dispose (): void {
27
+ console.info('Composition shutdown complete.')
28
+ }
29
+ }
30
+
31
+ function find (): string[] {
32
+ return entries().map((entry) => resolve(ROOT, entry.name))
33
+ }
34
+
35
+ function entries (): Dirent[] {
36
+ const entries = readdirSync(ROOT, { withFileTypes: true })
37
+
38
+ return entries.filter((entry) => entry.isDirectory())
39
+ }
40
+
41
+ export function components (): Components {
42
+ const labels: string[] = []
43
+ const paths: string[] = []
44
+
45
+ for (const entry of entries()) {
46
+ labels.push(entry.name.replace('.', '-'))
47
+ paths.push(resolve(ROOT, entry.name))
48
+ }
49
+
50
+ return { labels, paths }
51
+ }
52
+
53
+ interface Components {
54
+ labels: string[]
55
+ paths: string[]
56
+ }
57
+
58
+ const ROOT = resolve(__dirname, '../components/')
@@ -0,0 +1,6 @@
1
+ import { type Endpoint } from './Endpoint'
2
+ import { type Directives } from './Directive'
3
+ import { type Branch } from './Branch'
4
+ import type * as RTD from './RTD'
5
+
6
+ export type Context = RTD.Context<Endpoint, Directives, Branch>
@@ -0,0 +1,91 @@
1
+ import { generate } from 'randomstring'
2
+ import { DirectivesFactory, type Family } from './Directive'
3
+ import { type syntax } from './RTD'
4
+ import { type IncomingMessage } from './HTTP'
5
+ import { type Remotes } from './Remotes'
6
+
7
+ const families: Array<jest.MockedObject<Family>> = [
8
+ {
9
+ name: 'foo',
10
+ mandatory: true,
11
+ create: jest.fn((_0: any, _1: any, _2: any) => generate() as any),
12
+ preflight: jest.fn(),
13
+ settle: jest.fn()
14
+ },
15
+ {
16
+ name: 'bar',
17
+ mandatory: false,
18
+ create: jest.fn((_0: string, _1: any, _2: any) => generate() as any),
19
+ preflight: jest.fn(),
20
+ settle: jest.fn()
21
+ }
22
+ ]
23
+
24
+ let factory: DirectivesFactory
25
+
26
+ beforeEach(() => {
27
+ jest.clearAllMocks()
28
+
29
+ families[0].preflight.mockImplementation(() => null)
30
+ families[1].preflight.mockImplementation(() => null)
31
+ factory = new DirectivesFactory(families, {} as unknown as Remotes)
32
+ })
33
+
34
+ it('should create directive', async () => {
35
+ const declarations: syntax.Directive[] = [
36
+ {
37
+ family: 'foo',
38
+ name: generate(),
39
+ value: generate()
40
+ },
41
+ {
42
+ family: 'bar',
43
+ name: generate(),
44
+ value: generate()
45
+ }
46
+ ]
47
+
48
+ factory.create(declarations)
49
+
50
+ for (let i = 0; i < declarations.length; i++) {
51
+ expect(families[i].create.mock.calls[0][0]).toBe(declarations[i].name)
52
+ expect(families[i].create.mock.calls[0][1]).toBe(declarations[i].value)
53
+ }
54
+ })
55
+
56
+ it('should throw error if directive family is not found', async () => {
57
+ const declaration: syntax.Directive = {
58
+ family: generate(),
59
+ name: generate(),
60
+ value: generate()
61
+ }
62
+
63
+ expect(() => factory.create([declaration]))
64
+ .toThrowError(`Directive family '${declaration.family}' not found.`)
65
+ })
66
+
67
+ it('should apply directive', async () => {
68
+ const declaration: syntax.Directive = {
69
+ family: 'foo',
70
+ name: generate(),
71
+ value: generate()
72
+ }
73
+
74
+ const directives = factory.create([declaration])
75
+ const request = generate() as unknown as IncomingMessage
76
+ const directive = families[0].create.mock.results[0].value
77
+
78
+ await directives.preflight(request, [])
79
+
80
+ expect(families[0].preflight.mock.calls[0][0]).toStrictEqual([directive])
81
+ expect(families[0].preflight.mock.calls[0][1]).toEqual(request)
82
+ })
83
+
84
+ it('should apply mandatory families', async () => {
85
+ const directives = factory.create([])
86
+ const request = generate() as unknown as IncomingMessage
87
+
88
+ await directives.preflight(request, [])
89
+
90
+ expect(families[0].preflight).toHaveBeenCalled()
91
+ })
@@ -0,0 +1,120 @@
1
+ import { type IncomingMessage, type OutgoingMessage } from './HTTP'
2
+ import { type Remotes } from './Remotes'
3
+ import type * as RTD from './RTD'
4
+
5
+ export class Directives implements RTD.Directives<Directives> {
6
+ private readonly directives: DirectiveSet[]
7
+
8
+ public constructor (directives: DirectiveSet[]) {
9
+ this.directives = directives
10
+ }
11
+
12
+ public async preflight (request: IncomingMessage, parameters: RTD.Parameter[]): Promise<Output> {
13
+ for (const directive of this.directives) {
14
+ const output = await directive.family.preflight(directive.directives, request, parameters)
15
+
16
+ if (output !== null) {
17
+ await this.settle(request, output)
18
+
19
+ return output
20
+ }
21
+ }
22
+
23
+ return null
24
+ }
25
+
26
+ public async settle (request: IncomingMessage, response: OutgoingMessage): Promise<void> {
27
+ for (const directive of this.directives)
28
+ if (directive.family.settle !== undefined)
29
+ await directive.family.settle(directive.directives, request, response)
30
+ }
31
+
32
+ public merge (directives: Directives): void {
33
+ this.directives.push(...directives.directives)
34
+ }
35
+ }
36
+
37
+ export class DirectivesFactory implements RTD.DirectivesFactory<Directives> {
38
+ private readonly remtoes: Remotes
39
+ private readonly families: Record<string, Family> = {}
40
+ private readonly mandatory: string[] = []
41
+
42
+ public constructor (families: Family[], remotes: Remotes) {
43
+ for (const family of families) {
44
+ this.families[family.name] = family
45
+
46
+ if (family.mandatory)
47
+ this.mandatory.push(family.name)
48
+ }
49
+
50
+ this.remtoes = remotes
51
+ }
52
+
53
+ public create (declarations: RTD.syntax.Directive[]): Directives {
54
+ const groups: Record<string, any> = {}
55
+ const mandatory = new Set(this.mandatory)
56
+
57
+ declarations.sort((a, b) =>
58
+ (mandatory.has(b.family) ? 1 : 0) - (mandatory.has(a.family) ? 1 : 0))
59
+
60
+ for (const declaration of declarations) {
61
+ const family = this.families[declaration.family]
62
+
63
+ if (family === undefined)
64
+ throw new Error(`Directive family '${declaration.family}' not found.`)
65
+
66
+ const directive = family.create(declaration.name, declaration.value, this.remtoes)
67
+
68
+ groups[family.name] ??= []
69
+ groups[family.name].push(directive)
70
+ mandatory.delete(family.name)
71
+ }
72
+
73
+ const sets: DirectiveSet[] = []
74
+
75
+ for (const family of mandatory)
76
+ sets.push({
77
+ family: this.families[family],
78
+ directives: []
79
+ })
80
+
81
+ for (const [family, directives] of Object.entries(groups))
82
+ sets.push({
83
+ family: this.families[family],
84
+ directives
85
+ })
86
+
87
+ return new Directives(sets)
88
+ }
89
+ }
90
+
91
+ export const shortcuts: RTD.syntax.Shortcuts = new Map([
92
+ ['anonymous', 'auth:anonymous'],
93
+ ['id', 'auth:id'],
94
+ ['role', 'auth:role'],
95
+ ['rule', 'auth:rule'],
96
+ ['incept', 'auth:incept']
97
+ ])
98
+
99
+ export interface Family<TDirective = any, TExtension = any> {
100
+ readonly name: string
101
+ readonly mandatory: boolean
102
+
103
+ create: (name: string, value: any, remotes: Remotes) => TDirective
104
+
105
+ preflight: (directives: TDirective[],
106
+ request: IncomingMessage & TExtension,
107
+ parameters: RTD.Parameter[]) => Output | Promise<Output>
108
+
109
+ settle?: (directives: TDirective[],
110
+ request: IncomingMessage & TExtension,
111
+ response: OutgoingMessage) => void | Promise<void>
112
+ }
113
+
114
+ interface DirectiveSet {
115
+ family: Family
116
+ directives: any[]
117
+ }
118
+
119
+ export type Input = IncomingMessage
120
+ export type Output = OutgoingMessage | null
@@ -0,0 +1,57 @@
1
+ import { type Component, type Reply } from '@toa.io/core'
2
+ import { type Remotes } from './Remotes'
3
+ import { Mapping } from './Mapping'
4
+ import { type Context } from './Context'
5
+ import type * as RTD from './RTD'
6
+ import type * as http from './HTTP'
7
+
8
+ export class Endpoint implements RTD.Endpoint<Endpoint> {
9
+ private readonly endpoint: string
10
+ private readonly mapping: Mapping
11
+ private readonly discovery: Promise<Component>
12
+ private remote: Component | null = null
13
+
14
+ public constructor (endpoint: string, mapping: Mapping, discovery: Promise<Component>) {
15
+ this.endpoint = endpoint
16
+ this.mapping = mapping
17
+ this.discovery = discovery
18
+ }
19
+
20
+ public async call (body: any, query: http.Query, parameters: RTD.Parameter[]): Promise<Reply> {
21
+ const request = this.mapping.fit(body, query, parameters)
22
+
23
+ this.remote ??= await this.discovery
24
+
25
+ return await this.remote.invoke(this.endpoint, request)
26
+ }
27
+
28
+ public async close (): Promise<void> {
29
+ this.remote ??= await this.discovery
30
+
31
+ await this.remote.disconnect()
32
+ }
33
+ }
34
+
35
+ export class EndpointFactory implements RTD.EndpointsFactory<Endpoint> {
36
+ private readonly remotes: Remotes
37
+
38
+ public constructor (remotes: Remotes) {
39
+ this.remotes = remotes
40
+ }
41
+
42
+ public create (method: RTD.syntax.Method, context: Context): Endpoint {
43
+ if (method.mapping === undefined)
44
+ throw new Error('Cannot create Endpoint without mapping.')
45
+
46
+ const mapping = Mapping.create(method.verb, method.mapping.query)
47
+ const namespace = method.mapping.namespace ?? context.extension?.namespace
48
+ const component = method.mapping.component ?? context.extension?.component
49
+
50
+ if (namespace === undefined || component === undefined)
51
+ throw new Error('Annotation endpoints must be fully qualified.')
52
+
53
+ const discovery = this.remotes.discover(namespace, component)
54
+
55
+ return new Endpoint(method.mapping.endpoint, mapping, discovery)
56
+ }
57
+ }