@toa.io/norm 1.0.0-alpha.0 → 1.0.0-alpha.102
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 +6 -7
- package/src/.component/.expand/entity.js +1 -1
- package/src/.component/.expand/index.js +2 -0
- package/src/.component/.expand/version.js +45 -0
- package/src/.component/.normalize/operations.js +1 -1
- package/src/.component/collapse.js +11 -2
- package/src/.component/defaults.js +23 -8
- package/src/.component/dereference.js +24 -11
- package/src/.component/expand.js +5 -2
- package/src/.component/extensions.js +8 -1
- package/src/.component/merge.js +14 -3
- package/src/.component/schema.yaml +60 -23
- package/src/.component/validate.js +3 -3
- package/src/.context/.dependencies/connectors.js +11 -4
- package/src/.context/.dependencies/resolve.js +9 -0
- package/src/.context/schema.yaml +12 -6
- package/src/.context/validate.js +2 -5
- package/src/component.js +7 -8
- package/src/shortcuts.js +3 -1
- package/test/component/collapse.test.js +0 -13
- package/test/component/expand.fixtures.js +1 -0
- package/test/component/validate.test.js +13 -13
- package/test/context/dereference.fixtures.js +1 -2
- package/test/context/validate.test.js +3 -10
- package/types/component.d.ts +3 -5
- package/types/context.d.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toa.io/norm",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.102",
|
|
4
4
|
"description": "Toa declarations normalization and validation",
|
|
5
5
|
"author": "temich <tema.gurtovoy@gmail.com>",
|
|
6
6
|
"homepage": "https://github.com/toa-io/toa#readme",
|
|
@@ -20,11 +20,10 @@
|
|
|
20
20
|
"test": "echo \"Error: run tests from root\" && exit 1"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@toa.io/core": "1.0.0-alpha.
|
|
24
|
-
"@toa.io/generic": "1.0.0-alpha.
|
|
25
|
-
"@toa.io/
|
|
26
|
-
"@toa.io/
|
|
27
|
-
"@toa.io/yaml": "1.0.0-alpha.0"
|
|
23
|
+
"@toa.io/core": "1.0.0-alpha.93",
|
|
24
|
+
"@toa.io/generic": "1.0.0-alpha.93",
|
|
25
|
+
"@toa.io/schemas": "1.0.0-alpha.93",
|
|
26
|
+
"@toa.io/yaml": "1.0.0-alpha.93"
|
|
28
27
|
},
|
|
29
|
-
"gitHead": "
|
|
28
|
+
"gitHead": "ce5b6e22c1adbec4cc5d4dc5dd8f6bafc41d93d0"
|
|
30
29
|
}
|
|
@@ -5,7 +5,7 @@ const { expand } = require('@toa.io/schemas')
|
|
|
5
5
|
const { resolve } = require('../../shortcuts')
|
|
6
6
|
|
|
7
7
|
function entity (manifest) {
|
|
8
|
-
if (
|
|
8
|
+
if (!('entity' in manifest)) return
|
|
9
9
|
if ('schema' in manifest.entity) manifest.entity.schema = expand(manifest.entity.schema)
|
|
10
10
|
|
|
11
11
|
manifest.entity.storage = resolve(manifest.entity.storage)
|
|
@@ -7,6 +7,7 @@ const { extensions } = require('./extensions')
|
|
|
7
7
|
const { operations } = require('./operations')
|
|
8
8
|
const { properties } = require('./properties')
|
|
9
9
|
const { receivers } = require('./receivers')
|
|
10
|
+
const { version } = require('./version')
|
|
10
11
|
|
|
11
12
|
exports.bridge = bridge
|
|
12
13
|
exports.entity = entity
|
|
@@ -15,3 +16,4 @@ exports.extensions = extensions
|
|
|
15
16
|
exports.operations = operations
|
|
16
17
|
exports.properties = properties
|
|
17
18
|
exports.receivers = receivers
|
|
19
|
+
exports.version = version
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { join } = require('node:path')
|
|
4
|
+
const { createHash } = require('node:crypto')
|
|
5
|
+
const fs = require('node:fs/promises')
|
|
6
|
+
const { createReadStream } = require('node:fs')
|
|
7
|
+
const { once } = require('node:events')
|
|
8
|
+
|
|
9
|
+
async function version (manifest) {
|
|
10
|
+
manifest.version ??= await hash(manifest.path)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
async function hash (path) {
|
|
14
|
+
const hash = await hashd(path)
|
|
15
|
+
|
|
16
|
+
return hash.digest('hex').slice(0, 8)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @param {string} path
|
|
21
|
+
* @param {import('node:crypto').Hash} hash
|
|
22
|
+
*/
|
|
23
|
+
async function hashd (path, hash = createHash('sha256')) {
|
|
24
|
+
const stat = await fs.stat(path)
|
|
25
|
+
|
|
26
|
+
if (stat.isFile()) {
|
|
27
|
+
const stream = createReadStream(path)
|
|
28
|
+
|
|
29
|
+
stream.pipe(hash, { end: false })
|
|
30
|
+
|
|
31
|
+
return await once(stream, 'end')
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (stat.isDirectory()) {
|
|
35
|
+
const entries = await fs.opendir(path)
|
|
36
|
+
|
|
37
|
+
for await (const entry of entries) {
|
|
38
|
+
await hashd(join(path, entry.name), hash)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return hash
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
exports.version = version
|
|
@@ -4,7 +4,7 @@ const operations = (component) => {
|
|
|
4
4
|
if (component.operations === undefined) return
|
|
5
5
|
|
|
6
6
|
for (const [endpoint, operation] of Object.entries(component.operations)) {
|
|
7
|
-
if (operation.
|
|
7
|
+
if (operation.scope === 'none')
|
|
8
8
|
operation.query = false
|
|
9
9
|
|
|
10
10
|
if (operation.bindings === undefined) operation.bindings = component.bindings
|
|
@@ -34,10 +34,19 @@ const collapse = (manifest, prototype) => {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
delete prototype.entity?.storage // ???
|
|
38
|
-
|
|
39
37
|
const { entity, events, extensions } = prototype
|
|
40
38
|
|
|
39
|
+
if (manifest.entity?.schema?.properties.id !== undefined && entity?.schema?.properties.id !== undefined) {
|
|
40
|
+
manifest.entity.custom = true
|
|
41
|
+
|
|
42
|
+
delete prototype.entity.schema.properties.id
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (prototype.events !== undefined && manifest.events !== undefined)
|
|
46
|
+
for (const event of Object.keys(prototype.events))
|
|
47
|
+
if (event in manifest.events)
|
|
48
|
+
delete prototype.events[event]
|
|
49
|
+
|
|
41
50
|
merge(manifest, { entity, events, extensions })
|
|
42
51
|
}
|
|
43
52
|
|
|
@@ -3,21 +3,36 @@
|
|
|
3
3
|
const { hash } = require('@toa.io/generic')
|
|
4
4
|
|
|
5
5
|
// these defaults are required before validation
|
|
6
|
-
const defaults = (manifest) => {
|
|
7
|
-
if (manifest.
|
|
8
|
-
|
|
6
|
+
const defaults = (manifest, proto) => {
|
|
7
|
+
if (manifest.name === undefined)
|
|
8
|
+
if (proto) manifest.name = protoName(manifest)
|
|
9
|
+
else nameAfterDir(manifest)
|
|
10
|
+
|
|
9
11
|
if (manifest.bindings === undefined) manifest.bindings = ['@toa.io/bindings.amqp']
|
|
10
12
|
if (manifest.bridge === undefined) manifest.bridge = '@toa.io/bridges.node'
|
|
11
|
-
if (manifest.entity === null || manifest.entity === undefined) manifest.entity = { storage: null }
|
|
12
|
-
if (manifest.entity.storage === undefined) manifest.entity.storage = '@toa.io/storages.mongodb'
|
|
13
|
-
if (manifest.entity.storage === null) manifest.entity.storage = '@toa.io/storages.null'
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
if ('entity' in manifest) {
|
|
15
|
+
if (manifest.entity.storage === null)
|
|
16
|
+
manifest.entity.storage = '@toa.io/storages.null'
|
|
17
|
+
} else {
|
|
18
|
+
if (manifest.prototype === undefined)
|
|
19
|
+
manifest.prototype = null
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (manifest.prototype === undefined) manifest.prototype = '@toa.io/prototype'
|
|
17
23
|
}
|
|
18
24
|
|
|
19
25
|
function protoName (manifest) {
|
|
20
26
|
return 'proto' + hash(manifest.path)
|
|
21
27
|
}
|
|
22
28
|
|
|
29
|
+
function nameAfterDir (manifest) {
|
|
30
|
+
const parts = manifest.path.split('/')
|
|
31
|
+
const dirname = parts[parts.length - 1]
|
|
32
|
+
const [name, namespace] = dirname.split('.').reverse()
|
|
33
|
+
|
|
34
|
+
manifest.name = name
|
|
35
|
+
manifest.namespace = namespace
|
|
36
|
+
}
|
|
37
|
+
|
|
23
38
|
exports.defaults = defaults
|
|
@@ -4,13 +4,33 @@ const { merge } = require('@toa.io/generic')
|
|
|
4
4
|
|
|
5
5
|
const dereference = (manifest) => {
|
|
6
6
|
// schemas
|
|
7
|
-
const
|
|
7
|
+
const resolver = createResolver(manifest.entity?.schema?.properties)
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
if (manifest.entity !== undefined)
|
|
10
|
+
schema(manifest.entity.schema, resolver)
|
|
10
11
|
|
|
12
|
+
if ('operations' in manifest)
|
|
13
|
+
operations(manifest, resolver)
|
|
14
|
+
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const createResolver = (properties) => (property) => {
|
|
18
|
+
if (properties?.[property] === undefined) {
|
|
19
|
+
throw new Error(`Referenced property '${property}' is not defined`)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return properties[property]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function operations (manifest, resolver) {
|
|
11
26
|
for (const operation of Object.values(manifest.operations)) {
|
|
12
|
-
if (operation.input !== undefined) operation.input = schema(operation.input,
|
|
13
|
-
|
|
27
|
+
if (operation.input !== undefined) operation.input = schema(operation.input, resolver)
|
|
28
|
+
|
|
29
|
+
if (operation.output !== undefined)
|
|
30
|
+
if (Array.isArray(operation.output) && operation.output.length === 1)
|
|
31
|
+
operation.output = [schema(operation.output[0], resolver)]
|
|
32
|
+
else
|
|
33
|
+
operation.output = schema(operation.output, resolver)
|
|
14
34
|
}
|
|
15
35
|
|
|
16
36
|
// forwarding
|
|
@@ -21,14 +41,7 @@ const dereference = (manifest) => {
|
|
|
21
41
|
for (const operation of Object.values(manifest.operations)) {
|
|
22
42
|
delete operation.forwarded
|
|
23
43
|
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const resolve = (schema) => (property) => {
|
|
27
|
-
if (schema[property] === undefined) {
|
|
28
|
-
throw new Error(`Referenced property '${property}' is not defined`)
|
|
29
|
-
}
|
|
30
44
|
|
|
31
|
-
return schema[property]
|
|
32
45
|
}
|
|
33
46
|
|
|
34
47
|
const schema = (object, resolve) => {
|
package/src/.component/expand.js
CHANGED
|
@@ -7,10 +7,11 @@ const {
|
|
|
7
7
|
events,
|
|
8
8
|
receivers,
|
|
9
9
|
extensions,
|
|
10
|
-
properties
|
|
10
|
+
properties,
|
|
11
|
+
version
|
|
11
12
|
} = require('./.expand')
|
|
12
13
|
|
|
13
|
-
|
|
14
|
+
async function expand (manifest) {
|
|
14
15
|
entity(manifest)
|
|
15
16
|
bridge(manifest)
|
|
16
17
|
operations(manifest)
|
|
@@ -18,6 +19,8 @@ const expand = (manifest) => {
|
|
|
18
19
|
receivers(manifest)
|
|
19
20
|
properties(manifest)
|
|
20
21
|
extensions(manifest)
|
|
22
|
+
|
|
23
|
+
await version(manifest)
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
exports.expand = expand
|
|
@@ -4,7 +4,10 @@ const { directory: { find } } = require('@toa.io/filesystem')
|
|
|
4
4
|
const { resolve } = require('../shortcuts')
|
|
5
5
|
|
|
6
6
|
const extensions = (manifest) => {
|
|
7
|
-
if (manifest.extensions === undefined)
|
|
7
|
+
if (manifest.extensions === undefined)
|
|
8
|
+
manifest.extensions = PREDEFINED
|
|
9
|
+
else
|
|
10
|
+
manifest.extensions = Object.assign({}, PREDEFINED, manifest.extensions)
|
|
8
11
|
|
|
9
12
|
const extensions = manifest.extensions
|
|
10
13
|
|
|
@@ -29,4 +32,8 @@ const extensions = (manifest) => {
|
|
|
29
32
|
}
|
|
30
33
|
}
|
|
31
34
|
|
|
35
|
+
const PREDEFINED = {
|
|
36
|
+
'@toa.io/extensions.telemetry': null
|
|
37
|
+
}
|
|
38
|
+
|
|
32
39
|
exports.extensions = extensions
|
package/src/.component/merge.js
CHANGED
|
@@ -28,6 +28,10 @@ const define = async (root, manifest, property) => {
|
|
|
28
28
|
|
|
29
29
|
// default bridge
|
|
30
30
|
const definition = await scan(manifest.bridge, root, property)
|
|
31
|
+
|
|
32
|
+
if (definition === undefined)
|
|
33
|
+
return
|
|
34
|
+
|
|
31
35
|
const items = Object.entries(definition)
|
|
32
36
|
|
|
33
37
|
if (items.length) {
|
|
@@ -40,8 +44,13 @@ const define = async (root, manifest, property) => {
|
|
|
40
44
|
|
|
41
45
|
const declared = manifest[property][endpoint]
|
|
42
46
|
|
|
43
|
-
|
|
44
|
-
|
|
47
|
+
try {
|
|
48
|
+
if (declared === undefined) manifest[property][endpoint] = item
|
|
49
|
+
else merge(declared, item)
|
|
50
|
+
} catch (error) {
|
|
51
|
+
console.error(`Error merging ${singular} '${endpoint}' in ${root}`)
|
|
52
|
+
throw error
|
|
53
|
+
}
|
|
45
54
|
}
|
|
46
55
|
}
|
|
47
56
|
}
|
|
@@ -49,7 +58,9 @@ const define = async (root, manifest, property) => {
|
|
|
49
58
|
const scan = async (bridge, root, property) => {
|
|
50
59
|
const { define } = require(bridge)
|
|
51
60
|
|
|
52
|
-
|
|
61
|
+
if (property in define)
|
|
62
|
+
return define[property](root)
|
|
63
|
+
else return undefined
|
|
53
64
|
}
|
|
54
65
|
|
|
55
66
|
exports.merge = bridge
|
|
@@ -2,6 +2,9 @@ $schema: https://json-schema.org/draft/2019-09/schema
|
|
|
2
2
|
$id: https://schemas.toa.io/0.0.0/manifest
|
|
3
3
|
|
|
4
4
|
definitions:
|
|
5
|
+
name:
|
|
6
|
+
type: string
|
|
7
|
+
pattern: ^[a-zA-Z]+([_a-zA-Z0-9]*[a-zA-Z0-9]+)?$
|
|
5
8
|
binding:
|
|
6
9
|
type: string
|
|
7
10
|
not:
|
|
@@ -20,7 +23,7 @@ properties:
|
|
|
20
23
|
operations:
|
|
21
24
|
type: object
|
|
22
25
|
propertyNames:
|
|
23
|
-
$ref: '
|
|
26
|
+
$ref: '#/definitions/name'
|
|
24
27
|
patternProperties:
|
|
25
28
|
'.*':
|
|
26
29
|
type: object
|
|
@@ -35,30 +38,41 @@ properties:
|
|
|
35
38
|
type: object
|
|
36
39
|
properties:
|
|
37
40
|
id:
|
|
38
|
-
|
|
41
|
+
type: string
|
|
42
|
+
pattern: ^([a-zA-Z]+([_a-zA-Z0-9]*[a-zA-Z0-9]+)?)(\.([a-zA-Z]+([_a-zA-Z0-9]*[a-zA-Z0-9]+)?))$
|
|
39
43
|
label:
|
|
40
|
-
|
|
44
|
+
type: string
|
|
45
|
+
pattern: ^([a-zA-Z]+([_a-zA-Z0-9]*[a-zA-Z0-9]+)?)(-([a-zA-Z]+([_a-zA-Z0-9]*[a-zA-Z0-9]+)?))*$
|
|
41
46
|
|
|
42
47
|
name:
|
|
43
|
-
|
|
48
|
+
type: string
|
|
49
|
+
pattern: ^[a-zA-Z]([a-zA-Z0-9]{1,31})?$
|
|
44
50
|
|
|
45
51
|
namespace:
|
|
46
|
-
|
|
52
|
+
type: string
|
|
53
|
+
pattern: ^[a-zA-Z]([a-zA-Z0-9]{1,31})?$
|
|
47
54
|
default: 'default'
|
|
48
55
|
not:
|
|
49
56
|
oneOf:
|
|
50
57
|
- const: 'system'
|
|
51
58
|
|
|
52
59
|
version:
|
|
53
|
-
|
|
60
|
+
type: string
|
|
61
|
+
|
|
62
|
+
build:
|
|
63
|
+
type: object
|
|
64
|
+
properties:
|
|
65
|
+
image:
|
|
66
|
+
type: string
|
|
54
67
|
|
|
55
68
|
entity:
|
|
56
69
|
type: object
|
|
57
70
|
properties:
|
|
58
71
|
storage:
|
|
59
72
|
type: string
|
|
73
|
+
default: '@toa.io/storages.mongodb'
|
|
60
74
|
schema:
|
|
61
|
-
$ref:
|
|
75
|
+
$ref: https://json-schema.org/draft/2019-09/schema
|
|
62
76
|
type: object
|
|
63
77
|
properties:
|
|
64
78
|
type:
|
|
@@ -68,9 +82,28 @@ properties:
|
|
|
68
82
|
type: object
|
|
69
83
|
propertyNames:
|
|
70
84
|
oneOf:
|
|
71
|
-
- $ref: '
|
|
72
|
-
- enum: [_version]
|
|
73
|
-
|
|
85
|
+
- $ref: '#/definitions/name'
|
|
86
|
+
- enum: [_version, _created, _updated, _deleted]
|
|
87
|
+
unique:
|
|
88
|
+
type: object
|
|
89
|
+
patternProperties:
|
|
90
|
+
'.*':
|
|
91
|
+
type: array
|
|
92
|
+
items:
|
|
93
|
+
type: string
|
|
94
|
+
index:
|
|
95
|
+
type: object
|
|
96
|
+
patternProperties:
|
|
97
|
+
'.*':
|
|
98
|
+
type: object
|
|
99
|
+
patternProperties:
|
|
100
|
+
'.*':
|
|
101
|
+
type: string
|
|
102
|
+
enum: [asc, desc, hash]
|
|
103
|
+
associated:
|
|
104
|
+
type: boolean
|
|
105
|
+
default: false
|
|
106
|
+
custom:
|
|
74
107
|
type: boolean
|
|
75
108
|
default: false
|
|
76
109
|
required: [schema]
|
|
@@ -88,7 +121,7 @@ properties:
|
|
|
88
121
|
operations:
|
|
89
122
|
type: object
|
|
90
123
|
propertyNames:
|
|
91
|
-
$ref: '
|
|
124
|
+
$ref: '#/definitions/name'
|
|
92
125
|
patternProperties:
|
|
93
126
|
'.*':
|
|
94
127
|
type: object
|
|
@@ -96,19 +129,24 @@ properties:
|
|
|
96
129
|
type:
|
|
97
130
|
enum: [transition, observation, assignment, computation, effect]
|
|
98
131
|
scope:
|
|
99
|
-
enum: [object, objects, changeset, none]
|
|
132
|
+
enum: [object, objects, changeset, stream, none]
|
|
100
133
|
concurrency:
|
|
101
134
|
enum: [none, retry]
|
|
102
135
|
forward:
|
|
103
|
-
$ref: '
|
|
136
|
+
$ref: '#/definitions/name'
|
|
104
137
|
bridge:
|
|
105
138
|
type: string
|
|
106
139
|
bindings:
|
|
107
140
|
$ref: '#/properties/bindings'
|
|
108
141
|
input:
|
|
109
|
-
$ref:
|
|
142
|
+
$ref: https://json-schema.org/draft/2019-09/schema
|
|
110
143
|
output:
|
|
111
|
-
$ref:
|
|
144
|
+
$ref: https://json-schema.org/draft/2019-09/schema
|
|
145
|
+
default: { }
|
|
146
|
+
errors:
|
|
147
|
+
type: array
|
|
148
|
+
items:
|
|
149
|
+
type: string
|
|
112
150
|
query:
|
|
113
151
|
type: boolean
|
|
114
152
|
required: [type, scope, bindings]
|
|
@@ -145,7 +183,7 @@ properties:
|
|
|
145
183
|
then:
|
|
146
184
|
properties:
|
|
147
185
|
scope:
|
|
148
|
-
enum: [object, objects, none]
|
|
186
|
+
enum: [object, objects, stream, none]
|
|
149
187
|
query:
|
|
150
188
|
not:
|
|
151
189
|
const: false
|
|
@@ -176,18 +214,15 @@ properties:
|
|
|
176
214
|
then:
|
|
177
215
|
properties:
|
|
178
216
|
scope:
|
|
179
|
-
const: none
|
|
180
217
|
default: none
|
|
181
|
-
|
|
182
|
-
const: false
|
|
183
|
-
default: false
|
|
218
|
+
enum: [object, objects, stream, none]
|
|
184
219
|
additionalProperties: false
|
|
185
220
|
additionalProperties: false
|
|
186
221
|
|
|
187
222
|
events:
|
|
188
223
|
type: object
|
|
189
224
|
propertyNames:
|
|
190
|
-
$ref: '
|
|
225
|
+
$ref: '#/definitions/name'
|
|
191
226
|
patternProperties:
|
|
192
227
|
'.*':
|
|
193
228
|
type: object
|
|
@@ -214,13 +249,13 @@ properties:
|
|
|
214
249
|
type: object
|
|
215
250
|
properties:
|
|
216
251
|
operation:
|
|
217
|
-
$ref: '
|
|
252
|
+
$ref: '#/definitions/name'
|
|
218
253
|
bridge:
|
|
219
254
|
type: string
|
|
220
255
|
binding:
|
|
221
256
|
type: string
|
|
222
257
|
source:
|
|
223
|
-
$ref: '
|
|
258
|
+
$ref: '#/definitions/name'
|
|
224
259
|
not:
|
|
225
260
|
const: context
|
|
226
261
|
path:
|
|
@@ -231,6 +266,8 @@ properties:
|
|
|
231
266
|
adaptive:
|
|
232
267
|
type: boolean
|
|
233
268
|
default: false
|
|
269
|
+
arguments:
|
|
270
|
+
type: array
|
|
234
271
|
required: [operation]
|
|
235
272
|
additionalProperties: false
|
|
236
273
|
|
|
@@ -3,15 +3,15 @@
|
|
|
3
3
|
const path = require('node:path')
|
|
4
4
|
|
|
5
5
|
const { load } = require('@toa.io/yaml')
|
|
6
|
-
const
|
|
6
|
+
const schemas = require('@toa.io/schemas')
|
|
7
7
|
|
|
8
8
|
const object = load.sync(path.resolve(__dirname, 'schema.yaml'))
|
|
9
|
-
const schema =
|
|
9
|
+
const schema = schemas.schema(object)
|
|
10
10
|
|
|
11
11
|
const validate = (manifest) => {
|
|
12
12
|
const error = schema.fit(manifest)
|
|
13
13
|
|
|
14
|
-
if (error) throw
|
|
14
|
+
if (error) throw error
|
|
15
15
|
|
|
16
16
|
if (manifest.events !== undefined) events(manifest)
|
|
17
17
|
if (manifest.receivers !== undefined) receivers(manifest)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { resolve } = require('node:path')
|
|
4
|
+
|
|
3
5
|
const connectors = (context, extracted) => {
|
|
4
6
|
const connectors = {}
|
|
5
7
|
|
|
@@ -9,11 +11,16 @@ const connectors = (context, extracted) => {
|
|
|
9
11
|
) ?? []
|
|
10
12
|
|
|
11
13
|
for (const component of components) {
|
|
12
|
-
if (
|
|
13
|
-
|
|
14
|
-
}
|
|
14
|
+
if (component.entity !== undefined) {
|
|
15
|
+
let storage = component.entity.storage
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
if (storage[0] === '.') {
|
|
18
|
+
storage = resolve(component.path, storage)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
connectors[storage] ??= []
|
|
22
|
+
connectors[storage].push(component)
|
|
23
|
+
}
|
|
17
24
|
|
|
18
25
|
const bindings = new Set()
|
|
19
26
|
|
|
@@ -24,6 +24,15 @@ const resolve = (references, annotations) => {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
for (const dependency of Object.keys(annotations)) {
|
|
28
|
+
const { module } = load(dependency)
|
|
29
|
+
|
|
30
|
+
if (!module.standalone || (dependency in dependencies))
|
|
31
|
+
continue
|
|
32
|
+
|
|
33
|
+
dependencies[dependency] = []
|
|
34
|
+
}
|
|
35
|
+
|
|
27
36
|
return dependencies
|
|
28
37
|
}
|
|
29
38
|
|
package/src/.context/schema.yaml
CHANGED
|
@@ -4,13 +4,15 @@ $id: https://schemas.toa.io/0.0.0/context
|
|
|
4
4
|
type: object
|
|
5
5
|
properties:
|
|
6
6
|
version:
|
|
7
|
-
|
|
7
|
+
type: string
|
|
8
8
|
name:
|
|
9
|
-
|
|
9
|
+
type: string
|
|
10
|
+
pattern: ^([a-zA-Z]+([_a-zA-Z0-9]*[a-zA-Z0-9]+)?)(-([a-zA-Z]+([_a-zA-Z0-9]*[a-zA-Z0-9]+)?))*$
|
|
10
11
|
description:
|
|
11
12
|
type: string
|
|
12
13
|
packages:
|
|
13
14
|
type: string
|
|
15
|
+
default: components/*
|
|
14
16
|
build:
|
|
15
17
|
type: object
|
|
16
18
|
properties:
|
|
@@ -20,7 +22,7 @@ properties:
|
|
|
20
22
|
type: object
|
|
21
23
|
properties:
|
|
22
24
|
version:
|
|
23
|
-
|
|
25
|
+
type: string
|
|
24
26
|
registry:
|
|
25
27
|
type: string
|
|
26
28
|
format: uri
|
|
@@ -53,14 +55,18 @@ properties:
|
|
|
53
55
|
type: object
|
|
54
56
|
properties:
|
|
55
57
|
name:
|
|
56
|
-
|
|
58
|
+
type: string
|
|
59
|
+
pattern: ^[a-zA-Z]([a-zA-Z0-9]{1,31})?$
|
|
60
|
+
image:
|
|
61
|
+
type: string
|
|
57
62
|
components:
|
|
58
63
|
type: array
|
|
59
64
|
minItems: 1
|
|
60
65
|
items:
|
|
61
|
-
|
|
66
|
+
type: string
|
|
67
|
+
pattern: ^([a-zA-Z]+([_a-zA-Z0-9]*[a-zA-Z0-9]+)?)(\.([a-zA-Z]+([_a-zA-Z0-9]*[a-zA-Z0-9]+)?))$
|
|
62
68
|
required: [name, components]
|
|
63
69
|
annotations:
|
|
64
70
|
type: object
|
|
65
|
-
required: [runtime, name,
|
|
71
|
+
required: [runtime, name, registry]
|
|
66
72
|
additionalProperties: false
|
package/src/.context/validate.js
CHANGED
|
@@ -3,15 +3,12 @@
|
|
|
3
3
|
const { resolve } = require('node:path')
|
|
4
4
|
|
|
5
5
|
const { load } = require('@toa.io/yaml')
|
|
6
|
-
const
|
|
6
|
+
const schemas = require('@toa.io/schemas')
|
|
7
7
|
|
|
8
8
|
const path = resolve(__dirname, 'schema.yaml')
|
|
9
9
|
const object = load.sync(path)
|
|
10
|
-
const schema =
|
|
10
|
+
const schema = schemas.schema(object)
|
|
11
11
|
|
|
12
|
-
/**
|
|
13
|
-
* @param {toa.norm.context.Declaration} context
|
|
14
|
-
*/
|
|
15
12
|
const validate = (context) => {
|
|
16
13
|
schema.validate(context)
|
|
17
14
|
}
|
package/src/component.js
CHANGED
|
@@ -13,7 +13,6 @@ const {
|
|
|
13
13
|
collapse,
|
|
14
14
|
dereference,
|
|
15
15
|
defaults,
|
|
16
|
-
dependencies,
|
|
17
16
|
normalize,
|
|
18
17
|
extensions
|
|
19
18
|
} = require('./.component')
|
|
@@ -21,7 +20,7 @@ const {
|
|
|
21
20
|
const component = async (path) => {
|
|
22
21
|
const manifest = await load(path)
|
|
23
22
|
|
|
24
|
-
normalize(manifest)
|
|
23
|
+
normalize(manifest, path)
|
|
25
24
|
validate(manifest)
|
|
26
25
|
extensions(manifest)
|
|
27
26
|
|
|
@@ -30,27 +29,27 @@ const component = async (path) => {
|
|
|
30
29
|
return manifest
|
|
31
30
|
}
|
|
32
31
|
|
|
33
|
-
const load = async (path, base) => {
|
|
32
|
+
const load = async (path, base, proto = false) => {
|
|
34
33
|
if (base !== undefined) path = find(path, base, MANIFEST)
|
|
35
34
|
|
|
36
35
|
const file = join(path, MANIFEST)
|
|
37
|
-
const manifest =
|
|
36
|
+
const manifest = await yaml(file) ?? {}
|
|
38
37
|
|
|
39
38
|
manifest.path = path
|
|
40
39
|
|
|
41
|
-
defaults(manifest)
|
|
42
|
-
expand(manifest)
|
|
40
|
+
defaults(manifest, proto)
|
|
41
|
+
await expand(manifest)
|
|
43
42
|
|
|
44
43
|
await merge(path, manifest)
|
|
45
44
|
|
|
46
45
|
if (manifest.prototype !== null) {
|
|
47
|
-
const prototype = await load(manifest.prototype, path)
|
|
46
|
+
const prototype = await load(manifest.prototype, path, true)
|
|
48
47
|
|
|
49
48
|
collapse(manifest, prototype)
|
|
50
49
|
}
|
|
51
50
|
|
|
52
51
|
dereference(manifest)
|
|
53
|
-
dependencies(manifest)
|
|
52
|
+
// dependencies(manifest)
|
|
54
53
|
|
|
55
54
|
return manifest
|
|
56
55
|
}
|
package/src/shortcuts.js
CHANGED
|
@@ -41,6 +41,7 @@ const recognize = (shortcuts, object, group) => {
|
|
|
41
41
|
const SHORTCUTS = {
|
|
42
42
|
amqp: '@toa.io/bindings.amqp',
|
|
43
43
|
node: '@toa.io/bridges.node',
|
|
44
|
+
bash: '@toa.io/bridges.bash',
|
|
44
45
|
mongodb: '@toa.io/storages.mongodb',
|
|
45
46
|
sql: '@toa.io/storages.sql',
|
|
46
47
|
queues: '@toa.io/storages.queues',
|
|
@@ -49,7 +50,8 @@ const SHORTCUTS = {
|
|
|
49
50
|
configuration: '@toa.io/extensions.configuration',
|
|
50
51
|
origins: '@toa.io/extensions.origins',
|
|
51
52
|
stash: '@toa.io/extensions.stash',
|
|
52
|
-
storages: '@toa.io/extensions.storages'
|
|
53
|
+
storages: '@toa.io/extensions.storages',
|
|
54
|
+
telemetry: '@toa.io/extensions.telemetry'
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
exports.recognize = recognize
|
|
@@ -30,19 +30,6 @@ it('should remove prototype property', () => {
|
|
|
30
30
|
})
|
|
31
31
|
|
|
32
32
|
describe('entity', () => {
|
|
33
|
-
it('should ignore storage', () => {
|
|
34
|
-
const source = { entity: { storage: 'foo' } }
|
|
35
|
-
const prototype = { entity: { storage: 'bar' } }
|
|
36
|
-
const manifest = clone(source)
|
|
37
|
-
|
|
38
|
-
collapse(manifest, prototype)
|
|
39
|
-
expect(manifest).toStrictEqual(source)
|
|
40
|
-
|
|
41
|
-
delete manifest.entity.storage
|
|
42
|
-
collapse(manifest, prototype)
|
|
43
|
-
expect(manifest).toStrictEqual({ entity: {} })
|
|
44
|
-
})
|
|
45
|
-
|
|
46
33
|
it('should merge entity schema', () => {
|
|
47
34
|
const manifest = clone(samples.entity.manifest)
|
|
48
35
|
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// noinspection JSUnresolvedVariable
|
|
2
|
-
|
|
3
1
|
'use strict'
|
|
4
2
|
|
|
5
3
|
const clone = require('clone-deep')
|
|
@@ -20,13 +18,13 @@ it('should be ok', () => {
|
|
|
20
18
|
it('should provide error', () => {
|
|
21
19
|
manifest.foo = 'bar'
|
|
22
20
|
|
|
23
|
-
expect(() => validate(manifest)).toThrow(
|
|
21
|
+
expect(() => validate(manifest)).toThrow()
|
|
24
22
|
})
|
|
25
23
|
|
|
26
24
|
it('should not have additional properties', () => {
|
|
27
25
|
manifest.foo = 'bar'
|
|
28
26
|
|
|
29
|
-
expect(() => validate(manifest)).toThrow(
|
|
27
|
+
expect(() => validate(manifest)).toThrow()
|
|
30
28
|
})
|
|
31
29
|
|
|
32
30
|
describe('namespace', () => {
|
|
@@ -83,13 +81,13 @@ describe('entity', () => {
|
|
|
83
81
|
|
|
84
82
|
it('should not have additional properties', () => {
|
|
85
83
|
manifest.entity.foo = 'bar'
|
|
86
|
-
expect(() => validate(manifest)).toThrow(
|
|
84
|
+
expect(() => validate(manifest)).toThrow()
|
|
87
85
|
})
|
|
88
86
|
|
|
89
87
|
describe('schema', () => {
|
|
90
88
|
it('should be required', () => {
|
|
91
89
|
delete manifest.entity.schema
|
|
92
|
-
expect(() => validate(manifest)).toThrow(
|
|
90
|
+
expect(() => validate(manifest)).toThrow()
|
|
93
91
|
})
|
|
94
92
|
|
|
95
93
|
it('should be JSON schema object', () => {
|
|
@@ -99,7 +97,7 @@ describe('entity', () => {
|
|
|
99
97
|
|
|
100
98
|
it('should be JSON schema object of type object', () => {
|
|
101
99
|
manifest.entity.schema = { type: 'integer' }
|
|
102
|
-
expect(() => validate(manifest)).toThrow(/must be equal to constant
|
|
100
|
+
expect(() => validate(manifest)).toThrow(/must be equal to constant/)
|
|
103
101
|
|
|
104
102
|
manifest.entity.schema = {}
|
|
105
103
|
validate(manifest)
|
|
@@ -112,15 +110,18 @@ describe('entity', () => {
|
|
|
112
110
|
})
|
|
113
111
|
|
|
114
112
|
it('should allow default id', () => {
|
|
115
|
-
manifest.entity.schema.properties.id = {
|
|
113
|
+
manifest.entity.schema.properties.id = {
|
|
114
|
+
type: 'string',
|
|
115
|
+
pattern: '^[a-fA-F0-9]+$'
|
|
116
|
+
}
|
|
116
117
|
expect(() => validate(manifest)).not.toThrow()
|
|
117
118
|
})
|
|
118
119
|
})
|
|
119
120
|
|
|
120
|
-
describe('
|
|
121
|
+
describe('associated', () => {
|
|
121
122
|
it('should provide default', () => {
|
|
122
123
|
expect(() => validate(manifest)).not.toThrow()
|
|
123
|
-
expect(manifest.entity.
|
|
124
|
+
expect(manifest.entity.associated).toBe(false)
|
|
124
125
|
})
|
|
125
126
|
})
|
|
126
127
|
})
|
|
@@ -151,7 +152,7 @@ describe('operations', () => {
|
|
|
151
152
|
|
|
152
153
|
it('should not have additional properties', () => {
|
|
153
154
|
manifest.operations.get.foo = 'bar'
|
|
154
|
-
expect(() => validate(manifest)).toThrow(
|
|
155
|
+
expect(() => validate(manifest)).toThrow()
|
|
155
156
|
})
|
|
156
157
|
|
|
157
158
|
it('should have type (transition or observation)', () => {
|
|
@@ -173,8 +174,7 @@ describe('operations', () => {
|
|
|
173
174
|
})
|
|
174
175
|
|
|
175
176
|
it.each([
|
|
176
|
-
['computation', 'compute']
|
|
177
|
-
['effect', 'affect']
|
|
177
|
+
['computation', 'compute']
|
|
178
178
|
])('should set query: false for %s', async (_, operation) => {
|
|
179
179
|
validate(manifest)
|
|
180
180
|
|
|
@@ -21,14 +21,6 @@ describe('runtime', () => {
|
|
|
21
21
|
expect(() => validate(context)).toThrow(/required/)
|
|
22
22
|
})
|
|
23
23
|
|
|
24
|
-
it('should require version as semver', () => {
|
|
25
|
-
delete context.runtime.version
|
|
26
|
-
expect(() => validate(context)).toThrow(/required/)
|
|
27
|
-
|
|
28
|
-
context.runtime.version = '.'
|
|
29
|
-
expect(() => validate(context)).toThrow(/pattern/)
|
|
30
|
-
})
|
|
31
|
-
|
|
32
24
|
it('should require registry to match uri format', () => {
|
|
33
25
|
context.runtime.registry = 'not-a-uri'
|
|
34
26
|
expect(() => validate(context)).toThrow(/must match format/)
|
|
@@ -80,9 +72,10 @@ it('should require name as label', () => {
|
|
|
80
72
|
expect(() => validate(context)).not.toThrow(/pattern/)
|
|
81
73
|
})
|
|
82
74
|
|
|
83
|
-
it('should
|
|
75
|
+
it('should set default packages location', () => {
|
|
84
76
|
delete context.packages
|
|
85
|
-
expect(() => validate(context)).toThrow(
|
|
77
|
+
expect(() => validate(context)).not.toThrow()
|
|
78
|
+
expect(context.packages).toBe('components/*')
|
|
86
79
|
})
|
|
87
80
|
|
|
88
81
|
it('should require registry url', () => {
|
package/types/component.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ type Map = {
|
|
|
4
4
|
[id: string]: Component
|
|
5
5
|
}
|
|
6
6
|
|
|
7
|
-
type Operation = {
|
|
7
|
+
export type Operation = {
|
|
8
8
|
type: operations.type
|
|
9
9
|
scope?: operations.scope
|
|
10
10
|
bindings?: string[]
|
|
@@ -14,9 +14,7 @@ type Operation = {
|
|
|
14
14
|
query?: boolean
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
type Operations =
|
|
18
|
-
[key: string]: Operation
|
|
19
|
-
}
|
|
17
|
+
export type Operations = Record<string, Operation>
|
|
20
18
|
|
|
21
19
|
type Event = {
|
|
22
20
|
binding: string
|
|
@@ -39,7 +37,7 @@ type Receiver = {
|
|
|
39
37
|
type Entity = {
|
|
40
38
|
schema: Object
|
|
41
39
|
storage?: string
|
|
42
|
-
|
|
40
|
+
associated?: boolean
|
|
43
41
|
}
|
|
44
42
|
|
|
45
43
|
type Declaration = {
|
package/types/context.d.ts
CHANGED