@toa.io/norm 1.0.0-alpha.9 → 1.0.0-alpha.91

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.9",
3
+ "version": "1.0.0-alpha.91",
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.9",
24
- "@toa.io/generic": "1.0.0-alpha.9",
25
- "@toa.io/schema": "1.0.0-alpha.9",
26
- "@toa.io/schemas": "1.0.0-alpha.9",
27
- "@toa.io/yaml": "1.0.0-alpha.9"
23
+ "@toa.io/core": "1.0.0-alpha.91",
24
+ "@toa.io/generic": "1.0.0-alpha.63",
25
+ "@toa.io/schemas": "1.0.0-alpha.63",
26
+ "@toa.io/yaml": "1.0.0-alpha.63"
28
27
  },
29
- "gitHead": "017a3fa22e8c60654c240f8e55908773d44d4ed1"
28
+ "gitHead": "fb3644243517fad0327e75e055c1f663cee503d8"
30
29
  }
@@ -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
@@ -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:
@@ -68,7 +72,7 @@ properties:
68
72
  type: string
69
73
  default: '@toa.io/storages.mongodb'
70
74
  schema:
71
- $ref: 'definitions#/definitions/schema'
75
+ $ref: https://json-schema.org/draft/2019-09/schema
72
76
  type: object
73
77
  properties:
74
78
  type:
@@ -79,7 +83,7 @@ properties:
79
83
  propertyNames:
80
84
  oneOf:
81
85
  - $ref: '#/definitions/name'
82
- - enum: [_version]
86
+ - enum: [_version, _created, _updated, _deleted]
83
87
  unique:
84
88
  type: object
85
89
  patternProperties:
@@ -96,7 +100,7 @@ properties:
96
100
  '.*':
97
101
  type: string
98
102
  enum: [asc, desc, hash]
99
- dependent:
103
+ associated:
100
104
  type: boolean
101
105
  default: false
102
106
  custom:
@@ -125,7 +129,7 @@ properties:
125
129
  type:
126
130
  enum: [transition, observation, assignment, computation, effect]
127
131
  scope:
128
- enum: [object, objects, changeset, none]
132
+ enum: [object, objects, changeset, stream, none]
129
133
  concurrency:
130
134
  enum: [none, retry]
131
135
  forward:
@@ -135,9 +139,14 @@ properties:
135
139
  bindings:
136
140
  $ref: '#/properties/bindings'
137
141
  input:
138
- $ref: 'definitions#/definitions/schema'
142
+ $ref: https://json-schema.org/draft/2019-09/schema
139
143
  output:
140
- $ref: 'definitions#/definitions/schema'
144
+ $ref: https://json-schema.org/draft/2019-09/schema
145
+ default: { }
146
+ errors:
147
+ type: array
148
+ items:
149
+ type: string
141
150
  query:
142
151
  type: boolean
143
152
  required: [type, scope, bindings]
@@ -174,7 +183,7 @@ properties:
174
183
  then:
175
184
  properties:
176
185
  scope:
177
- enum: [object, objects, none]
186
+ enum: [object, objects, stream, none]
178
187
  query:
179
188
  not:
180
189
  const: false
@@ -205,18 +214,15 @@ properties:
205
214
  then:
206
215
  properties:
207
216
  scope:
208
- const: none
209
217
  default: none
210
- query:
211
- const: false
212
- default: false
218
+ enum: [object, objects, stream, none]
213
219
  additionalProperties: false
214
220
  additionalProperties: false
215
221
 
216
222
  events:
217
223
  type: object
218
224
  propertyNames:
219
- $ref: 'definitions#/definitions/name'
225
+ $ref: '#/definitions/name'
220
226
  patternProperties:
221
227
  '.*':
222
228
  type: object
@@ -243,13 +249,13 @@ properties:
243
249
  type: object
244
250
  properties:
245
251
  operation:
246
- $ref: 'definitions#/definitions/name'
252
+ $ref: '#/definitions/name'
247
253
  bridge:
248
254
  type: string
249
255
  binding:
250
256
  type: string
251
257
  source:
252
- $ref: 'definitions#/definitions/name'
258
+ $ref: '#/definitions/name'
253
259
  not:
254
260
  const: context
255
261
  path:
@@ -260,6 +266,8 @@ properties:
260
266
  adaptive:
261
267
  type: boolean
262
268
  default: false
269
+ arguments:
270
+ type: array
263
271
  required: [operation]
264
272
  additionalProperties: false
265
273
 
@@ -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)
@@ -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,11 +6,13 @@ 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:
13
14
  type: string
15
+ default: components/*
14
16
  build:
15
17
  type: object
16
18
  properties:
@@ -53,16 +55,18 @@ properties:
53
55
  type: object
54
56
  properties:
55
57
  name:
56
- $ref: 'definitions#/definitions/token'
58
+ type: string
59
+ pattern: ^[a-zA-Z]([a-zA-Z0-9]{1,31})?$
57
60
  image:
58
61
  type: string
59
62
  components:
60
63
  type: array
61
64
  minItems: 1
62
65
  items:
63
- $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]+)?))$
64
68
  required: [name, components]
65
69
  annotations:
66
70
  type: object
67
- required: [runtime, name, packages, registry]
71
+ required: [runtime, name, registry]
68
72
  additionalProperties: false
@@ -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,8 @@ 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'
54
55
  }
55
56
 
56
57
  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)
@@ -112,15 +110,18 @@ describe('entity', () => {
112
110
  })
113
111
 
114
112
  it('should allow default id', () => {
115
- manifest.entity.schema.properties.id = { type: 'string', pattern: '^[a-fA-F0-9]+$' }
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('dependent', () => {
121
+ describe('associated', () => {
121
122
  it('should provide default', () => {
122
123
  expect(() => validate(manifest)).not.toThrow()
123
- expect(manifest.entity.dependent).toBe(false)
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(/additional property/)
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
 
@@ -72,9 +72,10 @@ it('should require name as label', () => {
72
72
  expect(() => validate(context)).not.toThrow(/pattern/)
73
73
  })
74
74
 
75
- it('should require packages location', () => {
75
+ it('should set default packages location', () => {
76
76
  delete context.packages
77
- expect(() => validate(context)).toThrow(/required/)
77
+ expect(() => validate(context)).not.toThrow()
78
+ expect(context.packages).toBe('components/*')
78
79
  })
79
80
 
80
81
  it('should require registry url', () => {
@@ -37,7 +37,7 @@ type Receiver = {
37
37
  type Entity = {
38
38
  schema: Object
39
39
  storage?: string
40
- dependent?: boolean
40
+ associated?: boolean
41
41
  }
42
42
 
43
43
  type Declaration = {