@toa.io/norm 1.0.0-alpha.17 → 1.0.0-alpha.171

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/norm",
3
- "version": "1.0.0-alpha.17",
3
+ "version": "1.0.0-alpha.171",
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.17",
24
- "@toa.io/generic": "1.0.0-alpha.17",
25
- "@toa.io/schema": "1.0.0-alpha.17",
26
- "@toa.io/schemas": "1.0.0-alpha.17",
27
- "@toa.io/yaml": "1.0.0-alpha.17"
23
+ "@toa.io/core": "1.0.0-alpha.156",
24
+ "@toa.io/generic": "1.0.0-alpha.93",
25
+ "@toa.io/schemas": "1.0.0-alpha.143",
26
+ "@toa.io/yaml": "1.0.0-alpha.93"
28
27
  },
29
- "gitHead": "40ea6c9695dc6b8dc434cad4eb413d7477238ac5"
28
+ "gitHead": "d00cc7d043690d577fa4afd0adb73ac98e5bf0fb"
30
29
  }
@@ -14,7 +14,8 @@ const SHORTCUTS = {
14
14
  configuration: '@toa.io/extensions.configuration',
15
15
  state: '@toa.io/extensions.state',
16
16
  stash: '@toa.io/extensions.stash',
17
- storages: '@toa.io/extensions.storages'
17
+ storages: '@toa.io/extensions.storages',
18
+ mail: '@toa.io/extensions.mail'
18
19
  }
19
20
 
20
21
  exports.extensions = extensions
@@ -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.type === 'computation' || operation.type === 'effect')
7
+ if (operation.scope === 'none')
8
8
  operation.query = false
9
9
 
10
10
  if (operation.bindings === undefined) operation.bindings = component.bindings
@@ -42,6 +42,11 @@ const collapse = (manifest, prototype) => {
42
42
  delete prototype.entity.schema.properties.id
43
43
  }
44
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
+
45
50
  merge(manifest, { entity, events, extensions })
46
51
  }
47
52
 
@@ -1,11 +1,13 @@
1
1
  'use strict'
2
2
 
3
3
  const { hash } = require('@toa.io/generic')
4
- const assert = require('node:assert')
5
4
 
6
5
  // these defaults are required before validation
7
- const defaults = (manifest) => {
8
- if (manifest.name === undefined) manifest.name = protoName(manifest)
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
13
 
@@ -24,4 +26,13 @@ function protoName (manifest) {
24
26
  return 'proto' + hash(manifest.path)
25
27
  }
26
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
+
27
38
  exports.defaults = defaults
@@ -25,7 +25,12 @@ const createResolver = (properties) => (property) => {
25
25
  function operations (manifest, resolver) {
26
26
  for (const operation of Object.values(manifest.operations)) {
27
27
  if (operation.input !== undefined) operation.input = schema(operation.input, resolver)
28
- if (operation.output !== undefined) operation.output = schema(operation.output, 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)
29
34
  }
30
35
 
31
36
  // forwarding
@@ -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) return
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
@@ -44,8 +44,13 @@ const define = async (root, manifest, property) => {
44
44
 
45
45
  const declared = manifest[property][endpoint]
46
46
 
47
- if (declared === undefined) manifest[property][endpoint] = item
48
- else merge(declared, item)
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
+ }
49
54
  }
50
55
  }
51
56
  }
@@ -38,15 +38,19 @@ properties:
38
38
  type: object
39
39
  properties:
40
40
  id:
41
- $ref: 'definitions#/definitions/locator'
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]+)?))$
42
43
  label:
43
- $ref: 'definitions#/definitions/label'
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]+)?))*$
44
46
 
45
47
  name:
46
- $ref: 'definitions#/definitions/token'
48
+ type: string
49
+ pattern: ^[a-zA-Z]([a-zA-Z0-9]{1,31})?$
47
50
 
48
51
  namespace:
49
- $ref: 'definitions#/definitions/token'
52
+ type: string
53
+ pattern: ^[a-zA-Z]([a-zA-Z0-9]{1,31})?$
50
54
  default: 'default'
51
55
  not:
52
56
  oneOf:
@@ -60,6 +64,8 @@ properties:
60
64
  properties:
61
65
  image:
62
66
  type: string
67
+ run:
68
+ type: string
63
69
 
64
70
  entity:
65
71
  type: object
@@ -68,7 +74,7 @@ properties:
68
74
  type: string
69
75
  default: '@toa.io/storages.mongodb'
70
76
  schema:
71
- $ref: 'definitions#/definitions/schema'
77
+ $ref: https://json-schema.org/draft/2019-09/schema
72
78
  type: object
73
79
  properties:
74
80
  type:
@@ -95,7 +101,7 @@ properties:
95
101
  patternProperties:
96
102
  '.*':
97
103
  type: string
98
- enum: [asc, desc, hash]
104
+ enum: [asc, desc, hash, text]
99
105
  associated:
100
106
  type: boolean
101
107
  default: false
@@ -123,9 +129,9 @@ properties:
123
129
  type: object
124
130
  properties:
125
131
  type:
126
- enum: [transition, observation, assignment, computation, effect]
132
+ enum: [transition, observation, assignment, computation, effect, unmanaged]
127
133
  scope:
128
- enum: [object, objects, changeset, none]
134
+ enum: [object, objects, changeset, stream, none]
129
135
  concurrency:
130
136
  enum: [none, retry]
131
137
  forward:
@@ -135,9 +141,14 @@ properties:
135
141
  bindings:
136
142
  $ref: '#/properties/bindings'
137
143
  input:
138
- $ref: 'definitions#/definitions/schema'
144
+ $ref: https://json-schema.org/draft/2019-09/schema
139
145
  output:
140
- $ref: 'definitions#/definitions/schema'
146
+ $ref: https://json-schema.org/draft/2019-09/schema
147
+ default: { }
148
+ errors:
149
+ type: array
150
+ items:
151
+ type: string
141
152
  query:
142
153
  type: boolean
143
154
  required: [type, scope, bindings]
@@ -174,7 +185,7 @@ properties:
174
185
  then:
175
186
  properties:
176
187
  scope:
177
- enum: [object, objects, none]
188
+ enum: [object, objects, stream, none]
178
189
  query:
179
190
  not:
180
191
  const: false
@@ -189,7 +200,7 @@ properties:
189
200
  - if: # computation
190
201
  properties:
191
202
  type:
192
- const: computation
203
+ enum: [computation, unmanaged]
193
204
  then:
194
205
  properties:
195
206
  scope:
@@ -205,18 +216,15 @@ properties:
205
216
  then:
206
217
  properties:
207
218
  scope:
208
- const: none
209
219
  default: none
210
- query:
211
- const: false
212
- default: false
220
+ enum: [object, objects, stream, none]
213
221
  additionalProperties: false
214
222
  additionalProperties: false
215
223
 
216
224
  events:
217
225
  type: object
218
226
  propertyNames:
219
- $ref: 'definitions#/definitions/name'
227
+ $ref: '#/definitions/name'
220
228
  patternProperties:
221
229
  '.*':
222
230
  type: object
@@ -243,13 +251,13 @@ properties:
243
251
  type: object
244
252
  properties:
245
253
  operation:
246
- $ref: 'definitions#/definitions/name'
254
+ $ref: '#/definitions/name'
247
255
  bridge:
248
256
  type: string
249
257
  binding:
250
258
  type: string
251
259
  source:
252
- $ref: 'definitions#/definitions/name'
260
+ $ref: '#/definitions/name'
253
261
  not:
254
262
  const: context
255
263
  path:
@@ -260,6 +268,8 @@ properties:
260
268
  adaptive:
261
269
  type: boolean
262
270
  default: false
271
+ arguments:
272
+ type: array
263
273
  required: [operation]
264
274
  additionalProperties: false
265
275
 
@@ -3,15 +3,15 @@
3
3
  const path = require('node:path')
4
4
 
5
5
  const { load } = require('@toa.io/yaml')
6
- const { Schema } = require('@toa.io/schema')
6
+ const schemas = require('@toa.io/schemas')
7
7
 
8
8
  const object = load.sync(path.resolve(__dirname, 'schema.yaml'))
9
- const schema = new Schema(object)
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 new Error(error.message)
14
+ if (error) throw error
15
15
 
16
16
  if (manifest.events !== undefined) events(manifest)
17
17
  if (manifest.receivers !== undefined) receivers(manifest)
@@ -32,11 +32,11 @@ const receivers = (manifest) => {
32
32
  }
33
33
 
34
34
  if (!TYPES.has(manifest.operations[receiver.operation].type)) {
35
- throw new Error(`Receiver '${locator}' must refer to an operation of one of the allowed types: ${Array.from(TYPES).join(', ')}`)
35
+ throw new Error(`Receiver '${locator}' must refer to an operation of the allowed types: ${Array.from(TYPES).join(', ')}`)
36
36
  }
37
37
  }
38
38
  }
39
39
 
40
- const TYPES = new Set(['transition', 'effect'])
40
+ const TYPES = new Set(['transition', 'assignment', 'effect', 'unmanaged'])
41
41
 
42
42
  exports.validate = validate
@@ -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
 
@@ -6,7 +6,8 @@ properties:
6
6
  version:
7
7
  type: string
8
8
  name:
9
- $ref: 'definitions#/definitions/label'
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:
@@ -46,7 +47,7 @@ properties:
46
47
  - linux/amd64
47
48
  - linux/arm/v7
48
49
  - linux/arm64
49
- required: [base, platforms]
50
+ # required: [base]
50
51
  compositions:
51
52
  type: array
52
53
  minItems: 1
@@ -54,14 +55,16 @@ properties:
54
55
  type: object
55
56
  properties:
56
57
  name:
57
- $ref: 'definitions#/definitions/token'
58
+ type: string
59
+ pattern: ^[a-zA-Z]([a-zA-Z0-9]{1,31})?$
58
60
  image:
59
61
  type: string
60
62
  components:
61
63
  type: array
62
64
  minItems: 1
63
65
  items:
64
- $ref: 'definitions#/definitions/locator'
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]+)?))$
65
68
  required: [name, components]
66
69
  annotations:
67
70
  type: object
@@ -3,15 +3,12 @@
3
3
  const { resolve } = require('node:path')
4
4
 
5
5
  const { load } = require('@toa.io/yaml')
6
- const { Schema } = require('@toa.io/schema')
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 = new Schema(object)
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
@@ -20,7 +20,7 @@ const {
20
20
  const component = async (path) => {
21
21
  const manifest = await load(path)
22
22
 
23
- normalize(manifest)
23
+ normalize(manifest, path)
24
24
  validate(manifest)
25
25
  extensions(manifest)
26
26
 
@@ -29,21 +29,21 @@ const component = async (path) => {
29
29
  return manifest
30
30
  }
31
31
 
32
- const load = async (path, base) => {
32
+ const load = async (path, base, proto = false) => {
33
33
  if (base !== undefined) path = find(path, base, MANIFEST)
34
34
 
35
35
  const file = join(path, MANIFEST)
36
- const manifest = /** @type {toa.norm.Component} */ await yaml(file) ?? {}
36
+ const manifest = await yaml(file) ?? {}
37
37
 
38
38
  manifest.path = path
39
39
 
40
- defaults(manifest)
40
+ defaults(manifest, proto)
41
41
  await expand(manifest)
42
42
 
43
43
  await merge(path, manifest)
44
44
 
45
45
  if (manifest.prototype !== null) {
46
- const prototype = await load(manifest.prototype, path)
46
+ const prototype = await load(manifest.prototype, path, true)
47
47
 
48
48
  collapse(manifest, prototype)
49
49
  }
package/src/shortcuts.js CHANGED
@@ -50,7 +50,9 @@ const SHORTCUTS = {
50
50
  configuration: '@toa.io/extensions.configuration',
51
51
  origins: '@toa.io/extensions.origins',
52
52
  stash: '@toa.io/extensions.stash',
53
- storages: '@toa.io/extensions.storages'
53
+ storages: '@toa.io/extensions.storages',
54
+ telemetry: '@toa.io/extensions.telemetry',
55
+ mail: '@toa.io/extensions.mail'
54
56
  }
55
57
 
56
58
  exports.recognize = recognize
@@ -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(/must NOT have additional property/)
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(/must NOT have additional property/)
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(/must NOT have additional property/)
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(/must have required property/)
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 'object'/)
100
+ expect(() => validate(manifest)).toThrow(/must be equal to constant/)
103
101
 
104
102
  manifest.entity.schema = {}
105
103
  validate(manifest)
@@ -154,7 +152,7 @@ describe('operations', () => {
154
152
 
155
153
  it('should not have additional properties', () => {
156
154
  manifest.operations.get.foo = 'bar'
157
- expect(() => validate(manifest)).toThrow(/additional property/)
155
+ expect(() => validate(manifest)).toThrow()
158
156
  })
159
157
 
160
158
  it('should have type (transition or observation)', () => {
@@ -176,8 +174,7 @@ describe('operations', () => {
176
174
  })
177
175
 
178
176
  it.each([
179
- ['computation', 'compute'],
180
- ['effect', 'affect']
177
+ ['computation', 'compute']
181
178
  ])('should set query: false for %s', async (_, operation) => {
182
179
  validate(manifest)
183
180
 
@@ -250,7 +247,7 @@ describe('receivers', () => {
250
247
  it('should throw if transition points to observation', () => {
251
248
  manifest.receivers['foo.bar.happened'].operation = 'get'
252
249
 
253
- expect(() => validate(manifest)).toThrow(/one of the allowed types/)
250
+ expect(() => validate(manifest)).toThrow(/of the allowed types/)
254
251
  })
255
252
 
256
253
  it('should throw if source has a name `context`', async () => {
@@ -45,12 +45,6 @@ describe('registry', () => {
45
45
  expect(() => validate(context)).toThrow(/required property 'registry'/)
46
46
  })
47
47
 
48
- it('should require base', () => {
49
- delete context.registry.base
50
-
51
- expect(() => validate(context)).toThrow(/required property 'base'/)
52
- })
53
-
54
48
  it('should set default platforms', () => {
55
49
  delete context.registry.platforms
56
50