@toa.io/pointer 0.2.1-dev.3
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/package.json +31 -0
- package/readme.md +126 -0
- package/source/.deployment/.variables/credentials.js +35 -0
- package/source/.deployment/.variables/index.js +7 -0
- package/source/.deployment/.variables/pointer.js +18 -0
- package/source/.deployment/index.js +8 -0
- package/source/.deployment/validate.js +19 -0
- package/source/.deployment/variables.js +22 -0
- package/source/.pointer/env.js +44 -0
- package/source/.pointer/index.js +7 -0
- package/source/.pointer/local.js +17 -0
- package/source/deployment.js +17 -0
- package/source/index.js +11 -0
- package/source/pointer.js +50 -0
- package/source/uris/.construct/index.js +7 -0
- package/source/uris/.construct/normalize.js +17 -0
- package/source/uris/.construct/schema.yaml +19 -0
- package/source/uris/.construct/validate.js +17 -0
- package/source/uris/construct.js +14 -0
- package/source/uris/index.js +7 -0
- package/source/uris/resolve.js +30 -0
- package/source/variable.js +12 -0
- package/test/deployment.test.js +92 -0
- package/test/pointer.test.js +213 -0
- package/test/uris.fixtures.js +10 -0
- package/test/uris.test.js +140 -0
- package/test/variable.test.js +37 -0
- package/types/deployment.d.ts +22 -0
- package/types/index.d.ts +6 -0
- package/types/pointer.d.ts +21 -0
- package/types/uris.d.ts +32 -0
- package/types/variable.d.ts +8 -0
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@toa.io/pointer",
|
|
3
|
+
"version": "0.2.1-dev.3",
|
|
4
|
+
"description": "Toa Pointer",
|
|
5
|
+
"homepage": "https://toa.io",
|
|
6
|
+
"author": {
|
|
7
|
+
"name": "Artem Gurtovoi",
|
|
8
|
+
"url": "https://github.com/temich"
|
|
9
|
+
},
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/toa-io/toa.git"
|
|
14
|
+
},
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/toa-io/toa/issues"
|
|
17
|
+
},
|
|
18
|
+
"engines": {
|
|
19
|
+
"node": ">= 12.0.0"
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"main": "source/index.js",
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@toa.io/console": "*",
|
|
27
|
+
"@toa.io/generic": "*",
|
|
28
|
+
"@toa.io/schema": "*",
|
|
29
|
+
"@toa.io/yaml": "*"
|
|
30
|
+
}
|
|
31
|
+
}
|
package/readme.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Pointer
|
|
2
|
+
|
|
3
|
+
Library to declare, deploy and resolve URL to connect to at the runtime. Used by connectors.
|
|
4
|
+
|
|
5
|
+
## Pointer class
|
|
6
|
+
|
|
7
|
+
Pointer builds URL for a given locator, protocol and a package prefix. Prefix must be unique value
|
|
8
|
+
for a given package conforming [label format](#) (ex.: `bindings-ampq`).
|
|
9
|
+
|
|
10
|
+
### Constructor signature
|
|
11
|
+
|
|
12
|
+
<dl>
|
|
13
|
+
<dt><code>prefix</code></dt>
|
|
14
|
+
<dd><code>string</code> unique for each package</dd>
|
|
15
|
+
<dt><code>locator</code></dt>
|
|
16
|
+
<dd><code>toa.core.Locator</code> component for which Pointer is intended</dd>
|
|
17
|
+
<dt><code>options</code></dt>
|
|
18
|
+
<dd><code><a href="./types/pointer.d.ts">toa.pointer.Options</a></code> Pointer options</dd>
|
|
19
|
+
</dl>
|
|
20
|
+
|
|
21
|
+
### Local Environment
|
|
22
|
+
|
|
23
|
+
If `TOA_ENV` is `local` then these values are used:
|
|
24
|
+
|
|
25
|
+
| Property | Value |
|
|
26
|
+
|----------|-------------|
|
|
27
|
+
| hostname | `localhost` |
|
|
28
|
+
| username | `developer` |
|
|
29
|
+
| password | `secret` |
|
|
30
|
+
|
|
31
|
+
> In the local environment there is no way to provide values for `protocol` or `port`.
|
|
32
|
+
|
|
33
|
+
See [types](types/pointer.d.ts) and [tests](test/pointer.test.js) for details.
|
|
34
|
+
|
|
35
|
+
## Annotation
|
|
36
|
+
|
|
37
|
+
Declaration of a set of hosts, matching exact components, namespaces with a default value for
|
|
38
|
+
non-matched ones.
|
|
39
|
+
|
|
40
|
+
```yaml
|
|
41
|
+
something:
|
|
42
|
+
default: protocol://host1
|
|
43
|
+
namespace1: protocol://host2
|
|
44
|
+
namespace1.component1: protocol://host3
|
|
45
|
+
namespace2.component2: protocol://host4
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
See the [schema](source/uris/.construct/schema.yaml).
|
|
49
|
+
|
|
50
|
+
### Concise Declaration
|
|
51
|
+
|
|
52
|
+
Next two declarations are equivalent.
|
|
53
|
+
|
|
54
|
+
```yaml
|
|
55
|
+
something: host1
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```yaml
|
|
59
|
+
something:
|
|
60
|
+
default: host1
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Usage
|
|
64
|
+
|
|
65
|
+
```javascript
|
|
66
|
+
const { uris } = require('@toa.io/pointer')
|
|
67
|
+
|
|
68
|
+
const annotation = uris.construct(declaration)
|
|
69
|
+
const url = uris.resolve(annotation, locator)
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
See [types](types/uris.d.ts) and [tests](test/uris.test.js) for details.
|
|
73
|
+
|
|
74
|
+
### Custom Extensions
|
|
75
|
+
|
|
76
|
+
Packages using Pointer Annotation may use or require additional properties.
|
|
77
|
+
See [Deployment Extensions](#extensions) below.
|
|
78
|
+
|
|
79
|
+
## Deployment
|
|
80
|
+
|
|
81
|
+
Deployment function builds global[^1] variables: containing Annotation and a set of variables for
|
|
82
|
+
pointer credentials.
|
|
83
|
+
|
|
84
|
+
See [types](types/deployment.d.ts) and [tests](test/deployment.test.js) for details.
|
|
85
|
+
|
|
86
|
+
### Credentials
|
|
87
|
+
|
|
88
|
+
Each entry of the Annotation requires values for `username` and `password`. These values are being
|
|
89
|
+
deployed as global[^1] secret variables. Secret names are following the
|
|
90
|
+
convention: `toa-package-prefix-entry-label` and `key` names match corresponding URL
|
|
91
|
+
properties (`username` and `password`).
|
|
92
|
+
|
|
93
|
+
> Note that declaration is made of `Locator.id` while secret names contain `Locator.label`.
|
|
94
|
+
> See [Locator](#).
|
|
95
|
+
|
|
96
|
+
#### Example
|
|
97
|
+
|
|
98
|
+
```yaml
|
|
99
|
+
# context.toa.yaml
|
|
100
|
+
amqp:
|
|
101
|
+
default: amqp://host0
|
|
102
|
+
dummies.dummy: amqps://host1:5671
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
```shell
|
|
106
|
+
$ toa conceal bindings-amqp-default username admin
|
|
107
|
+
$ toa conceal bindings-amqp-default password ibreakthings
|
|
108
|
+
$ toa conceal bindings-amqp-dummies-dummy username developer
|
|
109
|
+
$ toa conceal bindings-amqp-dummies-dummy password iluvtests
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
See [`toa conceal`](../../runtime/cli/readme.md#conceal).
|
|
113
|
+
|
|
114
|
+
### Extensions
|
|
115
|
+
|
|
116
|
+
For each custom extension additional proxy is being deployed.
|
|
117
|
+
|
|
118
|
+
#### Example
|
|
119
|
+
|
|
120
|
+
AMQP binding is a systematic binding used by the runtime. That is a system proxy must be deployed
|
|
121
|
+
and compositions require variables to establish connection through that proxy.
|
|
122
|
+
|
|
123
|
+
Therefore, AMQP binding declares a `system` extension and deployment function declares additional
|
|
124
|
+
proxy `bindings-amqp-system`.
|
|
125
|
+
|
|
126
|
+
[^1]: [#174](https://github.com/toa-io/toa/issues/174)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { concat, letters: { up, down } } = require('@toa.io/generic')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {string} prefix
|
|
7
|
+
* @param {toa.pointer.URIs} uris
|
|
8
|
+
* @returns {toa.deployment.dependency.Variable[]}
|
|
9
|
+
*/
|
|
10
|
+
const credentials = (prefix, uris) => {
|
|
11
|
+
const variables = []
|
|
12
|
+
|
|
13
|
+
for (const key of Object.keys(uris)) {
|
|
14
|
+
const [namespace, name] = key.split('.')
|
|
15
|
+
const entry = namespace + concat('-', name)
|
|
16
|
+
|
|
17
|
+
const env = `TOA_${up(prefix)}_${up(entry)}_`
|
|
18
|
+
const sec = `toa-${down(prefix)}-${down(entry)}`
|
|
19
|
+
|
|
20
|
+
for (const property of ['username', 'password']) {
|
|
21
|
+
const name = env + up(property)
|
|
22
|
+
|
|
23
|
+
const secret = {
|
|
24
|
+
name: sec,
|
|
25
|
+
key: property
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
variables.push({ name, secret })
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return variables
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
exports.credentials = credentials
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { encode, letters: { up } } = require('@toa.io/generic')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {string} prefix
|
|
7
|
+
* @param {toa.pointer.URIs} uris
|
|
8
|
+
* @returns {toa.deployment.dependency.Variable}
|
|
9
|
+
*/
|
|
10
|
+
const pointer = (prefix, uris) => {
|
|
11
|
+
const name = `TOA_${up(prefix)}_POINTER`
|
|
12
|
+
const json = JSON.stringify(uris)
|
|
13
|
+
const value = encode(json)
|
|
14
|
+
|
|
15
|
+
return { name, value }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
exports.pointer = pointer
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {toa.pointer.URIs} uris
|
|
5
|
+
*/
|
|
6
|
+
const validate = (uris) => {
|
|
7
|
+
Object.values(uris).forEach(check)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* @param {string} uri
|
|
12
|
+
*/
|
|
13
|
+
const check = (uri) => {
|
|
14
|
+
const url = new URL(uri)
|
|
15
|
+
|
|
16
|
+
if (url.hostname === '') throw new Error(`URI ${url.href} must contain a hostname`)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
exports.validate = validate
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const declare = require('./.variables')
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* @param {toa.pointer.URIs} uris
|
|
7
|
+
* @param {string} prefix
|
|
8
|
+
* @returns {toa.deployment.dependency.Variable[]}
|
|
9
|
+
*/
|
|
10
|
+
const variables = (uris, prefix) => {
|
|
11
|
+
/** @type {toa.deployment.dependency.Variable[]} */
|
|
12
|
+
const variables = []
|
|
13
|
+
const pointer = declare.pointer(prefix, uris)
|
|
14
|
+
const credentials = declare.credentials(prefix, uris)
|
|
15
|
+
|
|
16
|
+
variables.push(pointer)
|
|
17
|
+
variables.push(...credentials)
|
|
18
|
+
|
|
19
|
+
return variables
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
exports.variables = variables
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { concat, decode, letters: { up, capitalize } } = require('@toa.io/generic')
|
|
4
|
+
const { console } = require('@toa.io/console')
|
|
5
|
+
|
|
6
|
+
const { resolve } = require('../uris')
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {string} prefix
|
|
10
|
+
* @param {toa.core.Locator} locator
|
|
11
|
+
*/
|
|
12
|
+
const env = (prefix, locator) => {
|
|
13
|
+
const key = `TOA_${up(prefix)}_POINTER`
|
|
14
|
+
const value = process.env[key]
|
|
15
|
+
|
|
16
|
+
if (value === undefined) throw new Error(`Environment variable ${key} is not set`)
|
|
17
|
+
|
|
18
|
+
const uris = decode(value)
|
|
19
|
+
const { url, entry } = resolve(locator, uris)
|
|
20
|
+
|
|
21
|
+
const suffix = convert(entry)
|
|
22
|
+
const env = `TOA_${up(prefix)}_${suffix}`
|
|
23
|
+
|
|
24
|
+
for (const property of ['username', 'password']) {
|
|
25
|
+
const value = process.env[env + '_' + up(property)]
|
|
26
|
+
|
|
27
|
+
if (value !== undefined) url[property] = value
|
|
28
|
+
else console.warn(`${capitalize(property)} for ${env} is not set`)
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return url
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @param {string} entry
|
|
36
|
+
* @returns {string}
|
|
37
|
+
*/
|
|
38
|
+
const convert = (entry) => {
|
|
39
|
+
const [namespace, name] = entry.toUpperCase().split('.')
|
|
40
|
+
|
|
41
|
+
return namespace + concat('_', name)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
exports.env = env
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {string} protocol
|
|
5
|
+
* @returns {URL}
|
|
6
|
+
*/
|
|
7
|
+
const local = (protocol) => {
|
|
8
|
+
const url = new URL(protocol + '//')
|
|
9
|
+
|
|
10
|
+
url.hostname = 'localhost'
|
|
11
|
+
url.username = 'developer'
|
|
12
|
+
url.password = 'secret'
|
|
13
|
+
|
|
14
|
+
return url
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
exports.local = local
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const get = require('./.deployment')
|
|
4
|
+
|
|
5
|
+
/** @type {toa.pointer.Deployment} */
|
|
6
|
+
const deployment = (instances, uris, options) => {
|
|
7
|
+
if (typeof uris === 'string') uris = { default: uris }
|
|
8
|
+
|
|
9
|
+
get.validate(uris)
|
|
10
|
+
|
|
11
|
+
const global = get.variables(uris, options.prefix)
|
|
12
|
+
const variables = { global }
|
|
13
|
+
|
|
14
|
+
return { variables }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
exports.deployment = deployment
|
package/source/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { Pointer } = require('./pointer')
|
|
4
|
+
const { deployment } = require('./deployment')
|
|
5
|
+
const { variable } = require('./variable')
|
|
6
|
+
|
|
7
|
+
exports.uris = require('./uris')
|
|
8
|
+
|
|
9
|
+
exports.Pointer = Pointer
|
|
10
|
+
exports.deployment = deployment
|
|
11
|
+
exports.variable = variable
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const get = require('./.pointer')
|
|
4
|
+
|
|
5
|
+
// noinspection JSClosureCompilerSyntax
|
|
6
|
+
/**
|
|
7
|
+
* @implements {toa.pointer.Pointer}
|
|
8
|
+
*/
|
|
9
|
+
class Pointer {
|
|
10
|
+
protocol
|
|
11
|
+
host
|
|
12
|
+
port
|
|
13
|
+
hostname
|
|
14
|
+
path
|
|
15
|
+
reference
|
|
16
|
+
label
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @param {string} prefix
|
|
20
|
+
* @param {toa.core.Locator} locator
|
|
21
|
+
* @param {toa.pointer.Options} options
|
|
22
|
+
*/
|
|
23
|
+
constructor (prefix, locator, options) {
|
|
24
|
+
const local = process.env.TOA_ENV === 'local'
|
|
25
|
+
const url = local ? get.local(options.protocol) : get.env(prefix, locator)
|
|
26
|
+
|
|
27
|
+
this.protocol = url.protocol
|
|
28
|
+
this.hostname = url.hostname
|
|
29
|
+
this.port = Number(url.port)
|
|
30
|
+
this.path = url.pathname
|
|
31
|
+
this.username = url.username
|
|
32
|
+
this.password = url.password
|
|
33
|
+
this.reference = url.href
|
|
34
|
+
this.label = label(url)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* @param {URL} url
|
|
40
|
+
* @returns {string}
|
|
41
|
+
*/
|
|
42
|
+
const label = (url) => {
|
|
43
|
+
const safe = new URL(url.href)
|
|
44
|
+
|
|
45
|
+
safe.password = ''
|
|
46
|
+
|
|
47
|
+
return safe.href
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
exports.Pointer = Pointer
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @param {toa.pointer.URIs | string} declaration
|
|
5
|
+
* @returns {toa.pointer.URIs}
|
|
6
|
+
*/
|
|
7
|
+
const normalize = (declaration) => {
|
|
8
|
+
/** @type {toa.pointer.URIs} */
|
|
9
|
+
let annotation
|
|
10
|
+
|
|
11
|
+
if (typeof declaration === 'string') annotation = { default: declaration }
|
|
12
|
+
else annotation = declaration
|
|
13
|
+
|
|
14
|
+
return annotation
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
exports.normalize = normalize
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
$schema: https://json-schema.org/draft/2019-09/schema
|
|
2
|
+
$id: https://schemas.toa.io/0.0.0/storages/mongodb/annotations
|
|
3
|
+
|
|
4
|
+
definitions:
|
|
5
|
+
node:
|
|
6
|
+
oneOf:
|
|
7
|
+
- $ref: '#/definitions/locator'
|
|
8
|
+
- type: object
|
|
9
|
+
patternProperties:
|
|
10
|
+
'.*':
|
|
11
|
+
$ref: '#/definitions/node'
|
|
12
|
+
locator:
|
|
13
|
+
type: string
|
|
14
|
+
format: uri
|
|
15
|
+
|
|
16
|
+
type: object
|
|
17
|
+
patternProperties:
|
|
18
|
+
'.*':
|
|
19
|
+
$ref: '#/definitions/node'
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const path = require('path')
|
|
4
|
+
|
|
5
|
+
const { Schema } = require('@toa.io/schema')
|
|
6
|
+
const { load } = require('@toa.io/yaml')
|
|
7
|
+
|
|
8
|
+
const schema = load.sync(path.resolve(__dirname, 'schema.yaml'))
|
|
9
|
+
const validator = new Schema(schema)
|
|
10
|
+
|
|
11
|
+
const validate = (declaration) => {
|
|
12
|
+
const error = validator.fit(declaration)
|
|
13
|
+
|
|
14
|
+
if (error) throw new TypeError(error.message)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
exports.validate = validate
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { normalize, validate } = require('./.construct')
|
|
4
|
+
|
|
5
|
+
/** @type {toa.pointer.uris.Constructor} */
|
|
6
|
+
const construct = (declaration) => {
|
|
7
|
+
const normalized = normalize(declaration)
|
|
8
|
+
|
|
9
|
+
validate(normalized)
|
|
10
|
+
|
|
11
|
+
return normalized
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
exports.construct = construct
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/** @type {toa.pointer.uris.Resolver} */
|
|
4
|
+
const resolve = (locator, uris) => {
|
|
5
|
+
let entry
|
|
6
|
+
|
|
7
|
+
/** @type {string} */
|
|
8
|
+
let uri
|
|
9
|
+
|
|
10
|
+
const entries = [locator.id, locator.namespace, 'default']
|
|
11
|
+
|
|
12
|
+
for (const key of entries) {
|
|
13
|
+
if (typeof uris[key] === 'string') {
|
|
14
|
+
uri = uris[key]
|
|
15
|
+
entry = key
|
|
16
|
+
|
|
17
|
+
break
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (uri === undefined) throw new Error(`URI annotation for '${locator.id}' is not found`)
|
|
22
|
+
|
|
23
|
+
const url = new URL(uri)
|
|
24
|
+
|
|
25
|
+
if (url.hostname === '') throw new Error(`URI annotation for '${locator.id}' must contain hostname`)
|
|
26
|
+
|
|
27
|
+
return { url, entry }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
exports.resolve = resolve
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { letters: { up } } = require('@toa.io/generic')
|
|
4
|
+
|
|
5
|
+
/** @type {toa.pointer.Variable} */
|
|
6
|
+
const variable = (scope, locator, label, value) => {
|
|
7
|
+
const name = `TOA_${up(scope)}_${locator.uppercase}_${up(label)}`
|
|
8
|
+
|
|
9
|
+
return { name, value }
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
exports.variable = variable
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { encode, letters: { up, down } } = require('@toa.io/generic')
|
|
4
|
+
const mock = require('@toa.io/mock')
|
|
5
|
+
|
|
6
|
+
const { deployment } = require('../')
|
|
7
|
+
|
|
8
|
+
const prefix = 'foo-bar'
|
|
9
|
+
|
|
10
|
+
/** @type {toa.norm.context.dependencies.Instance[]} */
|
|
11
|
+
let instances
|
|
12
|
+
|
|
13
|
+
/** @type {toa.pointer.deployment.Options} */
|
|
14
|
+
let options
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
instances = mock.dependencies.instances()
|
|
18
|
+
options = { prefix }
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
it('should be', () => {
|
|
22
|
+
expect(deployment).toBeInstanceOf(Function)
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('should throw if protocol is omitted', () => {
|
|
26
|
+
const annotation = { default: 'no-protocol' }
|
|
27
|
+
|
|
28
|
+
expect(() => deployment(instances, annotation, options)).toThrow('Invalid URL')
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
it('should throw if hostname is omitted', () => {
|
|
32
|
+
const annotation = { default: 'amqps:///' }
|
|
33
|
+
|
|
34
|
+
expect(() => deployment(instances, annotation, options)).toThrow('must contain a hostname')
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
it('should consider string annotation as default', () => {
|
|
38
|
+
const annotation = 'pg://host0'
|
|
39
|
+
|
|
40
|
+
const output = deployment(instances, annotation, options)
|
|
41
|
+
|
|
42
|
+
const name = `TOA_${up(prefix)}_POINTER`
|
|
43
|
+
const value = encode({ default: annotation })
|
|
44
|
+
|
|
45
|
+
expect(output.variables.global).toStrictEqual(expect.arrayContaining([{ name, value }]))
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
describe('variables', () => {
|
|
49
|
+
it('should export POINTER variable', () => {
|
|
50
|
+
const annotation = { default: 'amqps://host0' }
|
|
51
|
+
const json = JSON.stringify(annotation)
|
|
52
|
+
const value = encode(json)
|
|
53
|
+
const output = deployment(instances, annotation, options)
|
|
54
|
+
|
|
55
|
+
expect(output.variables.global).toStrictEqual(expect.arrayContaining([{
|
|
56
|
+
name: `TOA_${up(prefix)}_POINTER`,
|
|
57
|
+
value
|
|
58
|
+
}]))
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
it('should export USERNAME and PASSWORD for each entry of URI Set', () => {
|
|
62
|
+
const annotation = {
|
|
63
|
+
default: 'amqps://host0',
|
|
64
|
+
system: 'amqps://host2',
|
|
65
|
+
dummies: 'amqps://host3',
|
|
66
|
+
'dummies.one': 'amqps://host4'
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const expected = []
|
|
70
|
+
|
|
71
|
+
const env = `TOA_${up(prefix)}_`
|
|
72
|
+
const sec = `toa-${down(prefix)}-`
|
|
73
|
+
|
|
74
|
+
for (const key of Object.keys(annotation)) {
|
|
75
|
+
const label = key.replaceAll('.', '-').toLowerCase()
|
|
76
|
+
|
|
77
|
+
for (const property of ['username', 'password']) {
|
|
78
|
+
expected.push({
|
|
79
|
+
name: `${env}${up(label)}_${up(property)}`,
|
|
80
|
+
secret: {
|
|
81
|
+
name: `${sec}${down(label)}`,
|
|
82
|
+
key: property
|
|
83
|
+
}
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const output = deployment(instances, annotation, options)
|
|
89
|
+
|
|
90
|
+
expect(output.variables.global).toStrictEqual(expect.arrayContaining(expected))
|
|
91
|
+
})
|
|
92
|
+
})
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const mock = { console: { info: jest.fn(), warn: jest.fn() } }
|
|
4
|
+
|
|
5
|
+
jest.mock('@toa.io/console', () => mock)
|
|
6
|
+
|
|
7
|
+
const { generate } = require('randomstring')
|
|
8
|
+
const { Locator } = require('@toa.io/core')
|
|
9
|
+
const { encode, letters: { up } } = require('@toa.io/generic')
|
|
10
|
+
|
|
11
|
+
const { Pointer } = require('../')
|
|
12
|
+
|
|
13
|
+
it('should be', () => {
|
|
14
|
+
expect(Pointer).toBeDefined()
|
|
15
|
+
expect(Pointer).toBeInstanceOf(Function)
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const prefix = 'bindings-amqp'
|
|
19
|
+
|
|
20
|
+
/** @type {toa.core.Locator} */
|
|
21
|
+
let locator
|
|
22
|
+
|
|
23
|
+
/** @type {toa.pointer.Options} */
|
|
24
|
+
const options = { protocol: 'protocol:' }
|
|
25
|
+
|
|
26
|
+
/** @type {toa.pointer.Pointer} */
|
|
27
|
+
let pointer
|
|
28
|
+
|
|
29
|
+
/** @type {URL} */
|
|
30
|
+
let url
|
|
31
|
+
|
|
32
|
+
const uris = (uris) => (process.env[`TOA_${up(prefix)}_POINTER`] = encode(uris))
|
|
33
|
+
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
jest.clearAllMocks()
|
|
36
|
+
|
|
37
|
+
const name = generate()
|
|
38
|
+
const namespace = generate()
|
|
39
|
+
|
|
40
|
+
locator = new Locator(name, namespace)
|
|
41
|
+
|
|
42
|
+
url = new URL('amqp://whatever:3000')
|
|
43
|
+
|
|
44
|
+
uris({ default: url.href })
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('should throw if env not set', () => {
|
|
48
|
+
const name = `TOA_${up(prefix)}_POINTER`
|
|
49
|
+
delete process.env[name]
|
|
50
|
+
|
|
51
|
+
expect(() => new Pointer(prefix, locator, options)).toThrow(`Environment variable ${name} is not set`)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
it('should use declared protocol', () => {
|
|
55
|
+
pointer = new Pointer(prefix, locator, options)
|
|
56
|
+
|
|
57
|
+
expect(pointer.protocol).toStrictEqual(url.protocol)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('should point to host', () => {
|
|
61
|
+
pointer = new Pointer(prefix, locator, options)
|
|
62
|
+
|
|
63
|
+
expect(pointer.hostname).toStrictEqual(url.hostname)
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
it('should resolve credentials', () => {
|
|
67
|
+
const check = (entry) => {
|
|
68
|
+
const env = `TOA_${up(prefix)}_${up(entry)}_`
|
|
69
|
+
const username = generate()
|
|
70
|
+
const password = generate()
|
|
71
|
+
|
|
72
|
+
process.env[env + 'USERNAME'] = username
|
|
73
|
+
process.env[env + 'PASSWORD'] = password
|
|
74
|
+
|
|
75
|
+
pointer = new Pointer(prefix, locator, options)
|
|
76
|
+
|
|
77
|
+
delete process.env[env + 'USERNAME']
|
|
78
|
+
delete process.env[env + 'PASSWORD']
|
|
79
|
+
|
|
80
|
+
expect(pointer.username).toStrictEqual(username)
|
|
81
|
+
expect(pointer.password).toStrictEqual(password)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// default resolution
|
|
85
|
+
check('default')
|
|
86
|
+
|
|
87
|
+
// namespace resolution
|
|
88
|
+
uris({ [locator.namespace]: 'pg://host0/db' })
|
|
89
|
+
check(locator.namespace)
|
|
90
|
+
|
|
91
|
+
// id resolution
|
|
92
|
+
uris({ [locator.id]: 'pg://host0/db' })
|
|
93
|
+
check(locator.label)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
it('should expose port as number', () => {
|
|
97
|
+
pointer = new Pointer(prefix, locator, options)
|
|
98
|
+
|
|
99
|
+
expect(pointer.port).toStrictEqual(Number(url.port))
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
it('should expose reference', () => {
|
|
103
|
+
pointer = new Pointer(prefix, locator, options)
|
|
104
|
+
|
|
105
|
+
expect(pointer.reference).toStrictEqual(url.href)
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
it('should use localhost and given protocol on local environment', () => {
|
|
109
|
+
process.env.TOA_ENV = 'local'
|
|
110
|
+
|
|
111
|
+
pointer = new Pointer(prefix, locator, options)
|
|
112
|
+
|
|
113
|
+
delete process.env.TOA_ENV
|
|
114
|
+
|
|
115
|
+
expect(pointer.hostname).toStrictEqual('localhost')
|
|
116
|
+
expect(pointer.protocol).toStrictEqual(options.protocol)
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
it('should use default protocol and credentials on local environment', () => {
|
|
120
|
+
process.env.TOA_ENV = 'local'
|
|
121
|
+
|
|
122
|
+
pointer = new Pointer(prefix, locator, options)
|
|
123
|
+
|
|
124
|
+
delete process.env.TOA_ENV
|
|
125
|
+
|
|
126
|
+
expect(pointer.reference).toStrictEqual(`${options.protocol}//developer:secret@localhost`)
|
|
127
|
+
expect(pointer.label).toStrictEqual(`${options.protocol}//developer@localhost`)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
describe('environment variables', () => {
|
|
131
|
+
const username = generate()
|
|
132
|
+
const password = generate()
|
|
133
|
+
|
|
134
|
+
const env = `TOA_${up(prefix)}_DEFAULT`
|
|
135
|
+
|
|
136
|
+
let set
|
|
137
|
+
let unset
|
|
138
|
+
|
|
139
|
+
beforeEach(() => {
|
|
140
|
+
jest.clearAllMocks()
|
|
141
|
+
|
|
142
|
+
set = (name, value, prefix = env) => {
|
|
143
|
+
const key = prefix + '_' + up(name)
|
|
144
|
+
|
|
145
|
+
process.env[key] = value
|
|
146
|
+
url[name] = value
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
unset = (name, prefix = env) => {
|
|
150
|
+
const key = prefix + '_' + up(name)
|
|
151
|
+
|
|
152
|
+
delete process.env[key]
|
|
153
|
+
url[name] = ''
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
set('username', username)
|
|
157
|
+
set('password', password)
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
afterEach(() => {
|
|
161
|
+
unset('username')
|
|
162
|
+
unset('password')
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
it('should use env variables for credentials', () => {
|
|
166
|
+
pointer = new Pointer(prefix, locator, options)
|
|
167
|
+
|
|
168
|
+
expect(pointer.reference).toStrictEqual(url.href)
|
|
169
|
+
|
|
170
|
+
url.password = ''
|
|
171
|
+
|
|
172
|
+
expect(pointer.label).toStrictEqual(url.href)
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
it('should warn if username is not set', () => {
|
|
176
|
+
url.hostname = locator.hostname(prefix)
|
|
177
|
+
|
|
178
|
+
unset('username')
|
|
179
|
+
url.password = ''
|
|
180
|
+
|
|
181
|
+
const pointer = new Pointer(prefix, locator, options)
|
|
182
|
+
|
|
183
|
+
expect(pointer).toBeDefined()
|
|
184
|
+
expect(mock.console.warn).toHaveBeenCalledWith(`Username for ${env} is not set`)
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
it('should warn if password is not set', () => {
|
|
188
|
+
url.hostname = locator.hostname(prefix)
|
|
189
|
+
|
|
190
|
+
unset('password')
|
|
191
|
+
|
|
192
|
+
const pointer = new Pointer(prefix, locator, options)
|
|
193
|
+
|
|
194
|
+
expect(pointer).toBeDefined()
|
|
195
|
+
expect(mock.console.warn).toHaveBeenCalledWith(`Password for ${env} is not set`)
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it('should not warn it credentials are set', () => {
|
|
199
|
+
uris({ [locator.id]: 'amqp://host0' })
|
|
200
|
+
|
|
201
|
+
const username = generate()
|
|
202
|
+
const password = generate()
|
|
203
|
+
const env = `TOA_${up(prefix)}_${locator.uppercase}`
|
|
204
|
+
|
|
205
|
+
set('username', username, env)
|
|
206
|
+
set('password', password, env)
|
|
207
|
+
|
|
208
|
+
const pointer = new Pointer(prefix, locator, options)
|
|
209
|
+
|
|
210
|
+
expect(pointer).toBeDefined()
|
|
211
|
+
expect(mock.console.warn).not.toHaveBeenCalled()
|
|
212
|
+
})
|
|
213
|
+
})
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const clone = require('clone-deep')
|
|
4
|
+
const { generate } = require('randomstring')
|
|
5
|
+
const { random } = require('@toa.io/generic')
|
|
6
|
+
const { Locator } = require('@toa.io/core')
|
|
7
|
+
|
|
8
|
+
const fixtures = require('./uris.fixtures')
|
|
9
|
+
const { uris } = require('../')
|
|
10
|
+
|
|
11
|
+
const gen = () => 'schema://host-' + generate().toLowerCase() + ':' + (random(1000) + 1000)
|
|
12
|
+
|
|
13
|
+
describe('construct', () => {
|
|
14
|
+
const construct = uris.construct
|
|
15
|
+
|
|
16
|
+
let annotations
|
|
17
|
+
|
|
18
|
+
beforeEach(() => {
|
|
19
|
+
annotations = clone(fixtures.annotations)
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('should be', () => {
|
|
23
|
+
expect(construct).toBeDefined()
|
|
24
|
+
expect(construct).toBeInstanceOf(Function)
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
describe('normalize', () => {
|
|
28
|
+
it('should treat string value as default', () => {
|
|
29
|
+
const declaration = gen()
|
|
30
|
+
const result = construct(declaration)
|
|
31
|
+
|
|
32
|
+
expect(result).toStrictEqual({ default: declaration })
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
it('should allow dot notation', () => {
|
|
36
|
+
const url = gen()
|
|
37
|
+
|
|
38
|
+
annotations['foo.bar'] = url
|
|
39
|
+
|
|
40
|
+
construct(annotations)
|
|
41
|
+
|
|
42
|
+
expect(annotations['foo.bar']).toStrictEqual(url)
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
it('should allow dot notation with namespace value', () => {
|
|
46
|
+
annotations.foo = gen()
|
|
47
|
+
annotations['foo.bar'] = gen()
|
|
48
|
+
|
|
49
|
+
expect(() => construct(annotations)).not.toThrow()
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
describe('validate', () => {
|
|
54
|
+
it('should throw if declaration is not a string or an object', () => {
|
|
55
|
+
// noinspection JSCheckFunctionSignatures
|
|
56
|
+
expect(() => construct(1)).toThrow(TypeError)
|
|
57
|
+
expect(() => construct(null)).toThrow(TypeError)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('should throw if property is not url', () => {
|
|
61
|
+
annotations.foo = 'not url'
|
|
62
|
+
|
|
63
|
+
expect(() => construct(annotations)).toThrow(TypeError)
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
describe('resolve', () => {
|
|
69
|
+
const resolve = uris.resolve
|
|
70
|
+
|
|
71
|
+
let name
|
|
72
|
+
let namespace
|
|
73
|
+
let uri
|
|
74
|
+
|
|
75
|
+
/** @type {toa.pointer.URIs} */
|
|
76
|
+
let annotation
|
|
77
|
+
|
|
78
|
+
/** @type {toa.core.Locator} */
|
|
79
|
+
let locator
|
|
80
|
+
|
|
81
|
+
beforeEach(() => {
|
|
82
|
+
name = generate()
|
|
83
|
+
namespace = generate()
|
|
84
|
+
uri = gen()
|
|
85
|
+
|
|
86
|
+
annotation = {
|
|
87
|
+
[namespace + '.' + name]: uri
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
locator = new Locator(name, namespace)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it('should be', () => {
|
|
94
|
+
expect(uris.resolve).toBeDefined()
|
|
95
|
+
expect(uris.resolve).toBeInstanceOf(Function)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
it('should resolve by id', () => {
|
|
99
|
+
const { url } = resolve(locator, annotation)
|
|
100
|
+
|
|
101
|
+
expect(url).toBeInstanceOf(URL)
|
|
102
|
+
expect(url.href).toStrictEqual(uri)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it('should throw if not found', () => {
|
|
106
|
+
const name = generate()
|
|
107
|
+
const namespace = generate()
|
|
108
|
+
const locator = new Locator(name, namespace)
|
|
109
|
+
|
|
110
|
+
expect(() => resolve(locator, annotation)).toThrow(`URI annotation for '${locator.id}' is not found`)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
it('should resolve by namespace', () => {
|
|
114
|
+
annotation[namespace] = uri
|
|
115
|
+
|
|
116
|
+
const { url } = resolve(locator, annotation)
|
|
117
|
+
|
|
118
|
+
expect(url.href).toStrictEqual(uri)
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
it('should resolve default', () => {
|
|
122
|
+
annotation = { default: gen() }
|
|
123
|
+
|
|
124
|
+
const { url } = resolve(locator, annotation)
|
|
125
|
+
|
|
126
|
+
expect(url.href).toStrictEqual(annotation.default)
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
it('should resolve global locators', () => {
|
|
130
|
+
const name = generate()
|
|
131
|
+
const locator = new Locator(name)
|
|
132
|
+
const uri = gen()
|
|
133
|
+
|
|
134
|
+
annotation = { [name]: uri }
|
|
135
|
+
|
|
136
|
+
const { url } = resolve(locator, annotation)
|
|
137
|
+
|
|
138
|
+
expect(url.href).toStrictEqual(uri)
|
|
139
|
+
})
|
|
140
|
+
})
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { generate } = require('randomstring')
|
|
4
|
+
const { Locator } = require('@toa.io/core')
|
|
5
|
+
const { random, letters: { up } } = require('@toa.io/generic')
|
|
6
|
+
|
|
7
|
+
const { variable } = require('../')
|
|
8
|
+
|
|
9
|
+
it('should be', () => {
|
|
10
|
+
expect(variable).toBeInstanceOf(Function)
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
it('should create variable', () => {
|
|
14
|
+
const prefix = 'foo-bar'
|
|
15
|
+
const namespace = generate()
|
|
16
|
+
const component = generate()
|
|
17
|
+
const locator = new Locator(component, namespace)
|
|
18
|
+
const name = generate()
|
|
19
|
+
|
|
20
|
+
let value = generate()
|
|
21
|
+
|
|
22
|
+
const string = variable(prefix, locator, name, value)
|
|
23
|
+
|
|
24
|
+
const expected = {
|
|
25
|
+
name: `TOA_${up(prefix)}_${locator.uppercase}_${up(name)}`,
|
|
26
|
+
value
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
expect(string).toStrictEqual(expected)
|
|
30
|
+
|
|
31
|
+
value = random()
|
|
32
|
+
expected.value = value
|
|
33
|
+
|
|
34
|
+
const number = variable(prefix, locator, name, value)
|
|
35
|
+
|
|
36
|
+
expect(number).toStrictEqual(expected)
|
|
37
|
+
})
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// noinspection ES6UnusedImports
|
|
2
|
+
|
|
3
|
+
import { dependency } from '@toa.io/operations/types/deployment'
|
|
4
|
+
import { dependencies } from '@toa.io/norm/types'
|
|
5
|
+
import { URIs } from './uris'
|
|
6
|
+
|
|
7
|
+
declare namespace toa.pointer {
|
|
8
|
+
|
|
9
|
+
namespace deployment {
|
|
10
|
+
|
|
11
|
+
type Options = {
|
|
12
|
+
prefix: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type Deployment = (instances: dependencies.Instance[], uris: URIs, options: deployment.Options)
|
|
18
|
+
=> dependency.Declaration
|
|
19
|
+
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const deployment: toa.pointer.Deployment
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
declare namespace toa.pointer {
|
|
2
|
+
|
|
3
|
+
type Options = {
|
|
4
|
+
protocol: string // for local environment
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
class Pointer {
|
|
8
|
+
protocol: string
|
|
9
|
+
host: string
|
|
10
|
+
port: number
|
|
11
|
+
hostname: string
|
|
12
|
+
path: string
|
|
13
|
+
username: string
|
|
14
|
+
password: string
|
|
15
|
+
reference: string
|
|
16
|
+
label: string // safe for logging
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type Pointer = toa.pointer.Pointer
|
package/types/uris.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Locator } from '@toa.io/core/types'
|
|
2
|
+
|
|
3
|
+
declare namespace toa.pointer {
|
|
4
|
+
|
|
5
|
+
namespace uris {
|
|
6
|
+
|
|
7
|
+
type Node = {
|
|
8
|
+
[key: string]: string | Node
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
type Constructor = (declaration: URIs | string) => URIs
|
|
12
|
+
|
|
13
|
+
type Resolution = {
|
|
14
|
+
url: URL
|
|
15
|
+
entry: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
type Resolver = (locator: Locator, uris: URIs) => Resolution
|
|
19
|
+
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type URIs = {
|
|
23
|
+
default?: string
|
|
24
|
+
[key: string]: string | uris.Node
|
|
25
|
+
} | string
|
|
26
|
+
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export type URIs = toa.pointer.URIs
|
|
30
|
+
|
|
31
|
+
export const construct: toa.pointer.uris.Constructor
|
|
32
|
+
export const resolve: toa.pointer.uris.Resolver
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Locator } from '@toa.io/core/types'
|
|
2
|
+
import { dependency } from '@toa.io/operations/types/deployment'
|
|
3
|
+
|
|
4
|
+
declare namespace toa.pointer {
|
|
5
|
+
type Variable = (scope: string, locator: Locator, label: string, value: string | number) => dependency.Variable
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const variable: toa.pointer.Variable
|