@toa.io/norm 0.20.0-dev.8 → 0.20.0

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": "0.20.0-dev.8",
3
+ "version": "0.20.0",
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",
@@ -12,6 +12,7 @@
12
12
  "url": "https://github.com/toa-io/toa/issues"
13
13
  },
14
14
  "main": "src/index.js",
15
+ "types": "types/index.d.ts",
15
16
  "publishConfig": {
16
17
  "access": "public"
17
18
  },
@@ -19,11 +20,11 @@
19
20
  "test": "echo \"Error: run tests from root\" && exit 1"
20
21
  },
21
22
  "dependencies": {
22
- "@toa.io/core": "0.20.0-dev.8",
23
- "@toa.io/generic": "0.20.0-dev.8",
24
- "@toa.io/schema": "0.20.0-dev.8",
25
- "@toa.io/schemas": "0.20.0-dev.8",
26
- "@toa.io/yaml": "0.20.0-dev.8"
23
+ "@toa.io/core": "0.20.0",
24
+ "@toa.io/generic": "0.20.0",
25
+ "@toa.io/schema": "0.20.0",
26
+ "@toa.io/schemas": "0.20.0",
27
+ "@toa.io/yaml": "0.20.0"
27
28
  },
28
- "gitHead": "d8c08ec7def70ad93de06541af497cce8c4c1b50"
29
+ "gitHead": "28fc4b45c224c3683acaaf0e4abd1eb04e07b408"
29
30
  }
@@ -9,8 +9,11 @@ function extensions (manifest) {
9
9
 
10
10
  const SHORTCUTS = {
11
11
  exposition: '@toa.io/extensions.exposition',
12
+ realtime: '@toa.io/extensions.realtime',
12
13
  origins: '@toa.io/extensions.origins',
13
- configuration: '@toa.io/extensions.configuration'
14
+ configuration: '@toa.io/extensions.configuration',
15
+ state: '@toa.io/extensions.state',
16
+ stash: '@toa.io/extensions.stash'
14
17
  }
15
18
 
16
19
  exports.extensions = extensions
@@ -10,7 +10,10 @@ function operations (manifest) {
10
10
  if (operation.input !== undefined) operation.input = expand(operation.input)
11
11
  if (operation.output !== undefined) operation.output = expand(operation.output)
12
12
  if (operation.bridge !== undefined) operation.bridge = resolve(operation.bridge)
13
- if (operation.bindings !== undefined) operation.bindings = operation.bindings.map(resolve)
13
+
14
+ if (operation.bindings !== undefined && operation.bindings !== null) {
15
+ operation.bindings = operation.bindings.map(resolve)
16
+ }
14
17
  }
15
18
  }
16
19
 
@@ -1,11 +1,9 @@
1
1
  'use strict'
2
2
 
3
3
  const { events } = require('./events')
4
- const { extensions } = require('./extensions')
5
4
  const { operations } = require('./operations')
6
5
  const { receivers } = require('./receivers')
7
6
 
8
7
  exports.events = events
9
- exports.extensions = extensions
10
8
  exports.operations = operations
11
9
  exports.receivers = receivers
@@ -4,7 +4,11 @@ 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')
8
+ operation.query = false
9
+
7
10
  if (operation.bindings === undefined) operation.bindings = component.bindings
11
+ if (operation.bindings === null) operation.bindings = []
8
12
  if (operation.virtual === true) delete component.operations[endpoint]
9
13
  }
10
14
  }
@@ -1,11 +1,8 @@
1
1
  'use strict'
2
2
 
3
3
  const { directory: { find } } = require('@toa.io/filesystem')
4
- const { resolve } = require('../../shortcuts')
4
+ const { resolve } = require('../shortcuts')
5
5
 
6
- /**
7
- * @param {toa.norm.Component} manifest
8
- */
9
6
  const extensions = (manifest) => {
10
7
  if (manifest.extensions === undefined) return
11
8
 
@@ -8,6 +8,7 @@ const { expand } = require('./expand')
8
8
  const { merge } = require('./merge')
9
9
  const { normalize } = require('./normalize')
10
10
  const { validate } = require('./validate')
11
+ const { extensions } = require('./extensions')
11
12
 
12
13
  exports.collapse = collapse
13
14
  exports.defaults = defaults
@@ -17,3 +18,4 @@ exports.expand = expand
17
18
  exports.merge = merge
18
19
  exports.normalize = normalize
19
20
  exports.validate = validate
21
+ exports.extensions = extensions
@@ -1,14 +1,11 @@
1
1
  'use strict'
2
2
 
3
- const { convolve } = require('@toa.io/generic')
4
- const { events, operations, extensions, receivers } = require('./.normalize')
3
+ const { events, operations, receivers } = require('./.normalize')
5
4
 
6
- const normalize = (component, environment) => {
7
- convolve(component, environment)
5
+ const normalize = (component) => {
8
6
  operations(component)
9
7
  events(component)
10
8
  receivers(component)
11
- extensions(component)
12
9
  }
13
10
 
14
11
  exports.normalize = normalize
@@ -20,7 +20,7 @@ properties:
20
20
  operations:
21
21
  type: object
22
22
  propertyNames:
23
- $ref: 'definitions#/definitions/token'
23
+ $ref: 'definitions#/definitions/name'
24
24
  patternProperties:
25
25
  '.*':
26
26
  type: object
@@ -39,14 +39,15 @@ properties:
39
39
  label:
40
40
  $ref: 'definitions#/definitions/label'
41
41
 
42
+ name:
43
+ $ref: 'definitions#/definitions/token'
44
+
42
45
  namespace:
43
46
  $ref: 'definitions#/definitions/token'
44
47
  default: 'default'
45
48
  not:
46
49
  oneOf:
47
50
  - const: 'system'
48
- name:
49
- $ref: 'definitions#/definitions/token'
50
51
 
51
52
  version:
52
53
  $ref: 'definitions#/definitions/version'
@@ -67,18 +68,17 @@ properties:
67
68
  type: object
68
69
  propertyNames:
69
70
  oneOf:
70
- - $ref: 'definitions#/definitions/token'
71
- - enum: [ _version ]
71
+ - $ref: 'definitions#/definitions/name'
72
+ - enum: [_version]
72
73
  initialized:
73
74
  type: boolean
74
75
  default: false
75
- required: [ schema ]
76
+ required: [schema]
76
77
  additionalProperties: false
77
78
 
78
79
  bindings:
79
80
  type: array
80
81
  uniqueItems: true
81
- minItems: 1
82
82
  items:
83
83
  $ref: '#/definitions/binding'
84
84
 
@@ -88,19 +88,19 @@ properties:
88
88
  operations:
89
89
  type: object
90
90
  propertyNames:
91
- $ref: 'definitions#/definitions/token'
91
+ $ref: 'definitions#/definitions/name'
92
92
  patternProperties:
93
93
  '.*':
94
94
  type: object
95
95
  properties:
96
96
  type:
97
- enum: [ transition, observation, assignment, computation, effect ]
97
+ enum: [transition, observation, assignment, computation, effect]
98
98
  scope:
99
- enum: [ object, objects, changeset, none ]
99
+ enum: [object, objects, changeset, none]
100
100
  concurrency:
101
- enum: [ none, retry ]
101
+ enum: [none, retry]
102
102
  forward:
103
- $ref: 'definitions#/definitions/token'
103
+ $ref: 'definitions#/definitions/name'
104
104
  bridge:
105
105
  type: string
106
106
  bindings:
@@ -111,7 +111,7 @@ properties:
111
111
  $ref: 'definitions#/definitions/schema'
112
112
  query:
113
113
  type: boolean
114
- required: [ type, scope, bindings ]
114
+ required: [type, scope, bindings]
115
115
  allOf:
116
116
  - if: # transition
117
117
  properties:
@@ -120,15 +120,15 @@ properties:
120
120
  then:
121
121
  properties:
122
122
  scope:
123
- enum: [ object ]
123
+ enum: [object]
124
124
  if: # transition query: false
125
125
  not:
126
126
  properties:
127
127
  query:
128
128
  const: false
129
- required: [ query ]
129
+ required: [query]
130
130
  then:
131
- required: [ concurrency ]
131
+ required: [concurrency]
132
132
  - if: # not transition
133
133
  not:
134
134
  properties:
@@ -145,7 +145,7 @@ properties:
145
145
  then:
146
146
  properties:
147
147
  scope:
148
- enum: [ object, objects, none ]
148
+ enum: [object, objects, none]
149
149
  query:
150
150
  not:
151
151
  const: false
@@ -156,7 +156,7 @@ properties:
156
156
  then:
157
157
  properties:
158
158
  scope:
159
- enum: [ changeset ]
159
+ enum: [changeset]
160
160
  - if: # computation
161
161
  properties:
162
162
  type:
@@ -166,6 +166,9 @@ properties:
166
166
  scope:
167
167
  const: none
168
168
  default: none
169
+ query:
170
+ const: false
171
+ default: false
169
172
  - if: # effect
170
173
  properties:
171
174
  type:
@@ -175,13 +178,16 @@ properties:
175
178
  scope:
176
179
  const: none
177
180
  default: none
181
+ query:
182
+ const: false
183
+ default: false
178
184
  additionalProperties: false
179
185
  additionalProperties: false
180
186
 
181
187
  events:
182
188
  type: object
183
189
  propertyNames:
184
- $ref: 'definitions#/definitions/token'
190
+ $ref: 'definitions#/definitions/name'
185
191
  patternProperties:
186
192
  '.*':
187
193
  type: object
@@ -198,7 +204,7 @@ properties:
198
204
  subjective:
199
205
  type: boolean
200
206
  default: false
201
- required: [ bridge, path ]
207
+ required: [bridge, path]
202
208
  additionalProperties: false
203
209
 
204
210
  receivers:
@@ -208,13 +214,15 @@ properties:
208
214
  type: object
209
215
  properties:
210
216
  operation:
211
- $ref: 'definitions#/definitions/token'
217
+ $ref: 'definitions#/definitions/name'
212
218
  bridge:
213
219
  type: string
214
220
  binding:
215
221
  type: string
216
222
  source:
217
- $ref: 'definitions#/definitions/token'
223
+ $ref: 'definitions#/definitions/name'
224
+ not:
225
+ const: context
218
226
  path:
219
227
  type: string
220
228
  conditioned:
@@ -223,7 +231,7 @@ properties:
223
231
  adaptive:
224
232
  type: boolean
225
233
  default: false
226
- required: [ operation ]
234
+ required: [operation]
227
235
  additionalProperties: false
228
236
 
229
237
  extensions:
@@ -1,14 +1,14 @@
1
1
  'use strict'
2
2
 
3
- /**
4
- * @param {toa.norm.Context} context
5
- * @returns {toa.norm.context.dependencies.References}
6
- */
7
- const connectors = (context) => {
8
- /** @type {toa.norm.context.dependencies.References} */
3
+ const connectors = (context, extracted) => {
9
4
  const connectors = {}
10
5
 
11
- for (const component of context.components) {
6
+ const components = (context.components === undefined
7
+ ? extracted
8
+ : context.components.concat(extracted)
9
+ ) ?? []
10
+
11
+ for (const component of components) {
12
12
  if (connectors[component.entity.storage] === undefined) {
13
13
  connectors[component.entity.storage] = []
14
14
  }
@@ -1,24 +1,56 @@
1
1
  'use strict'
2
2
 
3
- /**
4
- * @param {toa.norm.Context} context
5
- * @returns {toa.norm.context.dependencies.References}
6
- */
7
- const extensions = (context) => {
8
- /** @type {toa.norm.context.dependencies.References} */
3
+ const extensions = async (context) => {
9
4
  const extensions = {}
5
+ const components = context.components?.slice() ?? []
6
+ const extracted = await extractExtensionComponents(components, extensions)
10
7
 
11
- for (const component of context.components) {
12
- if (component.extensions !== undefined) {
13
- for (const reference of Object.keys(component.extensions)) {
14
- if (extensions[reference] === undefined) extensions[reference] = []
8
+ components.push(...extracted)
15
9
 
16
- extensions[reference].push(component)
10
+ for (const component of components) {
11
+ if (component.extensions === undefined) continue
12
+
13
+ for (const reference of Object.keys(component.extensions)) {
14
+ if (extensions[reference] === undefined) extensions[reference] = []
15
+
16
+ extensions[reference].push(component)
17
+ }
18
+ }
19
+
20
+ return { extensions, components: extracted }
21
+ }
22
+
23
+ async function extractExtensionComponents (components, extensions) {
24
+ const { component: load } = require('../../component')
25
+
26
+ const extracted = []
27
+
28
+ for (const component of components) {
29
+ if (component.extensions === undefined) continue
30
+
31
+ for (const reference of Object.keys(component.extensions)) {
32
+ if (reference in extensions) continue
33
+
34
+ extensions[reference] = []
35
+
36
+ const mod = require(reference)
37
+
38
+ if (mod.components === undefined) continue
39
+
40
+ for (const path of mod.components().paths) {
41
+ const component = await load(path)
42
+
43
+ extracted.push(component)
17
44
  }
18
45
  }
19
46
  }
20
47
 
21
- return extensions
48
+ if (extracted.length === 0)
49
+ return extracted
50
+
51
+ const deeper = await extractExtensionComponents(extracted, extensions)
52
+
53
+ return extracted.concat(deeper)
22
54
  }
23
55
 
24
56
  exports.extensions = extensions
@@ -1,15 +1,8 @@
1
1
  'use strict'
2
2
 
3
- const { join } = require('node:path')
4
3
  const { load } = require('./load')
5
4
 
6
- /**
7
- * @param {toa.norm.context.dependencies.References} references
8
- * @param {Object} annotations
9
- * @returns {toa.norm.context.Dependencies}
10
- */
11
5
  const resolve = (references, annotations) => {
12
- /** @type {toa.norm.context.Dependencies} */
13
6
  const dependencies = {}
14
7
 
15
8
  for (const [dependency, components] of Object.entries(references)) {
@@ -18,7 +11,8 @@ const resolve = (references, annotations) => {
18
11
 
19
12
  const instances = components.map((component) => ({
20
13
  locator: component.locator,
21
- manifest: component.extensions?.[id]
14
+ manifest: component.extensions?.[id],
15
+ component
22
16
  }))
23
17
 
24
18
  dependencies[dependency] = instances
@@ -2,13 +2,10 @@
2
2
 
3
3
  const { connectors, extensions, resolve } = require('./.dependencies')
4
4
 
5
- /**
6
- * @param {toa.norm.Context} context
7
- * @returns {toa.norm.context.Dependencies}
8
- */
9
- const dependencies = (context) => {
10
- /** @type {toa.norm.context.dependencies.References} */
11
- const references = { ...connectors(context), ...extensions(context) }
5
+ const dependencies = async (context) => {
6
+ const { extensions: e, components } = await extensions(context)
7
+ const c = connectors(context, components)
8
+ const references = { ...c, ...e }
12
9
 
13
10
  return resolve(references, context.annotations)
14
11
  }
package/src/component.js CHANGED
@@ -14,17 +14,16 @@ const {
14
14
  dereference,
15
15
  defaults,
16
16
  dependencies,
17
- normalize
17
+ normalize,
18
+ extensions
18
19
  } = require('./.component')
19
20
 
20
- /**
21
- * @type {toa.norm.component.Constructor}
22
- */
23
21
  const component = async (path) => {
24
22
  const manifest = await load(path)
25
23
 
26
- normalize(manifest, process.env.TOA_ENV)
24
+ normalize(manifest)
27
25
  validate(manifest)
26
+ extensions(manifest)
28
27
 
29
28
  manifest.locator = new Locator(manifest.name, manifest.namespace)
30
29
 
package/src/context.js CHANGED
@@ -16,9 +16,6 @@ const {
16
16
  validate
17
17
  } = require('./.context')
18
18
 
19
- /**
20
- * @type {toa.norm.context.Constructor}
21
- */
22
19
  const context = async (root, environment = process.env.TOA_ENV) => {
23
20
  const path = resolve(root, CONTEXT)
24
21
  const context = /** @type {toa.norm.Context} */ await load(path)
@@ -35,7 +32,7 @@ const context = async (root, environment = process.env.TOA_ENV) => {
35
32
  const paths = await glob(pattern)
36
33
 
37
34
  context.components = await Promise.all(paths.map(component))
38
- context.dependencies = dependencies(context)
35
+ context.dependencies = await dependencies(context)
39
36
 
40
37
  dereference(context)
41
38
  complete(context)
package/src/shortcuts.js CHANGED
@@ -46,8 +46,10 @@ const SHORTCUTS = {
46
46
  sql: '@toa.io/storages.sql',
47
47
  queues: '@toa.io/storages.queues',
48
48
  exposition: '@toa.io/extensions.exposition',
49
+ realtime: '@toa.io/extensions.realtime',
49
50
  configuration: '@toa.io/extensions.configuration',
50
- origins: '@toa.io/extensions.origins'
51
+ origins: '@toa.io/extensions.origins',
52
+ stash: '@toa.io/extensions.stash'
51
53
  }
52
54
 
53
55
  exports.recognize = recognize
@@ -3,15 +3,19 @@
3
3
  const { generate } = require('randomstring')
4
4
 
5
5
  const operations = {
6
+ namespace: 'dummies',
7
+ name: 'dummy',
6
8
  path: __dirname,
7
9
  bindings: ['foo', 'bar'],
8
10
  'bindings@local': ['foo'],
9
11
  operations: {
10
- add: {}
12
+ add: {
13
+ type: 'assignment'
14
+ }
11
15
  },
12
16
  extensions: {
13
17
  '@toa.io/extensions.exposition': {
14
- ['/' + generate()]: ['add']
18
+ ['/' + generate()]: {}
15
19
  },
16
20
  './dummies/extension': {
17
21
  ok: true
@@ -12,13 +12,6 @@ beforeEach(() => {
12
12
  manifest = clone(fixtures.operations)
13
13
  })
14
14
 
15
- describe('environment', () => {
16
- it('should convolve with environment argument', () => {
17
- normalize(manifest, 'local')
18
- expect(manifest.operations.add.bindings).toStrictEqual(['foo'])
19
- })
20
- })
21
-
22
15
  describe('operations', () => {
23
16
  it('should set default bindings', () => {
24
17
  normalize(manifest)
@@ -36,12 +29,6 @@ describe('extensions', () => {
36
29
 
37
30
  expect(manifest.extensions[path]).toStrictEqual(origins)
38
31
  })
39
-
40
- it('should throw if manifest is undefined', () => {
41
- manifest.extensions['./dummies/extension'].ok = false
42
-
43
- expect(() => normalize(manifest)).toThrow(/hasn't returned manifest/)
44
- })
45
32
  })
46
33
 
47
34
  describe('receivers', () => {
@@ -34,6 +34,16 @@ const ok = {
34
34
  scope: 'changeset',
35
35
  bridge: 'whatever',
36
36
  bindings: ['@toa.io/bindings.http', '@toa.io/bindings.amqp']
37
+ },
38
+ compute: {
39
+ type: 'computation',
40
+ bridge: 'whatever',
41
+ bindings: ['@toa.io/bindings.http', '@toa.io/bindings.amqp']
42
+ },
43
+ affect: {
44
+ type: 'effect',
45
+ bridge: 'whatever',
46
+ bindings: ['@toa.io/bindings.http', '@toa.io/bindings.amqp']
37
47
  }
38
48
  },
39
49
  events: {
@@ -37,14 +37,14 @@ describe('namespace', () => {
37
37
  manifest.namespace = 'foo_'
38
38
  expect(() => validate(manifest)).toThrow(/must match pattern/)
39
39
 
40
- manifest.namespace = 'foo-'
40
+ manifest.namespace = 'foo_bar'
41
41
  expect(() => validate(manifest)).toThrow(/must match pattern/)
42
42
 
43
- manifest.namespace = 'foo-BAR'
44
- expect(() => validate(manifest)).not.toThrow()
43
+ manifest.namespace = 'foo-'
44
+ expect(() => validate(manifest)).toThrow(/must match pattern/)
45
45
 
46
- manifest.namespace = 'foo_bar'
47
- expect(() => validate(manifest)).not.toThrow()
46
+ manifest.namespace = 'foo-bar'
47
+ expect(() => validate(manifest)).toThrow('must match pattern')
48
48
 
49
49
  manifest.namespace = 'FooBar12'
50
50
  expect(() => validate(manifest)).not.toThrow()
@@ -172,6 +172,15 @@ describe('operations', () => {
172
172
  expect(() => validate(manifest)).toThrow(/must NOT be valid/)
173
173
  })
174
174
 
175
+ it.each([
176
+ ['computation', 'compute'],
177
+ ['effect', 'affect']
178
+ ])('should set query: false for %s', async (_, operation) => {
179
+ validate(manifest)
180
+
181
+ expect(manifest.operations[operation].query).toBe(false)
182
+ })
183
+
175
184
  describe('scope', () => {
176
185
  it('should have scope', () => {
177
186
  delete manifest.operations.get.scope
@@ -230,7 +239,7 @@ describe('operations', () => {
230
239
 
231
240
  describe('receivers', () => {
232
241
  it('should throw if transition points to undefined operation', () => {
233
- manifest.receivers['foo.bar.happened'].operation = 'not-exists'
242
+ manifest.receivers['foo.bar.happened'].operation = 'notExists'
234
243
 
235
244
  expect(() => validate(manifest)).toThrow(/refers to undefined operation/)
236
245
  })
@@ -240,4 +249,10 @@ describe('receivers', () => {
240
249
 
241
250
  expect(() => validate(manifest)).toThrow(/one of the allowed types/)
242
251
  })
252
+
253
+ it('should throw if source has a name `context`', async () => {
254
+ manifest.receivers['foo.bar.happened'].source = 'context'
255
+
256
+ expect(() => validate(manifest)).toThrow(/must NOT be valid/)
257
+ })
243
258
  })
@@ -1,81 +1,62 @@
1
- import type {Locator} from '@toa.io/core/types'
1
+ import type { Locator, operations } from '@toa.io/core'
2
2
 
3
- export namespace toa.norm {
4
-
5
- namespace component {
6
- namespace operations {
7
-
8
- type Type = 'transition' | 'observation' | 'assignment' | 'computation' | 'effect'
9
- type Scope = 'object' | 'objects' | 'changeset'
10
-
11
- }
12
-
13
- interface Map {
14
- [id: string]: Component
15
- }
16
-
17
- interface Operation {
18
- type?: operations.Type
19
- scope?: operations.Scope
20
- bindings?: string[]
21
- input?: any
22
- output?: any
23
- error?: any
24
- }
25
-
26
- interface Operations {
27
- [key: string]: Operation
28
- }
29
-
30
- interface Event {
31
- binding: string
32
- }
3
+ type Map = {
4
+ [id: string]: Component
5
+ }
33
6
 
34
- interface Events {
35
- [key: string]: Event
36
- }
7
+ type Operation = {
8
+ type: operations.type
9
+ scope?: operations.scope
10
+ bindings?: string[]
11
+ input?: any
12
+ output?: any
13
+ error?: any
14
+ query?: boolean
15
+ }
37
16
 
38
- interface Receiver {
39
- operation: string
40
- adaptive: boolean
41
- conditioned: boolean
42
- bridge: string
43
- binding: string
44
- path: string
45
- source?: string
46
- }
17
+ type Operations = {
18
+ [key: string]: Operation
19
+ }
47
20
 
48
- type Entity = {
49
- schema: Object
50
- storage?: string
51
- initialized?: boolean
52
- }
21
+ type Event = {
22
+ binding: string
23
+ }
53
24
 
54
- interface Declaration {
55
- prototype: string
56
- namespace: string
57
- name: string
58
- version: string
59
- entity: Entity
60
- bindings: string[]
61
- operations?: Operations
62
- events?: Events
63
- receivers: Record<string, Receiver>
64
- extensions?: Record<string, object>
65
- properties?: Record<string, object>
66
- }
25
+ type Events = {
26
+ [key: string]: Event
27
+ }
67
28
 
68
- type Constructor = (path: string) => Promise<Component>
69
- }
29
+ type Receiver = {
30
+ operation: string
31
+ adaptive: boolean
32
+ conditioned: boolean
33
+ bridge: string
34
+ binding: string
35
+ path: string
36
+ source?: string
37
+ }
70
38
 
71
- interface Component extends component.Declaration {
72
- locator: Locator
73
- path: string
74
- }
39
+ type Entity = {
40
+ schema: Object
41
+ storage?: string
42
+ initialized?: boolean
75
43
  }
76
44
 
77
- export type Component = toa.norm.Component
78
- export type Operation = toa.norm.component.Operation
79
- export type Declaration = toa.norm.component.Declaration
45
+ type Declaration = {
46
+ prototype?: string
47
+ namespace: string
48
+ name: string
49
+ version: string
50
+ entity?: Entity
51
+ bindings?: string[]
52
+ operations: Operations
53
+ events?: Events
54
+ receivers?: Record<string, Receiver>
55
+ extensions?: Record<string, object>
56
+ properties?: Record<string, object>
57
+ }
80
58
 
81
- export const component: toa.norm.component.Constructor
59
+ export type Manifest = Declaration & {
60
+ locator: Locator
61
+ path: string
62
+ }
@@ -0,0 +1,17 @@
1
+ import { Composition, Registry, Runtime } from '../context'
2
+
3
+ interface Composition {
4
+ name: string,
5
+ components: string[]
6
+ }
7
+
8
+ export interface Declaration {
9
+ name: string
10
+ description?: string
11
+ version?: string
12
+ runtime?: Runtime | string
13
+ registry?: Registry | string
14
+ packages?: string
15
+ compositions?: Composition[]
16
+ annotations?: Record<string, object>
17
+ }
@@ -1,76 +1,41 @@
1
1
  import * as _component from './component'
2
2
  import { Locator } from '@toa.io/core/types'
3
+ import type { Declaration } from './context/declaration'
3
4
 
4
- declare namespace toa.norm {
5
-
6
- namespace context {
7
-
8
- interface Runtime {
9
- version: string
10
- registry?: string
11
- proxy?: string
12
- }
13
-
14
- interface Registry {
15
- base?: string
16
- platforms?: string[] | null
17
- build?: {
18
- arguments?: string[]
19
- run?: string
20
- }
21
- }
22
-
23
- interface Composition {
24
- name: string,
25
- components: string[] | _component.Component[]
26
- }
27
-
28
- namespace dependencies {
29
-
30
- type Instance = {
31
- locator: Locator
32
- manifest?: Object
33
- }
34
-
35
- type References = {
36
- [reference: string]: _component.Component[]
37
- }
38
-
39
- }
40
-
41
- interface Dependencies {
42
- [reference: string]: dependencies.Instance[]
43
- }
44
-
45
- interface Declaration {
46
- name: string
47
- description?: string
48
- version?: string
49
- runtime?: Runtime | string
50
- registry?: Registry | string
51
- packages?: string
52
- compositions?: Composition[]
53
- annotations?: Record<string, object>
54
- }
55
-
56
- type Constructor = (path: string, environment?: string) => Promise<Context>
57
- }
5
+ interface Runtime {
6
+ version: string
7
+ registry?: string
8
+ proxy?: string
9
+ }
58
10
 
59
- interface Context extends context.Declaration {
60
- runtime?: context.Runtime
61
- environment?: string
62
- registry?: context.Registry
63
- components?: _component.Component[]
64
- dependencies?: context.Dependencies
65
- }
11
+ interface Registry {
12
+ base?: string
13
+ platforms?: string[] | null
14
+ build?: {
15
+ arguments?: string[]
16
+ run?: string
17
+ },
18
+ credentials: string
19
+ }
66
20
 
21
+ interface Composition {
22
+ name: string,
23
+ components: _component.Component[]
67
24
  }
68
25
 
69
- export type Composition = toa.norm.context.Composition
70
- export type Context = toa.norm.Context
26
+ export interface Dependency<T = undefined> {
27
+ locator: Locator
28
+ manifest: T,
29
+ component: _component.Component
30
+ }
71
31
 
72
- export namespace dependencies {
73
- export type Instance = toa.norm.context.dependencies.Instance
32
+ interface Context extends Declaration {
33
+ runtime?: Runtime
34
+ environment?: string
35
+ registry?: Registry
36
+ compositions?: Composition[]
37
+ components?: _component.Component[]
38
+ dependencies?: Record<string, Dependency[]>
74
39
  }
75
40
 
76
- export const context: toa.norm.context.Constructor
41
+ export function context (path: string, environment?: string): Promise<Context>
package/types/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- export { component } from './component'
2
- export { context } from './context'
1
+ export * as context from './context'
2
+ export * as component from './component'
3
3
 
4
- export type { Composition, dependencies } from './context'
5
- export type { Component, Operation } from './component'
4
+ export { Manifest } from './component'
@@ -1,54 +0,0 @@
1
- Feature: Operations declaration
2
-
3
- Scenario Outline: Operation <channel> entity references
4
-
5
- Operation IO schemas may reference entity's properties using syntax:
6
- - `.` - entity's property with the same name
7
- - '.foo` - entity's property with `foo` name
8
-
9
- Given I have an entity schema:
10
- """
11
- foo: string
12
- bar: 1
13
- """
14
- When I declare assignment with:
15
- """
16
- <channel>:
17
- foo: .
18
- baz: .bar
19
- """
20
- Then normalized assignment declaration must contain:
21
- """
22
- <channel>:
23
- type: object
24
- properties:
25
- foo:
26
- type: string
27
- baz:
28
- type: number
29
- default: 1
30
- """
31
-
32
- Examples:
33
- | channel |
34
- | input |
35
- | output |
36
-
37
- Scenario: Prototype property reference
38
-
39
- Operation IO schemas must be able to reference prototype's entity properties.
40
-
41
- When I declare assignment with:
42
- """
43
- output:
44
- id: .
45
- """
46
-
47
- Then normalized assignment declaration must contain:
48
- """
49
- output:
50
- type: object
51
- properties:
52
- id:
53
- $ref: https://schemas.toa.io/0.0.0/definitions#/definitions/id
54
- """
@@ -1,111 +0,0 @@
1
- 'use strict'
2
-
3
- const { AssertionError } = require('assert')
4
- const { generate } = require('randomstring')
5
- const { dump } = require('@toa.io/yaml')
6
- const gherkin = require('@toa.io/tomato')
7
-
8
- const mock = { gherkin }
9
-
10
- jest.mock('@cucumber/cucumber', () => mock.gherkin)
11
- require('../manifest')
12
-
13
- it('should be', () => undefined)
14
-
15
- /** @type {toa.norm.features.Context} */
16
- let context
17
-
18
- beforeEach(() => {
19
- const manifest = /** @type {toa.norm.component.Declaration} */ {
20
- name: 'test',
21
- namespace: 'features',
22
- version: '1.0.0',
23
- entity: null
24
- }
25
-
26
- context = { manifest }
27
- })
28
-
29
- describe('Given I have an entity schema:', () => {
30
- const step = gherkin.steps.Gi('I have an entity schema:')
31
-
32
- it('should be', () => undefined)
33
-
34
- it('should set entity schema', () => {
35
- const schema = { foo: 'string' }
36
- const yaml = dump(schema)
37
-
38
- step.call(context, yaml)
39
-
40
- expect(context.manifest.entity).toStrictEqual({ schema })
41
- })
42
- })
43
-
44
- describe('When I declare {operation} with:', () => {
45
- const step = gherkin.steps.Wh('I declare {operation} with:')
46
-
47
- it('should be', () => undefined)
48
-
49
- it('should declare operation', () => {
50
- const type = 'assignment'
51
- const scope = 'changeset'
52
- const input = {}
53
- const yaml = dump(input)
54
-
55
- step.call(context, type, yaml)
56
-
57
- expect(context.manifest.operations[type]).toStrictEqual({ type, scope })
58
- })
59
- })
60
-
61
- describe('Then normalized {operation} declaration must contain:', () => {
62
- const step = gherkin.steps.Th('normalized {operation} declaration must contain:')
63
-
64
- it('should be', () => undefined)
65
-
66
- it('should throw if does not contain', async () => {
67
- const type = 'assignment'
68
-
69
- context.manifest.operations = {
70
- [type]: {
71
- type,
72
- input: { bar: 'number' },
73
- scope: 'changeset'
74
- }
75
- }
76
-
77
- const input = {
78
- type: 'object',
79
- properties: {
80
- bar: { type: 'string' }
81
- }
82
- }
83
-
84
- const yaml = dump({ input })
85
-
86
- await expect(step.call(context, type, yaml)).rejects.toThrow(AssertionError)
87
- })
88
-
89
- it('should not throw if contain', async () => {
90
- const type = 'assignment'
91
-
92
- context.manifest.operations = {
93
- [type]: {
94
- type,
95
- input: { bar: 'number' },
96
- scope: 'changeset'
97
- }
98
- }
99
-
100
- const input = {
101
- type: 'object',
102
- properties: {
103
- bar: { type: 'number' }
104
- }
105
- }
106
-
107
- const yaml = dump({ input })
108
-
109
- await expect(step.call(context, type, yaml)).resolves.not.toThrow()
110
- })
111
- })
@@ -1,9 +0,0 @@
1
- import { Declaration } from '../../../types/component'
2
-
3
- declare namespace toa.norm.features {
4
-
5
- type Context = {
6
- manifest?: Declaration
7
- }
8
-
9
- }
@@ -1,18 +0,0 @@
1
- 'use strict'
2
-
3
- const { Before } = require('@cucumber/cucumber')
4
-
5
- Before(
6
- /**
7
- * @this {toa.norm.features.Context}
8
- */
9
- function () {
10
- this.manifest = {
11
- name: 'test',
12
- namespace: 'features',
13
- version: '1.0.0',
14
- entity: {
15
- storage: null
16
- }
17
- }
18
- })
@@ -1,66 +0,0 @@
1
- 'use strict'
2
-
3
- const assert = require('node:assert')
4
- const { join } = require('node:path')
5
- const { parse, save } = require('@toa.io/yaml')
6
- const { directory } = require('@toa.io/filesystem')
7
- const { match } = require('@toa.io/generic')
8
-
9
- const { component: load } = require('../../src')
10
-
11
- const { Given, When, Then } = require('@cucumber/cucumber')
12
-
13
- Given('I have an entity schema:',
14
- /**
15
- * @param {string} yaml
16
- * @this {toa.norm.features.Context}
17
- */
18
- function (yaml) {
19
- const schema = parse(yaml)
20
-
21
- this.manifest.entity = { schema }
22
- })
23
-
24
- When('I declare {operation} with:',
25
- /**
26
- * @param {toa.norm.component.operations.Type} type
27
- * @param {string} yaml
28
- * @this {toa.norm.features.Context}
29
- */
30
- function (type, yaml) {
31
- /** @type {toa.norm.component.Operation} */
32
- const declaration = parse(yaml)
33
-
34
- declaration.type = type
35
- declaration.scope = scope(type)
36
-
37
- this.manifest.operations = { [type]: declaration }
38
- })
39
-
40
- Then('normalized {operation} declaration must contain:',
41
- /**
42
- * @param {toa.norm.component.operations.Type} type
43
- * @param {string} yaml
44
- * @this {toa.norm.features.Context}
45
- */
46
- async function (type, yaml) {
47
- const temp = await directory.temp()
48
- const path = join(temp, 'manifest.toa.yaml')
49
-
50
- await save(this.manifest, path)
51
-
52
- const manifest = await load(temp)
53
- const operation = manifest.operations[type]
54
- const query = parse(yaml)
55
- const contains = match(operation, query)
56
-
57
- assert.equal(contains, true)
58
- })
59
-
60
- /**
61
- * @param {toa.norm.component.operations.Type} type
62
- * @returns {toa.norm.component.operations.Scope}
63
- */
64
- const scope = (type) => {
65
- return type === 'assignment' ? 'changeset' : 'object'
66
- }
@@ -1,9 +0,0 @@
1
- 'use strict'
2
-
3
- const { defineParameterType } = require('@cucumber/cucumber')
4
-
5
- defineParameterType({
6
- name: 'operation',
7
- regexp: /transition|observation|assignment/,
8
- transformer: (type) => type
9
- })