@toa.io/extensions.exposition 0.22.1 → 0.24.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (306) hide show
  1. package/components/identity.basic/source/authenticate.ts +3 -2
  2. package/components/identity.basic/source/transit.ts +4 -3
  3. package/components/octets.storage/manifest.toa.yaml +26 -0
  4. package/components/octets.storage/operations/delete.js +7 -0
  5. package/components/octets.storage/operations/fetch.js +46 -0
  6. package/components/octets.storage/operations/get.js +7 -0
  7. package/components/octets.storage/operations/list.js +7 -0
  8. package/components/octets.storage/operations/permute.js +7 -0
  9. package/components/octets.storage/operations/store.js +11 -0
  10. package/cucumber.js +0 -1
  11. package/documentation/access.md +2 -3
  12. package/documentation/cache.md +42 -0
  13. package/documentation/octets.md +196 -0
  14. package/documentation/protocol.md +49 -5
  15. package/documentation/tree.md +1 -5
  16. package/features/access.feature +1 -0
  17. package/features/cache.feature +160 -0
  18. package/features/errors.feature +18 -0
  19. package/features/identity.basic.feature +2 -0
  20. package/features/octets.feature +295 -0
  21. package/features/octets.workflows.feature +114 -0
  22. package/features/routes.feature +40 -0
  23. package/features/steps/HTTP.ts +54 -6
  24. package/features/steps/Parameters.ts +5 -2
  25. package/features/steps/Workspace.ts +8 -5
  26. package/features/steps/components/octets.tester/manifest.toa.yaml +15 -0
  27. package/features/steps/components/octets.tester/operations/bar.js +12 -0
  28. package/features/steps/components/octets.tester/operations/baz.js +11 -0
  29. package/features/steps/components/octets.tester/operations/diversify.js +14 -0
  30. package/features/steps/components/octets.tester/operations/err.js +16 -0
  31. package/features/steps/components/octets.tester/operations/foo.js +7 -0
  32. package/features/steps/components/octets.tester/operations/lenna.png +0 -0
  33. package/features/steps/components/pots/manifest.toa.yaml +1 -1
  34. package/features/streams.feature +5 -1
  35. package/package.json +16 -9
  36. package/readme.md +8 -5
  37. package/schemas/octets/context.cos.yaml +1 -0
  38. package/schemas/octets/delete.cos.yaml +1 -0
  39. package/schemas/octets/fetch.cos.yaml +3 -0
  40. package/schemas/octets/list.cos.yaml +1 -0
  41. package/schemas/octets/permute.cos.yaml +1 -0
  42. package/schemas/octets/store.cos.yaml +3 -0
  43. package/source/Gateway.ts +9 -4
  44. package/source/HTTP/Server.fixtures.ts +2 -6
  45. package/source/HTTP/Server.test.ts +9 -31
  46. package/source/HTTP/Server.ts +55 -28
  47. package/source/HTTP/exceptions.ts +2 -12
  48. package/source/HTTP/formats/index.ts +7 -4
  49. package/source/HTTP/formats/json.ts +3 -0
  50. package/source/HTTP/formats/msgpack.ts +3 -0
  51. package/source/HTTP/formats/text.ts +3 -0
  52. package/source/HTTP/formats/yaml.ts +3 -0
  53. package/source/HTTP/messages.test.ts +3 -49
  54. package/source/HTTP/messages.ts +60 -35
  55. package/source/RTD/Route.ts +1 -1
  56. package/source/RTD/segment.ts +2 -1
  57. package/source/RTD/syntax/parse.ts +2 -1
  58. package/source/Remotes.ts +8 -0
  59. package/source/Tenant.ts +5 -0
  60. package/source/directives/auth/Family.ts +26 -22
  61. package/source/directives/auth/Rule.ts +1 -1
  62. package/source/directives/cache/Control.ts +59 -0
  63. package/source/directives/cache/Exact.ts +7 -0
  64. package/source/directives/cache/Family.ts +36 -0
  65. package/source/directives/cache/index.ts +3 -0
  66. package/source/directives/cache/types.ts +9 -0
  67. package/source/directives/index.ts +3 -1
  68. package/source/directives/octets/Context.ts +18 -0
  69. package/source/directives/octets/Delete.ts +32 -0
  70. package/source/directives/octets/Family.ts +68 -0
  71. package/source/directives/octets/Fetch.ts +85 -0
  72. package/source/directives/octets/List.ts +32 -0
  73. package/source/directives/octets/Permute.ts +37 -0
  74. package/source/directives/octets/Store.ts +158 -0
  75. package/source/directives/octets/index.ts +3 -0
  76. package/source/directives/octets/schemas.ts +12 -0
  77. package/source/directives/octets/types.ts +13 -0
  78. package/transpiled/Annotation.d.ts +7 -0
  79. package/transpiled/Annotation.js +3 -0
  80. package/transpiled/Annotation.js.map +1 -0
  81. package/transpiled/Branch.d.ts +7 -0
  82. package/transpiled/Branch.js +3 -0
  83. package/transpiled/Branch.js.map +1 -0
  84. package/transpiled/Composition.d.ts +14 -0
  85. package/transpiled/Composition.js +43 -0
  86. package/transpiled/Composition.js.map +1 -0
  87. package/transpiled/Context.d.ts +5 -0
  88. package/transpiled/Context.js +3 -0
  89. package/transpiled/Context.js.map +1 -0
  90. package/transpiled/Directive.d.ts +32 -0
  91. package/transpiled/Directive.js +76 -0
  92. package/transpiled/Directive.js.map +1 -0
  93. package/transpiled/Endpoint.d.ts +20 -0
  94. package/transpiled/Endpoint.js +45 -0
  95. package/transpiled/Endpoint.js.map +1 -0
  96. package/transpiled/Factory.d.ts +10 -0
  97. package/transpiled/Factory.js +66 -0
  98. package/transpiled/Factory.js.map +1 -0
  99. package/transpiled/Gateway.d.ts +19 -0
  100. package/transpiled/Gateway.js +92 -0
  101. package/transpiled/Gateway.js.map +1 -0
  102. package/transpiled/HTTP/Server.d.ts +22 -0
  103. package/transpiled/HTTP/Server.fixtures.d.ts +11 -0
  104. package/transpiled/HTTP/Server.fixtures.js +32 -0
  105. package/transpiled/HTTP/Server.fixtures.js.map +1 -0
  106. package/transpiled/HTTP/Server.js +131 -0
  107. package/transpiled/HTTP/Server.js.map +1 -0
  108. package/transpiled/HTTP/exceptions.d.ts +34 -0
  109. package/transpiled/HTTP/exceptions.js +71 -0
  110. package/transpiled/HTTP/exceptions.js.map +1 -0
  111. package/transpiled/HTTP/formats/index.d.ts +10 -0
  112. package/transpiled/HTTP/formats/index.js +38 -0
  113. package/transpiled/HTTP/formats/index.js.map +1 -0
  114. package/transpiled/HTTP/formats/json.d.ts +6 -0
  115. package/transpiled/HTTP/formats/json.js +17 -0
  116. package/transpiled/HTTP/formats/json.js.map +1 -0
  117. package/transpiled/HTTP/formats/msgpack.d.ts +6 -0
  118. package/transpiled/HTTP/formats/msgpack.js +38 -0
  119. package/transpiled/HTTP/formats/msgpack.js.map +1 -0
  120. package/transpiled/HTTP/formats/text.d.ts +6 -0
  121. package/transpiled/HTTP/formats/text.js +15 -0
  122. package/transpiled/HTTP/formats/text.js.map +1 -0
  123. package/transpiled/HTTP/formats/yaml.d.ts +6 -0
  124. package/transpiled/HTTP/formats/yaml.js +41 -0
  125. package/transpiled/HTTP/formats/yaml.js.map +1 -0
  126. package/transpiled/HTTP/index.d.ts +3 -0
  127. package/transpiled/HTTP/index.js +20 -0
  128. package/transpiled/HTTP/index.js.map +1 -0
  129. package/transpiled/HTTP/messages.d.ts +28 -0
  130. package/transpiled/HTTP/messages.js +70 -0
  131. package/transpiled/HTTP/messages.js.map +1 -0
  132. package/transpiled/Mapping.d.ts +8 -0
  133. package/transpiled/Mapping.js +38 -0
  134. package/transpiled/Mapping.js.map +1 -0
  135. package/transpiled/Query.d.ts +13 -0
  136. package/transpiled/Query.js +107 -0
  137. package/transpiled/Query.js.map +1 -0
  138. package/transpiled/RTD/Context.d.ts +11 -0
  139. package/transpiled/RTD/Context.js +3 -0
  140. package/transpiled/RTD/Context.js.map +1 -0
  141. package/transpiled/RTD/Directives.d.ts +7 -0
  142. package/transpiled/RTD/Directives.js +3 -0
  143. package/transpiled/RTD/Directives.js.map +1 -0
  144. package/transpiled/RTD/Endpoint.d.ts +9 -0
  145. package/transpiled/RTD/Endpoint.js +3 -0
  146. package/transpiled/RTD/Endpoint.js.map +1 -0
  147. package/transpiled/RTD/Match.d.ts +11 -0
  148. package/transpiled/RTD/Match.js +3 -0
  149. package/transpiled/RTD/Match.js.map +1 -0
  150. package/transpiled/RTD/Method.d.ts +9 -0
  151. package/transpiled/RTD/Method.js +16 -0
  152. package/transpiled/RTD/Method.js.map +1 -0
  153. package/transpiled/RTD/Node.d.ts +21 -0
  154. package/transpiled/RTD/Node.js +61 -0
  155. package/transpiled/RTD/Node.js.map +1 -0
  156. package/transpiled/RTD/Route.d.ts +14 -0
  157. package/transpiled/RTD/Route.js +49 -0
  158. package/transpiled/RTD/Route.js.map +1 -0
  159. package/transpiled/RTD/Tree.d.ts +14 -0
  160. package/transpiled/RTD/Tree.js +40 -0
  161. package/transpiled/RTD/Tree.js.map +1 -0
  162. package/transpiled/RTD/factory.d.ts +6 -0
  163. package/transpiled/RTD/factory.js +36 -0
  164. package/transpiled/RTD/factory.js.map +1 -0
  165. package/transpiled/RTD/index.d.ts +8 -0
  166. package/transpiled/RTD/index.js +38 -0
  167. package/transpiled/RTD/index.js.map +1 -0
  168. package/transpiled/RTD/segment.d.ts +8 -0
  169. package/transpiled/RTD/segment.js +25 -0
  170. package/transpiled/RTD/segment.js.map +1 -0
  171. package/transpiled/RTD/syntax/index.d.ts +2 -0
  172. package/transpiled/RTD/syntax/index.js +19 -0
  173. package/transpiled/RTD/syntax/index.js.map +1 -0
  174. package/transpiled/RTD/syntax/parse.d.ts +4 -0
  175. package/transpiled/RTD/syntax/parse.js +128 -0
  176. package/transpiled/RTD/syntax/parse.js.map +1 -0
  177. package/transpiled/RTD/syntax/types.d.ts +41 -0
  178. package/transpiled/RTD/syntax/types.js +5 -0
  179. package/transpiled/RTD/syntax/types.js.map +1 -0
  180. package/transpiled/Remotes.d.ts +9 -0
  181. package/transpiled/Remotes.js +25 -0
  182. package/transpiled/Remotes.js.map +1 -0
  183. package/transpiled/Tenant.d.ts +13 -0
  184. package/transpiled/Tenant.js +34 -0
  185. package/transpiled/Tenant.js.map +1 -0
  186. package/transpiled/deployment.d.ts +3 -0
  187. package/transpiled/deployment.js +67 -0
  188. package/transpiled/deployment.js.map +1 -0
  189. package/transpiled/directives/auth/Anonymous.d.ts +6 -0
  190. package/transpiled/directives/auth/Anonymous.js +17 -0
  191. package/transpiled/directives/auth/Anonymous.js.map +1 -0
  192. package/transpiled/directives/auth/Echo.d.ts +6 -0
  193. package/transpiled/directives/auth/Echo.js +13 -0
  194. package/transpiled/directives/auth/Echo.js.map +1 -0
  195. package/transpiled/directives/auth/Family.d.ts +20 -0
  196. package/transpiled/directives/auth/Family.js +118 -0
  197. package/transpiled/directives/auth/Family.js.map +1 -0
  198. package/transpiled/directives/auth/Id.d.ts +7 -0
  199. package/transpiled/directives/auth/Id.js +17 -0
  200. package/transpiled/directives/auth/Id.js.map +1 -0
  201. package/transpiled/directives/auth/Incept.d.ts +10 -0
  202. package/transpiled/directives/auth/Incept.js +58 -0
  203. package/transpiled/directives/auth/Incept.js.map +1 -0
  204. package/transpiled/directives/auth/Role.d.ts +11 -0
  205. package/transpiled/directives/auth/Role.js +44 -0
  206. package/transpiled/directives/auth/Role.js.map +1 -0
  207. package/transpiled/directives/auth/Rule.d.ts +9 -0
  208. package/transpiled/directives/auth/Rule.js +22 -0
  209. package/transpiled/directives/auth/Rule.js.map +1 -0
  210. package/transpiled/directives/auth/Scheme.d.ts +7 -0
  211. package/transpiled/directives/auth/Scheme.js +47 -0
  212. package/transpiled/directives/auth/Scheme.js.map +1 -0
  213. package/transpiled/directives/auth/index.d.ts +2 -0
  214. package/transpiled/directives/auth/index.js +7 -0
  215. package/transpiled/directives/auth/index.js.map +1 -0
  216. package/transpiled/directives/auth/schemes.d.ts +3 -0
  217. package/transpiled/directives/auth/schemes.js +9 -0
  218. package/transpiled/directives/auth/schemes.js.map +1 -0
  219. package/transpiled/directives/auth/split.d.ts +2 -0
  220. package/transpiled/directives/auth/split.js +38 -0
  221. package/transpiled/directives/auth/split.js.map +1 -0
  222. package/transpiled/directives/auth/types.d.ts +31 -0
  223. package/transpiled/directives/auth/types.js +3 -0
  224. package/transpiled/directives/auth/types.js.map +1 -0
  225. package/transpiled/directives/cache/Control.d.ts +9 -0
  226. package/transpiled/directives/cache/Control.js +42 -0
  227. package/transpiled/directives/cache/Control.js.map +1 -0
  228. package/transpiled/directives/cache/Exact.d.ts +4 -0
  229. package/transpiled/directives/cache/Exact.js +11 -0
  230. package/transpiled/directives/cache/Exact.js.map +1 -0
  231. package/transpiled/directives/cache/Family.d.ts +12 -0
  232. package/transpiled/directives/cache/Family.js +26 -0
  233. package/transpiled/directives/cache/Family.js.map +1 -0
  234. package/transpiled/directives/cache/index.d.ts +2 -0
  235. package/transpiled/directives/cache/index.js +7 -0
  236. package/transpiled/directives/cache/index.js.map +1 -0
  237. package/transpiled/directives/cache/types.d.ts +7 -0
  238. package/transpiled/directives/cache/types.js +3 -0
  239. package/transpiled/directives/cache/types.js.map +1 -0
  240. package/transpiled/directives/dev/Family.d.ts +10 -0
  241. package/transpiled/directives/dev/Family.js +27 -0
  242. package/transpiled/directives/dev/Family.js.map +1 -0
  243. package/transpiled/directives/dev/Stub.d.ts +7 -0
  244. package/transpiled/directives/dev/Stub.js +14 -0
  245. package/transpiled/directives/dev/Stub.js.map +1 -0
  246. package/transpiled/directives/dev/Throw.d.ts +7 -0
  247. package/transpiled/directives/dev/Throw.js +14 -0
  248. package/transpiled/directives/dev/Throw.js.map +1 -0
  249. package/transpiled/directives/dev/index.d.ts +2 -0
  250. package/transpiled/directives/dev/index.js +7 -0
  251. package/transpiled/directives/dev/index.js.map +1 -0
  252. package/transpiled/directives/dev/types.d.ts +4 -0
  253. package/transpiled/directives/dev/types.js +3 -0
  254. package/transpiled/directives/dev/types.js.map +1 -0
  255. package/transpiled/directives/index.d.ts +2 -0
  256. package/transpiled/directives/index.js +12 -0
  257. package/transpiled/directives/index.js.map +1 -0
  258. package/transpiled/directives/octets/Context.d.ts +8 -0
  259. package/transpiled/directives/octets/Context.js +40 -0
  260. package/transpiled/directives/octets/Context.js.map +1 -0
  261. package/transpiled/directives/octets/Delete.d.ts +10 -0
  262. package/transpiled/directives/octets/Delete.js +47 -0
  263. package/transpiled/directives/octets/Delete.js.map +1 -0
  264. package/transpiled/directives/octets/Family.d.ts +12 -0
  265. package/transpiled/directives/octets/Family.js +49 -0
  266. package/transpiled/directives/octets/Family.js.map +1 -0
  267. package/transpiled/directives/octets/Fetch.d.ts +18 -0
  268. package/transpiled/directives/octets/Fetch.js +77 -0
  269. package/transpiled/directives/octets/Fetch.js.map +1 -0
  270. package/transpiled/directives/octets/List.d.ts +10 -0
  271. package/transpiled/directives/octets/List.js +47 -0
  272. package/transpiled/directives/octets/List.js.map +1 -0
  273. package/transpiled/directives/octets/Permute.d.ts +10 -0
  274. package/transpiled/directives/octets/Permute.js +51 -0
  275. package/transpiled/directives/octets/Permute.js.map +1 -0
  276. package/transpiled/directives/octets/Store.d.ts +33 -0
  277. package/transpiled/directives/octets/Store.js +124 -0
  278. package/transpiled/directives/octets/Store.js.map +1 -0
  279. package/transpiled/directives/octets/index.d.ts +2 -0
  280. package/transpiled/directives/octets/index.js +7 -0
  281. package/transpiled/directives/octets/index.js.map +1 -0
  282. package/transpiled/directives/octets/schemas.d.ts +6 -0
  283. package/transpiled/directives/octets/schemas.js +17 -0
  284. package/transpiled/directives/octets/schemas.js.map +1 -0
  285. package/transpiled/directives/octets/types.d.ts +9 -0
  286. package/transpiled/directives/octets/types.js +3 -0
  287. package/transpiled/directives/octets/types.js.map +1 -0
  288. package/transpiled/discovery.d.ts +1 -0
  289. package/transpiled/discovery.js +3 -0
  290. package/transpiled/discovery.js.map +1 -0
  291. package/transpiled/exceptions.d.ts +2 -0
  292. package/transpiled/exceptions.js +39 -0
  293. package/transpiled/exceptions.js.map +1 -0
  294. package/transpiled/index.d.ts +5 -0
  295. package/transpiled/index.js +12 -0
  296. package/transpiled/index.js.map +1 -0
  297. package/transpiled/manifest.d.ts +3 -0
  298. package/transpiled/manifest.js +61 -0
  299. package/transpiled/manifest.js.map +1 -0
  300. package/transpiled/root.d.ts +2 -0
  301. package/transpiled/root.js +39 -0
  302. package/transpiled/root.js.map +1 -0
  303. package/transpiled/schemas.d.ts +3 -0
  304. package/transpiled/schemas.js +14 -0
  305. package/transpiled/schemas.js.map +1 -0
  306. package/transpiled/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,7 @@
1
+ 'use strict'
2
+
3
+ function foo (input, context) {
4
+ return context.storages.octets.annotate(input.path, 'foo', 'bar')
5
+ }
6
+
7
+ exports.effect = foo
@@ -8,7 +8,7 @@ entity:
8
8
 
9
9
  operations:
10
10
  transit:
11
- concurrency: none
11
+ query: false
12
12
  input:
13
13
  title*: ~
14
14
  volume*: ~
@@ -18,9 +18,13 @@ Feature: Reply streams
18
18
  Then the following reply is sent:
19
19
  """
20
20
  201 Created
21
- transfer-encoding: chunked
21
+ content-type: multipart/text; boundary=cut
22
22
 
23
+ --cut
23
24
  0
25
+ --cut
24
26
  1
27
+ --cut
25
28
  2
29
+ --cut--
26
30
  """
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/extensions.exposition",
3
- "version": "0.22.1",
3
+ "version": "0.24.0-alpha.0",
4
4
  "description": "Toa Exposition",
5
5
  "author": "temich <tema.gurtovoy@gmail.com>",
6
6
  "homepage": "https://github.com/toa-io/toa#readme",
@@ -17,15 +17,17 @@
17
17
  "access": "public"
18
18
  },
19
19
  "dependencies": {
20
- "@toa.io/core": "0.22.1",
21
- "@toa.io/generic": "0.22.0",
22
- "@toa.io/http": "0.22.0",
23
- "@toa.io/schemas": "0.22.0",
24
- "@toa.io/streams": "0.22.0",
20
+ "@toa.io/core": "0.24.0-alpha.0",
21
+ "@toa.io/generic": "0.24.0-alpha.0",
22
+ "@toa.io/http": "0.24.0-alpha.0",
23
+ "@toa.io/schemas": "0.24.0-alpha.0",
24
+ "@toa.io/streams": "0.24.0-alpha.0",
25
25
  "bcryptjs": "2.4.3",
26
26
  "cors": "2.8.5",
27
+ "error-value": "0.3.0",
27
28
  "express": "4.18.2",
28
29
  "js-yaml": "4.1.0",
30
+ "matchacho": "0.3.5",
29
31
  "msgpackr": "1.9.5",
30
32
  "negotiator": "0.6.3",
31
33
  "paseto": "3.1.4"
@@ -35,16 +37,21 @@
35
37
  "testEnvironment": "node"
36
38
  },
37
39
  "scripts": {
38
- "transpile": "rm -rf transpiled && npx tsc && npm run transpile:basic && npm run transpile:tokens",
40
+ "test": "jest",
41
+ "transpile": "rm -rf transpiled && npx tsc && npm run transpile:basic && npm run transpile:tokens && npm run transpile:roles",
39
42
  "transpile:basic": "rm -rf ./components/identity.basic/operations && npx tsc -p ./components/identity.basic",
40
43
  "transpile:tokens": "rm -rf ./components/identity.tokens/operations && npx tsc -p ./components/identity.tokens",
44
+ "transpile:roles": "rm -rf ./components/identity.roles/operations && npx tsc -p ./components/identity.roles",
41
45
  "features": "npx cucumber-js"
42
46
  },
43
47
  "devDependencies": {
48
+ "@toa.io/extensions.storages": "0.24.0-alpha.0",
44
49
  "@types/bcryptjs": "2.4.3",
45
50
  "@types/cors": "2.8.13",
46
51
  "@types/express": "4.17.17",
47
- "@types/negotiator": "0.6.1"
52
+ "@types/fs-extra": "11.0.4",
53
+ "@types/negotiator": "0.6.1",
54
+ "fs-extra": "11.1.1"
48
55
  },
49
- "gitHead": "d1116e1a3c3d881d2204989851596f0715abcb0f"
56
+ "gitHead": "09a88bdf444bc5dba66dbf0005c382f3526f0296"
50
57
  }
package/readme.md CHANGED
@@ -39,11 +39,12 @@ See [features](features) for more examples.
39
39
  </picture>
40
40
  </a>
41
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.
42
+ The Exposition extension includes a Service, which is an HTTP server with ingress and a Tenant.
43
+ The Service communicates with Tenants to discover their resource declarations and exposes them as
44
+ HTTP resources.
45
+ An instance of the Tenant is running within each Composition that has at least one Component with a
46
+ resource
47
+ declaration.
47
48
 
48
49
  ## Resource tree discovery
49
50
 
@@ -179,5 +180,7 @@ exposition:
179
180
  - [Resource Tree Definition](documentation/tree.md)
180
181
  - [Identity authentication](documentation/identity.md)
181
182
  - [Access authorization](documentation/access.md)
183
+ - [BLOBs](documentation/octets.md)
182
184
  - [Components and resources](documentation/components.md)
185
+ - [Caching](documentation/cache.md)
183
186
  - [Features](features)
@@ -0,0 +1 @@
1
+ string
@@ -0,0 +1 @@
1
+ ~
@@ -0,0 +1,3 @@
1
+ blob: boolean
2
+ meta: boolean
3
+ _: true
@@ -0,0 +1 @@
1
+ ~
@@ -0,0 +1 @@
1
+ ~
@@ -0,0 +1,3 @@
1
+ accept+: string
2
+ workflow+: <string>
3
+ _: true
package/source/Gateway.ts CHANGED
@@ -35,9 +35,6 @@ export class Gateway extends Connector {
35
35
  }
36
36
 
37
37
  private async process (request: http.IncomingMessage): Promise<http.OutgoingMessage> {
38
- if (request.path[request.path.length - 1] !== '/')
39
- throw new http.NotFound('Trailing slash is required.')
40
-
41
38
  const match = this.tree.match(request.path)
42
39
 
43
40
  if (match === null)
@@ -60,11 +57,19 @@ export class Gateway extends Connector {
60
57
  private async call
61
58
  (method: Method<Endpoint, Directives>, request: http.IncomingMessage, parameters: Parameter[]):
62
59
  Promise<http.OutgoingMessage> {
60
+ if (request.path[request.path.length - 1] !== '/')
61
+ throw new http.NotFound('Trailing slash is required.')
62
+
63
+ if (request.encoder === null)
64
+ throw new http.NotAcceptable()
65
+
63
66
  if (method.endpoint === null)
64
67
  throw new http.MethodNotAllowed()
65
68
 
69
+ const body = await request.parse()
70
+
66
71
  const reply = await method.endpoint
67
- .call(request.body, request.query, parameters)
72
+ .call(body, request.query, parameters)
68
73
  .catch(rethrow) as Maybe<unknown>
69
74
 
70
75
  if (reply instanceof Error)
@@ -17,17 +17,13 @@ const app = {
17
17
  } as unknown as jest.Mock<Express>
18
18
 
19
19
  export function createRequest (req: Partial<Request> = {}, content: string | Buffer = ''):
20
- jest.MockedObject<Request> {
20
+ jest.MockedObject<IncomingMessage> {
21
21
  const buffer = Buffer.isBuffer(content) ? content : Buffer.from(content)
22
22
  const stream = Readable.from(buffer)
23
23
 
24
24
  Object.assign(stream, { headers: {} }, req)
25
25
 
26
- return stream as unknown as jest.MockedObject<Request>
27
- }
28
-
29
- export function createIncomingMessage (path: string, method: string = 'GET'): IncomingMessage {
30
- return { method, path, headers: {}, body: undefined, query: {} }
26
+ return stream as unknown as jest.MockedObject<IncomingMessage>
31
27
  }
32
28
 
33
29
  export const res = {
@@ -93,43 +93,22 @@ it('should send 501 on unknown method', async () => {
93
93
  expect(res.sendStatus).toHaveBeenCalledWith(501)
94
94
  })
95
95
 
96
- describe('request', () => {
97
- const process = jest.fn(async () => ({})) as unknown as Processing
98
-
99
- beforeEach(() => {
100
- server.attach(process)
101
- })
102
-
103
- it('should pass decoded request', async () => {
104
- const path = generate()
105
- const method = generate()
106
- const headers = { 'content-type': 'application/json' }
107
- const body = { [generate()]: generate() }
108
- const json = JSON.stringify(body)
109
- const req = createRequest({ path, method, headers }, json)
110
-
111
- await use(req)
112
-
113
- expect(process).toHaveBeenCalledWith(expect.objectContaining({ path, method, headers, body }))
114
- })
115
- })
116
-
117
96
  describe('result', () => {
118
97
  it('should send status code 200 if the result has a value', async () => {
119
- const process = async (): Promise<OutgoingMessage> => ({ headers: {}, body: generate() })
120
98
  const req = createRequest()
121
99
 
122
- server.attach(process)
100
+ server.attach(async (): Promise<OutgoingMessage> => ({
101
+ headers: new Headers(), body: generate()
102
+ }))
123
103
  await use(req)
124
104
 
125
105
  expect(res.status).toHaveBeenCalledWith(200)
126
106
  })
127
107
 
128
108
  it('should send status code 204 if the result has no value', async () => {
129
- const process = async (): Promise<OutgoingMessage> => ({ headers: {} })
130
109
  const req = createRequest()
131
110
 
132
- server.attach(process)
111
+ server.attach(async (): Promise<OutgoingMessage> => ({ headers: new Headers() }))
133
112
  await use(req)
134
113
 
135
114
  expect(res.status).toHaveBeenCalledWith(204)
@@ -139,17 +118,16 @@ describe('result', () => {
139
118
  const body = { [generate()]: generate() }
140
119
  const json = JSON.stringify(body)
141
120
  const buf = Buffer.from(json)
142
- const process = async (): Promise<OutgoingMessage> => ({ headers: {}, body })
143
121
  const req = createRequest({ headers: { accept: 'application/json' } })
144
122
 
145
- server.attach(process)
123
+ server.attach(async (): Promise<OutgoingMessage> => ({ headers: new Headers(), body }))
146
124
  await use(req)
147
125
 
148
- expect(res.send).toHaveBeenCalledWith(buf)
126
+ expect(res.end).toHaveBeenCalledWith(buf)
149
127
  })
150
128
 
151
129
  it('should return 500 on exception', async () => {
152
- const process = async (): Promise<OutgoingMessage> => {
130
+ async function process (): Promise<OutgoingMessage> {
153
131
  throw new Error('Bad')
154
132
  }
155
133
 
@@ -170,7 +148,7 @@ describe('result', () => {
170
148
  const message = generate()
171
149
  const req = createRequest()
172
150
 
173
- const process = async (): Promise<OutgoingMessage> => {
151
+ async function process (): Promise<OutgoingMessage> {
174
152
  throw new Error(message)
175
153
  }
176
154
 
@@ -184,7 +162,7 @@ describe('result', () => {
184
162
  const req = createRequest()
185
163
  const message = generate()
186
164
 
187
- const process = async (): Promise<OutgoingMessage> => {
165
+ async function process (): Promise<OutgoingMessage> {
188
166
  throw new BadRequest(message)
189
167
  }
190
168
 
@@ -1,9 +1,14 @@
1
+ import fs from 'node:fs'
2
+ import os from 'node:os'
1
3
  import express from 'express'
2
4
  import cors from 'cors'
3
5
  import { Connector } from '@toa.io/core'
4
6
  import { promex } from '@toa.io/generic'
7
+ import Negotiator from 'negotiator'
5
8
  import { read, write, type IncomingMessage, type OutgoingMessage } from './messages'
6
9
  import { ClientError, Exception } from './exceptions'
10
+ import { formats, types } from './formats'
11
+ import type { Format } from './formats'
7
12
  import type * as http from 'node:http'
8
13
  import type { Express, Request, Response, NextFunction } from 'express'
9
14
 
@@ -19,8 +24,10 @@ export class Server extends Connector {
19
24
  this.debug = debug
20
25
  }
21
26
 
22
- public static create (options: Partial<Properties> = {}): Server {
23
- const properties: Properties = Object.assign({}, defaults(), options)
27
+ public static create (options?: Partial<Properties>): Server {
28
+ const properties: Properties = options === undefined
29
+ ? DEFAULTS
30
+ : { ...DEFAULTS, ...options }
24
31
 
25
32
  const app = express()
26
33
 
@@ -32,8 +39,8 @@ export class Server extends Connector {
32
39
  }
33
40
 
34
41
  public attach (process: Processing): void {
35
- this.app.use((request: Request, response: Response): void => {
36
- this.read(request)
42
+ this.app.use((request: any, response: Response) => {
43
+ this.extend(request)
37
44
  .then(process)
38
45
  .then(this.success(request, response))
39
46
  .catch(this.fail(request, response))
@@ -62,14 +69,16 @@ export class Server extends Connector {
62
69
  console.info('HTTP Server has been stopped.')
63
70
  }
64
71
 
65
- private async read (request: Request): Promise<IncomingMessage> {
66
- const { method, path, headers, query } = request
67
- const body = await read(request)
72
+ private async extend (request: IncomingMessage): Promise<IncomingMessage> {
73
+ const message = request as unknown as IncomingMessage
68
74
 
69
- return { method, path, headers, query, body }
75
+ message.encoder = negotiate(request)
76
+ message.parse = async <T> (): Promise<T> => await read(request)
77
+
78
+ return message
70
79
  }
71
80
 
72
- private success (request: Request, response: Response) {
81
+ private success (request: IncomingMessage, response: Response) {
73
82
  return (message: OutgoingMessage) => {
74
83
  let status = message.status
75
84
 
@@ -79,34 +88,35 @@ export class Server extends Connector {
79
88
  else if (message.body === undefined) status = 204
80
89
  else status = 200
81
90
 
82
- response
83
- .status(status)
84
- .set(message.headers)
91
+ response.status(status)
92
+ message.headers?.forEach((value, key) => response.set(key, value))
85
93
 
86
94
  if (message.body !== undefined && message.body !== null)
87
- write(request, response, message.body)
95
+ write(request, response, message)
88
96
  else
89
97
  response.end()
90
98
  }
91
99
  }
92
100
 
93
- private fail (request: Request, response: Response) {
94
- return (exception: Error) => {
95
- let status = 500
101
+ private fail (request: IncomingMessage, response: Response) {
102
+ return async (exception: Error) => {
103
+ if (!request.complete)
104
+ await adam(request)
96
105
 
97
- if (exception instanceof Exception)
98
- status = exception.status
99
-
100
- const outputAllowed = exception instanceof ClientError || this.debug
106
+ const status = exception instanceof Exception
107
+ ? exception.status
108
+ : 500
101
109
 
102
110
  response.status(status)
103
111
 
112
+ const outputAllowed = exception instanceof ClientError || this.debug
113
+
104
114
  if (outputAllowed) {
105
115
  const body = exception instanceof Exception
106
116
  ? exception.body
107
117
  : (exception.stack ?? exception.message)
108
118
 
109
- write(request, response, body)
119
+ write(request, response, { body })
110
120
  } else
111
121
  response.end()
112
122
  }
@@ -120,16 +130,33 @@ function supportedMethods (methods: Set<string>) {
120
130
  }
121
131
  }
122
132
 
133
+ function negotiate (request: Request): Format | null {
134
+ const negotiator = new Negotiator(request)
135
+ const mediaType = negotiator.mediaType(types)
136
+
137
+ return mediaType === undefined ? null : formats[mediaType]
138
+ }
139
+
140
+ // https://github.com/whatwg/fetch/issues/1254
141
+ async function adam (request: Request): Promise<void> {
142
+ const completed = promex()
143
+ const devnull = fs.createWriteStream(os.devNull)
144
+
145
+ request
146
+ .on('end', completed.callback)
147
+ .pipe(devnull)
148
+
149
+ return await completed
150
+ }
151
+
152
+ const DEFAULTS = {
153
+ methods: new Set<string>(['GET', 'OPTIONS', 'POST', 'PUT', 'PATCH', 'DELETE']),
154
+ debug: false
155
+ }
156
+
123
157
  interface Properties {
124
158
  methods: Set<string>
125
159
  debug: boolean
126
160
  }
127
161
 
128
- function defaults (): Properties {
129
- return {
130
- methods: new Set<string>(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']),
131
- debug: false
132
- }
133
- }
134
-
135
162
  export type Processing = (input: IncomingMessage) => Promise<OutgoingMessage>
@@ -1,5 +1,3 @@
1
- import { types } from './formats'
2
-
3
1
  export class Exception extends Error {
4
2
  public readonly status: number
5
3
  public readonly body?: any
@@ -50,21 +48,13 @@ export class MethodNotAllowed extends ClientError {
50
48
  }
51
49
  }
52
50
 
53
- class MediaTypeException extends ClientError {
54
- private static readonly message = 'Supported media types:\n- ' + types.join('\n- ')
55
-
56
- protected constructor (status: number) {
57
- super(status, MediaTypeException.message)
58
- }
59
- }
60
-
61
- export class NotAcceptable extends MediaTypeException {
51
+ export class NotAcceptable extends ClientError {
62
52
  public constructor () {
63
53
  super(406)
64
54
  }
65
55
  }
66
56
 
67
- export class UnsupportedMediaType extends MediaTypeException {
57
+ export class UnsupportedMediaType extends ClientError {
68
58
  public constructor () {
69
59
  super(415)
70
60
  }
@@ -5,15 +5,18 @@ import * as msgpack from './msgpack'
5
5
  import * as text from './text'
6
6
 
7
7
  export const formats: Record<string, Format> = {
8
- 'application/yaml': yaml,
9
- 'application/msgpack': msgpack,
10
- 'application/json': json,
11
- 'text/plain': text
8
+ [msgpack.type]: msgpack,
9
+ [yaml.type]: yaml,
10
+ [json.type]: json,
11
+ [text.type]: text
12
12
  }
13
13
 
14
14
  export const types = Object.keys(formats)
15
15
 
16
16
  export interface Format {
17
+ readonly type: string
18
+ readonly multipart: string
19
+
17
20
  encode: (value: any) => Buffer
18
21
  decode: (buffer: Buffer) => any
19
22
  }
@@ -1,5 +1,8 @@
1
1
  import { Buffer } from 'node:buffer'
2
2
 
3
+ export const type = 'application/json'
4
+ export const multipart = 'multipart/json'
5
+
3
6
  export function decode (buffer: Buffer): any {
4
7
  const text = buffer.toString()
5
8
 
@@ -1,6 +1,9 @@
1
1
  import { type Buffer } from 'node:buffer'
2
2
  import * as msgpack from 'msgpackr'
3
3
 
4
+ export const type = 'application/msgpack'
5
+ export const multipart = 'multipart/msgpack'
6
+
4
7
  export function decode (buffer: Buffer): any {
5
8
  return msgpack.decode(buffer)
6
9
  }
@@ -1,5 +1,8 @@
1
1
  import { Buffer } from 'node:buffer'
2
2
 
3
+ export const type = 'text/plain'
4
+ export const multipart = 'multipart/text'
5
+
3
6
  export function decode (buffer: Buffer): any {
4
7
  return buffer.toString()
5
8
  }
@@ -1,6 +1,9 @@
1
1
  import { Buffer } from 'node:buffer'
2
2
  import * as yaml from 'js-yaml'
3
3
 
4
+ export const type = 'application/yaml'
5
+ export const multipart = 'multipart/yaml'
6
+
4
7
  export function decode (buffer: Buffer): any {
5
8
  const text = buffer.toString()
6
9
 
@@ -1,9 +1,8 @@
1
- import { Buffer } from 'node:buffer'
2
1
  import { generate } from 'randomstring'
3
2
  import * as msgpack from 'msgpackr'
4
- import { type OutgoingMessage, read, write } from './messages'
5
- import { createRequest, res } from './Server.fixtures'
6
- import { BadRequest, NotAcceptable, UnsupportedMediaType } from './exceptions'
3
+ import { read } from './messages'
4
+ import { createRequest } from './Server.fixtures'
5
+ import { BadRequest, UnsupportedMediaType } from './exceptions'
7
6
 
8
7
  beforeEach(() => {
9
8
  jest.clearAllMocks()
@@ -69,48 +68,3 @@ describe('read', () => {
69
68
  await expect(read(request)).rejects.toThrow(BadRequest)
70
69
  })
71
70
  })
72
-
73
- describe('write', () => {
74
- it('should write encoded response', async () => {
75
- const value = { [generate()]: generate() }
76
- const json = JSON.stringify(value)
77
- const buf = Buffer.from(json)
78
- const headers = { accept: 'application/json' }
79
- const request = createRequest({ headers }, buf)
80
-
81
- write(request, res, value)
82
-
83
- expect(res.set).toHaveBeenCalledWith('content-type', 'application/json')
84
- expect(res.send).toHaveBeenCalledWith(buf)
85
- })
86
-
87
- it('should throw on unsupported response media type', async () => {
88
- const headers = { accept: 'wtf/' + generate() }
89
- const request = createRequest({ headers })
90
- const value = generate()
91
-
92
- expect(() => {
93
- write(request, res, value)
94
- }).toThrow(NotAcceptable)
95
- })
96
-
97
- it('should use application/yaml as default', async () => {
98
- const request = createRequest()
99
- const message: OutgoingMessage = { headers: {}, body: 'hello' }
100
-
101
- write(request, res, message)
102
-
103
- expect(res.set).toHaveBeenCalledWith('content-type', 'application/yaml')
104
- expect(res.send).toHaveBeenCalled()
105
- })
106
-
107
- it('should negotiate', async () => {
108
- const headers = { accept: 'text/html, application/*;q=0.2, image/jpeg;q=0.8' }
109
- const request = createRequest({ headers })
110
- const message: OutgoingMessage = { headers: {}, body: 'hello' }
111
-
112
- write(request, res, message)
113
-
114
- expect(res.set).toHaveBeenCalledWith('content-type', 'application/yaml')
115
- })
116
- })