@toa.io/extensions.exposition 1.0.0-alpha.46 → 1.0.0-alpha.48
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/documentation/flow.md +31 -0
- package/documentation/octets.md +0 -4
- package/features/flow.feature +45 -0
- package/features/steps/components/octets.tester/manifest.toa.yaml +12 -1
- package/features/steps/components/octets.tester/operations/redirect.js +7 -2
- package/package.json +7 -7
- package/schemas/octets/fetch.cos.yaml +0 -1
- package/source/directives/auth/Incept.ts +1 -1
- package/source/directives/flow/Fetch.ts +88 -0
- package/source/directives/flow/Flow.ts +34 -0
- package/source/directives/flow/index.ts +3 -0
- package/source/directives/flow/types.ts +6 -0
- package/source/directives/index.ts +2 -1
- package/source/directives/octets/Fetch.ts +4 -57
- package/transpiled/directives/auth/Incept.js +1 -1
- package/transpiled/directives/auth/Incept.js.map +1 -1
- package/transpiled/directives/flow/Fetch.d.ts +13 -0
- package/transpiled/directives/flow/Fetch.js +59 -0
- package/transpiled/directives/flow/Fetch.js.map +1 -0
- package/transpiled/directives/flow/Flow.d.ts +10 -0
- package/transpiled/directives/flow/Flow.js +27 -0
- package/transpiled/directives/flow/Flow.js.map +1 -0
- package/transpiled/directives/flow/index.d.ts +2 -0
- package/transpiled/directives/flow/index.js +6 -0
- package/transpiled/directives/flow/index.js.map +1 -0
- package/transpiled/directives/flow/types.d.ts +5 -0
- package/transpiled/directives/flow/types.js +3 -0
- package/transpiled/directives/flow/types.js.map +1 -0
- package/transpiled/directives/index.js +2 -1
- package/transpiled/directives/index.js.map +1 -1
- package/transpiled/directives/octets/Fetch.d.ts +2 -9
- package/transpiled/directives/octets/Fetch.js +3 -41
- package/transpiled/directives/octets/Fetch.js.map +1 -1
- package/transpiled/tsconfig.tsbuildinfo +1 -1
- package/features/octets.redirect.feature +0 -27
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Request flow
|
|
2
|
+
|
|
3
|
+
## `flow:fetch`
|
|
4
|
+
|
|
5
|
+
Fetches the content from the resource returned by the specified endpoint.
|
|
6
|
+
|
|
7
|
+
The value of the directive is a `string` specifying endpoint to be called for the redirection
|
|
8
|
+
request.
|
|
9
|
+
|
|
10
|
+
Request `authority`, `path` and `parameters` are passed as input to the redirection endpoint,
|
|
11
|
+
and it must return a URL `string`, an `Error` or an object with the following properties:
|
|
12
|
+
|
|
13
|
+
```yaml
|
|
14
|
+
url: string
|
|
15
|
+
options?:
|
|
16
|
+
method?: string
|
|
17
|
+
headers?: Record<string, string>
|
|
18
|
+
body?: string
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
If it returns a URL or Request, then the response to the specified request is returned as the
|
|
22
|
+
response to the original request, along with the `content-type`, `content-length`, and `etag`
|
|
23
|
+
headers.
|
|
24
|
+
|
|
25
|
+
> Shortcut `redirect` is available.
|
|
26
|
+
|
|
27
|
+
```yaml
|
|
28
|
+
/:
|
|
29
|
+
GET:
|
|
30
|
+
flow:redirect: urls.resolve
|
|
31
|
+
```
|
package/documentation/octets.md
CHANGED
|
@@ -134,10 +134,6 @@ The value of the directive is an object with the following properties:
|
|
|
134
134
|
[BLOB variant](/extensions/storages/readme.md#async-fetchpath-string-maybereadable) must be
|
|
135
135
|
specified in the path otherwise.
|
|
136
136
|
Defaults to `true`.
|
|
137
|
-
- `redirect`: `string` specifying endpoint to be called for the redirection url.
|
|
138
|
-
|
|
139
|
-
Redirection endpoint must return a `string` or an `Error`.
|
|
140
|
-
Request `authority`, `path` and `parameters` are passed as input.
|
|
141
137
|
|
|
142
138
|
```yaml
|
|
143
139
|
/images:
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
Feature: Request flow
|
|
2
|
+
|
|
3
|
+
Scenario: Fetching url
|
|
4
|
+
Given the `octets.tester` is running
|
|
5
|
+
And the annotation:
|
|
6
|
+
"""yaml
|
|
7
|
+
/:
|
|
8
|
+
octets:context: octets
|
|
9
|
+
/:type:
|
|
10
|
+
GET:
|
|
11
|
+
anonymous: true
|
|
12
|
+
io:output: true
|
|
13
|
+
flow:fetch: octets.tester.redirect
|
|
14
|
+
"""
|
|
15
|
+
When the following request is received:
|
|
16
|
+
"""
|
|
17
|
+
GET /rfc HTTP/1.1
|
|
18
|
+
host: nex.toa.io
|
|
19
|
+
"""
|
|
20
|
+
Then the following reply is sent:
|
|
21
|
+
"""
|
|
22
|
+
200 OK
|
|
23
|
+
content-type: text/plain
|
|
24
|
+
|
|
25
|
+
Faster Than Light Speed Protocol (FLIP)
|
|
26
|
+
"""
|
|
27
|
+
When the following request is received:
|
|
28
|
+
"""
|
|
29
|
+
GET /img HTTP/1.1
|
|
30
|
+
host: nex.toa.io
|
|
31
|
+
"""
|
|
32
|
+
Then the following reply is sent:
|
|
33
|
+
"""
|
|
34
|
+
200 OK
|
|
35
|
+
content-type: image/svg+xml
|
|
36
|
+
"""
|
|
37
|
+
When the following request is received:
|
|
38
|
+
"""
|
|
39
|
+
GET /err HTTP/1.1
|
|
40
|
+
host: nex.toa.io
|
|
41
|
+
"""
|
|
42
|
+
Then the following reply is sent:
|
|
43
|
+
"""
|
|
44
|
+
404 Not Found
|
|
45
|
+
"""
|
|
@@ -26,4 +26,15 @@ operations:
|
|
|
26
26
|
authority*: string
|
|
27
27
|
path*: string
|
|
28
28
|
parameters: <string>
|
|
29
|
-
output:
|
|
29
|
+
output:
|
|
30
|
+
type: object
|
|
31
|
+
properties:
|
|
32
|
+
url: string
|
|
33
|
+
options:
|
|
34
|
+
type: object
|
|
35
|
+
properties:
|
|
36
|
+
method: string
|
|
37
|
+
headers: object
|
|
38
|
+
body: string
|
|
39
|
+
required:
|
|
40
|
+
- url
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
return
|
|
3
|
+
function redirect (input) {
|
|
4
|
+
return input.parameters.type in urls ? { url: urls[input.parameters.type] } : new Error()
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const urls = {
|
|
8
|
+
'rfc': 'https://www.rfc-editor.org/rfc/rfc9564.txt',
|
|
9
|
+
'img': 'https://www.w3.org/assets/logos/w3c/w3c-no-bars.svg'
|
|
5
10
|
}
|
|
6
11
|
|
|
7
12
|
exports.computation = redirect
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toa.io/extensions.exposition",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.48",
|
|
4
4
|
"description": "Toa Exposition",
|
|
5
5
|
"author": "temich <tema.gurtovoy@gmail.com>",
|
|
6
6
|
"homepage": "https://github.com/toa-io/toa#readme",
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
"access": "public"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@toa.io/core": "1.0.0-alpha.
|
|
21
|
-
"@toa.io/generic": "1.0.0-alpha.
|
|
22
|
-
"@toa.io/schemas": "1.0.0-alpha.
|
|
20
|
+
"@toa.io/core": "1.0.0-alpha.48",
|
|
21
|
+
"@toa.io/generic": "1.0.0-alpha.48",
|
|
22
|
+
"@toa.io/schemas": "1.0.0-alpha.48",
|
|
23
23
|
"bcryptjs": "2.4.3",
|
|
24
24
|
"error-value": "0.3.0",
|
|
25
25
|
"js-yaml": "4.1.0",
|
|
@@ -44,11 +44,11 @@
|
|
|
44
44
|
"features:security": "cucumber-js --tags @security"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@toa.io/agent": "1.0.0-alpha.
|
|
48
|
-
"@toa.io/extensions.storages": "1.0.0-alpha.
|
|
47
|
+
"@toa.io/agent": "1.0.0-alpha.48",
|
|
48
|
+
"@toa.io/extensions.storages": "1.0.0-alpha.48",
|
|
49
49
|
"@types/bcryptjs": "2.4.3",
|
|
50
50
|
"@types/cors": "2.8.13",
|
|
51
51
|
"@types/negotiator": "0.6.1"
|
|
52
52
|
},
|
|
53
|
-
"gitHead": "
|
|
53
|
+
"gitHead": "b0ace2cca06b1d29c5410ca1e244b04d32093fa9"
|
|
54
54
|
}
|
|
@@ -23,7 +23,7 @@ export class Incept implements Directive {
|
|
|
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
28
|
const [scheme, credentials] = split(input.request.headers.authorization!)
|
|
29
29
|
const provider = PROVIDERS[scheme]
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Readable } from 'node:stream'
|
|
2
|
+
import assert from 'node:assert'
|
|
3
|
+
import { match } from 'matchacho'
|
|
4
|
+
import { NotFound } from '../../HTTP'
|
|
5
|
+
import type { Directive } from './types'
|
|
6
|
+
import type { ReadableStream } from 'node:stream/web'
|
|
7
|
+
import type { Remotes } from '../../Remotes'
|
|
8
|
+
import type { Maybe } from '@toa.io/types'
|
|
9
|
+
import type { Component } from '@toa.io/core'
|
|
10
|
+
import type { Output } from '../../io'
|
|
11
|
+
import type { Input } from '../octets/types'
|
|
12
|
+
import type { Parameter } from '../../RTD'
|
|
13
|
+
|
|
14
|
+
export class Fetch implements Directive {
|
|
15
|
+
public readonly targeted = true
|
|
16
|
+
|
|
17
|
+
private readonly connecting: Promise<Component>
|
|
18
|
+
private remote: Component | null = null
|
|
19
|
+
private readonly operation: string
|
|
20
|
+
|
|
21
|
+
public constructor (endpoint: string, discovery: Remotes) {
|
|
22
|
+
assert.equal(typeof endpoint, 'string', '`flow:redirect` must be a string')
|
|
23
|
+
|
|
24
|
+
const [operation, name, namespace = 'default'] = endpoint.split('.').reverse()
|
|
25
|
+
|
|
26
|
+
this.operation = operation
|
|
27
|
+
this.connecting = discovery.discover(namespace, name)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
public async apply (input: Input, parameters: Parameter[]): Promise<Output> {
|
|
31
|
+
if ('if-none-match' in input.request.headers)
|
|
32
|
+
return { status: 304 }
|
|
33
|
+
|
|
34
|
+
this.remote ??= await this.connecting
|
|
35
|
+
|
|
36
|
+
const request = await this.remote.invoke<Maybe<Request | string>>(this.operation, {
|
|
37
|
+
input: {
|
|
38
|
+
authority: input.authority,
|
|
39
|
+
path: input.request.url,
|
|
40
|
+
parameters: Object.fromEntries(parameters.map(({ name, value }) => [name, value]))
|
|
41
|
+
}
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
if (request instanceof Error)
|
|
45
|
+
throw new NotFound(request)
|
|
46
|
+
|
|
47
|
+
const { url, options } = match<Request>(request,
|
|
48
|
+
String, { url: request },
|
|
49
|
+
(request: Request): Request => ({
|
|
50
|
+
url: request.url,
|
|
51
|
+
options: {
|
|
52
|
+
method: request.options?.method ?? 'GET',
|
|
53
|
+
body: request.options?.body,
|
|
54
|
+
headers: request.options?.headers
|
|
55
|
+
}
|
|
56
|
+
}))
|
|
57
|
+
|
|
58
|
+
const response = await fetch(url, options)
|
|
59
|
+
|
|
60
|
+
if (!response.ok)
|
|
61
|
+
throw new NotFound()
|
|
62
|
+
|
|
63
|
+
const headers = new Headers()
|
|
64
|
+
|
|
65
|
+
for (const header of ['content-type', 'content-length', 'etag']) {
|
|
66
|
+
const value = response.headers.get(header)
|
|
67
|
+
|
|
68
|
+
if (value !== null)
|
|
69
|
+
headers.set(header, value)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return {
|
|
73
|
+
headers,
|
|
74
|
+
body: response.body === null ? null : Readable.fromWeb(response.body as ReadableStream)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
interface Request {
|
|
80
|
+
url: string
|
|
81
|
+
options?: RequestOptions
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
interface RequestOptions {
|
|
85
|
+
method?: string
|
|
86
|
+
body?: string
|
|
87
|
+
headers?: Record<string, string>
|
|
88
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Fetch } from './Fetch'
|
|
2
|
+
import type { Directive } from './types'
|
|
3
|
+
import type { Input, Output } from '../../io'
|
|
4
|
+
import type { DirectiveFamily, Parameter } from '../../RTD'
|
|
5
|
+
import type { Remotes } from '../../Remotes'
|
|
6
|
+
|
|
7
|
+
export class Flow implements DirectiveFamily<Directive> {
|
|
8
|
+
public readonly name: string = 'flow'
|
|
9
|
+
public readonly mandatory: boolean = false
|
|
10
|
+
|
|
11
|
+
public create (name: string, value: unknown, remotes: Remotes): Directive {
|
|
12
|
+
const Class = constructors[name]
|
|
13
|
+
|
|
14
|
+
if (Class === undefined)
|
|
15
|
+
throw new Error(`Directive '${this.name}:${name}' is not implemented.`)
|
|
16
|
+
|
|
17
|
+
return new Class(value, remotes)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
public async preflight (directives: Directive[], input: Input, parameters: Parameter[]): Promise<Output> {
|
|
21
|
+
for (const directive of directives) {
|
|
22
|
+
const output = await directive.apply(input, parameters)
|
|
23
|
+
|
|
24
|
+
if (output !== null)
|
|
25
|
+
return output
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return null
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const constructors: Record<string, new (value: any, discovery: Remotes) => Directive> = {
|
|
33
|
+
fetch: Fetch
|
|
34
|
+
}
|
|
@@ -6,8 +6,9 @@ import { octets } from './octets'
|
|
|
6
6
|
import { io } from './io'
|
|
7
7
|
import { vary } from './vary'
|
|
8
8
|
import { req } from './require'
|
|
9
|
+
import { flow } from './flow'
|
|
9
10
|
import type { DirectiveFamily } from '../RTD'
|
|
10
11
|
import type { Interceptor } from '../Interception'
|
|
11
12
|
|
|
12
|
-
export const families: DirectiveFamily[] = [authorization, io, cache, vary, req, octets, dev]
|
|
13
|
+
export const families: DirectiveFamily[] = [authorization, io, cache, vary, req, flow, octets, dev]
|
|
13
14
|
export const interceptors: Interceptor[] = [cors]
|
|
@@ -1,45 +1,35 @@
|
|
|
1
1
|
import { posix } from 'node:path'
|
|
2
|
-
import { Readable } from 'node:stream'
|
|
3
2
|
import { Forbidden, NotFound } from '../../HTTP'
|
|
4
3
|
import * as schemas from './schemas'
|
|
5
4
|
import { Directive } from './Directive'
|
|
6
|
-
import type {
|
|
5
|
+
import type { Readable } from 'node:stream'
|
|
7
6
|
import type { Maybe } from '@toa.io/types'
|
|
8
7
|
import type { Entry } from '@toa.io/extensions.storages'
|
|
9
8
|
import type { Component } from '@toa.io/core'
|
|
10
9
|
import type { Output } from '../../io'
|
|
11
10
|
import type { Input } from './types'
|
|
12
|
-
import type { Parameter } from '../../RTD'
|
|
13
11
|
|
|
14
12
|
export class Fetch extends Directive {
|
|
15
13
|
public readonly targeted = true
|
|
16
14
|
|
|
17
15
|
private readonly options: Required<Options> = {
|
|
18
16
|
blob: true,
|
|
19
|
-
meta: false
|
|
20
|
-
redirect: null
|
|
17
|
+
meta: false
|
|
21
18
|
}
|
|
22
19
|
|
|
23
20
|
private readonly discovery: Promise<Component>
|
|
24
21
|
private storage!: Component
|
|
25
|
-
private readonly remotes: Remotes
|
|
26
|
-
private connecting: Promise<Component> | null = null
|
|
27
|
-
private remote!: Component
|
|
28
22
|
|
|
29
|
-
public constructor (options: Options | null, discovery: Promise<Component
|
|
23
|
+
public constructor (options: Options | null, discovery: Promise<Component>) {
|
|
30
24
|
super()
|
|
31
25
|
|
|
32
26
|
schemas.fetch.validate(options)
|
|
33
27
|
Object.assign(this.options, options)
|
|
34
28
|
|
|
35
29
|
this.discovery = discovery
|
|
36
|
-
this.remotes = remotes
|
|
37
30
|
}
|
|
38
31
|
|
|
39
|
-
public async apply (storage: string, input: Input
|
|
40
|
-
if (this.options.redirect !== null)
|
|
41
|
-
return this.redirect(input, parameters)
|
|
42
|
-
|
|
32
|
+
public async apply (storage: string, input: Input): Promise<Output> {
|
|
43
33
|
this.storage ??= await this.discovery
|
|
44
34
|
|
|
45
35
|
const variant = posix.basename(input.request.url).includes('.')
|
|
@@ -83,48 +73,6 @@ export class Fetch extends Directive {
|
|
|
83
73
|
}
|
|
84
74
|
}
|
|
85
75
|
|
|
86
|
-
private async redirect (input: Input, parameters: Parameter[]): Promise<Output> {
|
|
87
|
-
if ('if-none-match' in input.request.headers)
|
|
88
|
-
return { status: 304 }
|
|
89
|
-
|
|
90
|
-
const [operation, name, namespace = 'default'] = this.options.redirect!.split('.').reverse()
|
|
91
|
-
|
|
92
|
-
if (this.connecting === null)
|
|
93
|
-
this.connecting = this.remotes.discover(namespace, name)
|
|
94
|
-
|
|
95
|
-
this.remote ??= await this.connecting
|
|
96
|
-
|
|
97
|
-
const url = await this.remote.invoke<Maybe<string>>(operation, {
|
|
98
|
-
input: {
|
|
99
|
-
authority: input.authority,
|
|
100
|
-
path: input.request.url,
|
|
101
|
-
parameters: Object.fromEntries(parameters.map(({ name, value }) => [name, value]))
|
|
102
|
-
}
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
if (url instanceof Error)
|
|
106
|
-
throw new NotFound(url)
|
|
107
|
-
|
|
108
|
-
const response = await fetch(url)
|
|
109
|
-
|
|
110
|
-
if (!response.ok)
|
|
111
|
-
throw new NotFound()
|
|
112
|
-
|
|
113
|
-
const headers = new Headers()
|
|
114
|
-
|
|
115
|
-
for (const header of ['content-type', 'content-length', 'etag']) {
|
|
116
|
-
const value = response.headers.get(header)
|
|
117
|
-
|
|
118
|
-
if (value !== null)
|
|
119
|
-
headers.set(header, value)
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return {
|
|
123
|
-
headers,
|
|
124
|
-
body: response.body === null ? null : Readable.fromWeb(response.body as any)
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
76
|
private async get (storage: string, input: Input): Promise<Output> {
|
|
129
77
|
const entry = await this.storage.invoke<Maybe<Entry>>('get', {
|
|
130
78
|
input: {
|
|
@@ -143,7 +91,6 @@ export class Fetch extends Directive {
|
|
|
143
91
|
export interface Options {
|
|
144
92
|
blob?: boolean
|
|
145
93
|
meta?: boolean
|
|
146
|
-
redirect?: string | null
|
|
147
94
|
}
|
|
148
95
|
|
|
149
96
|
interface FetchResult {
|
|
@@ -42,7 +42,7 @@ class Incept {
|
|
|
42
42
|
const id = response.body?.[this.property];
|
|
43
43
|
if (id === undefined)
|
|
44
44
|
throw new http.Conflict('Identity inception has failed as the response body ' +
|
|
45
|
-
`
|
|
45
|
+
`does not contain the '${this.property}' property.`);
|
|
46
46
|
const [scheme, credentials] = (0, split_1.split)(input.request.headers.authorization);
|
|
47
47
|
const provider = schemes_1.PROVIDERS[scheme];
|
|
48
48
|
this.schemes[scheme] ??= await this.discovery[provider];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Incept.js","sourceRoot":"","sources":["../../../source/directives/auth/Incept.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AACA,iDAAkC;AAElC,mCAA+B;AAC/B,uCAAqC;AAErC,MAAa,MAAM;IACA,QAAQ,CAAQ;IAChB,SAAS,CAAW;IACpB,OAAO,GAAY,EAAwB,CAAA;IAE5D,YAAoB,QAAgB,EAAE,SAAoB;QACxD,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;QACxB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;IAC5B,CAAC;IAEM,SAAS,CAAE,QAAyB,EAAE,KAAY;QACvD,OAAO,QAAQ,KAAK,IAAI,IAAI,eAAe,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAA;IACtE,CAAC;IAEM,KAAK,CAAC,MAAM,CAAE,KAAY,EAAE,QAA8B;QAC/D,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QAEzC,IAAI,EAAE,KAAK,SAAS;YAClB,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,qDAAqD;gBAC3E,
|
|
1
|
+
{"version":3,"file":"Incept.js","sourceRoot":"","sources":["../../../source/directives/auth/Incept.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AACA,iDAAkC;AAElC,mCAA+B;AAC/B,uCAAqC;AAErC,MAAa,MAAM;IACA,QAAQ,CAAQ;IAChB,SAAS,CAAW;IACpB,OAAO,GAAY,EAAwB,CAAA;IAE5D,YAAoB,QAAgB,EAAE,SAAoB;QACxD,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAA;QACxB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;IAC5B,CAAC;IAEM,SAAS,CAAE,QAAyB,EAAE,KAAY;QACvD,OAAO,QAAQ,KAAK,IAAI,IAAI,eAAe,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAA;IACtE,CAAC;IAEM,KAAK,CAAC,MAAM,CAAE,KAAY,EAAE,QAA8B;QAC/D,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QAEzC,IAAI,EAAE,KAAK,SAAS;YAClB,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,qDAAqD;gBAC3E,yBAAyB,IAAI,CAAC,QAAQ,aAAa,CAAC,CAAA;QAExD,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,IAAA,aAAK,EAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,aAAc,CAAC,CAAA;QACzE,MAAM,QAAQ,GAAG,mBAAS,CAAC,MAAM,CAAC,CAAA;QAElC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAA;QAEvD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC;aACxC,MAAM,CAAkB,QAAQ,EAAE;YACnC,KAAK,EAAE;gBACL,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,EAAE;gBACF,WAAW;aACZ;SACF,CAAC,CAAA;QAEF,IAAI,QAAQ,YAAY,KAAK;YAC3B,MAAM,IAAI,IAAI,CAAC,mBAAmB,CAAC,QAAQ,CAAC,CAAA;QAE9C,KAAK,CAAC,QAAQ,GAAG,QAAQ,CAAA;QACzB,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,MAAM,CAAA;IAChC,CAAC;CACF;AAzCD,wBAyCC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { Directive } from './types';
|
|
2
|
+
import type { Remotes } from '../../Remotes';
|
|
3
|
+
import type { Output } from '../../io';
|
|
4
|
+
import type { Input } from '../octets/types';
|
|
5
|
+
import type { Parameter } from '../../RTD';
|
|
6
|
+
export declare class Fetch implements Directive {
|
|
7
|
+
readonly targeted = true;
|
|
8
|
+
private readonly connecting;
|
|
9
|
+
private remote;
|
|
10
|
+
private readonly operation;
|
|
11
|
+
constructor(endpoint: string, discovery: Remotes);
|
|
12
|
+
apply(input: Input, parameters: Parameter[]): Promise<Output>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Fetch = void 0;
|
|
7
|
+
const node_stream_1 = require("node:stream");
|
|
8
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
9
|
+
const matchacho_1 = require("matchacho");
|
|
10
|
+
const HTTP_1 = require("../../HTTP");
|
|
11
|
+
class Fetch {
|
|
12
|
+
targeted = true;
|
|
13
|
+
connecting;
|
|
14
|
+
remote = null;
|
|
15
|
+
operation;
|
|
16
|
+
constructor(endpoint, discovery) {
|
|
17
|
+
node_assert_1.default.equal(typeof endpoint, 'string', '`flow:redirect` must be a string');
|
|
18
|
+
const [operation, name, namespace = 'default'] = endpoint.split('.').reverse();
|
|
19
|
+
this.operation = operation;
|
|
20
|
+
this.connecting = discovery.discover(namespace, name);
|
|
21
|
+
}
|
|
22
|
+
async apply(input, parameters) {
|
|
23
|
+
if ('if-none-match' in input.request.headers)
|
|
24
|
+
return { status: 304 };
|
|
25
|
+
this.remote ??= await this.connecting;
|
|
26
|
+
const request = await this.remote.invoke(this.operation, {
|
|
27
|
+
input: {
|
|
28
|
+
authority: input.authority,
|
|
29
|
+
path: input.request.url,
|
|
30
|
+
parameters: Object.fromEntries(parameters.map(({ name, value }) => [name, value]))
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
if (request instanceof Error)
|
|
34
|
+
throw new HTTP_1.NotFound(request);
|
|
35
|
+
const { url, options } = (0, matchacho_1.match)(request, String, { url: request }, (request) => ({
|
|
36
|
+
url: request.url,
|
|
37
|
+
options: {
|
|
38
|
+
method: request.options?.method ?? 'GET',
|
|
39
|
+
body: request.options?.body,
|
|
40
|
+
headers: request.options?.headers
|
|
41
|
+
}
|
|
42
|
+
}));
|
|
43
|
+
const response = await fetch(url, options);
|
|
44
|
+
if (!response.ok)
|
|
45
|
+
throw new HTTP_1.NotFound();
|
|
46
|
+
const headers = new Headers();
|
|
47
|
+
for (const header of ['content-type', 'content-length', 'etag']) {
|
|
48
|
+
const value = response.headers.get(header);
|
|
49
|
+
if (value !== null)
|
|
50
|
+
headers.set(header, value);
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
headers,
|
|
54
|
+
body: response.body === null ? null : node_stream_1.Readable.fromWeb(response.body)
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.Fetch = Fetch;
|
|
59
|
+
//# sourceMappingURL=Fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Fetch.js","sourceRoot":"","sources":["../../../source/directives/flow/Fetch.ts"],"names":[],"mappings":";;;;;;AAAA,6CAAsC;AACtC,8DAAgC;AAChC,yCAAiC;AACjC,qCAAqC;AAUrC,MAAa,KAAK;IACA,QAAQ,GAAG,IAAI,CAAA;IAEd,UAAU,CAAoB;IACvC,MAAM,GAAqB,IAAI,CAAA;IACtB,SAAS,CAAQ;IAElC,YAAoB,QAAgB,EAAE,SAAkB;QACtD,qBAAM,CAAC,KAAK,CAAC,OAAO,QAAQ,EAAE,QAAQ,EAAE,kCAAkC,CAAC,CAAA;QAE3E,MAAM,CAAC,SAAS,EAAE,IAAI,EAAE,SAAS,GAAG,SAAS,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAA;QAE9E,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;QAC1B,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC,CAAA;IACvD,CAAC;IAEM,KAAK,CAAC,KAAK,CAAE,KAAY,EAAE,UAAuB;QACvD,IAAI,eAAe,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO;YAC1C,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,CAAA;QAExB,IAAI,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,UAAU,CAAA;QAErC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,CAA0B,IAAI,CAAC,SAAS,EAAE;YAChF,KAAK,EAAE;gBACL,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG;gBACvB,UAAU,EAAE,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;aACnF;SACF,CAAC,CAAA;QAEF,IAAI,OAAO,YAAY,KAAK;YAC1B,MAAM,IAAI,eAAQ,CAAC,OAAO,CAAC,CAAA;QAE7B,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,IAAA,iBAAK,EAAU,OAAO,EAC7C,MAAM,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,EACxB,CAAC,OAAgB,EAAW,EAAE,CAAC,CAAC;YAC9B,GAAG,EAAE,OAAO,CAAC,GAAG;YAChB,OAAO,EAAE;gBACP,MAAM,EAAE,OAAO,CAAC,OAAO,EAAE,MAAM,IAAI,KAAK;gBACxC,IAAI,EAAE,OAAO,CAAC,OAAO,EAAE,IAAI;gBAC3B,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,OAAO;aAClC;SACF,CAAC,CAAC,CAAA;QAEL,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;QAE1C,IAAI,CAAC,QAAQ,CAAC,EAAE;YACd,MAAM,IAAI,eAAQ,EAAE,CAAA;QAEtB,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAA;QAE7B,KAAK,MAAM,MAAM,IAAI,CAAC,cAAc,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,CAAC;YAChE,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAA;YAE1C,IAAI,KAAK,KAAK,IAAI;gBAChB,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;QAC9B,CAAC;QAED,OAAO;YACL,OAAO;YACP,IAAI,EAAE,QAAQ,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,sBAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAsB,CAAC;SACxF,CAAA;IACH,CAAC;CACF;AA/DD,sBA+DC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Directive } from './types';
|
|
2
|
+
import type { Input, Output } from '../../io';
|
|
3
|
+
import type { DirectiveFamily, Parameter } from '../../RTD';
|
|
4
|
+
import type { Remotes } from '../../Remotes';
|
|
5
|
+
export declare class Flow implements DirectiveFamily<Directive> {
|
|
6
|
+
readonly name: string;
|
|
7
|
+
readonly mandatory: boolean;
|
|
8
|
+
create(name: string, value: unknown, remotes: Remotes): Directive;
|
|
9
|
+
preflight(directives: Directive[], input: Input, parameters: Parameter[]): Promise<Output>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Flow = void 0;
|
|
4
|
+
const Fetch_1 = require("./Fetch");
|
|
5
|
+
class Flow {
|
|
6
|
+
name = 'flow';
|
|
7
|
+
mandatory = false;
|
|
8
|
+
create(name, value, remotes) {
|
|
9
|
+
const Class = constructors[name];
|
|
10
|
+
if (Class === undefined)
|
|
11
|
+
throw new Error(`Directive '${this.name}:${name}' is not implemented.`);
|
|
12
|
+
return new Class(value, remotes);
|
|
13
|
+
}
|
|
14
|
+
async preflight(directives, input, parameters) {
|
|
15
|
+
for (const directive of directives) {
|
|
16
|
+
const output = await directive.apply(input, parameters);
|
|
17
|
+
if (output !== null)
|
|
18
|
+
return output;
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.Flow = Flow;
|
|
24
|
+
const constructors = {
|
|
25
|
+
fetch: Fetch_1.Fetch
|
|
26
|
+
};
|
|
27
|
+
//# sourceMappingURL=Flow.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Flow.js","sourceRoot":"","sources":["../../../source/directives/flow/Flow.ts"],"names":[],"mappings":";;;AAAA,mCAA+B;AAM/B,MAAa,IAAI;IACC,IAAI,GAAW,MAAM,CAAA;IACrB,SAAS,GAAY,KAAK,CAAA;IAEnC,MAAM,CAAE,IAAY,EAAE,KAAc,EAAE,OAAgB;QAC3D,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,CAAA;QAEhC,IAAI,KAAK,KAAK,SAAS;YACrB,MAAM,IAAI,KAAK,CAAC,cAAc,IAAI,CAAC,IAAI,IAAI,IAAI,uBAAuB,CAAC,CAAA;QAEzE,OAAO,IAAI,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;IAClC,CAAC;IAEM,KAAK,CAAC,SAAS,CAAE,UAAuB,EAAE,KAAY,EAAE,UAAuB;QACpF,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC,KAAK,CAAC,KAAK,EAAE,UAAU,CAAC,CAAA;YAEvD,IAAI,MAAM,KAAK,IAAI;gBACjB,OAAO,MAAM,CAAA;QACjB,CAAC;QAED,OAAO,IAAI,CAAA;IACb,CAAC;CACF;AAvBD,oBAuBC;AAED,MAAM,YAAY,GAAsE;IACtF,KAAK,EAAE,aAAK;CACb,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../source/directives/flow/index.ts"],"names":[],"mappings":";;;AAAA,iCAA6B;AAEhB,QAAA,IAAI,GAAG,IAAI,WAAI,EAAE,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../source/directives/flow/types.ts"],"names":[],"mappings":""}
|
|
@@ -9,6 +9,7 @@ const octets_1 = require("./octets");
|
|
|
9
9
|
const io_1 = require("./io");
|
|
10
10
|
const vary_1 = require("./vary");
|
|
11
11
|
const require_1 = require("./require");
|
|
12
|
-
|
|
12
|
+
const flow_1 = require("./flow");
|
|
13
|
+
exports.families = [auth_1.authorization, io_1.io, cache_1.cache, vary_1.vary, require_1.req, flow_1.flow, octets_1.octets, dev_1.dev];
|
|
13
14
|
exports.interceptors = [cors_1.cors];
|
|
14
15
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../source/directives/index.ts"],"names":[],"mappings":";;;AAAA,iCAAsC;AACtC,mCAA+B;AAC/B,iCAA6B;AAC7B,+BAA2B;AAC3B,qCAAiC;AACjC,6BAAyB;AACzB,iCAA6B;AAC7B,uCAA+B;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../source/directives/index.ts"],"names":[],"mappings":";;;AAAA,iCAAsC;AACtC,mCAA+B;AAC/B,iCAA6B;AAC7B,+BAA2B;AAC3B,qCAAiC;AACjC,6BAAyB;AACzB,iCAA6B;AAC7B,uCAA+B;AAC/B,iCAA6B;AAIhB,QAAA,QAAQ,GAAsB,CAAC,oBAAa,EAAE,OAAE,EAAE,aAAK,EAAE,WAAI,EAAE,aAAG,EAAE,WAAI,EAAE,eAAM,EAAE,SAAG,CAAC,CAAA;AACtF,QAAA,YAAY,GAAkB,CAAC,WAAI,CAAC,CAAA"}
|
|
@@ -1,25 +1,18 @@
|
|
|
1
1
|
import { Directive } from './Directive';
|
|
2
|
-
import type { Remotes } from '../../Remotes';
|
|
3
2
|
import type { Component } from '@toa.io/core';
|
|
4
3
|
import type { Output } from '../../io';
|
|
5
4
|
import type { Input } from './types';
|
|
6
|
-
import type { Parameter } from '../../RTD';
|
|
7
5
|
export declare class Fetch extends Directive {
|
|
8
6
|
readonly targeted = true;
|
|
9
7
|
private readonly options;
|
|
10
8
|
private readonly discovery;
|
|
11
9
|
private storage;
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
private remote;
|
|
15
|
-
constructor(options: Options | null, discovery: Promise<Component>, remotes: Remotes);
|
|
16
|
-
apply(storage: string, input: Input, parameters: Parameter[]): Promise<Output>;
|
|
10
|
+
constructor(options: Options | null, discovery: Promise<Component>);
|
|
11
|
+
apply(storage: string, input: Input): Promise<Output>;
|
|
17
12
|
private fetch;
|
|
18
|
-
private redirect;
|
|
19
13
|
private get;
|
|
20
14
|
}
|
|
21
15
|
export interface Options {
|
|
22
16
|
blob?: boolean;
|
|
23
17
|
meta?: boolean;
|
|
24
|
-
redirect?: string | null;
|
|
25
18
|
}
|