@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.
- package/components/identity.bans/manifest.toa.yaml +1 -0
- package/components/identity.basic/manifest.toa.yaml +1 -0
- package/components/identity.basic/operations/tsconfig.tsbuildinfo +1 -1
- package/components/identity.federation/manifest.toa.yaml +1 -0
- package/components/identity.federation/operations/tsconfig.tsbuildinfo +1 -1
- package/components/identity.roles/manifest.toa.yaml +1 -0
- package/components/identity.roles/operations/tsconfig.tsbuildinfo +1 -1
- package/components/identity.tokens/manifest.toa.yaml +1 -0
- package/components/identity.tokens/operations/tsconfig.tsbuildinfo +1 -1
- package/components/octets.storage/manifest.toa.yaml +1 -0
- package/components/octets.storage/operations/store.js +1 -1
- package/documentation/components.md +5 -5
- package/documentation/query.md +45 -2
- package/features/body.feature +1 -1
- package/features/cors.feature +2 -2
- package/features/errors.feature +1 -1
- package/features/etag.feature +96 -0
- package/features/octets.entries.feature +1 -1
- package/features/steps/Gateway.ts +3 -0
- package/features/steps/components/echo/manifest.toa.yaml +1 -0
- package/features/steps/components/greeter/manifest.toa.yaml +1 -0
- package/features/steps/components/octets.tester/manifest.toa.yaml +1 -0
- package/features/steps/components/pots/manifest.toa.yaml +10 -3
- package/features/steps/components/sequences/manifest.toa.yaml +1 -0
- package/features/timing.feature +43 -0
- package/package.json +7 -10
- package/readme.md +7 -6
- package/schemas/annotation.cos.yaml +1 -0
- package/schemas/querystring.cos.yaml +1 -0
- package/source/Annotation.ts +1 -0
- package/source/Context.ts +6 -4
- package/source/Directive.test.ts +7 -7
- package/source/Directive.ts +16 -42
- package/source/Endpoint.ts +55 -6
- package/source/Factory.ts +17 -7
- package/source/Gateway.ts +38 -50
- package/source/HTTP/Context.ts +67 -0
- package/source/HTTP/Server.test.ts +1 -1
- package/source/HTTP/Server.ts +60 -95
- package/source/HTTP/Timing.ts +40 -0
- package/source/HTTP/index.ts +1 -0
- package/source/HTTP/messages.test.ts +27 -8
- package/source/HTTP/messages.ts +32 -48
- package/source/Mapping.ts +7 -8
- package/source/RTD/Context.ts +7 -10
- package/source/RTD/Directives.ts +28 -4
- package/source/RTD/Endpoint.ts +6 -4
- package/source/RTD/Match.ts +2 -7
- package/source/RTD/Method.ts +7 -13
- package/source/RTD/Node.ts +13 -14
- package/source/RTD/Tree.ts +17 -16
- package/source/RTD/factory.ts +2 -5
- package/source/deployment.ts +6 -0
- package/source/directives/auth/Anonymous.ts +3 -2
- package/source/directives/auth/Authorization.ts +7 -6
- package/source/directives/auth/Incept.ts +11 -6
- package/source/directives/auth/Role.ts +5 -3
- package/source/directives/auth/Scheme.ts +2 -2
- package/source/directives/cache/Cache.ts +4 -4
- package/source/directives/cache/Control.ts +5 -5
- package/source/directives/cache/types.ts +1 -1
- package/source/directives/cors/CORS.ts +18 -10
- package/source/directives/dev/Development.ts +3 -3
- package/source/directives/index.ts +2 -2
- package/source/directives/octets/Context.ts +1 -1
- package/source/directives/octets/Delete.ts +19 -9
- package/source/directives/octets/Fetch.ts +29 -14
- package/source/directives/octets/List.ts +14 -6
- package/source/directives/octets/Octets.ts +6 -5
- package/source/directives/octets/Permute.ts +12 -6
- package/source/directives/octets/Store.ts +25 -17
- package/source/directives/octets/Workflow.ts +3 -3
- package/source/directives/octets/workflows/Workflow.ts +2 -2
- package/source/directives/vary/Vary.ts +3 -3
- package/source/directives/vary/embeddings/Header.ts +2 -2
- package/source/directives/vary/embeddings/Language.ts +2 -2
- package/source/io.ts +2 -2
- package/transpiled/Annotation.d.ts +1 -0
- package/transpiled/Context.d.ts +6 -4
- package/transpiled/Directive.d.ts +8 -21
- package/transpiled/Directive.js +8 -11
- package/transpiled/Directive.js.map +1 -1
- package/transpiled/Endpoint.d.ts +7 -5
- package/transpiled/Endpoint.js +60 -2
- package/transpiled/Endpoint.js.map +1 -1
- package/transpiled/Factory.js +8 -2
- package/transpiled/Factory.js.map +1 -1
- package/transpiled/Gateway.d.ts +4 -8
- package/transpiled/Gateway.js +22 -32
- package/transpiled/Gateway.js.map +1 -1
- package/transpiled/HTTP/Context.d.ts +24 -0
- package/transpiled/HTTP/Context.js +47 -0
- package/transpiled/HTTP/Context.js.map +1 -0
- package/transpiled/HTTP/Server.d.ts +8 -7
- package/transpiled/HTTP/Server.js +68 -76
- package/transpiled/HTTP/Server.js.map +1 -1
- package/transpiled/HTTP/Timing.d.ts +10 -0
- package/transpiled/HTTP/Timing.js +29 -0
- package/transpiled/HTTP/Timing.js.map +1 -0
- package/transpiled/HTTP/index.d.ts +1 -0
- package/transpiled/HTTP/index.js +1 -0
- package/transpiled/HTTP/index.js.map +1 -1
- package/transpiled/HTTP/messages.d.ts +7 -21
- package/transpiled/HTTP/messages.js +24 -26
- package/transpiled/HTTP/messages.js.map +1 -1
- package/transpiled/Mapping.js +7 -7
- package/transpiled/Mapping.js.map +1 -1
- package/transpiled/RTD/Context.d.ts +7 -6
- package/transpiled/RTD/Directives.d.ts +19 -4
- package/transpiled/RTD/Endpoint.d.ts +6 -4
- package/transpiled/RTD/Match.d.ts +2 -4
- package/transpiled/RTD/Method.d.ts +7 -7
- package/transpiled/RTD/Method.js.map +1 -1
- package/transpiled/RTD/Node.d.ts +4 -6
- package/transpiled/RTD/Node.js +2 -1
- package/transpiled/RTD/Node.js.map +1 -1
- package/transpiled/RTD/Tree.d.ts +6 -6
- package/transpiled/RTD/Tree.js +4 -1
- package/transpiled/RTD/Tree.js.map +1 -1
- package/transpiled/RTD/factory.d.ts +2 -4
- package/transpiled/RTD/factory.js.map +1 -1
- package/transpiled/deployment.js +5 -0
- package/transpiled/deployment.js.map +1 -1
- package/transpiled/directives/auth/Anonymous.js +3 -4
- package/transpiled/directives/auth/Anonymous.js.map +1 -1
- package/transpiled/directives/auth/Authorization.d.ts +2 -3
- package/transpiled/directives/auth/Authorization.js +1 -1
- package/transpiled/directives/auth/Authorization.js.map +1 -1
- package/transpiled/directives/auth/Incept.d.ts +1 -1
- package/transpiled/directives/auth/Incept.js +11 -6
- package/transpiled/directives/auth/Incept.js.map +1 -1
- package/transpiled/directives/auth/Role.js +5 -3
- package/transpiled/directives/auth/Role.js.map +1 -1
- package/transpiled/directives/auth/Scheme.js +2 -2
- package/transpiled/directives/auth/Scheme.js.map +1 -1
- package/transpiled/directives/cache/Cache.d.ts +3 -3
- package/transpiled/directives/cache/Cache.js +2 -2
- package/transpiled/directives/cache/Cache.js.map +1 -1
- package/transpiled/directives/cache/Control.d.ts +3 -3
- package/transpiled/directives/cache/Control.js +3 -3
- package/transpiled/directives/cache/Control.js.map +1 -1
- package/transpiled/directives/cache/types.d.ts +1 -1
- package/transpiled/directives/cors/CORS.d.ts +2 -3
- package/transpiled/directives/cors/CORS.js +17 -10
- package/transpiled/directives/cors/CORS.js.map +1 -1
- package/transpiled/directives/dev/Development.d.ts +3 -3
- package/transpiled/directives/dev/Development.js.map +1 -1
- package/transpiled/directives/index.d.ts +2 -2
- package/transpiled/directives/index.js.map +1 -1
- package/transpiled/directives/octets/Context.d.ts +1 -1
- package/transpiled/directives/octets/Context.js.map +1 -1
- package/transpiled/directives/octets/Delete.d.ts +1 -1
- package/transpiled/directives/octets/Delete.js +19 -9
- package/transpiled/directives/octets/Delete.js.map +1 -1
- package/transpiled/directives/octets/Fetch.d.ts +1 -1
- package/transpiled/directives/octets/Fetch.js +28 -14
- package/transpiled/directives/octets/Fetch.js.map +1 -1
- package/transpiled/directives/octets/List.d.ts +1 -1
- package/transpiled/directives/octets/List.js +13 -6
- package/transpiled/directives/octets/List.js.map +1 -1
- package/transpiled/directives/octets/Octets.d.ts +2 -3
- package/transpiled/directives/octets/Octets.js +4 -2
- package/transpiled/directives/octets/Octets.js.map +1 -1
- package/transpiled/directives/octets/Permute.d.ts +1 -1
- package/transpiled/directives/octets/Permute.js +11 -6
- package/transpiled/directives/octets/Permute.js.map +1 -1
- package/transpiled/directives/octets/Store.d.ts +1 -1
- package/transpiled/directives/octets/Store.js +17 -12
- package/transpiled/directives/octets/Store.js.map +1 -1
- package/transpiled/directives/octets/Workflow.d.ts +1 -1
- package/transpiled/directives/octets/Workflow.js +3 -3
- package/transpiled/directives/octets/Workflow.js.map +1 -1
- package/transpiled/directives/octets/workflows/Workflow.d.ts +1 -1
- package/transpiled/directives/octets/workflows/Workflow.js +2 -2
- package/transpiled/directives/octets/workflows/Workflow.js.map +1 -1
- package/transpiled/directives/vary/Vary.d.ts +2 -2
- package/transpiled/directives/vary/Vary.js +1 -1
- package/transpiled/directives/vary/embeddings/Header.js +2 -2
- package/transpiled/directives/vary/embeddings/Header.js.map +1 -1
- package/transpiled/directives/vary/embeddings/Language.js +2 -2
- package/transpiled/directives/vary/embeddings/Language.js.map +1 -1
- package/transpiled/io.d.ts +2 -2
- package/transpiled/tsconfig.tsbuildinfo +1 -1
- package/source/HTTP/Server.fixtures.ts +0 -40
- package/transpiled/HTTP/Server.fixtures.d.ts +0 -10
- package/transpiled/HTTP/Server.fixtures.js +0 -31
- package/transpiled/HTTP/Server.fixtures.js.map +0 -1
package/source/RTD/Method.ts
CHANGED
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import type { Endpoint } from './Endpoint'
|
|
2
|
+
import type { Directives } from './Directives'
|
|
3
3
|
|
|
4
|
-
export class Method
|
|
5
|
-
|
|
6
|
-
|
|
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:
|
|
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>
|
package/source/RTD/Node.ts
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
32
|
+
public merge (node: Node): void {
|
|
38
33
|
this.intermediate = node.intermediate
|
|
39
34
|
|
|
40
|
-
if (!this.protected)
|
|
41
|
-
|
|
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
|
|
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()
|
|
50
|
+
void method.close()
|
|
51
|
+
|
|
52
|
+
// race condition is really unlikely
|
|
54
53
|
}
|
|
55
54
|
|
|
56
|
-
private append (node: Node
|
|
55
|
+
private append (node: Node): void {
|
|
57
56
|
for (const route of node.routes)
|
|
58
57
|
this.mergeRoute(route)
|
|
59
58
|
|
package/source/RTD/Tree.ts
CHANGED
|
@@ -1,32 +1,32 @@
|
|
|
1
|
-
import { type Node } from './Node'
|
|
2
1
|
import { createNode } from './factory'
|
|
3
2
|
import { fragment } from './segment'
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import
|
|
7
|
-
import
|
|
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
|
|
16
|
-
private readonly endpoints: EndpointsFactory
|
|
17
|
-
private readonly directives:
|
|
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:
|
|
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
|
|
24
|
+
public match (path: string): Match | null {
|
|
28
25
|
if (path === '/')
|
|
29
|
-
return {
|
|
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
|
|
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,
|
package/source/RTD/factory.ts
CHANGED
|
@@ -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 {
|
|
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
|
|
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
|
package/source/deployment.ts
CHANGED
|
@@ -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
|
-
|
|
12
|
-
|
|
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 {
|
|
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
|
|
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)
|
|
71
|
-
|
|
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 (
|
|
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', {
|
|
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
|
-
|
|
40
|
-
|
|
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 = {
|
|
18
|
-
|
|
17
|
+
const query: Query = {
|
|
18
|
+
criteria: `identity==${identity.id}`,
|
|
19
|
+
limit: 1024
|
|
20
|
+
}
|
|
19
21
|
|
|
20
|
-
identity.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 {
|
|
5
|
+
import type { DirectiveFamily } from '../../RTD'
|
|
6
6
|
import type * as http from '../../HTTP'
|
|
7
7
|
|
|
8
|
-
export class Cache implements
|
|
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[],
|
|
26
|
+
(directives: Directive[], input: Input, response: http.OutgoingMessage): Promise<void> {
|
|
27
27
|
response.headers ??= new Headers()
|
|
28
|
-
directives[0]?.set(
|
|
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 {
|
|
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 (
|
|
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(
|
|
16
|
+
this.cache ??= this.resolve(context)
|
|
17
17
|
|
|
18
18
|
headers.set('cache-control', this.cache)
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
protected resolve (request:
|
|
21
|
+
protected resolve (request: AuthenticatedContext): string {
|
|
22
22
|
if (request.identity === null)
|
|
23
23
|
return this.value
|
|
24
24
|
|
|
@@ -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
|
|
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.
|
|
19
|
+
'access-control-allow-headers': Array.from(this.requestHeaders).join(', '),
|
|
14
20
|
'access-control-max-age': '3600',
|
|
15
|
-
'cache-control': '
|
|
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
|
-
|
|
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
|
|
42
|
-
this.
|
|
43
|
-
this.headers.set('access-control-allow-headers', Array.from(this.
|
|
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 {
|
|
5
|
+
import type { DirectiveFamily } from '../../RTD'
|
|
6
6
|
|
|
7
|
-
export class Development implements
|
|
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:
|
|
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 {
|
|
7
|
+
import type { DirectiveFamily } from '../RTD'
|
|
8
8
|
import type { Interceptor } from '../Interception'
|
|
9
9
|
|
|
10
|
-
export const families:
|
|
10
|
+
export const families: DirectiveFamily[] = [authorization, cache, octets, vary, dev]
|
|
11
11
|
export const interceptors: Interceptor[] = [cors]
|
|
@@ -27,11 +27,16 @@ export class Delete implements Directive {
|
|
|
27
27
|
this.discovery = discovery
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
public async apply (storage: string,
|
|
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
|
-
{
|
|
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(
|
|
48
|
+
output.body = Readable.from(this.execute(input, storage, entry, parameters))
|
|
44
49
|
} else
|
|
45
|
-
await this.delete(storage,
|
|
50
|
+
await this.delete(storage, input)
|
|
46
51
|
|
|
47
52
|
return output
|
|
48
53
|
}
|
|
49
54
|
|
|
50
|
-
private async delete (storage: string,
|
|
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
|
-
{
|
|
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
|
-
(
|
|
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(
|
|
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,
|
|
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> = {
|
|
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,
|
|
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 =
|
|
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,
|
|
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,
|
|
45
|
+
return await this.fetch(storage, input)
|
|
42
46
|
}
|
|
43
47
|
|
|
44
|
-
private async fetch (storage: string,
|
|
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
|
|
49
|
-
|
|
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 {
|
|
68
|
+
return {
|
|
69
|
+
headers,
|
|
70
|
+
body: result.stream
|
|
71
|
+
}
|
|
61
72
|
}
|
|
62
73
|
|
|
63
|
-
private async get (storage: string,
|
|
64
|
-
const
|
|
65
|
-
|
|
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,
|
|
25
|
+
public async apply (storage: string, input: Input): Promise<Output> {
|
|
26
26
|
this.storage ??= await this.discovery
|
|
27
27
|
|
|
28
|
-
const metadata =
|
|
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
|
|
34
|
-
|
|
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
|
-
|
|
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 })
|