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

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 (187) hide show
  1. package/components/identity.bans/manifest.toa.yaml +1 -0
  2. package/components/identity.basic/manifest.toa.yaml +1 -0
  3. package/components/identity.basic/operations/tsconfig.tsbuildinfo +1 -1
  4. package/components/identity.federation/manifest.toa.yaml +1 -0
  5. package/components/identity.federation/operations/tsconfig.tsbuildinfo +1 -1
  6. package/components/identity.roles/manifest.toa.yaml +1 -0
  7. package/components/identity.roles/operations/tsconfig.tsbuildinfo +1 -1
  8. package/components/identity.tokens/manifest.toa.yaml +1 -0
  9. package/components/identity.tokens/operations/tsconfig.tsbuildinfo +1 -1
  10. package/components/octets.storage/manifest.toa.yaml +1 -0
  11. package/components/octets.storage/operations/store.js +1 -1
  12. package/documentation/components.md +5 -5
  13. package/documentation/query.md +45 -2
  14. package/features/body.feature +1 -1
  15. package/features/cors.feature +2 -2
  16. package/features/errors.feature +1 -1
  17. package/features/etag.feature +96 -0
  18. package/features/octets.entries.feature +1 -1
  19. package/features/steps/Gateway.ts +3 -0
  20. package/features/steps/components/echo/manifest.toa.yaml +1 -0
  21. package/features/steps/components/greeter/manifest.toa.yaml +1 -0
  22. package/features/steps/components/octets.tester/manifest.toa.yaml +1 -0
  23. package/features/steps/components/pots/manifest.toa.yaml +10 -3
  24. package/features/steps/components/sequences/manifest.toa.yaml +1 -0
  25. package/features/timing.feature +43 -0
  26. package/package.json +7 -10
  27. package/readme.md +7 -6
  28. package/schemas/annotation.cos.yaml +1 -0
  29. package/schemas/querystring.cos.yaml +1 -0
  30. package/source/Annotation.ts +1 -0
  31. package/source/Context.ts +6 -4
  32. package/source/Directive.test.ts +7 -7
  33. package/source/Directive.ts +16 -42
  34. package/source/Endpoint.ts +55 -6
  35. package/source/Factory.ts +17 -7
  36. package/source/Gateway.ts +38 -50
  37. package/source/HTTP/Context.ts +67 -0
  38. package/source/HTTP/Server.test.ts +1 -1
  39. package/source/HTTP/Server.ts +60 -95
  40. package/source/HTTP/Timing.ts +40 -0
  41. package/source/HTTP/index.ts +1 -0
  42. package/source/HTTP/messages.test.ts +27 -8
  43. package/source/HTTP/messages.ts +32 -48
  44. package/source/Mapping.ts +7 -8
  45. package/source/RTD/Context.ts +7 -10
  46. package/source/RTD/Directives.ts +28 -4
  47. package/source/RTD/Endpoint.ts +6 -4
  48. package/source/RTD/Match.ts +2 -7
  49. package/source/RTD/Method.ts +7 -13
  50. package/source/RTD/Node.ts +13 -14
  51. package/source/RTD/Tree.ts +17 -16
  52. package/source/RTD/factory.ts +2 -5
  53. package/source/deployment.ts +6 -0
  54. package/source/directives/auth/Anonymous.ts +3 -2
  55. package/source/directives/auth/Authorization.ts +7 -6
  56. package/source/directives/auth/Incept.ts +11 -6
  57. package/source/directives/auth/Role.ts +5 -3
  58. package/source/directives/auth/Scheme.ts +2 -2
  59. package/source/directives/cache/Cache.ts +4 -4
  60. package/source/directives/cache/Control.ts +5 -5
  61. package/source/directives/cache/types.ts +1 -1
  62. package/source/directives/cors/CORS.ts +18 -10
  63. package/source/directives/dev/Development.ts +3 -3
  64. package/source/directives/index.ts +2 -2
  65. package/source/directives/octets/Context.ts +1 -1
  66. package/source/directives/octets/Delete.ts +19 -9
  67. package/source/directives/octets/Fetch.ts +29 -14
  68. package/source/directives/octets/List.ts +14 -6
  69. package/source/directives/octets/Octets.ts +6 -5
  70. package/source/directives/octets/Permute.ts +12 -6
  71. package/source/directives/octets/Store.ts +25 -17
  72. package/source/directives/octets/Workflow.ts +3 -3
  73. package/source/directives/octets/workflows/Workflow.ts +2 -2
  74. package/source/directives/vary/Vary.ts +3 -3
  75. package/source/directives/vary/embeddings/Header.ts +2 -2
  76. package/source/directives/vary/embeddings/Language.ts +2 -2
  77. package/source/io.ts +2 -2
  78. package/transpiled/Annotation.d.ts +1 -0
  79. package/transpiled/Context.d.ts +6 -4
  80. package/transpiled/Directive.d.ts +8 -21
  81. package/transpiled/Directive.js +8 -11
  82. package/transpiled/Directive.js.map +1 -1
  83. package/transpiled/Endpoint.d.ts +7 -5
  84. package/transpiled/Endpoint.js +60 -2
  85. package/transpiled/Endpoint.js.map +1 -1
  86. package/transpiled/Factory.js +8 -2
  87. package/transpiled/Factory.js.map +1 -1
  88. package/transpiled/Gateway.d.ts +4 -8
  89. package/transpiled/Gateway.js +22 -32
  90. package/transpiled/Gateway.js.map +1 -1
  91. package/transpiled/HTTP/Context.d.ts +24 -0
  92. package/transpiled/HTTP/Context.js +47 -0
  93. package/transpiled/HTTP/Context.js.map +1 -0
  94. package/transpiled/HTTP/Server.d.ts +8 -7
  95. package/transpiled/HTTP/Server.js +68 -76
  96. package/transpiled/HTTP/Server.js.map +1 -1
  97. package/transpiled/HTTP/Timing.d.ts +10 -0
  98. package/transpiled/HTTP/Timing.js +29 -0
  99. package/transpiled/HTTP/Timing.js.map +1 -0
  100. package/transpiled/HTTP/index.d.ts +1 -0
  101. package/transpiled/HTTP/index.js +1 -0
  102. package/transpiled/HTTP/index.js.map +1 -1
  103. package/transpiled/HTTP/messages.d.ts +7 -21
  104. package/transpiled/HTTP/messages.js +24 -26
  105. package/transpiled/HTTP/messages.js.map +1 -1
  106. package/transpiled/Mapping.js +7 -7
  107. package/transpiled/Mapping.js.map +1 -1
  108. package/transpiled/RTD/Context.d.ts +7 -6
  109. package/transpiled/RTD/Directives.d.ts +19 -4
  110. package/transpiled/RTD/Endpoint.d.ts +6 -4
  111. package/transpiled/RTD/Match.d.ts +2 -4
  112. package/transpiled/RTD/Method.d.ts +7 -7
  113. package/transpiled/RTD/Method.js.map +1 -1
  114. package/transpiled/RTD/Node.d.ts +4 -6
  115. package/transpiled/RTD/Node.js +2 -1
  116. package/transpiled/RTD/Node.js.map +1 -1
  117. package/transpiled/RTD/Tree.d.ts +6 -6
  118. package/transpiled/RTD/Tree.js +4 -1
  119. package/transpiled/RTD/Tree.js.map +1 -1
  120. package/transpiled/RTD/factory.d.ts +2 -4
  121. package/transpiled/RTD/factory.js.map +1 -1
  122. package/transpiled/deployment.js +5 -0
  123. package/transpiled/deployment.js.map +1 -1
  124. package/transpiled/directives/auth/Anonymous.js +3 -4
  125. package/transpiled/directives/auth/Anonymous.js.map +1 -1
  126. package/transpiled/directives/auth/Authorization.d.ts +2 -3
  127. package/transpiled/directives/auth/Authorization.js +1 -1
  128. package/transpiled/directives/auth/Authorization.js.map +1 -1
  129. package/transpiled/directives/auth/Incept.d.ts +1 -1
  130. package/transpiled/directives/auth/Incept.js +11 -6
  131. package/transpiled/directives/auth/Incept.js.map +1 -1
  132. package/transpiled/directives/auth/Role.js +5 -3
  133. package/transpiled/directives/auth/Role.js.map +1 -1
  134. package/transpiled/directives/auth/Scheme.js +2 -2
  135. package/transpiled/directives/auth/Scheme.js.map +1 -1
  136. package/transpiled/directives/cache/Cache.d.ts +3 -3
  137. package/transpiled/directives/cache/Cache.js +2 -2
  138. package/transpiled/directives/cache/Cache.js.map +1 -1
  139. package/transpiled/directives/cache/Control.d.ts +3 -3
  140. package/transpiled/directives/cache/Control.js +3 -3
  141. package/transpiled/directives/cache/Control.js.map +1 -1
  142. package/transpiled/directives/cache/types.d.ts +1 -1
  143. package/transpiled/directives/cors/CORS.d.ts +2 -3
  144. package/transpiled/directives/cors/CORS.js +17 -10
  145. package/transpiled/directives/cors/CORS.js.map +1 -1
  146. package/transpiled/directives/dev/Development.d.ts +3 -3
  147. package/transpiled/directives/dev/Development.js.map +1 -1
  148. package/transpiled/directives/index.d.ts +2 -2
  149. package/transpiled/directives/index.js.map +1 -1
  150. package/transpiled/directives/octets/Context.d.ts +1 -1
  151. package/transpiled/directives/octets/Context.js.map +1 -1
  152. package/transpiled/directives/octets/Delete.d.ts +1 -1
  153. package/transpiled/directives/octets/Delete.js +19 -9
  154. package/transpiled/directives/octets/Delete.js.map +1 -1
  155. package/transpiled/directives/octets/Fetch.d.ts +1 -1
  156. package/transpiled/directives/octets/Fetch.js +28 -14
  157. package/transpiled/directives/octets/Fetch.js.map +1 -1
  158. package/transpiled/directives/octets/List.d.ts +1 -1
  159. package/transpiled/directives/octets/List.js +13 -6
  160. package/transpiled/directives/octets/List.js.map +1 -1
  161. package/transpiled/directives/octets/Octets.d.ts +2 -3
  162. package/transpiled/directives/octets/Octets.js +4 -2
  163. package/transpiled/directives/octets/Octets.js.map +1 -1
  164. package/transpiled/directives/octets/Permute.d.ts +1 -1
  165. package/transpiled/directives/octets/Permute.js +11 -6
  166. package/transpiled/directives/octets/Permute.js.map +1 -1
  167. package/transpiled/directives/octets/Store.d.ts +1 -1
  168. package/transpiled/directives/octets/Store.js +17 -12
  169. package/transpiled/directives/octets/Store.js.map +1 -1
  170. package/transpiled/directives/octets/Workflow.d.ts +1 -1
  171. package/transpiled/directives/octets/Workflow.js +3 -3
  172. package/transpiled/directives/octets/Workflow.js.map +1 -1
  173. package/transpiled/directives/octets/workflows/Workflow.d.ts +1 -1
  174. package/transpiled/directives/octets/workflows/Workflow.js +2 -2
  175. package/transpiled/directives/octets/workflows/Workflow.js.map +1 -1
  176. package/transpiled/directives/vary/Vary.d.ts +2 -2
  177. package/transpiled/directives/vary/Vary.js +1 -1
  178. package/transpiled/directives/vary/embeddings/Header.js +2 -2
  179. package/transpiled/directives/vary/embeddings/Header.js.map +1 -1
  180. package/transpiled/directives/vary/embeddings/Language.js +2 -2
  181. package/transpiled/directives/vary/embeddings/Language.js.map +1 -1
  182. package/transpiled/io.d.ts +2 -2
  183. package/transpiled/tsconfig.tsbuildinfo +1 -1
  184. package/source/HTTP/Server.fixtures.ts +0 -40
  185. package/transpiled/HTTP/Server.fixtures.d.ts +0 -10
  186. package/transpiled/HTTP/Server.fixtures.js +0 -31
  187. package/transpiled/HTTP/Server.fixtures.js.map +0 -1
@@ -1,14 +1,11 @@
1
- import { type Directives } from './Directives'
2
- import { type Endpoint } from './Endpoint'
1
+ import type { Endpoint } from './Endpoint'
2
+ import type { Directives } from './Directives'
3
3
 
4
- export class Method<
5
- TEndpoint extends Endpoint<TEndpoint> = any,
6
- TDirectives extends Directives<TDirectives> = any
7
- > {
8
- public readonly endpoint: TEndpoint | null
9
- public readonly directives: TDirectives
4
+ export class Method {
5
+ public readonly endpoint: Endpoint | null
6
+ public readonly directives: Directives
10
7
 
11
- public constructor (endpoint: TEndpoint | null, directives: TDirectives) {
8
+ public constructor (endpoint: Endpoint | null, directives: Directives) {
12
9
  this.endpoint = endpoint
13
10
  this.directives = directives
14
11
  }
@@ -18,7 +15,4 @@ export class Method<
18
15
  }
19
16
  }
20
17
 
21
- export type Methods<
22
- TEndpoint extends Endpoint<TEndpoint> = any,
23
- TDirectives extends Directives<TDirectives> = any
24
- > = Record<string, Method<TEndpoint, TDirectives>>
18
+ export type Methods = Record<string, Method>
@@ -1,20 +1,15 @@
1
1
  import { type Route } from './Route'
2
2
  import { type Methods } from './Method'
3
3
  import { type Match, type Parameter } from './Match'
4
- import { type Directives } from './Directives'
5
- import { type Endpoint } from './Endpoint'
6
4
 
7
- export class Node<
8
- TEndpoint extends Endpoint<TEndpoint> = any,
9
- TDirectives extends Directives<TDirectives> = any
10
- > {
5
+ export class Node {
11
6
  public intermediate: boolean
12
- public methods: Methods<TEndpoint, TDirectives>
7
+ public methods: Methods
13
8
  private readonly protected: boolean
14
9
  private routes: Route[]
15
10
 
16
11
  public constructor
17
- (routes: Route[], methods: Methods<TEndpoint, TDirectives>, properties: Properties) {
12
+ (routes: Route[], methods: Methods, properties: Properties) {
18
13
  this.routes = routes
19
14
  this.methods = methods
20
15
  this.protected = properties.protected
@@ -34,26 +29,30 @@ export class Node<
34
29
  return null
35
30
  }
36
31
 
37
- public merge (node: Node<TEndpoint, TDirectives>): void {
32
+ public merge (node: Node): void {
38
33
  this.intermediate = node.intermediate
39
34
 
40
- if (!this.protected) this.replace(node)
41
- else this.append(node)
35
+ if (!this.protected)
36
+ this.replace(node)
37
+ else
38
+ this.append(node)
42
39
 
43
40
  this.sort()
44
41
  }
45
42
 
46
- private replace (node: Node<TEndpoint, TDirectives>): void {
43
+ private replace (node: Node): void {
47
44
  const methods = Object.values(this.methods)
48
45
 
49
46
  this.routes = node.routes
50
47
  this.methods = node.methods
51
48
 
52
49
  for (const method of methods)
53
- void method.close() // race condition is really unlikely
50
+ void method.close()
51
+
52
+ // race condition is really unlikely
54
53
  }
55
54
 
56
- private append (node: Node<TEndpoint, TDirectives>): void {
55
+ private append (node: Node): void {
57
56
  for (const route of node.routes)
58
57
  this.mergeRoute(route)
59
58
 
@@ -1,32 +1,32 @@
1
- import { type Node } from './Node'
2
1
  import { createNode } from './factory'
3
2
  import { fragment } from './segment'
4
- import { type Match } from './Match'
5
- import { type Context } from './Context'
6
- import { type Directives, type DirectivesFactory } from './Directives'
7
- import { type Endpoint, type EndpointsFactory } from './Endpoint'
3
+ import type { Node } from './Node'
4
+ import type { Match } from './Match'
5
+ import type { Context } from './Context'
6
+ import type { DirectiveFactory } from './Directives'
7
+ import type { EndpointsFactory } from './Endpoint'
8
8
  import type * as syntax from './syntax'
9
9
 
10
- export class Tree<
11
- TEndpoint extends Endpoint<TEndpoint> = any,
12
- TDirectives extends Directives<TDirectives> = any
13
- > {
10
+ export class Tree {
14
11
  private readonly root: syntax.Node
15
- private readonly trunk: Node<TEndpoint, TDirectives>
16
- private readonly endpoints: EndpointsFactory<TEndpoint>
17
- private readonly directives: DirectivesFactory
12
+ private readonly trunk: Node
13
+ private readonly endpoints: EndpointsFactory
14
+ private readonly directives: DirectiveFactory
18
15
 
19
16
  public constructor
20
- (node: syntax.Node, endpoints: EndpointsFactory, directives: DirectivesFactory) {
17
+ (node: syntax.Node, endpoints: EndpointsFactory, directives: DirectiveFactory) {
21
18
  this.endpoints = endpoints
22
19
  this.directives = directives
23
20
  this.trunk = this.createNode(node, PROTECTED)
24
21
  this.root = node
25
22
  }
26
23
 
27
- public match (path: string): Match<TEndpoint, TDirectives> | null {
24
+ public match (path: string): Match | null {
28
25
  if (path === '/')
29
- return { node: this.trunk, parameters: [] }
26
+ return {
27
+ node: this.trunk,
28
+ parameters: []
29
+ }
30
30
 
31
31
  const fragments = fragment(path)
32
32
 
@@ -39,7 +39,8 @@ export class Tree<
39
39
  this.trunk.merge(branch)
40
40
  }
41
41
 
42
- private createNode (node: syntax.Node, protect: boolean, extension?: any): Node {
42
+ private createNode
43
+ (node: syntax.Node, protect: boolean, extension?: any): Node {
43
44
  const context: Context = {
44
45
  protected: protect,
45
46
  endpoints: this.endpoints,
@@ -1,14 +1,11 @@
1
1
  import { Node, type Properties } from './Node'
2
2
  import { Route } from './Route'
3
- import { type Context } from './Context'
4
3
  import { segment } from './segment'
5
4
  import { Method, type Methods } from './Method'
6
- import { type Endpoint } from './Endpoint'
7
- import { type Directives } from './Directives'
5
+ import type { Context } from './Context'
8
6
  import type * as syntax from './syntax'
9
7
 
10
- export function createNode<TEndpoint extends Endpoint, TDirectives extends Directives>
11
- (node: syntax.Node, context: Context): Node<TEndpoint, TDirectives> {
8
+ export function createNode (node: syntax.Node, context: Context): Node {
12
9
  if (node.isolated === true)
13
10
  context.directives.stack = node.directives
14
11
  else
@@ -41,6 +41,12 @@ export function deployment (_: unknown, annotation: Annotation | undefined): Dep
41
41
  value: '1'
42
42
  })
43
43
 
44
+ if (annotation?.trace === true)
45
+ service.variables.push({
46
+ name: 'TOA_EXPOSITION_TRACE',
47
+ value: '1'
48
+ })
49
+
44
50
  if (annotation !== undefined)
45
51
  schemas.annotaion.validate(annotation)
46
52
 
@@ -8,7 +8,8 @@ export class Anonymous implements Directive {
8
8
  }
9
9
 
10
10
  public authorize (_: any, input: Input): boolean {
11
- if ('authorization' in input.headers) return false
12
- else return this.allow
11
+ return 'authorization' in input.request.headers
12
+ ? false
13
+ : this.allow
13
14
  }
14
15
  }
@@ -13,8 +13,7 @@ import { PRIMARY, PROVIDERS } from './schemes'
13
13
  import type { Output } from '../../io'
14
14
  import type { Component } from '@toa.io/core'
15
15
  import type { Remotes } from '../../Remotes'
16
- import type { Family } from '../../Directive'
17
- import type { Parameter } from '../../RTD'
16
+ import type { Parameter, DirectiveFamily } from '../../RTD'
18
17
  import type {
19
18
  AuthenticationResult,
20
19
  Ban,
@@ -27,7 +26,7 @@ import type {
27
26
  Schemes
28
27
  } from './types'
29
28
 
30
- export class Authorization implements Family<Directive, Extension> {
29
+ export class Authorization implements DirectiveFamily<Directive, Extension> {
31
30
  public readonly depends: string[] = ['Vary']
32
31
  public readonly name: string = 'auth'
33
32
  public readonly mandatory: boolean = true
@@ -56,7 +55,7 @@ export class Authorization implements Family<Directive, Extension> {
56
55
  public async preflight (directives: Directive[],
57
56
  input: Input,
58
57
  parameters: Parameter[]): Promise<Output> {
59
- const identity = await this.resolve(input.headers.authorization)
58
+ const identity = await this.resolve(input.request.headers.authorization)
60
59
 
61
60
  input.identity = identity
62
61
 
@@ -67,8 +66,10 @@ export class Authorization implements Family<Directive, Extension> {
67
66
  return directive.reply?.(identity) ?? null
68
67
  }
69
68
 
70
- if (identity === null) throw new http.Unauthorized()
71
- else throw new http.Forbidden()
69
+ if (identity === null)
70
+ throw new http.Unauthorized()
71
+ else
72
+ throw new http.Forbidden()
72
73
  }
73
74
 
74
75
  public async settle (directives: Directive[],
@@ -15,28 +15,33 @@ export class Incept implements Directive {
15
15
  }
16
16
 
17
17
  public authorize (identity: Identity | null, input: Input): boolean {
18
- return identity === null && 'authorization' in input.headers
18
+ return identity === null && 'authorization' in input.request.headers
19
19
  }
20
20
 
21
- public async settle (request: Input, response: http.OutgoingMessage): Promise<void> {
21
+ public async settle (input: Input, response: http.OutgoingMessage): Promise<void> {
22
22
  const id = response.body?.[this.property]
23
23
 
24
24
  if (id === undefined)
25
25
  throw new http.Conflict('Identity inception has failed as the response body ' +
26
26
  ` does not contain the '${this.property}' property.`)
27
27
 
28
- const [scheme, credentials] = split(request.headers.authorization!)
28
+ const [scheme, credentials] = split(input.request.headers.authorization!)
29
29
  const provider = PROVIDERS[scheme]
30
30
 
31
31
  this.schemes[scheme] ??= await this.discovery[provider]
32
32
 
33
33
  const identity = await this.schemes[scheme]
34
- .invoke<Maybe<Identity>>('create', { input: { id, credentials } })
34
+ .invoke<Maybe<Identity>>('create', {
35
+ input: {
36
+ id,
37
+ credentials
38
+ }
39
+ })
35
40
 
36
41
  if (identity instanceof Error)
37
42
  throw new http.Conflict(identity)
38
43
 
39
- request.identity = identity
40
- request.identity.scheme = scheme
44
+ input.identity = identity
45
+ input.identity.scheme = scheme
41
46
  }
42
47
  }
@@ -14,10 +14,12 @@ export class Role implements Directive {
14
14
  public static async set (identity: Identity, discovery: Promise<Component>): Promise<void> {
15
15
  this.remote ??= await discovery
16
16
 
17
- const query: Query = { criteria: `identity==${identity.id}`, limit: 1024 }
18
- const roles: string[] = await this.remote.invoke('list', { query })
17
+ const query: Query = {
18
+ criteria: `identity==${identity.id}`,
19
+ limit: 1024
20
+ }
19
21
 
20
- identity.roles = roles
22
+ identity.roles = await this.remote.invoke('list', { query })
21
23
  }
22
24
 
23
25
  public async authorize (identity: Identity | null): Promise<boolean> {
@@ -12,10 +12,10 @@ export class Scheme implements Directive {
12
12
  }
13
13
 
14
14
  public authorize (_: Identity | null, input: Input): boolean {
15
- if (input.headers.authorization === undefined)
15
+ if (input.request.headers.authorization === undefined)
16
16
  return false
17
17
 
18
- const [scheme] = split(input.headers.authorization)
18
+ const [scheme] = split(input.request.headers.authorization)
19
19
 
20
20
  if (scheme !== this.scheme)
21
21
  throw new http.Forbidden(this.Scheme +
@@ -2,10 +2,10 @@ import { Control } from './Control'
2
2
  import { Exact } from './Exact'
3
3
  import type { Input, Output } from '../../io'
4
4
  import type { Directive } from './types'
5
- import type { Family } from '../../Directive'
5
+ import type { DirectiveFamily } from '../../RTD'
6
6
  import type * as http from '../../HTTP'
7
7
 
8
- export class Cache implements Family<Directive> {
8
+ export class Cache implements DirectiveFamily<Directive> {
9
9
  public readonly name: string = 'cache'
10
10
  public readonly mandatory: boolean = false
11
11
 
@@ -23,9 +23,9 @@ export class Cache implements Family<Directive> {
23
23
  }
24
24
 
25
25
  public async settle
26
- (directives: Directive[], request: Input, response: http.OutgoingMessage): Promise<void> {
26
+ (directives: Directive[], input: Input, response: http.OutgoingMessage): Promise<void> {
27
27
  response.headers ??= new Headers()
28
- directives[0]?.set(request, response.headers)
28
+ directives[0]?.set(input, response.headers)
29
29
  }
30
30
  }
31
31
 
@@ -1,5 +1,5 @@
1
1
  import { match } from 'matchacho'
2
- import type { AuthenticatedRequest, Directive } from './types'
2
+ import type { AuthenticatedContext, Directive } from './types'
3
3
 
4
4
  export class Control implements Directive {
5
5
  protected readonly value: string
@@ -9,16 +9,16 @@ export class Control implements Directive {
9
9
  this.value = value
10
10
  }
11
11
 
12
- public set (request: AuthenticatedRequest, headers: Headers): void {
13
- if (!['GET', 'HEAD', 'OPTIONS'].includes(request.method))
12
+ public set (context: AuthenticatedContext, headers: Headers): void {
13
+ if (!['GET', 'HEAD', 'OPTIONS'].includes(context.request.method))
14
14
  return
15
15
 
16
- this.cache ??= this.resolve(request)
16
+ this.cache ??= this.resolve(context)
17
17
 
18
18
  headers.set('cache-control', this.cache)
19
19
  }
20
20
 
21
- protected resolve (request: AuthenticatedRequest): string {
21
+ protected resolve (request: AuthenticatedContext): string {
22
22
  if (request.identity === null)
23
23
  return this.value
24
24
 
@@ -4,6 +4,6 @@ export interface Directive {
4
4
  set: (input: Input, headers: Headers) => void
5
5
  }
6
6
 
7
- export interface AuthenticatedRequest extends Input {
7
+ export interface AuthenticatedContext extends Input {
8
8
  identity?: unknown | null
9
9
  }
@@ -3,26 +3,32 @@ import type { Interceptor } from '../../Interception'
3
3
 
4
4
  export class CORS implements Interceptor {
5
5
  public readonly name = 'cors'
6
- public readonly mandatory = true
7
6
 
8
- private readonly allowedHeaders = new Set<string>(['accept', 'authorization', 'content-type'])
7
+ private readonly requestHeaders = new Set<string>([
8
+ 'accept',
9
+ 'authorization',
10
+ 'content-type',
11
+ 'etag',
12
+ 'if-match',
13
+ 'if-none-match'
14
+ ])
9
15
 
10
16
  private readonly headers = new Headers({
11
17
  'access-control-allow-methods': 'GET, POST, PUT, PATCH, DELETE',
12
18
  'access-control-allow-credentials': 'true',
13
- 'access-control-allow-headers': Array.from(this.allowedHeaders).join(', '),
19
+ 'access-control-allow-headers': Array.from(this.requestHeaders).join(', '),
14
20
  'access-control-max-age': '3600',
15
- 'cache-control': 'public, max-age=3600',
21
+ 'cache-control': 'max-age=3600',
16
22
  vary: 'origin'
17
23
  })
18
24
 
19
25
  public intercept (input: Input): Output {
20
- const origin = input.headers.origin
26
+ const origin = input.request.headers.origin
21
27
 
22
28
  if (origin === undefined)
23
29
  return null
24
30
 
25
- if (input.method === 'OPTIONS')
31
+ if (input.request.method === 'OPTIONS')
26
32
  return this.preflightResponse(origin)
27
33
 
28
34
  input.pipelines.response.push((output) => {
@@ -31,16 +37,18 @@ export class CORS implements Interceptor {
31
37
  output.headers.set('access-control-expose-headers',
32
38
  'authorization, content-type, content-length, etag')
33
39
 
34
- if (input.method === 'GET' || input.method === 'HEAD' || input.method === 'OPTIONS')
40
+ const method = input.request.method
41
+
42
+ if (method === 'GET' || method === 'HEAD' || method === 'OPTIONS')
35
43
  output.headers.append('vary', 'origin')
36
44
  })
37
45
 
38
46
  return null
39
47
  }
40
48
 
41
- public allowHeader (header: string): void {
42
- this.allowedHeaders.add(header.toLowerCase())
43
- this.headers.set('access-control-allow-headers', Array.from(this.allowedHeaders).join(', '))
49
+ public allow (header: string): void {
50
+ this.requestHeaders.add(header.toLowerCase())
51
+ this.headers.set('access-control-allow-headers', Array.from(this.requestHeaders).join(', '))
44
52
  }
45
53
 
46
54
  private preflightResponse (origin: string): Output {
@@ -2,13 +2,13 @@ import { Stub } from './Stub'
2
2
  import { Throw } from './Throw'
3
3
  import { type Directive } from './types'
4
4
  import type { Input, Output } from '../../io'
5
- import type { Family } from '../../Directive'
5
+ import type { DirectiveFamily } from '../../RTD'
6
6
 
7
- export class Development implements Family<Directive> {
7
+ export class Development implements DirectiveFamily<Directive> {
8
8
  public readonly name: string = 'dev'
9
9
  public readonly mandatory: boolean = false
10
10
 
11
- public create (name: string, value: any): Directive {
11
+ public create (name: string, value: unknown): Directive {
12
12
  const Class = constructors[name]
13
13
 
14
14
  if (Class === undefined)
@@ -4,8 +4,8 @@ import { cache } from './cache'
4
4
  import { octets } from './octets'
5
5
  import { cors } from './cors'
6
6
  import { vary } from './vary'
7
- import type { Family } from '../Directive'
7
+ import type { DirectiveFamily } from '../RTD'
8
8
  import type { Interceptor } from '../Interception'
9
9
 
10
- export const families: Family[] = [authorization, cache, octets, vary, dev]
10
+ export const families: DirectiveFamily[] = [authorization, cache, octets, vary, dev]
11
11
  export const interceptors: Interceptor[] = [cors]
@@ -6,7 +6,7 @@ export class Context implements Directive {
6
6
  public readonly targeted = false
7
7
  public readonly storage: string
8
8
 
9
- public constructor (value: any) {
9
+ public constructor (value: unknown) {
10
10
  schemas.context.validate(value)
11
11
 
12
12
  this.storage = value
@@ -27,11 +27,16 @@ export class Delete implements Directive {
27
27
  this.discovery = discovery
28
28
  }
29
29
 
30
- public async apply (storage: string, request: Input, parameters: Parameter[]): Promise<Output> {
30
+ public async apply (storage: string, input: Input, parameters: Parameter[]): Promise<Output> {
31
31
  this.storage ??= await this.discovery
32
32
 
33
33
  const entry = await this.storage.invoke<Maybe<Entry>>('get',
34
- { input: { storage, path: request.url } })
34
+ {
35
+ input: {
36
+ storage,
37
+ path: input.request.url
38
+ }
39
+ })
35
40
 
36
41
  if (entry instanceof Error)
37
42
  throw new NotFound()
@@ -40,31 +45,36 @@ export class Delete implements Directive {
40
45
 
41
46
  if (this.workflow !== undefined) {
42
47
  output.status = 202
43
- output.body = Readable.from(this.execute(request, storage, entry, parameters))
48
+ output.body = Readable.from(this.execute(input, storage, entry, parameters))
44
49
  } else
45
- await this.delete(storage, request)
50
+ await this.delete(storage, input)
46
51
 
47
52
  return output
48
53
  }
49
54
 
50
- private async delete (storage: string, request: Input): Promise<void> {
55
+ private async delete (storage: string, input: Input): Promise<void> {
51
56
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
52
57
  await this.storage!.invoke('delete',
53
- { input: { storage, path: request.url } })
58
+ {
59
+ input: {
60
+ storage,
61
+ path: input.request.url
62
+ }
63
+ })
54
64
  }
55
65
 
56
66
  // eslint-disable-next-line max-params
57
67
  private async * execute
58
- (request: Input, storage: string, entry: Entry, parameters: Parameter[]): AsyncGenerator {
68
+ (input: Input, storage: string, entry: Entry, parameters: Parameter[]): AsyncGenerator {
59
69
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
60
- for await (const chunk of this.workflow!.execute(request, storage, entry, parameters)) {
70
+ for await (const chunk of this.workflow!.execute(input, storage, entry, parameters)) {
61
71
  yield chunk
62
72
 
63
73
  if (typeof chunk === 'object' && chunk !== null && 'error' in chunk)
64
74
  return
65
75
  }
66
76
 
67
- await this.delete(storage, request)
77
+ await this.delete(storage, input)
68
78
  }
69
79
  }
70
80
 
@@ -12,7 +12,11 @@ import type { Directive, Input } from './types'
12
12
  export class Fetch implements Directive {
13
13
  public readonly targeted = true
14
14
 
15
- private readonly permissions: Required<Permissions> = { blob: true, meta: false }
15
+ private readonly permissions: Required<Permissions> = {
16
+ blob: true,
17
+ meta: false
18
+ }
19
+
16
20
  private readonly discovery: Promise<Component>
17
21
  private storage: Component = null as unknown as Component
18
22
 
@@ -23,30 +27,34 @@ export class Fetch implements Directive {
23
27
  this.discovery = discovery
24
28
  }
25
29
 
26
- public async apply (storage: string, request: Input): Promise<Output> {
30
+ public async apply (storage: string, input: Input): Promise<Output> {
27
31
  this.storage ??= await this.discovery
28
32
 
29
- const variant = posix.basename(request.url).includes('.')
30
- const metadata = request.subtype === 'octets.entry'
33
+ const variant = posix.basename(input.request.url).includes('.')
34
+ const metadata = input.subtype === 'octets.entry'
31
35
 
32
36
  if (!variant && metadata)
33
37
  if (this.permissions.meta)
34
- return this.get(storage, request)
38
+ return this.get(storage, input)
35
39
  else
36
40
  throw new Forbidden('Metadata is not accessible.')
37
41
 
38
42
  if (!variant && !this.permissions.blob)
39
43
  throw new Forbidden('BLOB variant must be specified.')
40
44
 
41
- return await this.fetch(storage, request)
45
+ return await this.fetch(storage, input)
42
46
  }
43
47
 
44
- private async fetch (storage: string, request: Input): Promise<Output> {
45
- if ('if-none-match' in request.headers)
48
+ private async fetch (storage: string, input: Input): Promise<Output> {
49
+ if ('if-none-match' in input.request.headers)
46
50
  return { status: 304 }
47
51
 
48
- const input = { storage, path: request.url }
49
- const result = await this.storage.invoke<Maybe<FetchResult>>('fetch', { input })
52
+ const result = await this.storage.invoke<Maybe<FetchResult>>('fetch', {
53
+ input: {
54
+ storage,
55
+ path: input.request.url
56
+ }
57
+ })
50
58
 
51
59
  if (result instanceof Error)
52
60
  throw new NotFound()
@@ -57,12 +65,19 @@ export class Fetch implements Directive {
57
65
  etag: result.checksum
58
66
  })
59
67
 
60
- return { headers, body: result.stream }
68
+ return {
69
+ headers,
70
+ body: result.stream
71
+ }
61
72
  }
62
73
 
63
- private async get (storage: string, request: Input): Promise<Output> {
64
- const input = { storage, path: request.url }
65
- const entry = await this.storage.invoke<Maybe<Entry>>('get', { input })
74
+ private async get (storage: string, input: Input): Promise<Output> {
75
+ const entry = await this.storage.invoke<Maybe<Entry>>('get', {
76
+ input: {
77
+ storage,
78
+ path: input.request.url
79
+ }
80
+ })
66
81
 
67
82
  if (entry instanceof Error)
68
83
  throw new NotFound()
@@ -22,22 +22,26 @@ export class List implements Directive {
22
22
  this.discovery = discovery
23
23
  }
24
24
 
25
- public async apply (storage: string, request: Input): Promise<Output> {
25
+ public async apply (storage: string, input: Input): Promise<Output> {
26
26
  this.storage ??= await this.discovery
27
27
 
28
- const metadata = request.subtype === 'octets.entries'
28
+ const metadata = input.subtype === 'octets.entries'
29
29
 
30
30
  if (metadata && !this.permissions.meta)
31
31
  throw new Forbidden('Metadata is not accessible.')
32
32
 
33
- const input = { storage, path: request.url }
34
- const list = await this.storage.invoke<Maybe<string[]>>('list', { input })
33
+ const list = await this.storage.invoke<Maybe<string[]>>('list', {
34
+ input: {
35
+ storage,
36
+ path: input.request.url
37
+ }
38
+ })
35
39
 
36
40
  if (list instanceof Error)
37
41
  throw new NotFound()
38
42
 
39
43
  const body = metadata
40
- ? await this.expand(storage, request.url, list)
44
+ ? await this.expand(storage, input.request.url, list)
41
45
  : list
42
46
 
43
47
  return { body }
@@ -47,7 +51,11 @@ export class List implements Directive {
47
51
  Promise<Array<Maybe<Entry>>> {
48
52
  const promises = list.map(async (id) => {
49
53
  const path = posix.join(prefix, id)
50
- const input = { storage, path }
54
+
55
+ const input = {
56
+ storage,
57
+ path
58
+ }
51
59
 
52
60
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion -- ensured in `apply`
53
61
  return this.storage!.invoke<Maybe<Entry>>('get', { input })