@toa.io/extensions.exposition 0.20.0-alpha.0 → 0.20.0-alpha.2

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 (225) hide show
  1. package/components/context.toa.yaml +2 -2
  2. package/documentation/notes/sse.md +71 -0
  3. package/documentation/protocol.md +4 -1
  4. package/features/access.feature +48 -42
  5. package/features/annotation.feature +6 -4
  6. package/features/body.feature +24 -0
  7. package/features/directives.feature +2 -2
  8. package/features/dynamic.feature +11 -1
  9. package/features/errors.feature +14 -29
  10. package/features/identity.basic.feature +47 -6
  11. package/features/identity.tokens.feature +19 -16
  12. package/features/steps/Gateway.ts +26 -18
  13. package/features/steps/components/echo/manifest.toa.yaml +9 -0
  14. package/features/steps/components/echo/operations/affect.js +7 -0
  15. package/features/steps/components/echo/operations/compute.js +7 -0
  16. package/features/steps/components/greeter/manifest.toa.yaml +0 -4
  17. package/features/steps/components/greeter/operations/greet.js +1 -1
  18. package/features/steps/components/sequences/manifest.toa.yaml +10 -0
  19. package/features/steps/components/sequences/operations/numbers.js +7 -0
  20. package/features/steps/components/sequences/operations/tokens.js +16 -0
  21. package/features/streams.feature +26 -0
  22. package/package.json +7 -6
  23. package/readme.md +2 -0
  24. package/schemas/annotation.cos.yaml +5 -4
  25. package/schemas/method.cos.yaml +0 -1
  26. package/schemas/query.cos.yaml +1 -0
  27. package/source/Annotation.ts +1 -0
  28. package/source/Composition.ts +1 -2
  29. package/source/Endpoint.ts +5 -3
  30. package/source/Factory.ts +8 -10
  31. package/source/HTTP/Server.ts +1 -1
  32. package/source/HTTP/messages.ts +18 -6
  33. package/source/Mapping.ts +12 -9
  34. package/source/RTD/Node.ts +5 -5
  35. package/source/RTD/Route.ts +5 -4
  36. package/source/RTD/Tree.ts +2 -5
  37. package/source/RTD/syntax/parse.ts +1 -1
  38. package/source/Remotes.test.ts +2 -1
  39. package/source/Remotes.ts +2 -0
  40. package/source/deployment.ts +9 -2
  41. package/source/directives/dev/Family.ts +3 -1
  42. package/source/directives/dev/Throw.ts +14 -0
  43. package/source/manifest.test.ts +3 -1
  44. package/source/manifest.ts +22 -9
  45. package/transpiled/Annotation.d.ts +0 -6
  46. package/transpiled/Annotation.js +0 -3
  47. package/transpiled/Annotation.js.map +0 -1
  48. package/transpiled/Branch.d.ts +0 -7
  49. package/transpiled/Branch.js +0 -3
  50. package/transpiled/Branch.js.map +0 -1
  51. package/transpiled/Composition.d.ts +0 -14
  52. package/transpiled/Composition.js +0 -43
  53. package/transpiled/Composition.js.map +0 -1
  54. package/transpiled/Context.d.ts +0 -5
  55. package/transpiled/Context.js +0 -3
  56. package/transpiled/Context.js.map +0 -1
  57. package/transpiled/Directive.d.ts +0 -32
  58. package/transpiled/Directive.js +0 -76
  59. package/transpiled/Directive.js.map +0 -1
  60. package/transpiled/Endpoint.d.ts +0 -20
  61. package/transpiled/Endpoint.js +0 -44
  62. package/transpiled/Endpoint.js.map +0 -1
  63. package/transpiled/Factory.d.ts +0 -11
  64. package/transpiled/Factory.js +0 -67
  65. package/transpiled/Factory.js.map +0 -1
  66. package/transpiled/Gateway.d.ts +0 -19
  67. package/transpiled/Gateway.js +0 -90
  68. package/transpiled/Gateway.js.map +0 -1
  69. package/transpiled/HTTP/Server.d.ts +0 -22
  70. package/transpiled/HTTP/Server.fixtures.d.ts +0 -12
  71. package/transpiled/HTTP/Server.fixtures.js +0 -36
  72. package/transpiled/HTTP/Server.fixtures.js.map +0 -1
  73. package/transpiled/HTTP/Server.js +0 -111
  74. package/transpiled/HTTP/Server.js.map +0 -1
  75. package/transpiled/HTTP/exceptions.d.ts +0 -39
  76. package/transpiled/HTTP/exceptions.js +0 -78
  77. package/transpiled/HTTP/exceptions.js.map +0 -1
  78. package/transpiled/HTTP/formats/index.d.ts +0 -8
  79. package/transpiled/HTTP/formats/index.js +0 -38
  80. package/transpiled/HTTP/formats/index.js.map +0 -1
  81. package/transpiled/HTTP/formats/json.d.ts +0 -4
  82. package/transpiled/HTTP/formats/json.js +0 -15
  83. package/transpiled/HTTP/formats/json.js.map +0 -1
  84. package/transpiled/HTTP/formats/msgpack.d.ts +0 -4
  85. package/transpiled/HTTP/formats/msgpack.js +0 -36
  86. package/transpiled/HTTP/formats/msgpack.js.map +0 -1
  87. package/transpiled/HTTP/formats/text.d.ts +0 -4
  88. package/transpiled/HTTP/formats/text.js +0 -13
  89. package/transpiled/HTTP/formats/text.js.map +0 -1
  90. package/transpiled/HTTP/formats/yaml.d.ts +0 -4
  91. package/transpiled/HTTP/formats/yaml.js +0 -39
  92. package/transpiled/HTTP/formats/yaml.js.map +0 -1
  93. package/transpiled/HTTP/index.d.ts +0 -3
  94. package/transpiled/HTTP/index.js +0 -20
  95. package/transpiled/HTTP/index.js.map +0 -1
  96. package/transpiled/HTTP/messages.d.ts +0 -27
  97. package/transpiled/HTTP/messages.js +0 -49
  98. package/transpiled/HTTP/messages.js.map +0 -1
  99. package/transpiled/Mapping.d.ts +0 -8
  100. package/transpiled/Mapping.js +0 -35
  101. package/transpiled/Mapping.js.map +0 -1
  102. package/transpiled/Query.d.ts +0 -13
  103. package/transpiled/Query.js +0 -107
  104. package/transpiled/Query.js.map +0 -1
  105. package/transpiled/RTD/Context.d.ts +0 -11
  106. package/transpiled/RTD/Context.js +0 -3
  107. package/transpiled/RTD/Context.js.map +0 -1
  108. package/transpiled/RTD/Directives.d.ts +0 -7
  109. package/transpiled/RTD/Directives.js +0 -3
  110. package/transpiled/RTD/Directives.js.map +0 -1
  111. package/transpiled/RTD/Endpoint.d.ts +0 -9
  112. package/transpiled/RTD/Endpoint.js +0 -3
  113. package/transpiled/RTD/Endpoint.js.map +0 -1
  114. package/transpiled/RTD/Match.d.ts +0 -11
  115. package/transpiled/RTD/Match.js +0 -3
  116. package/transpiled/RTD/Match.js.map +0 -1
  117. package/transpiled/RTD/Method.d.ts +0 -9
  118. package/transpiled/RTD/Method.js +0 -16
  119. package/transpiled/RTD/Method.js.map +0 -1
  120. package/transpiled/RTD/Node.d.ts +0 -21
  121. package/transpiled/RTD/Node.js +0 -61
  122. package/transpiled/RTD/Node.js.map +0 -1
  123. package/transpiled/RTD/Route.d.ts +0 -14
  124. package/transpiled/RTD/Route.js +0 -48
  125. package/transpiled/RTD/Route.js.map +0 -1
  126. package/transpiled/RTD/Tree.d.ts +0 -14
  127. package/transpiled/RTD/Tree.js +0 -45
  128. package/transpiled/RTD/Tree.js.map +0 -1
  129. package/transpiled/RTD/factory.d.ts +0 -6
  130. package/transpiled/RTD/factory.js +0 -36
  131. package/transpiled/RTD/factory.js.map +0 -1
  132. package/transpiled/RTD/index.d.ts +0 -8
  133. package/transpiled/RTD/index.js +0 -38
  134. package/transpiled/RTD/index.js.map +0 -1
  135. package/transpiled/RTD/segment.d.ts +0 -8
  136. package/transpiled/RTD/segment.js +0 -23
  137. package/transpiled/RTD/segment.js.map +0 -1
  138. package/transpiled/RTD/syntax/index.d.ts +0 -2
  139. package/transpiled/RTD/syntax/index.js +0 -19
  140. package/transpiled/RTD/syntax/index.js.map +0 -1
  141. package/transpiled/RTD/syntax/parse.d.ts +0 -4
  142. package/transpiled/RTD/syntax/parse.js +0 -128
  143. package/transpiled/RTD/syntax/parse.js.map +0 -1
  144. package/transpiled/RTD/syntax/types.d.ts +0 -41
  145. package/transpiled/RTD/syntax/types.js +0 -5
  146. package/transpiled/RTD/syntax/types.js.map +0 -1
  147. package/transpiled/Remotes.d.ts +0 -7
  148. package/transpiled/Remotes.js +0 -19
  149. package/transpiled/Remotes.js.map +0 -1
  150. package/transpiled/Tenant.d.ts +0 -12
  151. package/transpiled/Tenant.js +0 -30
  152. package/transpiled/Tenant.js.map +0 -1
  153. package/transpiled/deployment.d.ts +0 -3
  154. package/transpiled/deployment.js +0 -61
  155. package/transpiled/deployment.js.map +0 -1
  156. package/transpiled/directives/auth/Anonymous.d.ts +0 -6
  157. package/transpiled/directives/auth/Anonymous.js +0 -17
  158. package/transpiled/directives/auth/Anonymous.js.map +0 -1
  159. package/transpiled/directives/auth/Echo.d.ts +0 -6
  160. package/transpiled/directives/auth/Echo.js +0 -13
  161. package/transpiled/directives/auth/Echo.js.map +0 -1
  162. package/transpiled/directives/auth/Family.d.ts +0 -20
  163. package/transpiled/directives/auth/Family.js +0 -125
  164. package/transpiled/directives/auth/Family.js.map +0 -1
  165. package/transpiled/directives/auth/Id.d.ts +0 -7
  166. package/transpiled/directives/auth/Id.js +0 -17
  167. package/transpiled/directives/auth/Id.js.map +0 -1
  168. package/transpiled/directives/auth/Incept.d.ts +0 -10
  169. package/transpiled/directives/auth/Incept.js +0 -59
  170. package/transpiled/directives/auth/Incept.js.map +0 -1
  171. package/transpiled/directives/auth/Role.d.ts +0 -11
  172. package/transpiled/directives/auth/Role.js +0 -44
  173. package/transpiled/directives/auth/Role.js.map +0 -1
  174. package/transpiled/directives/auth/Rule.d.ts +0 -9
  175. package/transpiled/directives/auth/Rule.js +0 -22
  176. package/transpiled/directives/auth/Rule.js.map +0 -1
  177. package/transpiled/directives/auth/Scheme.d.ts +0 -7
  178. package/transpiled/directives/auth/Scheme.js +0 -47
  179. package/transpiled/directives/auth/Scheme.js.map +0 -1
  180. package/transpiled/directives/auth/index.d.ts +0 -2
  181. package/transpiled/directives/auth/index.js +0 -7
  182. package/transpiled/directives/auth/index.js.map +0 -1
  183. package/transpiled/directives/auth/schemes.d.ts +0 -3
  184. package/transpiled/directives/auth/schemes.js +0 -9
  185. package/transpiled/directives/auth/schemes.js.map +0 -1
  186. package/transpiled/directives/auth/split.d.ts +0 -2
  187. package/transpiled/directives/auth/split.js +0 -38
  188. package/transpiled/directives/auth/split.js.map +0 -1
  189. package/transpiled/directives/auth/types.d.ts +0 -31
  190. package/transpiled/directives/auth/types.js +0 -3
  191. package/transpiled/directives/auth/types.js.map +0 -1
  192. package/transpiled/directives/dev/Family.d.ts +0 -10
  193. package/transpiled/directives/dev/Family.js +0 -25
  194. package/transpiled/directives/dev/Family.js.map +0 -1
  195. package/transpiled/directives/dev/Stub.d.ts +0 -7
  196. package/transpiled/directives/dev/Stub.js +0 -14
  197. package/transpiled/directives/dev/Stub.js.map +0 -1
  198. package/transpiled/directives/dev/index.d.ts +0 -2
  199. package/transpiled/directives/dev/index.js +0 -7
  200. package/transpiled/directives/dev/index.js.map +0 -1
  201. package/transpiled/directives/dev/types.d.ts +0 -4
  202. package/transpiled/directives/dev/types.js +0 -3
  203. package/transpiled/directives/dev/types.js.map +0 -1
  204. package/transpiled/directives/index.d.ts +0 -2
  205. package/transpiled/directives/index.js +0 -10
  206. package/transpiled/directives/index.js.map +0 -1
  207. package/transpiled/discovery.d.ts +0 -1
  208. package/transpiled/discovery.js +0 -3
  209. package/transpiled/discovery.js.map +0 -1
  210. package/transpiled/exceptions.d.ts +0 -2
  211. package/transpiled/exceptions.js +0 -39
  212. package/transpiled/exceptions.js.map +0 -1
  213. package/transpiled/index.d.ts +0 -5
  214. package/transpiled/index.js +0 -12
  215. package/transpiled/index.js.map +0 -1
  216. package/transpiled/manifest.d.ts +0 -3
  217. package/transpiled/manifest.js +0 -30
  218. package/transpiled/manifest.js.map +0 -1
  219. package/transpiled/root.d.ts +0 -2
  220. package/transpiled/root.js +0 -39
  221. package/transpiled/root.js.map +0 -1
  222. package/transpiled/schemas.d.ts +0 -3
  223. package/transpiled/schemas.js +0 -14
  224. package/transpiled/schemas.js.map +0 -1
  225. package/transpiled/tsconfig.tsbuildinfo +0 -1
@@ -0,0 +1,26 @@
1
+ Feature: Reply streams
2
+
3
+ Scenario: Getting a Reply stream
4
+ Given the `sequences` is running with the following manifest:
5
+ """yaml
6
+ exposition:
7
+ /:
8
+ POST: numbers
9
+ """
10
+ When the following request is received:
11
+ """
12
+ POST /sequences/ HTTP/1.1
13
+ content-type: text/plain
14
+ accept: text/plain
15
+
16
+ 3
17
+ """
18
+ Then the following reply is sent:
19
+ """
20
+ 201 Created
21
+ transfer-encoding: chunked
22
+
23
+ 0
24
+ 1
25
+ 2
26
+ """
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/extensions.exposition",
3
- "version": "0.20.0-alpha.0",
3
+ "version": "0.20.0-alpha.2",
4
4
  "description": "Toa Exposition",
5
5
  "author": "temich <tema.gurtovoy@gmail.com>",
6
6
  "homepage": "https://github.com/toa-io/toa#readme",
@@ -20,10 +20,11 @@
20
20
  "nopeable": "*"
21
21
  },
22
22
  "dependencies": {
23
- "@toa.io/core": "0.20.0-alpha.0",
24
- "@toa.io/generic": "0.20.0-alpha.0",
25
- "@toa.io/http": "0.20.0-alpha.0",
26
- "@toa.io/schemas": "0.20.0-alpha.0",
23
+ "@toa.io/core": "0.20.0-alpha.2",
24
+ "@toa.io/generic": "0.20.0-alpha.2",
25
+ "@toa.io/http": "0.20.0-alpha.2",
26
+ "@toa.io/schemas": "0.20.0-alpha.2",
27
+ "@toa.io/streams": "0.1.0-alpha.2",
27
28
  "bcryptjs": "2.4.3",
28
29
  "cors": "2.8.5",
29
30
  "express": "4.18.2",
@@ -46,5 +47,5 @@
46
47
  "@types/express": "4.17.17",
47
48
  "@types/negotiator": "0.6.1"
48
49
  },
49
- "gitHead": "d047190899218b5249901a01a2a4caec5b34cf09"
50
+ "gitHead": "d6e8c5bdae26e2beb66f4d7f37f71a3494fe9fcb"
50
51
  }
package/readme.md CHANGED
@@ -163,6 +163,7 @@ exposition:
163
163
  host: the.example.com
164
164
  host@staging: the.example.dev
165
165
  class: alb
166
+ debug@staging: true
166
167
  annotations:
167
168
  alb.ingress.kubernetes.io/scheme: internet-facing
168
169
  alb.ingress.kubernetes.io/target-type: ip
@@ -178,4 +179,5 @@ exposition:
178
179
  - [Resource Tree Definition](documentation/tree.md)
179
180
  - [Identity authentication](documentation/identity.md)
180
181
  - [Access authorization](documentation/access.md)
182
+ - [Components and resources](documentation/components.md)
181
183
  - [Features](features)
@@ -1,4 +1,5 @@
1
- host?: string
2
- class?: string
3
- annotations?: <string>
4
- /?: ref:node
1
+ host: string
2
+ class: string
3
+ annotations: <string>
4
+ debug: boolean
5
+ /: ~
@@ -5,5 +5,4 @@ mapping:
5
5
  endpoint: string
6
6
  query:
7
7
  $ref: query
8
- default: { }
9
8
  directives: [ref:directive]
@@ -1,3 +1,4 @@
1
+ _: true
1
2
  id?: string
2
3
  criteria?: string
3
4
  sort?: string
@@ -2,5 +2,6 @@ export interface Annotation {
2
2
  host?: string
3
3
  class?: string
4
4
  annotations?: Record<string, string>
5
+ debug: boolean
5
6
  '/'?: object // parsed and validated by RTD.syntax.parse
6
7
  }
@@ -1,6 +1,5 @@
1
- import { readdirSync } from 'node:fs'
1
+ import { readdirSync, type Dirent } from 'node:fs'
2
2
  import { resolve } from 'node:path'
3
- import { type Dirent } from 'fs'
4
3
  import { Connector } from '@toa.io/core'
5
4
  import { type Bootloader } from './Factory'
6
5
 
@@ -28,11 +28,11 @@ export class Endpoint implements RTD.Endpoint<Endpoint> {
28
28
  public async close (): Promise<void> {
29
29
  this.remote ??= await this.discovery
30
30
 
31
- await this.remote.disconnect()
31
+ await this.remote.disconnect(INTERRUPT)
32
32
  }
33
33
  }
34
34
 
35
- export class EndpointFactory implements RTD.EndpointsFactory<Endpoint> {
35
+ export class EndpointsFactory implements RTD.EndpointsFactory<Endpoint> {
36
36
  private readonly remotes: Remotes
37
37
 
38
38
  public constructor (remotes: Remotes) {
@@ -43,7 +43,7 @@ export class EndpointFactory implements RTD.EndpointsFactory<Endpoint> {
43
43
  if (method.mapping === undefined)
44
44
  throw new Error('Cannot create Endpoint without mapping.')
45
45
 
46
- const mapping = Mapping.create(method.verb, method.mapping.query)
46
+ const mapping = Mapping.create(method.mapping.query)
47
47
  const namespace = method.mapping.namespace ?? context.extension?.namespace
48
48
  const component = method.mapping.component ?? context.extension?.component
49
49
 
@@ -55,3 +55,5 @@ export class EndpointFactory implements RTD.EndpointsFactory<Endpoint> {
55
55
  return new Endpoint(method.mapping.endpoint, mapping, discovery)
56
56
  }
57
57
  }
58
+
59
+ const INTERRUPT = true
package/source/Factory.ts CHANGED
@@ -3,7 +3,7 @@ import { Gateway } from './Gateway'
3
3
  import { Remotes } from './Remotes'
4
4
  import { Tree, syntax } from './RTD'
5
5
  import { Server } from './HTTP'
6
- import { type Endpoint, EndpointFactory } from './Endpoint'
6
+ import { type Endpoint, EndpointsFactory } from './Endpoint'
7
7
  import * as directives from './directives'
8
8
  import { type Directives, DirectivesFactory, type Family } from './Directive'
9
9
  import { Composition } from './Composition'
@@ -19,28 +19,26 @@ export class Factory implements extensions.Factory {
19
19
  this.families = directives.families
20
20
  }
21
21
 
22
- public tenant (locator: Locator, manifest: syntax.Node): Connector {
22
+ public tenant (locator: Locator, node: syntax.Node): Connector {
23
23
  const broadcast = this.boot.bindings.broadcast(CHANNEL, locator.id)
24
24
 
25
- return new Tenant(broadcast, locator, manifest)
25
+ return new Tenant(broadcast, locator, node)
26
26
  }
27
27
 
28
28
  public service (): Connector | null {
29
- return this.gateway()
30
- }
31
-
32
- private gateway (): Gateway {
29
+ const debug = process.env.TOA_EXPOSITION_DEBUG === '1'
33
30
  const broadcast = this.boot.bindings.broadcast(CHANNEL)
34
- const server = Server.create({ methods: syntax.verbs })
31
+ const server = Server.create({ methods: syntax.verbs, debug })
35
32
  const remotes = new Remotes(this.boot)
36
- const methods = new EndpointFactory(remotes)
37
- const directives = new DirectivesFactory(this.families, remotes)
38
33
  const node = root.resolve()
34
+ const methods = new EndpointsFactory(remotes)
35
+ const directives = new DirectivesFactory(this.families, remotes)
39
36
  const tree = new Tree<Endpoint, Directives>(node, methods, directives)
40
37
 
41
38
  const composition = new Composition(this.boot)
42
39
  const gateway = new Gateway(broadcast, server, tree)
43
40
 
41
+ gateway.depends(remotes)
44
42
  gateway.depends(composition)
45
43
 
46
44
  return gateway
@@ -128,7 +128,7 @@ interface Properties {
128
128
  function defaults (): Properties {
129
129
  return {
130
130
  methods: new Set<string>(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']),
131
- debug: process.env.TOA_DEV === '1'
131
+ debug: false
132
132
  }
133
133
  }
134
134
 
@@ -1,14 +1,15 @@
1
1
  import { type IncomingHttpHeaders, type OutgoingHttpHeaders } from 'node:http'
2
+ import { Readable } from 'node:stream'
2
3
  import { type Request, type Response } from 'express'
3
4
  import Negotiator from 'negotiator'
4
5
  import { buffer } from '@toa.io/generic'
6
+ import { map } from '@toa.io/streams'
5
7
  import { formats, types } from './formats'
6
8
  import { BadRequest, NotAcceptable, UnsupportedMediaType } from './exceptions'
7
9
 
8
10
  export function write (request: Request, response: Response, body: any): void {
9
- const buf = format(body, request, response)
10
-
11
- response.send(buf)
11
+ if (body instanceof Readable) void pipe(body, request, response)
12
+ else send(body, request, response)
12
13
  }
13
14
 
14
15
  export async function read (request: Request): Promise<any> {
@@ -29,8 +30,12 @@ export async function read (request: Request): Promise<any> {
29
30
  }
30
31
  }
31
32
 
32
- function format (body: any, request: Request, response: Response): Buffer | undefined {
33
- if (body === undefined || body?.length === 0) return
33
+ function send (body: any, request: Request, response: Response): void {
34
+ if (body === undefined || body?.length === 0) {
35
+ response.end()
36
+
37
+ return
38
+ }
34
39
 
35
40
  const type = negotiate(request)
36
41
  const format = formats[type]
@@ -38,8 +43,15 @@ function format (body: any, request: Request, response: Response): Buffer | unde
38
43
 
39
44
  // content-length and etag are set by Express
40
45
  response.set('content-type', type)
46
+ response.send(buf)
47
+ }
41
48
 
42
- return buf
49
+ async function pipe (stream: Readable, request: Request, response: Response): Promise<void> {
50
+ const type = negotiate(request)
51
+ const format = formats[type]
52
+
53
+ response.set('content-type', type)
54
+ map(stream, format.encode).pipe(response)
43
55
  }
44
56
 
45
57
  function negotiate (request: Request): string {
package/source/Mapping.ts CHANGED
@@ -5,13 +5,10 @@ import type * as syntax from './RTD/syntax'
5
5
  import type * as core from '@toa.io/core'
6
6
 
7
7
  export abstract class Mapping {
8
- public static create (verb: string, query?: syntax.Query): Mapping {
9
- if (verb === 'POST')
8
+ public static create (query?: syntax.Query): Mapping {
9
+ if (query === undefined || query === null)
10
10
  return new InputMapping()
11
11
 
12
- if (query === undefined)
13
- throw new Error(`Query constraints must be defined for ${verb}`)
14
-
15
12
  const q = new Query(query)
16
13
 
17
14
  return new QueryableMapping(q)
@@ -37,12 +34,18 @@ class QueryableMapping extends Mapping {
37
34
  }
38
35
 
39
36
  class InputMapping extends Mapping {
40
- public fit (body: any, _: unknown, parameters: Parameter[]): core.Request {
41
- const input = { ...body }
37
+ public fit (input: any, _: unknown, parameters: Parameter[]): core.Request {
38
+ if (input === undefined && parameters.length > 0)
39
+ input = {}
42
40
 
43
- for (const parameter of parameters)
44
- input[parameter.name] = parameter.value
41
+ if (typeof input === 'object')
42
+ this.assign(input, parameters)
45
43
 
46
44
  return { input }
47
45
  }
46
+
47
+ private assign (input: Record<string, any>, parameters: Parameter[]): void {
48
+ for (const parameter of parameters)
49
+ input[parameter.name] = parameter.value
50
+ }
48
51
  }
@@ -1,6 +1,6 @@
1
1
  import { type Route } from './Route'
2
2
  import { type Methods } from './Method'
3
- import { type Parameter } from './Match'
3
+ import { type Match, type Parameter } from './Match'
4
4
  import { type Directives } from './Directives'
5
5
  import { type Endpoint } from './Endpoint'
6
6
 
@@ -23,12 +23,12 @@ export class Node<
23
23
  this.sort()
24
24
  }
25
25
 
26
- public match (fragments: string[], parameters: Parameter[]): Node<TEndpoint, TDirectives> | null {
26
+ public match (fragments: string[], parameters: Parameter[] = []): Match | null {
27
27
  for (const route of this.routes) {
28
- const node = route.match(fragments, parameters)
28
+ const match = route.match(fragments, parameters)
29
29
 
30
- if (node !== null)
31
- return node
30
+ if (match !== null)
31
+ return match
32
32
  }
33
33
 
34
34
  return null
@@ -1,6 +1,6 @@
1
1
  import { type Node } from './Node'
2
2
  import { type Segment } from './segment'
3
- import { type Parameter } from './Match'
3
+ import { type Match, type Parameter } from './Match'
4
4
 
5
5
  export class Route {
6
6
  public readonly root: boolean
@@ -18,7 +18,7 @@ export class Route {
18
18
  this.variables++
19
19
  }
20
20
 
21
- public match (fragments: string[], parameters: Parameter[]): Node | null {
21
+ public match (fragments: string[], parameters: Parameter[]): Match | null {
22
22
  for (let i = 0; i < this.segments.length; i++) {
23
23
  const segment = this.segments[i]
24
24
 
@@ -31,7 +31,7 @@ export class Route {
31
31
 
32
32
  const exact = this.segments.length === fragments.length
33
33
 
34
- if (exact && !this.node.intermediate) return this.node
34
+ if (exact && !this.node.intermediate) return { node: this.node, parameters }
35
35
  else return this.matchNested(fragments, parameters)
36
36
  }
37
37
 
@@ -50,8 +50,9 @@ export class Route {
50
50
  this.node.merge(route.node)
51
51
  }
52
52
 
53
- private matchNested (fragments: string[], parameters: Parameter[]): Node | null {
53
+ private matchNested (fragments: string[], parameters: Parameter[]): Match | null {
54
54
  fragments = fragments.slice(this.segments.length)
55
+ parameters = parameters.slice()
55
56
 
56
57
  return this.node.match(fragments, parameters)
57
58
  }
@@ -1,7 +1,7 @@
1
1
  import { type Node } from './Node'
2
2
  import { createNode } from './factory'
3
3
  import { fragment } from './segment'
4
- import { type Match, type Parameter } from './Match'
4
+ import { type Match } from './Match'
5
5
  import { type Context } from './Context'
6
6
  import { type Directives, type DirectivesFactory } from './Directives'
7
7
  import { type Endpoint, type EndpointsFactory } from './Endpoint'
@@ -26,11 +26,8 @@ export class Tree<
26
26
 
27
27
  public match (path: string): Match<TEndpoint, TDirectives> | null {
28
28
  const fragments = fragment(path)
29
- const parameters: Parameter[] = []
30
- const node = this.trunk.match(fragments, parameters)
31
29
 
32
- if (node === null) return null
33
- else return { node, parameters }
30
+ return this.trunk.match(fragments)
34
31
  }
35
32
 
36
33
  public merge (node: syntax.Node, extension: any): void {
@@ -101,7 +101,7 @@ function parseEndpoint (mapping: Mapping): void {
101
101
  function parseQuery (mapping: any): void {
102
102
  const query = mapping.query
103
103
 
104
- if (query === undefined)
104
+ if (query === undefined || query === null)
105
105
  return
106
106
 
107
107
  if (typeof query.limit === 'number')
@@ -10,7 +10,8 @@ jest.mock('@toa.io/boot', () => ({
10
10
 
11
11
  const boot = {
12
12
  remote: jest.fn(async (..._) => ({
13
- link: jest.fn((connector: Connector) => undefined)
13
+ connect: jest.fn(() => undefined),
14
+ link: jest.fn(() => undefined)
14
15
  }))
15
16
  } as unknown as jest.MockedObjectDeep<Bootloader>
16
17
 
package/source/Remotes.ts CHANGED
@@ -15,6 +15,8 @@ export class Remotes extends Connector {
15
15
 
16
16
  this.depends(remote)
17
17
 
18
+ await remote.connect()
19
+
18
20
  return remote
19
21
  }
20
22
  }
@@ -27,14 +27,21 @@ export function deployment (_: unknown, annotation: Annotation | undefined): Dep
27
27
  }
28
28
 
29
29
  if (annotation?.['/'] !== undefined) {
30
- annotation['/'] = parse(annotation['/'], shortcuts)
30
+ const node = { '/': annotation['/'] }
31
+ const tree = parse(node, shortcuts)
31
32
 
32
33
  service.variables.push({
33
34
  name: 'TOA_EXPOSITION',
34
- value: encode(annotation['/'])
35
+ value: encode(tree)
35
36
  })
36
37
  }
37
38
 
39
+ if (annotation?.debug === true)
40
+ service.variables.push({
41
+ name: 'TOA_EXPOSITION_DEBUG',
42
+ value: '1'
43
+ })
44
+
38
45
  if (annotation !== undefined)
39
46
  schemas.annotaion.validate(annotation)
40
47
 
@@ -1,5 +1,6 @@
1
1
  import { type Input, type Output, type Family } from '../../Directive'
2
2
  import { Stub } from './Stub'
3
+ import { Throw } from './Throw'
3
4
  import { type Directive } from './types'
4
5
 
5
6
  class Development implements Family<Directive> {
@@ -28,7 +29,8 @@ class Development implements Family<Directive> {
28
29
  }
29
30
 
30
31
  const constructors: Record<string, new (value: any) => Directive> = {
31
- stub: Stub
32
+ stub: Stub,
33
+ throw: Throw
32
34
  }
33
35
 
34
36
  export = new Development()
@@ -0,0 +1,14 @@
1
+ import { type Output } from '../../Directive'
2
+ import { type Directive } from './types'
3
+
4
+ export class Throw implements Directive {
5
+ private readonly message: any
6
+
7
+ public constructor (message: any) {
8
+ this.message = message
9
+ }
10
+
11
+ public apply (): Output {
12
+ throw new Error(this.message)
13
+ }
14
+ }
@@ -7,8 +7,10 @@ const namespace = 'ns' + generate()
7
7
 
8
8
  let mf: Manifest
9
9
 
10
+ const operations = { observe: {} }
11
+
10
12
  beforeEach(() => {
11
- mf = { namespace, name } as unknown as Manifest
13
+ mf = { namespace, name, operations } as unknown as Manifest
12
14
  })
13
15
 
14
16
  const declaration = {
@@ -1,5 +1,6 @@
1
- import { parse, type Node } from './RTD/syntax'
1
+ import { parse, type Node, type Method, type Query } from './RTD/syntax'
2
2
  import { shortcuts } from './Directive'
3
+ import * as schemas from './schemas'
3
4
  import type { Manifest } from '@toa.io/norm'
4
5
 
5
6
  export function manifest (declaration: object, manifest: Manifest): Node {
@@ -12,6 +13,8 @@ export function manifest (declaration: object, manifest: Manifest): Node {
12
13
 
13
14
  concretize(node, manifest)
14
15
 
16
+ schemas.node.validate(node)
17
+
15
18
  return node
16
19
  }
17
20
 
@@ -21,15 +24,25 @@ function wrap (segment: string, declaration: object): object {
21
24
 
22
25
  function concretize (node: Node, manifest: Manifest): void {
23
26
  for (const route of node.routes) {
24
- for (const method of route.node.methods) {
25
- // eslint-disable-next-line max-depth
26
- if (method.mapping === undefined)
27
- continue
28
-
29
- method.mapping.namespace = manifest.namespace
30
- method.mapping.component = manifest.name
31
- }
27
+ for (const method of route.node.methods)
28
+ concretizeMethod(method, manifest)
32
29
 
33
30
  concretize(route.node, manifest)
34
31
  }
35
32
  }
33
+
34
+ function concretizeMethod (method: Method, manifest: Manifest): void {
35
+ if (method.mapping === undefined)
36
+ return
37
+
38
+ const operation = manifest.operations[method.mapping.endpoint]
39
+
40
+ if (operation === undefined)
41
+ throw new Error(`Operation '${method.mapping.endpoint}' not found`)
42
+
43
+ if (method.mapping.query === undefined && operation.query !== false)
44
+ method.mapping.query = {} as unknown as Query // schema will substitute default values
45
+
46
+ method.mapping.namespace = manifest.namespace
47
+ method.mapping.component = manifest.name
48
+ }
@@ -1,6 +0,0 @@
1
- export interface Annotation {
2
- host?: string;
3
- class?: string;
4
- annotations?: Record<string, string>;
5
- '/'?: object;
6
- }
@@ -1,3 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=Annotation.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Annotation.js","sourceRoot":"","sources":["../source/Annotation.ts"],"names":[],"mappings":""}
@@ -1,7 +0,0 @@
1
- import type * as RTD from './RTD/syntax';
2
- export interface Branch {
3
- namespace: string;
4
- component: string;
5
- isolated: boolean;
6
- node: RTD.Node;
7
- }
@@ -1,3 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=Branch.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Branch.js","sourceRoot":"","sources":["../source/Branch.ts"],"names":[],"mappings":""}
@@ -1,14 +0,0 @@
1
- import { Connector } from '@toa.io/core';
2
- import { type Bootloader } from './Factory';
3
- export declare class Composition extends Connector {
4
- private readonly boot;
5
- constructor(boot: Bootloader);
6
- protected open(): Promise<void>;
7
- protected dispose(): void;
8
- }
9
- export declare function components(): Components;
10
- interface Components {
11
- labels: string[];
12
- paths: string[];
13
- }
14
- export {};
@@ -1,43 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.components = exports.Composition = void 0;
4
- const node_fs_1 = require("node:fs");
5
- const node_path_1 = require("node:path");
6
- const core_1 = require("@toa.io/core");
7
- class Composition extends core_1.Connector {
8
- boot;
9
- constructor(boot) {
10
- super();
11
- this.boot = boot;
12
- }
13
- async open() {
14
- const paths = find();
15
- const composition = await this.boot.composition(paths);
16
- await composition.connect();
17
- this.depends(composition);
18
- console.info('Composition complete.');
19
- }
20
- dispose() {
21
- console.info('Composition shutdown complete.');
22
- }
23
- }
24
- exports.Composition = Composition;
25
- function find() {
26
- return entries().map((entry) => (0, node_path_1.resolve)(ROOT, entry.name));
27
- }
28
- function entries() {
29
- const entries = (0, node_fs_1.readdirSync)(ROOT, { withFileTypes: true });
30
- return entries.filter((entry) => entry.isDirectory());
31
- }
32
- function components() {
33
- const labels = [];
34
- const paths = [];
35
- for (const entry of entries()) {
36
- labels.push(entry.name.replace('.', '-'));
37
- paths.push((0, node_path_1.resolve)(ROOT, entry.name));
38
- }
39
- return { labels, paths };
40
- }
41
- exports.components = components;
42
- const ROOT = (0, node_path_1.resolve)(__dirname, '../components/');
43
- //# sourceMappingURL=Composition.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Composition.js","sourceRoot":"","sources":["../source/Composition.ts"],"names":[],"mappings":";;;AAAA,qCAAqC;AACrC,yCAAmC;AAEnC,uCAAwC;AAGxC,MAAa,WAAY,SAAQ,gBAAS;IACvB,IAAI,CAAY;IAEjC,YAAoB,IAAgB;QAClC,KAAK,EAAE,CAAA;QACP,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IAClB,CAAC;IAEkB,KAAK,CAAC,IAAI;QAC3B,MAAM,KAAK,GAAG,IAAI,EAAE,CAAA;QACpB,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAA;QAEtD,MAAM,WAAW,CAAC,OAAO,EAAE,CAAA;QAE3B,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;QAEzB,OAAO,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAA;IACvC,CAAC;IAEkB,OAAO;QACxB,OAAO,CAAC,IAAI,CAAC,gCAAgC,CAAC,CAAA;IAChD,CAAC;CACF;AAtBD,kCAsBC;AAED,SAAS,IAAI;IACX,OAAO,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAA,mBAAO,EAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;AAC5D,CAAC;AAED,SAAS,OAAO;IACd,MAAM,OAAO,GAAG,IAAA,qBAAW,EAAC,IAAI,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAA;IAE1D,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAA;AACvD,CAAC;AAED,SAAgB,UAAU;IACxB,MAAM,MAAM,GAAa,EAAE,CAAA;IAC3B,MAAM,KAAK,GAAa,EAAE,CAAA;IAE1B,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,EAAE;QAC7B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAA;QACzC,KAAK,CAAC,IAAI,CAAC,IAAA,mBAAO,EAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAA;KACtC;IAED,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;AAC1B,CAAC;AAVD,gCAUC;AAOD,MAAM,IAAI,GAAG,IAAA,mBAAO,EAAC,SAAS,EAAE,gBAAgB,CAAC,CAAA"}
@@ -1,5 +0,0 @@
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
- export type Context = RTD.Context<Endpoint, Directives, Branch>;
@@ -1,3 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=Context.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"Context.js","sourceRoot":"","sources":["../source/Context.ts"],"names":[],"mappings":""}