@toa.io/extensions.exposition 0.24.0-alpha.17 → 0.24.0-alpha.19

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 (234) hide show
  1. package/components/context.toa.yaml +12 -0
  2. package/components/identity.bans/manifest.toa.yaml +1 -1
  3. package/components/identity.basic/manifest.toa.yaml +1 -1
  4. package/components/identity.basic/operations/authenticate.js +1 -2
  5. package/components/identity.basic/operations/authenticate.js.map +1 -1
  6. package/components/identity.basic/operations/transit.js.map +1 -1
  7. package/components/identity.basic/operations/tsconfig.tsbuildinfo +1 -1
  8. package/components/identity.basic/source/authenticate.ts +0 -1
  9. package/components/identity.federation/events/principal.js +22 -0
  10. package/components/identity.federation/manifest.toa.yaml +88 -0
  11. package/components/identity.federation/operations/assertions-as-values.cjs +45 -0
  12. package/components/identity.federation/operations/assertions-as-values.cjs.map +1 -0
  13. package/components/identity.federation/operations/assertions-as-values.d.cts +4 -0
  14. package/components/identity.federation/operations/authenticate.d.ts +3 -0
  15. package/components/identity.federation/operations/authenticate.js +20 -0
  16. package/components/identity.federation/operations/authenticate.js.map +1 -0
  17. package/components/identity.federation/operations/create.d.ts +10 -0
  18. package/components/identity.federation/operations/create.js +15 -0
  19. package/components/identity.federation/operations/create.js.map +1 -0
  20. package/components/identity.federation/operations/jwt.cjs +112 -0
  21. package/components/identity.federation/operations/jwt.cjs.map +1 -0
  22. package/components/identity.federation/operations/jwt.d.cts +19 -0
  23. package/components/identity.federation/operations/schemas.d.ts +43 -0
  24. package/components/identity.federation/operations/schemas.js +9 -0
  25. package/components/identity.federation/operations/schemas.js.map +1 -0
  26. package/components/identity.federation/operations/tsconfig.tsbuildinfo +1 -0
  27. package/components/identity.federation/operations/types.d.ts +51 -0
  28. package/components/identity.federation/operations/types.js +3 -0
  29. package/components/identity.federation/operations/types.js.map +1 -0
  30. package/components/identity.federation/source/assertions-as-values.cts +20 -0
  31. package/components/identity.federation/source/authenticate.ts +28 -0
  32. package/components/identity.federation/source/create.ts +26 -0
  33. package/components/identity.federation/source/jwt.cts +143 -0
  34. package/components/identity.federation/source/schemas.ts +45 -0
  35. package/components/identity.federation/source/types.ts +56 -0
  36. package/components/identity.federation/tsconfig.json +9 -0
  37. package/components/identity.roles/operations/tsconfig.tsbuildinfo +1 -1
  38. package/components/identity.tokens/manifest.toa.yaml +1 -1
  39. package/components/identity.tokens/operations/authenticate.js.map +1 -1
  40. package/components/identity.tokens/operations/decrypt.js.map +1 -1
  41. package/components/identity.tokens/operations/tsconfig.tsbuildinfo +1 -1
  42. package/cucumber.js +0 -1
  43. package/documentation/components.md +24 -1
  44. package/documentation/identity.md +7 -7
  45. package/documentation/protocol.md +20 -1
  46. package/documentation/query.md +1 -1
  47. package/documentation/vary.md +69 -0
  48. package/features/cors.feature +6 -26
  49. package/features/identity.feature +17 -1
  50. package/features/identity.federation.feature +125 -0
  51. package/features/response.feature +0 -0
  52. package/features/steps/Captures.ts +5 -0
  53. package/features/steps/Components.ts +5 -0
  54. package/features/steps/HTTP.ts +33 -5
  55. package/features/steps/IdP.ts +120 -0
  56. package/features/steps/Parameters.ts +8 -2
  57. package/features/steps/Workspace.ts +5 -7
  58. package/features/vary.feature +120 -0
  59. package/package.json +17 -18
  60. package/source/Directive.test.ts +8 -2
  61. package/source/Directive.ts +19 -16
  62. package/source/Factory.ts +8 -7
  63. package/source/Gateway.ts +22 -8
  64. package/source/HTTP/Server.fixtures.ts +0 -1
  65. package/source/HTTP/Server.test.ts +61 -138
  66. package/source/HTTP/Server.ts +45 -31
  67. package/source/HTTP/formats/text.ts +1 -1
  68. package/source/HTTP/formats/yaml.ts +1 -1
  69. package/source/HTTP/messages.ts +8 -2
  70. package/source/Interception.ts +24 -0
  71. package/source/RTD/Directives.ts +2 -2
  72. package/source/RTD/syntax/parse.ts +6 -6
  73. package/source/RTD/syntax/types.ts +1 -1
  74. package/source/directives/auth/{Family.ts → Authorization.ts} +29 -33
  75. package/source/directives/auth/Incept.ts +1 -1
  76. package/source/directives/auth/Rule.ts +2 -2
  77. package/source/directives/auth/index.ts +2 -2
  78. package/source/directives/auth/schemes.ts +2 -1
  79. package/source/directives/auth/types.ts +9 -6
  80. package/source/directives/cache/{Family.ts → Cache.ts} +4 -5
  81. package/source/directives/cache/index.ts +2 -2
  82. package/source/directives/cache/types.ts +1 -1
  83. package/source/directives/cors/CORS.ts +52 -0
  84. package/source/directives/cors/index.ts +3 -0
  85. package/source/directives/dev/{Family.ts → Development.ts} +3 -4
  86. package/source/directives/dev/Stub.ts +4 -4
  87. package/source/directives/dev/Throw.ts +4 -4
  88. package/source/directives/dev/index.ts +2 -2
  89. package/source/directives/dev/types.ts +1 -1
  90. package/source/directives/index.ts +10 -6
  91. package/source/directives/octets/Context.ts +1 -1
  92. package/source/directives/octets/Delete.ts +1 -2
  93. package/source/directives/octets/Fetch.ts +1 -1
  94. package/source/directives/octets/List.ts +1 -1
  95. package/source/directives/octets/{Family.ts → Octets.ts} +3 -4
  96. package/source/directives/octets/Permute.ts +1 -1
  97. package/source/directives/octets/Store.ts +3 -3
  98. package/source/directives/octets/index.ts +2 -2
  99. package/source/directives/octets/types.ts +3 -3
  100. package/source/directives/vary/Directive.ts +6 -0
  101. package/source/directives/vary/Embed.ts +62 -0
  102. package/source/directives/vary/Properties.ts +17 -0
  103. package/source/directives/vary/Vary.ts +48 -0
  104. package/source/directives/vary/embeddings/Embedding.ts +6 -0
  105. package/source/directives/vary/embeddings/Header.ts +30 -0
  106. package/source/directives/vary/embeddings/Language.ts +31 -0
  107. package/source/directives/vary/embeddings/index.ts +11 -0
  108. package/source/directives/vary/index.ts +3 -0
  109. package/source/io.ts +4 -0
  110. package/transpiled/Composition.js.map +1 -1
  111. package/transpiled/Directive.d.ts +6 -7
  112. package/transpiled/Directive.js +12 -10
  113. package/transpiled/Directive.js.map +1 -1
  114. package/transpiled/Factory.d.ts +0 -1
  115. package/transpiled/Factory.js +7 -6
  116. package/transpiled/Factory.js.map +1 -1
  117. package/transpiled/Gateway.d.ts +8 -5
  118. package/transpiled/Gateway.js +12 -2
  119. package/transpiled/Gateway.js.map +1 -1
  120. package/transpiled/HTTP/Server.d.ts +4 -2
  121. package/transpiled/HTTP/Server.fixtures.d.ts +0 -1
  122. package/transpiled/HTTP/Server.fixtures.js +1 -2
  123. package/transpiled/HTTP/Server.fixtures.js.map +1 -1
  124. package/transpiled/HTTP/Server.js +33 -22
  125. package/transpiled/HTTP/Server.js.map +1 -1
  126. package/transpiled/HTTP/formats/text.d.ts +3 -1
  127. package/transpiled/HTTP/formats/text.js.map +1 -1
  128. package/transpiled/HTTP/formats/yaml.js +1 -1
  129. package/transpiled/HTTP/formats/yaml.js.map +1 -1
  130. package/transpiled/HTTP/messages.d.ts +4 -0
  131. package/transpiled/HTTP/messages.js +4 -2
  132. package/transpiled/HTTP/messages.js.map +1 -1
  133. package/transpiled/Interception.d.ts +9 -0
  134. package/transpiled/Interception.js +19 -0
  135. package/transpiled/Interception.js.map +1 -0
  136. package/transpiled/Query.js.map +1 -1
  137. package/transpiled/RTD/Directives.d.ts +2 -2
  138. package/transpiled/RTD/Node.js.map +1 -1
  139. package/transpiled/RTD/Route.js.map +1 -1
  140. package/transpiled/RTD/syntax/parse.js +1 -1
  141. package/transpiled/RTD/syntax/parse.js.map +1 -1
  142. package/transpiled/RTD/syntax/types.js +1 -1
  143. package/transpiled/RTD/syntax/types.js.map +1 -1
  144. package/transpiled/deployment.js.map +1 -1
  145. package/transpiled/directives/auth/{Family.d.ts → Authorization.d.ts} +4 -4
  146. package/transpiled/directives/auth/{Family.js → Authorization.js} +15 -8
  147. package/transpiled/directives/auth/Authorization.js.map +1 -0
  148. package/transpiled/directives/auth/Incept.js.map +1 -1
  149. package/transpiled/directives/auth/Role.js.map +1 -1
  150. package/transpiled/directives/auth/Rule.d.ts +2 -2
  151. package/transpiled/directives/auth/Rule.js.map +1 -1
  152. package/transpiled/directives/auth/index.d.ts +2 -2
  153. package/transpiled/directives/auth/index.js +4 -5
  154. package/transpiled/directives/auth/index.js.map +1 -1
  155. package/transpiled/directives/auth/schemes.js +2 -1
  156. package/transpiled/directives/auth/schemes.js.map +1 -1
  157. package/transpiled/directives/auth/types.d.ts +4 -4
  158. package/transpiled/directives/cache/{Family.d.ts → Cache.d.ts} +4 -5
  159. package/transpiled/directives/cache/{Family.js → Cache.js} +4 -2
  160. package/transpiled/directives/cache/{Family.js.map → Cache.js.map} +1 -1
  161. package/transpiled/directives/cache/index.d.ts +2 -2
  162. package/transpiled/directives/cache/index.js +4 -5
  163. package/transpiled/directives/cache/index.js.map +1 -1
  164. package/transpiled/directives/cache/types.d.ts +1 -1
  165. package/transpiled/directives/cors/CORS.d.ts +14 -0
  166. package/transpiled/directives/cors/CORS.js +42 -0
  167. package/transpiled/directives/cors/CORS.js.map +1 -0
  168. package/transpiled/directives/cors/index.d.ts +2 -0
  169. package/transpiled/directives/cors/index.js +6 -0
  170. package/transpiled/directives/cors/index.js.map +1 -0
  171. package/transpiled/directives/dev/{Family.d.ts → Development.d.ts} +3 -4
  172. package/transpiled/directives/dev/{Family.js → Development.js} +4 -2
  173. package/transpiled/directives/dev/Development.js.map +1 -0
  174. package/transpiled/directives/dev/Stub.d.ts +3 -3
  175. package/transpiled/directives/dev/Stub.js.map +1 -1
  176. package/transpiled/directives/dev/Throw.d.ts +3 -3
  177. package/transpiled/directives/dev/Throw.js.map +1 -1
  178. package/transpiled/directives/dev/index.d.ts +2 -2
  179. package/transpiled/directives/dev/index.js +4 -5
  180. package/transpiled/directives/dev/index.js.map +1 -1
  181. package/transpiled/directives/dev/types.d.ts +1 -1
  182. package/transpiled/directives/index.d.ts +3 -1
  183. package/transpiled/directives/index.js +9 -9
  184. package/transpiled/directives/index.js.map +1 -1
  185. package/transpiled/directives/octets/Context.d.ts +1 -1
  186. package/transpiled/directives/octets/Delete.d.ts +1 -1
  187. package/transpiled/directives/octets/Delete.js.map +1 -1
  188. package/transpiled/directives/octets/Fetch.d.ts +1 -1
  189. package/transpiled/directives/octets/List.d.ts +1 -1
  190. package/transpiled/directives/octets/{Family.d.ts → Octets.d.ts} +3 -4
  191. package/transpiled/directives/octets/{Family.js → Octets.js} +4 -2
  192. package/transpiled/directives/octets/Octets.js.map +1 -0
  193. package/transpiled/directives/octets/Permute.d.ts +1 -1
  194. package/transpiled/directives/octets/Store.d.ts +1 -1
  195. package/transpiled/directives/octets/Store.js.map +1 -1
  196. package/transpiled/directives/octets/index.d.ts +2 -2
  197. package/transpiled/directives/octets/index.js +4 -5
  198. package/transpiled/directives/octets/index.js.map +1 -1
  199. package/transpiled/directives/octets/types.d.ts +3 -3
  200. package/transpiled/directives/vary/Directive.d.ts +5 -0
  201. package/transpiled/directives/vary/Directive.js +3 -0
  202. package/transpiled/directives/vary/Directive.js.map +1 -0
  203. package/transpiled/directives/vary/Embed.d.ts +10 -0
  204. package/transpiled/directives/vary/Embed.js +49 -0
  205. package/transpiled/directives/vary/Embed.js.map +1 -0
  206. package/transpiled/directives/vary/Properties.d.ts +9 -0
  207. package/transpiled/directives/vary/Properties.js +16 -0
  208. package/transpiled/directives/vary/Properties.js.map +1 -0
  209. package/transpiled/directives/vary/Vary.d.ts +10 -0
  210. package/transpiled/directives/vary/Vary.js +36 -0
  211. package/transpiled/directives/vary/Vary.js.map +1 -0
  212. package/transpiled/directives/vary/embeddings/Embedding.d.ts +5 -0
  213. package/transpiled/directives/vary/embeddings/Embedding.js +3 -0
  214. package/transpiled/directives/vary/embeddings/Embedding.js.map +1 -0
  215. package/transpiled/directives/vary/embeddings/Header.d.ts +7 -0
  216. package/transpiled/directives/vary/embeddings/Header.js +26 -0
  217. package/transpiled/directives/vary/embeddings/Header.js.map +1 -0
  218. package/transpiled/directives/vary/embeddings/Language.d.ts +7 -0
  219. package/transpiled/directives/vary/embeddings/Language.js +28 -0
  220. package/transpiled/directives/vary/embeddings/Language.js.map +1 -0
  221. package/transpiled/directives/vary/embeddings/index.d.ts +5 -0
  222. package/transpiled/directives/vary/embeddings/index.js +10 -0
  223. package/transpiled/directives/vary/embeddings/index.js.map +1 -0
  224. package/transpiled/directives/vary/index.d.ts +2 -0
  225. package/transpiled/directives/vary/index.js +6 -0
  226. package/transpiled/directives/vary/index.js.map +1 -0
  227. package/transpiled/io.d.ts +3 -0
  228. package/transpiled/io.js +3 -0
  229. package/transpiled/io.js.map +1 -0
  230. package/transpiled/manifest.js.map +1 -1
  231. package/transpiled/tsconfig.tsbuildinfo +1 -1
  232. package/transpiled/directives/auth/Family.js.map +0 -1
  233. package/transpiled/directives/dev/Family.js.map +0 -1
  234. package/transpiled/directives/octets/Family.js.map +0 -1
@@ -28,7 +28,7 @@ function parseNode (input: object, shortcuts?: Shortcuts): Node {
28
28
  }
29
29
 
30
30
  if (key[0] === '/') {
31
- const route = parseRoute(key, value, shortcuts)
31
+ const route = parseRoute(key, value as Node, shortcuts)
32
32
 
33
33
  node.routes.push(route)
34
34
 
@@ -36,7 +36,7 @@ function parseNode (input: object, shortcuts?: Shortcuts): Node {
36
36
  }
37
37
 
38
38
  if (verbs.has(key)) {
39
- const method = parseMethod(key, value, shortcuts)
39
+ const method = parseMethod(key, value as Mapping, shortcuts)
40
40
 
41
41
  node.methods.push(method)
42
42
 
@@ -65,7 +65,7 @@ export function createNode (): Node {
65
65
  }
66
66
  }
67
67
 
68
- function parseRoute (path: string, value: object, shortcuts?: Shortcuts): Route {
68
+ function parseRoute (path: string, value: Node, shortcuts?: Shortcuts): Route {
69
69
  const node = parse(value, shortcuts)
70
70
 
71
71
  return createRoute(path, node)
@@ -106,10 +106,10 @@ function parseQuery (mapping: any): void {
106
106
  return
107
107
 
108
108
  if (typeof query.limit === 'number')
109
- query.limit = expandRange(query.limit)
109
+ query.limit = expandRange(query.limit as number)
110
110
 
111
111
  if (typeof query.omit === 'number')
112
- query.omit = expandRange(query.omit)
112
+ query.omit = expandRange(query.omit as number)
113
113
  }
114
114
 
115
115
  function parseDirectives (mapping: Record<string, any>, shortcuts?: Shortcuts): Directive[] {
@@ -132,7 +132,7 @@ function parseDirectives (mapping: Record<string, any>, shortcuts?: Shortcuts):
132
132
 
133
133
  function parseDirective (key: string, value: any, shortcuts?: Shortcuts): Directive | null {
134
134
  if (shortcuts?.has(key) === true)
135
- key = shortcuts.get(key) as string
135
+ key = shortcuts.get(key)! // eslint-disable-line @typescript-eslint/no-non-null-assertion
136
136
 
137
137
  const match = key.match(DIRECTIVE_RX)
138
138
 
@@ -45,4 +45,4 @@ export interface Range {
45
45
  range: [number, number]
46
46
  }
47
47
 
48
- export const verbs = new Set<string>(['GET', 'POST', 'PUT', 'PATCH', 'DELETE'])
48
+ export const verbs = new Set<string>(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'])
@@ -1,3 +1,4 @@
1
+ import assert from 'node:assert'
1
2
  import { match } from 'matchacho'
2
3
  import * as http from '../../HTTP'
3
4
  import { Anonymous } from './Anonymous'
@@ -9,9 +10,10 @@ import { Echo } from './Echo'
9
10
  import { split } from './split'
10
11
  import { Scheme } from './Scheme'
11
12
  import { PRIMARY, PROVIDERS } from './schemes'
13
+ import type { Output } from '../../io'
12
14
  import type { Component } from '@toa.io/core'
13
15
  import type { Remotes } from '../../Remotes'
14
- import type { Family, Output } from '../../Directive'
16
+ import type { Family } from '../../Directive'
15
17
  import type { Parameter } from '../../RTD'
16
18
  import type {
17
19
  AuthenticationResult,
@@ -25,7 +27,8 @@ import type {
25
27
  Schemes
26
28
  } from './types'
27
29
 
28
- class Authorization implements Family<Directive, Extension> {
30
+ export class Authorization implements Family<Directive, Extension> {
31
+ public readonly depends: string[] = ['Vary']
29
32
  public readonly name: string = 'auth'
30
33
  public readonly mandatory: boolean = true
31
34
 
@@ -35,23 +38,24 @@ class Authorization implements Family<Directive, Extension> {
35
38
  private bans: Component | null = null
36
39
 
37
40
  public create (name: string, value: any, remotes: Remotes): Directive {
38
- const Class = CLASSES[name]
41
+ assert.ok(name in CLASSES,
42
+ `Directive '${name}' is not provided by the '${this.name}' family.`)
39
43
 
40
- if (Class === undefined)
41
- throw new Error(`Directive '${name}' is not provided by the '${this.name}' family.`)
44
+ const Class = CLASSES[name]
42
45
 
43
46
  for (const name of REMOTES)
44
47
  this.discovery[name] ??= remotes.discover('identity', name)
45
48
 
46
49
  return match(Class,
47
- Role, () => new Role(value, this.discovery.roles),
48
- Rule, () => new Rule(value, this.create.bind(this)),
49
- Incept, () => new Incept(value, this.discovery),
50
+ Role, () => new Role(value as string | string[], this.discovery.roles),
51
+ Rule, () => new Rule(value as Record<string, string>, this.create.bind(this)),
52
+ Incept, () => new Incept(value as string, this.discovery),
50
53
  () => new Class(value))
51
54
  }
52
55
 
53
- public async preflight
54
- (directives: Directive[], input: Input, parameters: Parameter[]): Promise<Output> {
56
+ public async preflight (directives: Directive[],
57
+ input: Input,
58
+ parameters: Parameter[]): Promise<Output> {
55
59
  const identity = await this.resolve(input.headers.authorization)
56
60
 
57
61
  input.identity = identity
@@ -67,37 +71,32 @@ class Authorization implements Family<Directive, Extension> {
67
71
  else throw new http.Forbidden()
68
72
  }
69
73
 
70
- public async settle
71
- (directives: Directive[], request: Input, response: http.OutgoingMessage): Promise<void> {
72
- for (const directive of directives)
73
- await directive.settle?.(request, response)
74
+ public async settle (directives: Directive[],
75
+ request: Input,
76
+ response: http.OutgoingMessage): Promise<void> {
77
+ for (const directive of directives) await directive.settle?.(request, response)
74
78
 
75
79
  const identity = request.identity
76
80
 
77
- if (identity === null)
78
- return
81
+ if (identity === null) return
79
82
 
80
- if (identity.scheme === PRIMARY && !identity.refresh)
81
- return
83
+ if (identity.scheme === PRIMARY && !identity.refresh) return
82
84
 
83
85
  // Role directive may have already set the value
84
- if (identity.roles === undefined)
85
- await Role.set(identity, this.discovery.roles)
86
+ if (identity.roles === undefined) await Role.set(identity, this.discovery.roles)
86
87
 
87
88
  this.tokens ??= await this.discovery.tokens
88
89
 
89
90
  const token = await this.tokens.invoke<string>('encrypt', { input: { identity } })
90
91
  const authorization = `Token ${token}`
91
92
 
92
- if (response.headers === undefined)
93
- response.headers = new Headers()
93
+ if (response.headers === undefined) response.headers = new Headers()
94
94
 
95
95
  response.headers.set('authorization', authorization)
96
96
  }
97
97
 
98
98
  private async resolve (authorization: string | undefined): Promise<Identity | null> {
99
- if (authorization === undefined)
100
- return null
99
+ if (authorization === undefined) return null
101
100
 
102
101
  const [scheme, credentials] = split(authorization)
103
102
  const provider = PROVIDERS[scheme]
@@ -107,16 +106,15 @@ class Authorization implements Family<Directive, Extension> {
107
106
 
108
107
  this.schemes[scheme] ??= await this.discovery[provider]
109
108
 
110
- const result = await this.schemes[scheme]
111
- .invoke<AuthenticationResult>('authenticate', { input: credentials })
109
+ const result = await this.schemes[scheme].invoke<AuthenticationResult>('authenticate', {
110
+ input: credentials
111
+ })
112
112
 
113
- if (result instanceof Error)
114
- return null
113
+ if (result instanceof Error) return null
115
114
 
116
115
  const identity = result.identity
117
116
 
118
- if (scheme !== PRIMARY && await this.banned(identity))
119
- throw new http.Unauthorized()
117
+ if (scheme !== PRIMARY && (await this.banned(identity))) throw new http.Unauthorized()
120
118
 
121
119
  identity.scheme = scheme
122
120
  identity.refresh = result.refresh
@@ -143,6 +141,4 @@ const CLASSES: Record<string, new (value: any, argument?: any) => Directive> = {
143
141
  echo: Echo
144
142
  }
145
143
 
146
- const REMOTES: Remote[] = ['basic', 'tokens', 'roles', 'bans']
147
-
148
- export = new Authorization()
144
+ const REMOTES: Remote[] = ['basic', 'federation', 'tokens', 'roles', 'bans']
@@ -25,7 +25,7 @@ export class Incept implements Directive {
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 as string)
28
+ const [scheme, credentials] = split(request.headers.authorization!)
29
29
  const provider = PROVIDERS[scheme]
30
30
 
31
31
  this.schemes[scheme] ??= await this.discovery[provider]
@@ -1,5 +1,5 @@
1
1
  import { type Parameter } from '../../RTD'
2
- import { type Directive, type Identity } from './types'
2
+ import type { Input, Directive, Identity } from './types'
3
3
 
4
4
  export class Rule implements Directive {
5
5
  private readonly directives: Directive[] = []
@@ -13,7 +13,7 @@ export class Rule implements Directive {
13
13
  }
14
14
 
15
15
  public async authorize
16
- (identity: Identity | null, input: any, parameters: Parameter[]): Promise<boolean> {
16
+ (identity: Identity | null, input: Input, parameters: Parameter[]): Promise<boolean> {
17
17
  for (const directive of this.directives) {
18
18
  const authorized = await directive.authorize(identity, input, parameters)
19
19
 
@@ -1,3 +1,3 @@
1
- import Family from './Family'
1
+ import { Authorization } from './Authorization'
2
2
 
3
- export = Family
3
+ export const authorization = new Authorization()
@@ -2,7 +2,8 @@ import { type Remote, type Scheme } from './types'
2
2
 
3
3
  export const PROVIDERS: Record<Scheme, Remote> = {
4
4
  basic: 'basic',
5
- token: 'tokens'
5
+ token: 'tokens',
6
+ bearer: 'federation'
6
7
  }
7
8
 
8
9
  export const PRIMARY: Scheme = 'token'
@@ -2,11 +2,14 @@ import { type Component } from '@toa.io/core'
2
2
  import { type Maybe } from '@toa.io/types'
3
3
  import { type Parameter } from '../../RTD'
4
4
  import type * as http from '../../HTTP'
5
- import type * as directive from '../../Directive'
5
+ import type * as io from '../../io'
6
6
 
7
7
  export interface Directive {
8
- authorize: (identity: Identity | null, input: Input, parameters: Parameter[]) =>
9
- boolean | Promise<boolean>
8
+ authorize: (
9
+ identity: Identity | null,
10
+ input: Input,
11
+ parameters: Parameter[],
12
+ ) => boolean | Promise<boolean>
10
13
 
11
14
  reply?: (identity: Identity | null) => http.OutgoingMessage
12
15
 
@@ -28,10 +31,10 @@ export interface Ban {
28
31
  banned: boolean
29
32
  }
30
33
 
31
- export type Input = directive.Input & Extension
34
+ export type Input = io.Input & Extension
32
35
  export type AuthenticationResult = Maybe<{ identity: Identity, refresh: boolean }>
33
36
 
34
- export type Scheme = 'basic' | 'token'
35
- export type Remote = 'basic' | 'tokens' | 'roles' | 'bans'
37
+ export type Scheme = 'basic' | 'token' | 'bearer'
38
+ export type Remote = 'basic' | 'federation' | 'tokens' | 'roles' | 'bans'
36
39
  export type Discovery = Record<Remote, Promise<Component>>
37
40
  export type Schemes = Record<Scheme, Component>
@@ -1,10 +1,11 @@
1
- import { type Input, type Output, type Family } from '../../Directive'
2
1
  import { Control } from './Control'
3
- import { type Directive } from './types'
4
2
  import { Exact } from './Exact'
3
+ import type { Input, Output } from '../../io'
4
+ import type { Directive } from './types'
5
+ import type { Family } from '../../Directive'
5
6
  import type * as http from '../../HTTP'
6
7
 
7
- class Cache implements Family<Directive> {
8
+ export class Cache implements Family<Directive> {
8
9
  public readonly name: string = 'cache'
9
10
  public readonly mandatory: boolean = false
10
11
 
@@ -32,5 +33,3 @@ const constructors: Record<string, new (value: any) => Directive> = {
32
33
  control: Control,
33
34
  exact: Exact
34
35
  }
35
-
36
- export = new Cache()
@@ -1,3 +1,3 @@
1
- import Family from './Family'
1
+ import { Cache } from './Cache'
2
2
 
3
- export = Family
3
+ export const cache = new Cache()
@@ -1,4 +1,4 @@
1
- import { type Input } from '../../Directive'
1
+ import type { Input } from '../../io'
2
2
 
3
3
  export interface Directive {
4
4
  set: (input: Input, headers: Headers) => void
@@ -0,0 +1,52 @@
1
+ import type { OutgoingMessage } from '../../HTTP'
2
+ import type { Input, Output } from '../../io'
3
+ import type { Family } from '../../Directive'
4
+ import type { Interceptor } from '../../Interception'
5
+
6
+ export class CORS implements Family, Interceptor {
7
+ public readonly name = 'cors'
8
+ public readonly mandatory = true
9
+
10
+ private readonly allowedHeaders = new Set<string>(['accept', 'content-type'])
11
+
12
+ private readonly headers = new Headers({
13
+ 'access-control-allow-methods': 'GET, POST, PUT, PATCH, DELETE',
14
+ 'access-control-allow-credentials': 'true',
15
+ 'access-control-allow-headers': Array.from(this.allowedHeaders).join(', '),
16
+ 'access-control-max-age': '86400',
17
+ 'cache-control': 'public, max-age=86400',
18
+ vary: 'origin'
19
+ })
20
+
21
+ public create (): null {
22
+ return null
23
+ }
24
+
25
+ public settle (_: unknown[], input: Input, output: OutgoingMessage): void {
26
+ if (input.headers.origin === undefined)
27
+ return
28
+
29
+ output.headers ??= new Headers()
30
+ output.headers.set('access-control-allow-origin', input.headers.origin)
31
+ output.headers.set('access-control-expose-headers',
32
+ 'authorization, content-type, content-length, etag')
33
+ output.headers.append('vary', 'origin')
34
+ }
35
+
36
+ public intercept (input: Input): Output {
37
+ if (input.method !== 'OPTIONS' || input.headers.origin === undefined)
38
+ return null
39
+
40
+ this.headers.set('access-control-allow-origin', input.headers.origin)
41
+
42
+ return {
43
+ status: 204,
44
+ headers: this.headers
45
+ }
46
+ }
47
+
48
+ public allowHeader (header: string): void {
49
+ this.allowedHeaders.add(header.toLowerCase())
50
+ this.headers.set('access-control-allow-headers', Array.from(this.allowedHeaders).join(', '))
51
+ }
52
+ }
@@ -0,0 +1,3 @@
1
+ import { CORS } from './CORS'
2
+
3
+ export const cors = new CORS()
@@ -1,9 +1,10 @@
1
- import { type Input, type Output, type Family } from '../../Directive'
2
1
  import { Stub } from './Stub'
3
2
  import { Throw } from './Throw'
4
3
  import { type Directive } from './types'
4
+ import type { Input, Output } from '../../io'
5
+ import type { Family } from '../../Directive'
5
6
 
6
- class Development implements Family<Directive> {
7
+ export class Development implements Family<Directive> {
7
8
  public readonly name: string = 'dev'
8
9
  public readonly mandatory: boolean = false
9
10
 
@@ -32,5 +33,3 @@ const constructors: Record<string, new (value: any) => Directive> = {
32
33
  stub: Stub,
33
34
  throw: Throw
34
35
  }
35
-
36
- export = new Development()
@@ -1,10 +1,10 @@
1
- import { type Output } from '../../Directive'
2
- import { type Directive } from './types'
1
+ import type { Output } from '../../io'
2
+ import type { Directive } from './types'
3
3
 
4
4
  export class Stub implements Directive {
5
- private readonly value: any
5
+ private readonly value: unknown
6
6
 
7
- public constructor (value: any) {
7
+ public constructor (value: unknown) {
8
8
  this.value = value
9
9
  }
10
10
 
@@ -1,10 +1,10 @@
1
- import { type Output } from '../../Directive'
2
- import { type Directive } from './types'
1
+ import type { Output } from '../../io'
2
+ import type { Directive } from './types'
3
3
 
4
4
  export class Throw implements Directive {
5
- private readonly message: any
5
+ private readonly message: string
6
6
 
7
- public constructor (message: any) {
7
+ public constructor (message: string) {
8
8
  this.message = message
9
9
  }
10
10
 
@@ -1,3 +1,3 @@
1
- import Family from './Family'
1
+ import { Development } from './Development'
2
2
 
3
- export = Family
3
+ export const dev = new Development()
@@ -1,4 +1,4 @@
1
- import { type Input, type Output } from '../../Directive'
1
+ import type { Input, Output } from '../../io'
2
2
 
3
3
  export interface Directive {
4
4
  apply: (input: Input) => Output
@@ -1,7 +1,11 @@
1
- import { type Family } from '../Directive'
2
- import Dev from './dev'
3
- import Auth from './auth'
4
- import Cache from './cache'
5
- import Octets from './octets'
1
+ import { dev } from './dev'
2
+ import { authorization } from './auth'
3
+ import { cache } from './cache'
4
+ import { octets } from './octets'
5
+ import { cors } from './cors'
6
+ import { vary } from './vary'
7
+ import type { Family } from '../Directive'
8
+ import type { Interceptor } from '../Interception'
6
9
 
7
- export const families: Family[] = [Auth, Octets, Dev, Cache]
10
+ export const families: Family[] = [authorization, cache, octets, cors, vary, dev]
11
+ export const interceptors: Interceptor[] = [cors]
@@ -1,5 +1,5 @@
1
1
  import * as schemas from './schemas'
2
- import type { Output } from '../../Directive'
2
+ import type { Output } from '../../io'
3
3
  import type { Directive } from './types'
4
4
 
5
5
  export class Context implements Directive {
@@ -2,8 +2,7 @@ import { NotFound } from '../../HTTP'
2
2
  import * as schemas from './schemas'
3
3
  import type { Maybe } from '@toa.io/types'
4
4
  import type { Component } from '@toa.io/core'
5
- import type { Output } from '../../Directive'
6
-
5
+ import type { Output } from '../../io'
7
6
  import type { Directive, Input } from './types'
8
7
 
9
8
  export class Delete implements Directive {
@@ -5,7 +5,7 @@ import type { Maybe } from '@toa.io/types'
5
5
  import type { Entry } from '@toa.io/extensions.storages'
6
6
  import type { Readable } from 'node:stream'
7
7
  import type { Component } from '@toa.io/core'
8
- import type { Output } from '../../Directive'
8
+ import type { Output } from '../../io'
9
9
 
10
10
  import type { Directive, Input } from './types'
11
11
 
@@ -2,7 +2,7 @@ import { NotFound } from '../../HTTP'
2
2
  import * as schemas from './schemas'
3
3
  import type { Maybe } from '@toa.io/types'
4
4
  import type { Component } from '@toa.io/core'
5
- import type { Output } from '../../Directive'
5
+ import type { Output } from '../../io'
6
6
 
7
7
  import type { Directive, Input } from './types'
8
8
 
@@ -5,12 +5,13 @@ import { Fetch } from './Fetch'
5
5
  import { List } from './List'
6
6
  import { Delete } from './Delete'
7
7
  import { Permute } from './Permute'
8
+ import type { Output } from '../../io'
8
9
  import type { Component } from '@toa.io/core'
9
10
  import type { Remotes } from '../../Remotes'
10
- import type { Output, Family } from '../../Directive'
11
+ import type { Family } from '../../Directive'
11
12
  import type { Directive, Input } from './types'
12
13
 
13
- class Octets implements Family<Directive> {
14
+ export class Octets implements Family<Directive> {
14
15
  public readonly name: string = 'octets'
15
16
  public readonly mandatory: boolean = false
16
17
 
@@ -64,5 +65,3 @@ const DIRECTIVES: Record<string, Constructor> = {
64
65
  }
65
66
 
66
67
  type Constructor = new (value: any, discovery: Promise<Component>, remotes: Remotes) => Directive
67
-
68
- export = new Octets()
@@ -2,7 +2,7 @@ import { NotAcceptable, NotFound } from '../../HTTP'
2
2
  import * as schemas from './schemas'
3
3
  import type { Maybe } from '@toa.io/types'
4
4
  import type { Component } from '@toa.io/core'
5
- import type { Output } from '../../Directive'
5
+ import type { Output } from '../../io'
6
6
 
7
7
  import type { Directive, Input } from './types'
8
8
 
@@ -9,7 +9,7 @@ import type { Entry } from '@toa.io/extensions.storages'
9
9
  import type { Remotes } from '../../Remotes'
10
10
  import type { ErrorType } from 'error-value'
11
11
  import type { Component } from '@toa.io/core'
12
- import type { Output } from '../../Directive'
12
+ import type { Output } from '../../io'
13
13
  import type { Directive, Input } from './types'
14
14
 
15
15
  export class Store implements Directive {
@@ -44,7 +44,7 @@ export class Store implements Directive {
44
44
  this.storage ??= await this.discovery.storage
45
45
 
46
46
  const input = { storage, request, accept: this.accept }
47
- const entry = await this.storage.invoke('store', { input })
47
+ const entry = await this.storage.invoke<Entry>('store', { input })
48
48
 
49
49
  return match<Output>(entry,
50
50
  Error, (error: ErrorType) => this.throw(error),
@@ -80,7 +80,7 @@ export class Store implements Directive {
80
80
  const path = posix.join(request.path, entry.id)
81
81
  let interrupted = false
82
82
 
83
- for (const unit of this.workflow as Workflow) {
83
+ for (const unit of this.workflow!) {
84
84
  if (interrupted)
85
85
  break
86
86
 
@@ -1,3 +1,3 @@
1
- import Family from './Family'
1
+ import { Octets } from './Octets'
2
2
 
3
- export = Family
3
+ export const octets = new Octets()
@@ -1,13 +1,13 @@
1
- import type * as directive from '../../Directive'
1
+ import type * as io from '../../io'
2
2
 
3
3
  export interface Directive {
4
4
  readonly targeted: boolean
5
5
 
6
- apply: (storage: string, input: Input) => directive.Output | Promise<directive.Output>
6
+ apply: (storage: string, input: Input) => io.Output | Promise<io.Output>
7
7
  }
8
8
 
9
9
  export interface Extension {
10
10
  octets?: string
11
11
  }
12
12
 
13
- export type Input = directive.Input & Extension
13
+ export type Input = io.Input & Extension
@@ -0,0 +1,6 @@
1
+ import type { Properties } from './Properties'
2
+ import type { Input } from '../../io'
3
+
4
+ export interface Directive {
5
+ preflight: (request: Input, properties: Properties) => void
6
+ }
@@ -0,0 +1,62 @@
1
+ import assert from 'node:assert'
2
+ import { Header, embeddings } from './embeddings'
3
+ import type { Embedding } from './embeddings'
4
+ import type { Input } from '../../io'
5
+ import type { Directive } from './Directive'
6
+ import type { Properties } from './Properties'
7
+
8
+ export class Embed implements Directive {
9
+ private readonly embeddings: Array<[string, Embedding[]]> = []
10
+
11
+ public constructor (map: Record<string, string | string[]>) {
12
+ for (const [key, values] of Object.entries(map)) {
13
+ const names = Array.isArray(values) ? values : [values]
14
+
15
+ const instances = names.map((name) => {
16
+ if (name[0] === ':')
17
+ return new Header(name.slice(1))
18
+
19
+ assert.ok(name in embeddings, `Unknown embedding: ${name}`)
20
+
21
+ return new embeddings[name]()
22
+ })
23
+
24
+ this.embeddings.push([key, instances])
25
+ }
26
+ }
27
+
28
+ public preflight (input: Input, properties: Properties): void {
29
+ const values: Record<string, unknown> = {}
30
+
31
+ for (const [key, instances] of this.embeddings)
32
+ values[key] = this.resolve(instances, input, properties)
33
+
34
+ input.pipelines.body.push(this.embedding(values))
35
+ }
36
+
37
+ private resolve (instances: Embedding[], input: Input, properties: Properties): unknown {
38
+ let value
39
+
40
+ for (const instance of instances) {
41
+ value = instance.resolve(input, properties)
42
+
43
+ if (value !== undefined)
44
+ break
45
+ }
46
+
47
+ assert.ok(value !== undefined, 'Neither embedding resolved a value.')
48
+
49
+ return value
50
+ }
51
+
52
+ private embedding (values: Record<string, unknown>) {
53
+ return (body: unknown): object => {
54
+ if (body === undefined || body === null || typeof body !== 'object')
55
+ return values
56
+
57
+ Object.assign(body, values)
58
+
59
+ return body
60
+ }
61
+ }
62
+ }