@toa.io/core 0.2.0-dev.3 → 0.2.1-dev.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.
Files changed (69) hide show
  1. package/package.json +5 -4
  2. package/src/assignment.js +7 -7
  3. package/src/cascade.js +1 -1
  4. package/src/{runtime.js → component.js} +7 -4
  5. package/src/composition.js +6 -7
  6. package/src/connector.js +89 -24
  7. package/src/context.js +23 -11
  8. package/src/contract/reply.js +4 -1
  9. package/src/contract/request.js +13 -3
  10. package/src/contract/schemas/index.js +4 -3
  11. package/src/discovery.js +11 -0
  12. package/src/entities/changeset.js +4 -3
  13. package/src/entities/entity.js +17 -6
  14. package/src/entities/factory.js +3 -3
  15. package/src/event.js +12 -5
  16. package/src/exceptions.js +9 -1
  17. package/src/exposition.js +12 -9
  18. package/src/index.js +4 -2
  19. package/src/locator.js +21 -35
  20. package/src/observation.js +6 -6
  21. package/src/operation.js +20 -13
  22. package/src/query/options.js +4 -7
  23. package/src/query.js +6 -1
  24. package/src/receiver.js +36 -7
  25. package/src/reflection.js +28 -0
  26. package/src/remote.js +3 -3
  27. package/src/state.js +33 -25
  28. package/src/transition.js +24 -16
  29. package/test/call.fixtures.js +1 -0
  30. package/test/{runtime.fixtures.js → component.fixtures.js} +4 -2
  31. package/test/{runtime.test.js → component.test.js} +7 -7
  32. package/test/connector.fixtures.js +1 -1
  33. package/test/connector.test.js +6 -29
  34. package/test/context.fixtures.js +18 -0
  35. package/test/context.test.js +29 -0
  36. package/test/contract/contract.fixtures.js +8 -3
  37. package/test/contract/request.test.js +14 -3
  38. package/test/emission.fixtures.js +1 -0
  39. package/test/entities/entity.fixtures.js +4 -4
  40. package/test/entities/entity.test.js +22 -10
  41. package/test/entities/factory.test.js +6 -6
  42. package/test/event.fixtures.js +1 -0
  43. package/test/event.test.js +16 -4
  44. package/test/locator.test.js +76 -19
  45. package/test/receiver.fixtures.js +6 -5
  46. package/test/receiver.test.js +36 -7
  47. package/test/reflection.test.js +30 -0
  48. package/test/state.fixtures.js +6 -5
  49. package/test/state.test.js +8 -16
  50. package/types/bindings.d.ts +39 -0
  51. package/types/bridges.d.ts +35 -0
  52. package/types/component.ts +16 -0
  53. package/types/connector.d.ts +22 -0
  54. package/types/context.d.ts +24 -0
  55. package/types/entity.d.ts +46 -0
  56. package/types/event.d.ts +11 -0
  57. package/types/exception.d.ts +10 -0
  58. package/types/extensions.d.ts +36 -0
  59. package/types/index.d.ts +14 -0
  60. package/types/locator.d.ts +16 -0
  61. package/types/message.d.ts +9 -0
  62. package/types/query.d.ts +15 -0
  63. package/types/receiver.d.ts +12 -0
  64. package/types/reflection.d.ts +16 -0
  65. package/types/reply.d.ts +15 -0
  66. package/types/request.d.ts +25 -0
  67. package/types/state.d.ts +35 -0
  68. package/types/storages.d.ts +71 -0
  69. package/LICENSE +0 -22
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/core",
3
- "version": "0.2.0-dev.3+8334154",
3
+ "version": "0.2.1-dev.0",
4
4
  "description": "Toa Core",
5
5
  "author": "temich <tema.gurtovoy@gmail.com>",
6
6
  "homepage": "https://github.com/toa-io/toa#readme",
@@ -20,9 +20,10 @@
20
20
  },
21
21
  "dependencies": {
22
22
  "@rsql/parser": "1.2.4",
23
- "@toa.io/gears": "0.2.0-dev.3+8334154",
24
- "@toa.io/schema": "0.2.0-dev.3+8334154",
23
+ "@toa.io/console": "*",
24
+ "@toa.io/generic": "*",
25
+ "@toa.io/yaml": "*",
25
26
  "clone-deep": "4.0.1"
26
27
  },
27
- "gitHead": "8334154c1b8a8268ad90adfb15b43a876459014f"
28
+ "gitHead": "2be07592325b2e4dc823e81d882a4e50bf50de24"
28
29
  }
package/src/assignment.js CHANGED
@@ -3,19 +3,19 @@
3
3
  const { Operation } = require('./operation')
4
4
 
5
5
  class Assignment extends Operation {
6
- async acquire (scope) {
7
- scope.subject = this.subject.changeset(scope.request.query)
8
- scope.state = scope.subject.get()
6
+ async acquire (store) {
7
+ store.scope = this.scope.changeset(store.request.query)
8
+ store.state = store.scope.get()
9
9
  }
10
10
 
11
- async commit (scope) {
12
- const { subject, state, reply } = scope
11
+ async commit (store) {
12
+ const { scope, state, reply } = store
13
13
 
14
14
  if (reply.error !== undefined) return
15
15
 
16
- subject.set(state)
16
+ scope.set(state)
17
17
 
18
- await this.subject.apply(subject)
18
+ await this.scope.apply(scope)
19
19
  }
20
20
  }
21
21
 
package/src/cascade.js CHANGED
@@ -1,6 +1,6 @@
1
1
  'use strict'
2
2
 
3
- const { merge } = require('@toa.io/gears')
3
+ const { merge } = require('@toa.io/generic')
4
4
  const { Connector } = require('./connector')
5
5
 
6
6
  class Cascade extends Connector {
@@ -1,10 +1,13 @@
1
1
  'use strict'
2
2
 
3
- const { console } = require('@toa.io/gears')
3
+ const { console } = require('@toa.io/console')
4
4
  const { Connector } = require('./connector')
5
5
  const { NotImplementedException } = require('./exceptions')
6
6
 
7
- class Runtime extends Connector {
7
+ /**
8
+ * @implements {toa.core.Component}
9
+ */
10
+ class Component extends Connector {
8
11
  locator
9
12
 
10
13
  #operations
@@ -18,7 +21,7 @@ class Runtime extends Connector {
18
21
  Object.values(operations).forEach((operation) => this.depends(operation))
19
22
  }
20
23
 
21
- connection () {
24
+ async connection () {
22
25
  console.info(`Runtime '${this.locator.id}' connected`)
23
26
  }
24
27
 
@@ -35,4 +38,4 @@ class Runtime extends Connector {
35
38
  }
36
39
  }
37
40
 
38
- exports.Runtime = Runtime
41
+ exports.Component = Component
@@ -1,17 +1,16 @@
1
1
  'use strict'
2
2
 
3
- const { console } = require('@toa.io/gears')
4
-
3
+ const { console } = require('@toa.io/console')
5
4
  const { Connector } = require('./connector')
6
5
 
7
6
  class Composition extends Connector {
8
- constructor (expositions, producers, receivers, extensions) {
7
+ constructor (expositions, producers, receivers, tenants) {
9
8
  super()
10
9
 
11
- this.depends(expositions)
12
- this.depends(producers)
13
- this.depends(receivers)
14
- this.depends(extensions)
10
+ if (expositions.length > 0) this.depends(expositions)
11
+ if (producers.length > 0) this.depends(producers)
12
+ if (receivers.length > 0) this.depends(receivers)
13
+ if (tenants.length > 0) this.depends(tenants)
15
14
  }
16
15
 
17
16
  async connection () {
package/src/connector.js CHANGED
@@ -1,60 +1,93 @@
1
1
  'use strict'
2
2
 
3
- const { console } = require('@toa.io/gears')
4
- const { newid } = require('@toa.io/gears')
5
-
3
+ const { console } = require('@toa.io/console')
4
+ const { newid } = require('@toa.io/generic')
5
+
6
+ // noinspection JSClosureCompilerSyntax
7
+ /**
8
+ * Abstract connections hierarchy
9
+ * @implements {toa.core.Connector}
10
+ */
6
11
  class Connector {
7
- #connectors = []
8
- #linked = []
12
+ /** @type {Array<Connector>} */
13
+ #dependencies = []
14
+ /** @type {Array<Connector>} */
15
+ #links = []
16
+ /** @type {Promise} */
9
17
  #connecting
18
+ /** @type {Promise} */
10
19
  #disconnecting
11
20
 
21
+ /** @type {string} */
12
22
  id
23
+ /** @type {boolean} */
13
24
  connected = false
14
25
 
15
26
  constructor () {
16
27
  this.id = this.constructor.name + '#' + newid().substring(0, 8)
17
28
  }
18
29
 
30
+ /**
31
+ * Creates a dependency and backlink with another Connector or a set of Connectors
32
+ *
33
+ * See .connect() and .disconnect()
34
+ *
35
+ * @param connector {Connector | any | Array<Connector | any>}
36
+ * @returns {Connector}
37
+ */
19
38
  depends (connector) {
39
+ /** @type {Connector} */
20
40
  let next
21
41
 
22
42
  if (connector instanceof Array) {
23
- connector = connector.filter((item) => item instanceof Connector)
43
+ if (connector.length === 0) throw new Error('Connectors array must not be empty')
24
44
 
25
- if (connector.length > 0) {
45
+ if (connector.length > 1) {
26
46
  next = new Connector()
27
47
 
28
48
  for (const item of connector) {
29
- this.#connectors.push(item)
49
+ this.#dependencies.push(item)
30
50
  item.depends(next)
31
51
  }
32
- }
33
- } else {
34
- if (connector instanceof Connector) {
35
- next = connector
36
- }
37
- }
52
+ } else next = connector[0]
53
+ } else next = connector
38
54
 
39
- if (next !== undefined) {
40
- this.#connectors.push(next)
41
- next.link(this)
55
+ this.#dependencies.push(next)
56
+ next.link(this)
42
57
 
43
- return next
44
- } else return this
58
+ return next
45
59
  }
46
60
 
61
+ /**
62
+ * Creates a backlink to another Connector
63
+ *
64
+ * See .connect() and .disconnect()
65
+ *
66
+ * @param connector {Connector}
67
+ * @returns {void}
68
+ */
47
69
  link (connector) {
48
- this.#linked.push(connector)
70
+ this.#links.push(connector)
49
71
  }
50
72
 
73
+ /**
74
+ * Connects dependants then self
75
+ *
76
+ * In case of exception disconnects with current connection interruption
77
+ *
78
+ * Method is idempotent
79
+ *
80
+ * @returns {Promise<void>}
81
+ */
51
82
  async connect () {
52
83
  if (this.#connecting) return this.#connecting
53
84
 
54
85
  this.#disconnecting = undefined
55
86
 
87
+ console.debug(`Connecting '${this.id}' with ${this.#dependencies.length} dependencies...`)
88
+
56
89
  this.#connecting = (async () => {
57
- await Promise.all(this.#connectors.map(connector => connector.connect()))
90
+ await Promise.all(this.#dependencies.map((connector) => connector.connect()))
58
91
  await this.connection()
59
92
  })()
60
93
 
@@ -69,12 +102,22 @@ class Connector {
69
102
  console.debug(`Connector '${this.id}' connected`)
70
103
  }
71
104
 
105
+ /**
106
+ * Disconnects self then dependants
107
+ *
108
+ * Does nothing if there are connected linked Connectors
109
+ *
110
+ * Method is idempotent
111
+ *
112
+ * @param [interrupt] {boolean}
113
+ * @returns {Promise<void>}
114
+ */
72
115
  async disconnect (interrupt) {
73
116
  if (interrupt !== true) await this.#connecting
74
117
 
75
118
  if (this.#disconnecting) return this.#disconnecting
76
119
 
77
- const linked = this.#linked.reduce((acc, parent) => acc || parent.connected, false)
120
+ const linked = this.#links.reduce((acc, parent) => acc || parent.connected, false)
78
121
 
79
122
  if (linked && interrupt !== true) return
80
123
 
@@ -83,6 +126,7 @@ class Connector {
83
126
 
84
127
  this.#disconnecting = (async () => {
85
128
  const start = +new Date()
129
+
86
130
  const interval = setInterval(() => {
87
131
  const delay = +new Date() - start
88
132
 
@@ -93,7 +137,8 @@ class Connector {
93
137
 
94
138
  clearInterval(interval)
95
139
 
96
- await Promise.all(this.#connectors.map(connector => connector.disconnect()))
140
+ await Promise.all(this.#dependencies.map(connector => connector.disconnect()))
141
+
97
142
  this.disconnected()
98
143
  })()
99
144
 
@@ -102,18 +147,38 @@ class Connector {
102
147
  console.debug(`Connector '${this.id}' disconnected`)
103
148
  }
104
149
 
150
+ async reconnect () {
151
+ await this.disconnect()
152
+ await this.connect()
153
+ }
154
+
105
155
  debug (node = {}) {
106
156
  node[this.id] = { connected: this.connected }
107
157
 
108
- if (this.#connectors.length > 0) for (const connector of this.#connectors) connector.debug?.(node[this.id])
158
+ if (this.#dependencies.length > 0) for (const connector of this.#dependencies) connector.debug?.(node[this.id])
109
159
 
110
160
  return node
111
161
  }
112
162
 
163
+ /**
164
+ * Called on connection
165
+ *
166
+ * @returns {Promise<void>}
167
+ */
113
168
  async connection () {}
114
169
 
170
+ /**
171
+ * Called on disconnection
172
+ *
173
+ * @returns {Promise<void>}
174
+ */
115
175
  async disconnection () {}
116
176
 
177
+ /**
178
+ * Called after self and dependants disconnection is complete
179
+ *
180
+ * @returns {void}
181
+ */
117
182
  disconnected () {}
118
183
  }
119
184
 
package/src/context.js CHANGED
@@ -2,41 +2,53 @@
2
2
 
3
3
  const { Connector } = require('./connector')
4
4
 
5
+ /**
6
+ * @implements {toa.core.Context}
7
+ */
5
8
  class Context extends Connector {
9
+ aspects
10
+
6
11
  #local
7
12
  #discover
8
13
  #remotes = {}
9
14
 
10
- constructor (local, discover) {
15
+ constructor (local, discover, aspects = []) {
11
16
  super()
12
17
 
18
+ this.aspects = aspects
19
+
13
20
  this.#local = local
14
21
  this.#discover = discover
15
22
 
16
23
  this.depends(local)
24
+
25
+ if (aspects.length > 0) this.depends(aspects)
17
26
  }
18
27
 
19
28
  async apply (endpoint, request) {
20
29
  return this.#local.invoke(endpoint, request)
21
30
  }
22
31
 
23
- async call (domain, name, endpoint, request) {
24
- const remote = await this.#remote(domain, name)
32
+ async call (namespace, name, endpoint, request) {
33
+ const remote = await this.#remote(namespace, name)
25
34
 
26
35
  return remote.invoke(endpoint, request)
27
36
  }
28
37
 
29
- async #remote (domain, name) {
30
- if (this.#remotes[domain] === undefined) this.#remotes[domain] = {}
38
+ async #remote (namespace, name) {
39
+ const key = namespace + '.' + name
40
+
41
+ if (this.#remotes[key] === undefined) this.#remotes[key] = this.#connect(namespace, name)
42
+
43
+ return this.#remotes[key]
44
+ }
31
45
 
32
- if (this.#remotes[domain][name] === undefined) {
33
- const remote = await this.#discover(domain, name)
46
+ async #connect (namespace, name) {
47
+ const remote = await this.#discover(namespace, name)
34
48
 
35
- this.depends(remote)
36
- this.#remotes[domain][name] = remote
37
- }
49
+ this.depends(remote)
38
50
 
39
- return this.#remotes[domain][name]
51
+ return remote
40
52
  }
41
53
  }
42
54
 
@@ -7,8 +7,11 @@ const { ResponseContractException } = require('../exceptions')
7
7
  class Reply extends Conditions {
8
8
  static Exception = ResponseContractException
9
9
 
10
+ /**
11
+ * @returns {toa.schema.JSON}
12
+ */
10
13
  static schema (output, error) {
11
- const schema = { properties: {}, additionalProperties: false }
14
+ const schema = { type: 'object', properties: {}, additionalProperties: false }
12
15
 
13
16
  if (output !== undefined) schema.properties.output = output
14
17
 
@@ -1,6 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const clone = require('clone-deep')
4
+
4
5
  const schemas = require('./schemas')
5
6
  const { RequestContractException } = require('../exceptions')
6
7
  const { Conditions } = require('./conditions')
@@ -8,18 +9,27 @@ const { Conditions } = require('./conditions')
8
9
  class Request extends Conditions {
9
10
  static Exception = RequestContractException
10
11
 
12
+ /**
13
+ * @returns {toa.schema.JSON}
14
+ */
11
15
  static schema (definition) {
12
- const schema = { properties: {}, additionalProperties: false }
16
+ const schema = { type: 'object', properties: {}, additionalProperties: true }
13
17
  const required = []
14
18
 
15
- if (definition.input) {
19
+ if (definition.input !== undefined) {
16
20
  definition.input.additionalProperties = false
17
21
  schema.properties.input = definition.input
18
22
  required.push('input')
23
+ } else {
24
+ schema.properties.input = { type: 'null' }
19
25
  }
20
26
 
21
27
  if (definition.query === true) required.push('query')
22
28
 
29
+ if (definition.query === false) {
30
+ schema.not = { required: ['query'] }
31
+ }
32
+
23
33
  if (definition.query !== false) {
24
34
  const query = clone(schemas.query)
25
35
 
@@ -29,7 +39,7 @@ class Request extends Conditions {
29
39
  delete query.properties.projection
30
40
  }
31
41
 
32
- if (definition.type !== 'observation' || definition.subject !== 'set') {
42
+ if (definition.type !== 'observation' || definition.scope !== 'objects') {
33
43
  delete query.properties.omit
34
44
  delete query.properties.limit
35
45
  } else {
@@ -1,7 +1,8 @@
1
1
  'use strict'
2
2
 
3
3
  const { resolve } = require('path')
4
- const { yaml, freeze } = require('@toa.io/gears')
4
+ const { load } = require('@toa.io/yaml')
5
+ const { freeze } = require('@toa.io/generic')
5
6
 
6
- exports.query = freeze(yaml.sync(resolve(__dirname, './query.yaml')))
7
- exports.error = freeze(yaml.sync(resolve(__dirname, './error.yaml')))
7
+ exports.query = freeze(load.sync(resolve(__dirname, './query.yaml')))
8
+ exports.error = freeze(load.sync(resolve(__dirname, './error.yaml')))
package/src/discovery.js CHANGED
@@ -1,5 +1,6 @@
1
1
  'use strict'
2
2
 
3
+ const { console } = require('@toa.io/console')
3
4
  const { Connector } = require('./connector')
4
5
 
5
6
  class Discovery extends Connector {
@@ -24,10 +25,20 @@ class Discovery extends Connector {
24
25
  this.depends(this.#lookups[id])
25
26
  }
26
27
 
28
+ console.debug(`Sending lookup request to '${id}'`)
29
+
30
+ const warning = () => console.warn(`Waiting for lookup response from '${id}'...`)
31
+ const timeout = setTimeout(warning, TIMEOUT)
32
+
27
33
  const { output } = await this.#lookups[id].invoke()
28
34
 
35
+ console.debug(`Lookup response from '${id}' received`)
36
+ clearTimeout(timeout)
37
+
29
38
  return output
30
39
  }
31
40
  }
32
41
 
42
+ const TIMEOUT = 5000
43
+
33
44
  exports.Discovery = Discovery
@@ -1,9 +1,10 @@
1
1
  'use strict'
2
2
 
3
- const { merge, newid } = require('@toa.io/gears')
3
+ const { merge, overwrite, newid } = require('@toa.io/generic')
4
4
  const { EntityContractException } = require('../exceptions')
5
5
 
6
6
  class Changeset {
7
+ /** @type {toa.core.Query} */
7
8
  query
8
9
 
9
10
  #schema
@@ -21,7 +22,7 @@ class Changeset {
21
22
  }
22
23
 
23
24
  set (value) {
24
- const error = this.#schema.match(value)
25
+ const error = this.#schema.adapt(value)
25
26
 
26
27
  if (error !== null) throw new EntityContractException(error)
27
28
 
@@ -36,7 +37,7 @@ class Changeset {
36
37
 
37
38
  if (error === null) {
38
39
  delete insert.id
39
- result.insert = merge(insert, changeset, { override: true })
40
+ result.insert = overwrite(insert, changeset)
40
41
  }
41
42
 
42
43
  return result
@@ -1,7 +1,7 @@
1
1
  'use strict'
2
2
 
3
3
  const clone = require('clone-deep')
4
- const { difference, newid } = require('@toa.io/gears')
4
+ const { difference, newid } = require('@toa.io/generic')
5
5
 
6
6
  const { EntityContractException } = require('../exceptions')
7
7
 
@@ -14,11 +14,12 @@ class Entity {
14
14
  this.#schema = schema
15
15
 
16
16
  if (typeof argument === 'object') {
17
- this.#state = clone(argument)
17
+ const object = clone(argument)
18
+ this.set(object)
18
19
  this.#origin = argument
19
20
  } else {
20
21
  const id = typeof argument === 'string' ? argument : newid()
21
- this.#state = this.#initial(id)
22
+ this.#init(id)
22
23
  }
23
24
  }
24
25
 
@@ -29,9 +30,9 @@ class Entity {
29
30
  set (value) {
30
31
  const error = this.#schema.fit(value)
31
32
 
32
- if (error) throw new EntityContractException(error)
33
+ if (error !== null) throw new EntityContractException(error)
33
34
 
34
- this.#state = value
35
+ this.#set(value)
35
36
  }
36
37
 
37
38
  event () {
@@ -42,7 +43,17 @@ class Entity {
42
43
  }
43
44
  }
44
45
 
45
- #initial = (id) => this.#schema.defaults({ id })
46
+ #init (id) {
47
+ const value = { ...this.#schema.defaults({ id }), _version: 0 }
48
+
49
+ this.#set(value)
50
+ }
51
+
52
+ #set (value) {
53
+ Object.defineProperty(value, 'id', { writable: false, configurable: false })
54
+
55
+ this.#state = value
56
+ }
46
57
  }
47
58
 
48
59
  exports.Entity = Entity
@@ -15,12 +15,12 @@ class Factory {
15
15
  return new Entity(this.#schema, id)
16
16
  }
17
17
 
18
- entity (record) {
18
+ object (record) {
19
19
  return new Entity(this.#schema, record)
20
20
  }
21
21
 
22
- set (recordset) {
23
- const set = recordset.map((record) => this.entity(record))
22
+ objects (recordset) {
23
+ const set = recordset.map((record) => this.object(record))
24
24
 
25
25
  return new EntitySet(set)
26
26
  }
package/src/event.js CHANGED
@@ -2,21 +2,25 @@
2
2
 
3
3
  const { Connector } = require('./connector')
4
4
 
5
+ /**
6
+ * @implements {toa.core.Event}
7
+ */
5
8
  class Event extends Connector {
6
- #binding
9
+ /** @type {toa.core.bindings.Emitter} */
10
+ #emitter
7
11
  #bridge
8
12
  #conditioned
9
13
  #subjective
10
14
 
11
- constructor (definition, binding, bridge = undefined) {
15
+ constructor (definition, emitter, bridge = undefined) {
12
16
  super()
13
17
 
14
18
  this.#conditioned = definition.conditioned
15
19
  this.#subjective = definition.subjective
16
- this.#binding = binding
20
+ this.#emitter = emitter
17
21
  this.#bridge = bridge
18
22
 
19
- this.depends(binding)
23
+ this.depends(emitter)
20
24
 
21
25
  if (bridge !== undefined) this.depends(bridge)
22
26
  }
@@ -25,7 +29,10 @@ class Event extends Connector {
25
29
  if (this.#conditioned === false || await this.#bridge.condition(event) === true) {
26
30
  const payload = this.#subjective ? await this.#bridge.payload(event) : event.state
27
31
 
28
- await this.#binding.emit(payload)
32
+ /** @type {toa.core.Message} */
33
+ const message = { payload }
34
+
35
+ await this.#emitter.emit(message)
29
36
  }
30
37
  }
31
38
  }
package/src/exceptions.js CHANGED
@@ -22,6 +22,9 @@ const codes = {
22
22
  Transmission: 401
23
23
  }
24
24
 
25
+ /**
26
+ * @implements {toa.core.Exception}
27
+ */
25
28
  class Exception {
26
29
  code
27
30
  message
@@ -71,6 +74,7 @@ class EntityContractException extends ContractException {
71
74
  }
72
75
 
73
76
  // #region exports
77
+ exports.Exception = Exception
74
78
  exports.SystemException = SystemException
75
79
  exports.RequestContractException = RequestContractException
76
80
  exports.ResponseContractException = ResponseContractException
@@ -80,7 +84,11 @@ for (const [name, code] of Object.entries(codes)) {
80
84
  const classname = name + 'Exception'
81
85
 
82
86
  if (exports[classname] === undefined) {
83
- exports[classname] = class extends Exception {constructor (message) { super(code, message) }}
87
+ exports[classname] = class extends Exception {
88
+ constructor (message) {
89
+ super(code, message || classname)
90
+ }
91
+ }
84
92
  }
85
93
  }
86
94